| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975149761497714978149791498014981149821498314984149851498614987149881498914990149911499214993149941499514996149971499814999150001500115002150031500415005150061500715008150091501015011150121501315014150151501615017150181501915020150211502215023150241502515026150271502815029150301503115032150331503415035150361503715038150391504015041150421504315044150451504615047150481504915050150511505215053150541505515056150571505815059150601506115062150631506415065150661506715068150691507015071150721507315074150751507615077150781507915080150811508215083150841508515086150871508815089150901509115092150931509415095150961509715098150991510015101151021510315104151051510615107151081510915110151111511215113151141511515116151171511815119151201512115122151231512415125151261512715128151291513015131151321513315134151351513615137151381513915140151411514215143151441514515146151471514815149151501515115152151531515415155151561515715158151591516015161151621516315164151651516615167151681516915170151711517215173151741517515176151771517815179151801518115182151831518415185151861518715188151891519015191151921519315194151951519615197151981519915200152011520215203152041520515206152071520815209152101521115212152131521415215152161521715218152191522015221152221522315224152251522615227152281522915230152311523215233152341523515236152371523815239152401524115242152431524415245152461524715248152491525015251152521525315254152551525615257152581525915260152611526215263152641526515266152671526815269152701527115272152731527415275152761527715278152791528015281152821528315284152851528615287152881528915290152911529215293152941529515296152971529815299153001530115302153031530415305153061530715308153091531015311153121531315314153151531615317153181531915320153211532215323153241532515326153271532815329153301533115332153331533415335153361533715338153391534015341153421534315344153451534615347153481534915350153511535215353153541535515356153571535815359153601536115362153631536415365153661536715368153691537015371153721537315374153751537615377153781537915380153811538215383153841538515386153871538815389153901539115392153931539415395153961539715398153991540015401154021540315404154051540615407154081540915410154111541215413154141541515416154171541815419154201542115422154231542415425154261542715428154291543015431154321543315434154351543615437154381543915440154411544215443154441544515446154471544815449154501545115452154531545415455154561545715458154591546015461154621546315464154651546615467154681546915470154711547215473154741547515476154771547815479154801548115482154831548415485154861548715488154891549015491154921549315494154951549615497154981549915500155011550215503155041550515506155071550815509155101551115512155131551415515155161551715518155191552015521155221552315524155251552615527155281552915530155311553215533155341553515536155371553815539155401554115542155431554415545155461554715548155491555015551155521555315554155551555615557155581555915560155611556215563155641556515566155671556815569155701557115572155731557415575155761557715578155791558015581155821558315584155851558615587155881558915590155911559215593155941559515596155971559815599156001560115602156031560415605156061560715608156091561015611156121561315614156151561615617156181561915620156211562215623156241562515626156271562815629156301563115632156331563415635156361563715638156391564015641156421564315644156451564615647156481564915650156511565215653156541565515656156571565815659156601566115662156631566415665156661566715668156691567015671156721567315674156751567615677156781567915680156811568215683156841568515686156871568815689156901569115692156931569415695156961569715698156991570015701157021570315704157051570615707157081570915710157111571215713157141571515716157171571815719157201572115722157231572415725157261572715728157291573015731157321573315734157351573615737157381573915740157411574215743157441574515746157471574815749157501575115752157531575415755157561575715758157591576015761157621576315764157651576615767157681576915770157711577215773157741577515776157771577815779157801578115782157831578415785157861578715788157891579015791157921579315794157951579615797157981579915800158011580215803158041580515806158071580815809158101581115812158131581415815158161581715818158191582015821158221582315824158251582615827158281582915830158311583215833158341583515836158371583815839158401584115842158431584415845158461584715848158491585015851158521585315854158551585615857158581585915860158611586215863158641586515866158671586815869158701587115872158731587415875158761587715878158791588015881158821588315884158851588615887158881588915890158911589215893158941589515896158971589815899159001590115902159031590415905159061590715908159091591015911159121591315914159151591615917159181591915920159211592215923159241592515926159271592815929159301593115932159331593415935159361593715938159391594015941159421594315944159451594615947159481594915950159511595215953159541595515956159571595815959159601596115962159631596415965159661596715968159691597015971159721597315974159751597615977159781597915980159811598215983159841598515986159871598815989159901599115992159931599415995159961599715998159991600016001160021600316004160051600616007160081600916010160111601216013160141601516016160171601816019160201602116022160231602416025160261602716028160291603016031160321603316034160351603616037160381603916040160411604216043160441604516046160471604816049160501605116052160531605416055160561605716058160591606016061160621606316064160651606616067160681606916070160711607216073160741607516076160771607816079160801608116082160831608416085160861608716088160891609016091160921609316094160951609616097160981609916100161011610216103161041610516106161071610816109161101611116112161131611416115161161611716118161191612016121161221612316124161251612616127161281612916130161311613216133161341613516136161371613816139161401614116142161431614416145161461614716148161491615016151161521615316154161551615616157161581615916160161611616216163161641616516166161671616816169161701617116172161731617416175161761617716178161791618016181161821618316184161851618616187161881618916190161911619216193161941619516196161971619816199162001620116202162031620416205162061620716208162091621016211162121621316214162151621616217162181621916220162211622216223162241622516226162271622816229162301623116232162331623416235162361623716238162391624016241162421624316244162451624616247162481624916250162511625216253162541625516256162571625816259162601626116262162631626416265162661626716268162691627016271162721627316274162751627616277162781627916280162811628216283162841628516286162871628816289162901629116292162931629416295162961629716298162991630016301163021630316304163051630616307163081630916310163111631216313163141631516316163171631816319163201632116322163231632416325163261632716328163291633016331163321633316334163351633616337163381633916340163411634216343163441634516346163471634816349163501635116352163531635416355163561635716358163591636016361163621636316364163651636616367163681636916370163711637216373163741637516376163771637816379163801638116382163831638416385163861638716388163891639016391163921639316394163951639616397163981639916400164011640216403164041640516406164071640816409164101641116412164131641416415164161641716418164191642016421164221642316424164251642616427164281642916430164311643216433164341643516436164371643816439164401644116442164431644416445164461644716448164491645016451164521645316454164551645616457164581645916460164611646216463164641646516466164671646816469164701647116472164731647416475164761647716478164791648016481164821648316484164851648616487164881648916490164911649216493164941649516496164971649816499165001650116502165031650416505165061650716508165091651016511165121651316514165151651616517165181651916520165211652216523165241652516526165271652816529165301653116532165331653416535165361653716538165391654016541165421654316544165451654616547165481654916550165511655216553165541655516556165571655816559165601656116562165631656416565165661656716568165691657016571165721657316574165751657616577165781657916580165811658216583165841658516586165871658816589165901659116592165931659416595165961659716598165991660016601166021660316604166051660616607166081660916610166111661216613166141661516616166171661816619166201662116622166231662416625166261662716628166291663016631166321663316634166351663616637166381663916640166411664216643166441664516646166471664816649166501665116652166531665416655166561665716658166591666016661166621666316664166651666616667166681666916670166711667216673166741667516676166771667816679166801668116682166831668416685166861668716688166891669016691166921669316694166951669616697166981669916700167011670216703167041670516706167071670816709167101671116712167131671416715167161671716718167191672016721167221672316724167251672616727167281672916730167311673216733167341673516736167371673816739167401674116742167431674416745167461674716748167491675016751167521675316754167551675616757167581675916760167611676216763167641676516766167671676816769167701677116772167731677416775167761677716778167791678016781167821678316784167851678616787167881678916790167911679216793167941679516796167971679816799168001680116802168031680416805168061680716808168091681016811168121681316814168151681616817168181681916820168211682216823168241682516826168271682816829168301683116832168331683416835168361683716838168391684016841168421684316844168451684616847168481684916850168511685216853168541685516856168571685816859168601686116862168631686416865168661686716868168691687016871168721687316874168751687616877168781687916880168811688216883168841688516886168871688816889168901689116892168931689416895168961689716898168991690016901169021690316904169051690616907169081690916910169111691216913169141691516916169171691816919169201692116922169231692416925169261692716928169291693016931169321693316934169351693616937169381693916940169411694216943169441694516946169471694816949169501695116952169531695416955169561695716958169591696016961169621696316964169651696616967169681696916970169711697216973169741697516976169771697816979169801698116982169831698416985169861698716988169891699016991169921699316994169951699616997169981699917000170011700217003170041700517006170071700817009170101701117012170131701417015170161701717018170191702017021170221702317024170251702617027170281702917030170311703217033170341703517036170371703817039170401704117042170431704417045170461704717048170491705017051170521705317054170551705617057170581705917060170611706217063170641706517066170671706817069170701707117072170731707417075170761707717078170791708017081170821708317084170851708617087170881708917090170911709217093170941709517096170971709817099171001710117102171031710417105171061710717108171091711017111171121711317114171151711617117171181711917120171211712217123171241712517126171271712817129171301713117132171331713417135171361713717138171391714017141171421714317144171451714617147171481714917150171511715217153171541715517156171571715817159171601716117162171631716417165171661716717168171691717017171171721717317174171751717617177171781717917180171811718217183171841718517186171871718817189171901719117192171931719417195171961719717198171991720017201172021720317204172051720617207172081720917210172111721217213172141721517216172171721817219172201722117222172231722417225172261722717228172291723017231172321723317234172351723617237172381723917240172411724217243172441724517246172471724817249172501725117252172531725417255172561725717258172591726017261172621726317264172651726617267172681726917270172711727217273172741727517276172771727817279172801728117282172831728417285172861728717288172891729017291172921729317294172951729617297172981729917300173011730217303173041730517306173071730817309173101731117312173131731417315173161731717318173191732017321173221732317324173251732617327173281732917330173311733217333173341733517336173371733817339173401734117342173431734417345173461734717348173491735017351173521735317354173551735617357173581735917360173611736217363173641736517366173671736817369173701737117372173731737417375173761737717378173791738017381173821738317384173851738617387173881738917390173911739217393173941739517396173971739817399174001740117402174031740417405174061740717408174091741017411174121741317414174151741617417174181741917420174211742217423174241742517426174271742817429174301743117432174331743417435174361743717438174391744017441174421744317444174451744617447174481744917450174511745217453174541745517456174571745817459174601746117462174631746417465174661746717468174691747017471174721747317474174751747617477174781747917480174811748217483174841748517486174871748817489174901749117492174931749417495174961749717498174991750017501175021750317504175051750617507175081750917510175111751217513175141751517516175171751817519175201752117522175231752417525175261752717528175291753017531175321753317534175351753617537175381753917540175411754217543175441754517546175471754817549175501755117552175531755417555175561755717558175591756017561175621756317564175651756617567175681756917570175711757217573175741757517576175771757817579175801758117582175831758417585175861758717588175891759017591175921759317594175951759617597175981759917600176011760217603176041760517606176071760817609176101761117612176131761417615176161761717618176191762017621176221762317624176251762617627176281762917630176311763217633176341763517636176371763817639176401764117642176431764417645176461764717648176491765017651176521765317654176551765617657176581765917660176611766217663176641766517666176671766817669176701767117672176731767417675176761767717678176791768017681176821768317684176851768617687176881768917690176911769217693176941769517696176971769817699177001770117702177031770417705177061770717708177091771017711177121771317714177151771617717177181771917720177211772217723177241772517726177271772817729177301773117732177331773417735177361773717738177391774017741177421774317744177451774617747177481774917750177511775217753177541775517756177571775817759177601776117762177631776417765177661776717768177691777017771177721777317774177751777617777177781777917780177811778217783177841778517786177871778817789177901779117792177931779417795177961779717798177991780017801178021780317804178051780617807178081780917810178111781217813178141781517816178171781817819178201782117822178231782417825178261782717828178291783017831178321783317834178351783617837178381783917840178411784217843178441784517846178471784817849178501785117852178531785417855178561785717858178591786017861178621786317864178651786617867178681786917870178711787217873178741787517876178771787817879178801788117882178831788417885178861788717888178891789017891178921789317894178951789617897178981789917900179011790217903179041790517906179071790817909179101791117912179131791417915179161791717918179191792017921179221792317924179251792617927179281792917930179311793217933179341793517936179371793817939179401794117942179431794417945179461794717948179491795017951179521795317954179551795617957179581795917960179611796217963179641796517966179671796817969179701797117972179731797417975179761797717978179791798017981179821798317984179851798617987179881798917990179911799217993179941799517996179971799817999180001800118002180031800418005180061800718008180091801018011180121801318014180151801618017180181801918020180211802218023180241802518026180271802818029180301803118032180331803418035180361803718038180391804018041180421804318044180451804618047180481804918050180511805218053180541805518056180571805818059180601806118062180631806418065180661806718068180691807018071180721807318074180751807618077180781807918080180811808218083180841808518086180871808818089180901809118092180931809418095180961809718098180991810018101181021810318104181051810618107181081810918110181111811218113181141811518116181171811818119181201812118122181231812418125181261812718128181291813018131181321813318134181351813618137181381813918140181411814218143181441814518146181471814818149181501815118152181531815418155181561815718158181591816018161181621816318164181651816618167181681816918170181711817218173181741817518176181771817818179181801818118182181831818418185181861818718188181891819018191181921819318194181951819618197181981819918200182011820218203182041820518206182071820818209182101821118212182131821418215182161821718218182191822018221182221822318224182251822618227182281822918230182311823218233182341823518236182371823818239182401824118242182431824418245182461824718248182491825018251182521825318254182551825618257182581825918260182611826218263182641826518266182671826818269182701827118272182731827418275182761827718278182791828018281182821828318284182851828618287182881828918290182911829218293182941829518296182971829818299183001830118302183031830418305183061830718308183091831018311183121831318314183151831618317183181831918320183211832218323183241832518326183271832818329183301833118332183331833418335183361833718338183391834018341183421834318344183451834618347183481834918350183511835218353183541835518356183571835818359183601836118362183631836418365183661836718368183691837018371183721837318374183751837618377183781837918380183811838218383183841838518386183871838818389183901839118392183931839418395183961839718398183991840018401184021840318404184051840618407184081840918410184111841218413184141841518416184171841818419184201842118422184231842418425184261842718428184291843018431184321843318434184351843618437184381843918440184411844218443184441844518446184471844818449184501845118452184531845418455184561845718458184591846018461184621846318464184651846618467184681846918470184711847218473184741847518476184771847818479184801848118482184831848418485184861848718488184891849018491184921849318494184951849618497184981849918500185011850218503185041850518506185071850818509185101851118512185131851418515185161851718518185191852018521185221852318524185251852618527185281852918530185311853218533185341853518536185371853818539185401854118542185431854418545185461854718548185491855018551185521855318554185551855618557185581855918560185611856218563185641856518566185671856818569185701857118572185731857418575185761857718578185791858018581185821858318584185851858618587185881858918590185911859218593185941859518596185971859818599186001860118602186031860418605186061860718608186091861018611186121861318614186151861618617186181861918620186211862218623186241862518626186271862818629186301863118632186331863418635186361863718638186391864018641186421864318644186451864618647186481864918650186511865218653186541865518656186571865818659186601866118662186631866418665186661866718668186691867018671186721867318674186751867618677186781867918680186811868218683186841868518686186871868818689186901869118692186931869418695186961869718698186991870018701187021870318704187051870618707187081870918710187111871218713187141871518716187171871818719187201872118722187231872418725187261872718728187291873018731187321873318734187351873618737187381873918740187411874218743187441874518746187471874818749187501875118752187531875418755187561875718758187591876018761187621876318764187651876618767187681876918770187711877218773187741877518776187771877818779187801878118782187831878418785187861878718788187891879018791187921879318794187951879618797187981879918800188011880218803188041880518806188071880818809188101881118812188131881418815188161881718818188191882018821188221882318824188251882618827188281882918830188311883218833188341883518836188371883818839188401884118842188431884418845188461884718848188491885018851188521885318854188551885618857188581885918860188611886218863188641886518866188671886818869188701887118872188731887418875188761887718878188791888018881188821888318884188851888618887188881888918890188911889218893188941889518896188971889818899189001890118902189031890418905189061890718908189091891018911189121891318914189151891618917189181891918920189211892218923189241892518926189271892818929189301893118932189331893418935189361893718938189391894018941189421894318944189451894618947189481894918950189511895218953189541895518956189571895818959189601896118962189631896418965189661896718968189691897018971189721897318974189751897618977189781897918980189811898218983189841898518986189871898818989189901899118992189931899418995189961899718998189991900019001190021900319004190051900619007190081900919010190111901219013190141901519016190171901819019190201902119022190231902419025190261902719028190291903019031190321903319034190351903619037190381903919040190411904219043190441904519046190471904819049190501905119052190531905419055190561905719058190591906019061190621906319064190651906619067190681906919070190711907219073190741907519076190771907819079190801908119082190831908419085190861908719088190891909019091190921909319094190951909619097190981909919100191011910219103191041910519106191071910819109191101911119112191131911419115191161911719118191191912019121191221912319124191251912619127191281912919130191311913219133191341913519136191371913819139191401914119142191431914419145191461914719148191491915019151191521915319154191551915619157191581915919160191611916219163191641916519166191671916819169191701917119172191731917419175191761917719178191791918019181191821918319184191851918619187191881918919190191911919219193191941919519196191971919819199192001920119202192031920419205192061920719208192091921019211192121921319214192151921619217192181921919220192211922219223192241922519226192271922819229192301923119232192331923419235192361923719238192391924019241192421924319244192451924619247192481924919250192511925219253192541925519256192571925819259192601926119262192631926419265192661926719268192691927019271192721927319274192751927619277192781927919280192811928219283192841928519286192871928819289192901929119292192931929419295192961929719298192991930019301193021930319304193051930619307193081930919310193111931219313193141931519316193171931819319193201932119322193231932419325193261932719328193291933019331193321933319334193351933619337193381933919340193411934219343193441934519346193471934819349193501935119352193531935419355193561935719358193591936019361193621936319364193651936619367193681936919370193711937219373193741937519376193771937819379193801938119382193831938419385193861938719388193891939019391193921939319394193951939619397193981939919400194011940219403194041940519406194071940819409194101941119412194131941419415194161941719418194191942019421194221942319424194251942619427194281942919430194311943219433194341943519436194371943819439194401944119442194431944419445194461944719448194491945019451194521945319454194551945619457194581945919460194611946219463194641946519466194671946819469194701947119472194731947419475194761947719478194791948019481194821948319484194851948619487194881948919490194911949219493194941949519496194971949819499195001950119502195031950419505195061950719508195091951019511195121951319514195151951619517195181951919520195211952219523195241952519526195271952819529195301953119532195331953419535195361953719538195391954019541195421954319544195451954619547195481954919550195511955219553195541955519556195571955819559195601956119562195631956419565195661956719568195691957019571195721957319574195751957619577195781957919580195811958219583195841958519586195871958819589195901959119592195931959419595195961959719598195991960019601196021960319604196051960619607196081960919610196111961219613196141961519616196171961819619196201962119622196231962419625196261962719628196291963019631196321963319634196351963619637196381963919640196411964219643196441964519646196471964819649196501965119652196531965419655196561965719658196591966019661196621966319664196651966619667196681966919670196711967219673196741967519676196771967819679196801968119682196831968419685196861968719688196891969019691196921969319694196951969619697196981969919700197011970219703197041970519706197071970819709197101971119712197131971419715197161971719718197191972019721197221972319724197251972619727197281972919730197311973219733197341973519736197371973819739197401974119742197431974419745197461974719748197491975019751197521975319754197551975619757197581975919760197611976219763197641976519766197671976819769197701977119772197731977419775197761977719778197791978019781197821978319784197851978619787197881978919790197911979219793197941979519796197971979819799198001980119802198031980419805198061980719808198091981019811198121981319814198151981619817198181981919820198211982219823198241982519826198271982819829198301983119832198331983419835198361983719838198391984019841198421984319844198451984619847198481984919850198511985219853198541985519856198571985819859198601986119862198631986419865198661986719868198691987019871198721987319874198751987619877198781987919880198811988219883198841988519886198871988819889198901989119892198931989419895198961989719898198991990019901199021990319904199051990619907199081990919910199111991219913199141991519916199171991819919199201992119922199231992419925199261992719928199291993019931199321993319934199351993619937199381993919940199411994219943199441994519946199471994819949199501995119952199531995419955199561995719958199591996019961199621996319964199651996619967199681996919970199711997219973199741997519976199771997819979199801998119982199831998419985199861998719988199891999019991199921999319994199951999619997199981999920000200012000220003200042000520006200072000820009200102001120012200132001420015200162001720018200192002020021200222002320024200252002620027200282002920030200312003220033200342003520036200372003820039200402004120042200432004420045200462004720048200492005020051200522005320054200552005620057200582005920060200612006220063200642006520066200672006820069200702007120072200732007420075200762007720078200792008020081200822008320084200852008620087200882008920090200912009220093200942009520096200972009820099201002010120102201032010420105201062010720108201092011020111201122011320114201152011620117201182011920120201212012220123201242012520126201272012820129201302013120132201332013420135201362013720138201392014020141201422014320144201452014620147201482014920150201512015220153201542015520156201572015820159201602016120162201632016420165201662016720168201692017020171201722017320174201752017620177201782017920180201812018220183201842018520186201872018820189201902019120192201932019420195201962019720198201992020020201202022020320204202052020620207202082020920210202112021220213202142021520216202172021820219202202022120222202232022420225202262022720228202292023020231202322023320234202352023620237202382023920240202412024220243202442024520246202472024820249202502025120252202532025420255202562025720258202592026020261202622026320264202652026620267202682026920270202712027220273202742027520276202772027820279202802028120282202832028420285202862028720288202892029020291202922029320294202952029620297202982029920300203012030220303203042030520306203072030820309203102031120312203132031420315203162031720318203192032020321203222032320324203252032620327203282032920330203312033220333203342033520336203372033820339203402034120342203432034420345203462034720348203492035020351203522035320354203552035620357203582035920360203612036220363203642036520366203672036820369203702037120372203732037420375203762037720378203792038020381203822038320384203852038620387203882038920390203912039220393203942039520396203972039820399204002040120402204032040420405204062040720408204092041020411204122041320414204152041620417204182041920420204212042220423204242042520426204272042820429204302043120432204332043420435204362043720438204392044020441204422044320444204452044620447204482044920450204512045220453204542045520456204572045820459204602046120462204632046420465204662046720468204692047020471204722047320474204752047620477204782047920480204812048220483204842048520486204872048820489204902049120492204932049420495204962049720498204992050020501205022050320504205052050620507205082050920510205112051220513205142051520516205172051820519205202052120522205232052420525205262052720528205292053020531205322053320534205352053620537205382053920540205412054220543205442054520546205472054820549205502055120552205532055420555205562055720558205592056020561205622056320564205652056620567205682056920570205712057220573205742057520576205772057820579205802058120582205832058420585205862058720588205892059020591205922059320594205952059620597205982059920600206012060220603206042060520606206072060820609206102061120612206132061420615206162061720618206192062020621206222062320624206252062620627206282062920630206312063220633206342063520636206372063820639206402064120642206432064420645206462064720648206492065020651206522065320654206552065620657206582065920660206612066220663206642066520666206672066820669206702067120672206732067420675206762067720678206792068020681206822068320684206852068620687206882068920690206912069220693206942069520696206972069820699207002070120702207032070420705207062070720708207092071020711207122071320714207152071620717207182071920720207212072220723207242072520726207272072820729207302073120732207332073420735207362073720738207392074020741207422074320744207452074620747207482074920750207512075220753207542075520756207572075820759207602076120762207632076420765207662076720768207692077020771207722077320774207752077620777207782077920780207812078220783207842078520786207872078820789207902079120792207932079420795207962079720798207992080020801208022080320804208052080620807208082080920810208112081220813208142081520816208172081820819208202082120822208232082420825208262082720828208292083020831208322083320834208352083620837208382083920840208412084220843208442084520846208472084820849208502085120852208532085420855208562085720858208592086020861208622086320864208652086620867208682086920870208712087220873208742087520876208772087820879208802088120882208832088420885208862088720888208892089020891208922089320894208952089620897208982089920900209012090220903209042090520906209072090820909209102091120912209132091420915209162091720918209192092020921209222092320924209252092620927209282092920930209312093220933209342093520936209372093820939209402094120942209432094420945209462094720948209492095020951209522095320954209552095620957209582095920960209612096220963209642096520966209672096820969209702097120972209732097420975209762097720978209792098020981209822098320984209852098620987209882098920990209912099220993209942099520996209972099820999210002100121002210032100421005210062100721008210092101021011210122101321014210152101621017210182101921020210212102221023210242102521026210272102821029210302103121032210332103421035210362103721038210392104021041210422104321044210452104621047210482104921050210512105221053210542105521056210572105821059210602106121062210632106421065210662106721068210692107021071210722107321074210752107621077210782107921080210812108221083210842108521086210872108821089210902109121092210932109421095210962109721098210992110021101211022110321104211052110621107211082110921110211112111221113211142111521116211172111821119211202112121122211232112421125211262112721128211292113021131211322113321134211352113621137211382113921140211412114221143211442114521146211472114821149211502115121152211532115421155211562115721158211592116021161211622116321164211652116621167211682116921170211712117221173211742117521176211772117821179211802118121182211832118421185211862118721188211892119021191211922119321194211952119621197211982119921200212012120221203212042120521206212072120821209212102121121212212132121421215212162121721218212192122021221212222122321224212252122621227212282122921230212312123221233212342123521236212372123821239212402124121242212432124421245212462124721248212492125021251212522125321254212552125621257212582125921260212612126221263212642126521266212672126821269212702127121272212732127421275212762127721278212792128021281212822128321284212852128621287212882128921290212912129221293212942129521296212972129821299213002130121302213032130421305213062130721308213092131021311213122131321314213152131621317213182131921320213212132221323213242132521326213272132821329213302133121332213332133421335213362133721338213392134021341213422134321344213452134621347213482134921350213512135221353213542135521356213572135821359213602136121362213632136421365213662136721368213692137021371213722137321374213752137621377213782137921380213812138221383213842138521386213872138821389213902139121392213932139421395213962139721398213992140021401214022140321404214052140621407214082140921410214112141221413214142141521416214172141821419214202142121422214232142421425214262142721428214292143021431214322143321434214352143621437214382143921440214412144221443214442144521446214472144821449214502145121452214532145421455214562145721458214592146021461214622146321464214652146621467214682146921470214712147221473214742147521476214772147821479214802148121482214832148421485214862148721488214892149021491214922149321494214952149621497214982149921500215012150221503215042150521506215072150821509215102151121512215132151421515215162151721518215192152021521215222152321524215252152621527215282152921530215312153221533215342153521536215372153821539215402154121542215432154421545215462154721548215492155021551215522155321554215552155621557215582155921560215612156221563215642156521566215672156821569215702157121572215732157421575215762157721578215792158021581215822158321584215852158621587215882158921590215912159221593215942159521596215972159821599216002160121602216032160421605216062160721608216092161021611216122161321614216152161621617216182161921620216212162221623216242162521626216272162821629216302163121632216332163421635216362163721638216392164021641216422164321644216452164621647216482164921650216512165221653216542165521656216572165821659216602166121662216632166421665216662166721668216692167021671216722167321674216752167621677216782167921680216812168221683216842168521686216872168821689216902169121692216932169421695216962169721698216992170021701217022170321704217052170621707217082170921710217112171221713217142171521716217172171821719217202172121722217232172421725217262172721728217292173021731217322173321734217352173621737217382173921740217412174221743217442174521746217472174821749217502175121752217532175421755217562175721758217592176021761217622176321764217652176621767217682176921770217712177221773217742177521776217772177821779217802178121782217832178421785217862178721788217892179021791217922179321794217952179621797217982179921800218012180221803218042180521806218072180821809218102181121812218132181421815218162181721818218192182021821218222182321824218252182621827218282182921830218312183221833218342183521836218372183821839218402184121842218432184421845218462184721848218492185021851218522185321854218552185621857218582185921860218612186221863218642186521866218672186821869218702187121872218732187421875218762187721878218792188021881218822188321884218852188621887218882188921890218912189221893218942189521896218972189821899219002190121902219032190421905219062190721908219092191021911219122191321914219152191621917219182191921920219212192221923219242192521926219272192821929219302193121932219332193421935219362193721938219392194021941219422194321944219452194621947219482194921950219512195221953219542195521956219572195821959219602196121962219632196421965219662196721968219692197021971219722197321974219752197621977219782197921980219812198221983219842198521986219872198821989219902199121992219932199421995219962199721998219992200022001220022200322004220052200622007220082200922010220112201222013220142201522016220172201822019220202202122022220232202422025220262202722028220292203022031220322203322034220352203622037220382203922040220412204222043220442204522046220472204822049220502205122052220532205422055220562205722058220592206022061220622206322064220652206622067220682206922070220712207222073220742207522076220772207822079220802208122082220832208422085220862208722088220892209022091220922209322094220952209622097220982209922100221012210222103221042210522106221072210822109221102211122112221132211422115221162211722118221192212022121221222212322124221252212622127221282212922130221312213222133221342213522136221372213822139221402214122142221432214422145221462214722148221492215022151221522215322154221552215622157221582215922160221612216222163221642216522166221672216822169221702217122172221732217422175221762217722178221792218022181221822218322184221852218622187221882218922190221912219222193221942219522196221972219822199222002220122202222032220422205222062220722208222092221022211222122221322214222152221622217222182221922220222212222222223222242222522226222272222822229222302223122232222332223422235222362223722238222392224022241222422224322244222452224622247222482224922250222512225222253222542225522256222572225822259222602226122262222632226422265222662226722268222692227022271222722227322274222752227622277222782227922280222812228222283222842228522286222872228822289222902229122292222932229422295222962229722298222992230022301223022230322304223052230622307223082230922310223112231222313223142231522316223172231822319223202232122322223232232422325223262232722328223292233022331223322233322334223352233622337223382233922340223412234222343223442234522346223472234822349223502235122352223532235422355223562235722358223592236022361223622236322364223652236622367223682236922370223712237222373223742237522376223772237822379223802238122382223832238422385223862238722388223892239022391223922239322394223952239622397223982239922400224012240222403224042240522406224072240822409224102241122412224132241422415224162241722418224192242022421224222242322424224252242622427224282242922430224312243222433224342243522436224372243822439224402244122442224432244422445224462244722448224492245022451224522245322454224552245622457224582245922460224612246222463224642246522466224672246822469224702247122472224732247422475224762247722478224792248022481224822248322484224852248622487224882248922490224912249222493224942249522496224972249822499225002250122502225032250422505225062250722508225092251022511225122251322514225152251622517225182251922520225212252222523225242252522526225272252822529225302253122532225332253422535225362253722538225392254022541225422254322544225452254622547225482254922550225512255222553225542255522556225572255822559225602256122562225632256422565225662256722568225692257022571225722257322574225752257622577225782257922580225812258222583225842258522586225872258822589225902259122592225932259422595225962259722598225992260022601226022260322604226052260622607226082260922610226112261222613226142261522616226172261822619226202262122622226232262422625226262262722628226292263022631226322263322634226352263622637226382263922640226412264222643226442264522646226472264822649226502265122652226532265422655226562265722658226592266022661226622266322664226652266622667226682266922670226712267222673226742267522676226772267822679226802268122682226832268422685226862268722688226892269022691226922269322694226952269622697226982269922700227012270222703227042270522706227072270822709227102271122712227132271422715227162271722718227192272022721227222272322724227252272622727227282272922730227312273222733227342273522736227372273822739227402274122742227432274422745227462274722748227492275022751227522275322754227552275622757227582275922760227612276222763227642276522766227672276822769227702277122772227732277422775227762277722778227792278022781227822278322784227852278622787227882278922790227912279222793227942279522796227972279822799228002280122802228032280422805228062280722808228092281022811228122281322814228152281622817228182281922820228212282222823228242282522826228272282822829228302283122832228332283422835228362283722838228392284022841228422284322844228452284622847228482284922850228512285222853228542285522856228572285822859228602286122862228632286422865228662286722868228692287022871228722287322874228752287622877228782287922880228812288222883228842288522886228872288822889228902289122892228932289422895228962289722898228992290022901229022290322904229052290622907229082290922910229112291222913229142291522916229172291822919229202292122922229232292422925229262292722928229292293022931229322293322934229352293622937229382293922940229412294222943229442294522946229472294822949229502295122952229532295422955229562295722958229592296022961229622296322964229652296622967229682296922970229712297222973229742297522976229772297822979229802298122982229832298422985229862298722988229892299022991229922299322994229952299622997229982299923000230012300223003230042300523006230072300823009230102301123012230132301423015230162301723018230192302023021230222302323024230252302623027230282302923030230312303223033230342303523036230372303823039230402304123042230432304423045230462304723048230492305023051230522305323054230552305623057230582305923060230612306223063230642306523066230672306823069230702307123072230732307423075230762307723078230792308023081230822308323084230852308623087230882308923090230912309223093230942309523096230972309823099231002310123102231032310423105231062310723108231092311023111231122311323114231152311623117231182311923120231212312223123231242312523126231272312823129231302313123132231332313423135231362313723138231392314023141231422314323144231452314623147231482314923150231512315223153231542315523156231572315823159231602316123162231632316423165231662316723168231692317023171231722317323174231752317623177231782317923180231812318223183231842318523186231872318823189231902319123192231932319423195231962319723198231992320023201232022320323204232052320623207232082320923210232112321223213232142321523216232172321823219232202322123222232232322423225232262322723228232292323023231232322323323234232352323623237232382323923240232412324223243232442324523246232472324823249232502325123252232532325423255232562325723258232592326023261232622326323264232652326623267232682326923270232712327223273232742327523276232772327823279232802328123282232832328423285232862328723288232892329023291232922329323294232952329623297232982329923300233012330223303233042330523306233072330823309233102331123312233132331423315233162331723318233192332023321233222332323324233252332623327233282332923330233312333223333233342333523336233372333823339233402334123342233432334423345233462334723348233492335023351233522335323354233552335623357233582335923360233612336223363233642336523366233672336823369233702337123372233732337423375233762337723378233792338023381233822338323384233852338623387233882338923390233912339223393233942339523396233972339823399234002340123402234032340423405234062340723408234092341023411234122341323414234152341623417234182341923420234212342223423234242342523426234272342823429234302343123432234332343423435234362343723438234392344023441234422344323444234452344623447234482344923450234512345223453234542345523456234572345823459234602346123462234632346423465234662346723468234692347023471234722347323474234752347623477234782347923480234812348223483234842348523486234872348823489234902349123492234932349423495234962349723498234992350023501235022350323504235052350623507235082350923510235112351223513235142351523516235172351823519235202352123522235232352423525235262352723528235292353023531235322353323534235352353623537235382353923540235412354223543235442354523546235472354823549235502355123552235532355423555235562355723558235592356023561235622356323564235652356623567235682356923570235712357223573235742357523576235772357823579235802358123582235832358423585235862358723588235892359023591235922359323594235952359623597235982359923600236012360223603236042360523606236072360823609236102361123612236132361423615236162361723618236192362023621236222362323624236252362623627236282362923630236312363223633236342363523636236372363823639236402364123642236432364423645236462364723648236492365023651236522365323654236552365623657236582365923660236612366223663236642366523666236672366823669236702367123672236732367423675236762367723678236792368023681236822368323684236852368623687236882368923690236912369223693236942369523696236972369823699237002370123702237032370423705237062370723708237092371023711237122371323714237152371623717237182371923720237212372223723237242372523726237272372823729237302373123732237332373423735237362373723738237392374023741237422374323744237452374623747237482374923750237512375223753237542375523756237572375823759237602376123762237632376423765237662376723768237692377023771237722377323774237752377623777237782377923780237812378223783237842378523786237872378823789237902379123792237932379423795237962379723798237992380023801238022380323804238052380623807238082380923810238112381223813238142381523816238172381823819238202382123822238232382423825238262382723828238292383023831238322383323834238352383623837238382383923840238412384223843238442384523846238472384823849238502385123852238532385423855238562385723858238592386023861238622386323864238652386623867238682386923870238712387223873238742387523876238772387823879238802388123882238832388423885238862388723888238892389023891238922389323894238952389623897238982389923900239012390223903239042390523906239072390823909239102391123912239132391423915239162391723918239192392023921239222392323924239252392623927239282392923930239312393223933239342393523936239372393823939239402394123942239432394423945239462394723948239492395023951239522395323954239552395623957239582395923960239612396223963239642396523966239672396823969239702397123972239732397423975239762397723978239792398023981239822398323984239852398623987239882398923990239912399223993239942399523996239972399823999240002400124002240032400424005240062400724008240092401024011240122401324014240152401624017240182401924020240212402224023240242402524026240272402824029240302403124032240332403424035240362403724038240392404024041240422404324044240452404624047240482404924050240512405224053240542405524056240572405824059240602406124062240632406424065240662406724068240692407024071240722407324074240752407624077240782407924080240812408224083240842408524086240872408824089240902409124092240932409424095240962409724098240992410024101241022410324104241052410624107241082410924110241112411224113241142411524116241172411824119241202412124122241232412424125241262412724128241292413024131241322413324134241352413624137241382413924140241412414224143241442414524146241472414824149241502415124152241532415424155241562415724158241592416024161241622416324164241652416624167241682416924170241712417224173241742417524176241772417824179241802418124182241832418424185241862418724188241892419024191241922419324194241952419624197241982419924200242012420224203242042420524206242072420824209242102421124212242132421424215242162421724218242192422024221242222422324224242252422624227242282422924230242312423224233242342423524236242372423824239242402424124242242432424424245242462424724248242492425024251242522425324254242552425624257242582425924260242612426224263242642426524266242672426824269242702427124272242732427424275242762427724278242792428024281242822428324284242852428624287242882428924290242912429224293242942429524296242972429824299243002430124302243032430424305243062430724308243092431024311243122431324314243152431624317243182431924320243212432224323243242432524326243272432824329243302433124332243332433424335243362433724338243392434024341243422434324344243452434624347243482434924350243512435224353243542435524356243572435824359243602436124362243632436424365243662436724368243692437024371243722437324374243752437624377243782437924380243812438224383243842438524386243872438824389243902439124392243932439424395243962439724398243992440024401244022440324404244052440624407244082440924410244112441224413244142441524416244172441824419244202442124422244232442424425244262442724428244292443024431244322443324434244352443624437244382443924440244412444224443244442444524446244472444824449244502445124452244532445424455244562445724458244592446024461244622446324464244652446624467244682446924470244712447224473244742447524476244772447824479244802448124482244832448424485244862448724488244892449024491244922449324494244952449624497244982449924500245012450224503245042450524506245072450824509245102451124512245132451424515245162451724518245192452024521245222452324524245252452624527245282452924530245312453224533245342453524536245372453824539245402454124542245432454424545245462454724548245492455024551245522455324554245552455624557245582455924560245612456224563245642456524566245672456824569245702457124572245732457424575245762457724578245792458024581245822458324584245852458624587245882458924590245912459224593245942459524596245972459824599246002460124602246032460424605246062460724608246092461024611246122461324614246152461624617246182461924620246212462224623246242462524626246272462824629246302463124632246332463424635246362463724638246392464024641246422464324644246452464624647246482464924650246512465224653246542465524656246572465824659246602466124662246632466424665246662466724668246692467024671246722467324674246752467624677246782467924680246812468224683246842468524686246872468824689246902469124692246932469424695246962469724698246992470024701247022470324704247052470624707247082470924710247112471224713247142471524716247172471824719247202472124722247232472424725247262472724728247292473024731247322473324734247352473624737247382473924740247412474224743247442474524746247472474824749247502475124752247532475424755247562475724758247592476024761247622476324764247652476624767247682476924770247712477224773247742477524776247772477824779247802478124782247832478424785247862478724788247892479024791247922479324794247952479624797247982479924800248012480224803248042480524806248072480824809248102481124812248132481424815248162481724818248192482024821248222482324824248252482624827248282482924830248312483224833248342483524836248372483824839248402484124842248432484424845248462484724848248492485024851248522485324854248552485624857248582485924860248612486224863248642486524866248672486824869248702487124872248732487424875248762487724878248792488024881248822488324884248852488624887248882488924890248912489224893248942489524896248972489824899249002490124902249032490424905249062490724908249092491024911249122491324914249152491624917249182491924920249212492224923249242492524926249272492824929249302493124932249332493424935249362493724938249392494024941249422494324944249452494624947249482494924950249512495224953249542495524956249572495824959249602496124962249632496424965249662496724968249692497024971249722497324974249752497624977249782497924980249812498224983249842498524986249872498824989249902499124992249932499424995249962499724998249992500025001250022500325004250052500625007250082500925010250112501225013250142501525016250172501825019250202502125022250232502425025250262502725028250292503025031250322503325034250352503625037250382503925040250412504225043250442504525046250472504825049250502505125052250532505425055250562505725058250592506025061250622506325064250652506625067250682506925070250712507225073250742507525076250772507825079250802508125082250832508425085250862508725088250892509025091250922509325094250952509625097250982509925100251012510225103251042510525106251072510825109251102511125112251132511425115251162511725118251192512025121251222512325124251252512625127251282512925130251312513225133251342513525136251372513825139251402514125142251432514425145251462514725148251492515025151251522515325154251552515625157251582515925160251612516225163251642516525166251672516825169251702517125172251732517425175251762517725178251792518025181251822518325184251852518625187251882518925190251912519225193251942519525196251972519825199252002520125202252032520425205252062520725208252092521025211252122521325214252152521625217252182521925220252212522225223252242522525226252272522825229252302523125232252332523425235252362523725238252392524025241252422524325244252452524625247252482524925250252512525225253252542525525256252572525825259252602526125262252632526425265252662526725268252692527025271252722527325274252752527625277252782527925280252812528225283252842528525286252872528825289252902529125292252932529425295252962529725298252992530025301253022530325304253052530625307253082530925310253112531225313253142531525316253172531825319253202532125322253232532425325253262532725328253292533025331253322533325334253352533625337253382533925340253412534225343253442534525346253472534825349253502535125352253532535425355253562535725358253592536025361253622536325364253652536625367253682536925370253712537225373253742537525376253772537825379253802538125382253832538425385253862538725388253892539025391253922539325394253952539625397253982539925400254012540225403254042540525406254072540825409254102541125412254132541425415254162541725418254192542025421254222542325424254252542625427254282542925430254312543225433254342543525436254372543825439254402544125442254432544425445254462544725448254492545025451254522545325454254552545625457254582545925460254612546225463254642546525466254672546825469254702547125472254732547425475254762547725478254792548025481254822548325484254852548625487254882548925490254912549225493254942549525496254972549825499255002550125502255032550425505255062550725508255092551025511255122551325514255152551625517255182551925520255212552225523255242552525526255272552825529255302553125532255332553425535255362553725538255392554025541255422554325544255452554625547255482554925550255512555225553255542555525556255572555825559255602556125562255632556425565255662556725568255692557025571255722557325574255752557625577255782557925580255812558225583255842558525586255872558825589255902559125592255932559425595255962559725598255992560025601256022560325604256052560625607256082560925610256112561225613256142561525616256172561825619256202562125622256232562425625256262562725628256292563025631256322563325634256352563625637256382563925640256412564225643256442564525646256472564825649256502565125652256532565425655256562565725658256592566025661256622566325664256652566625667256682566925670256712567225673256742567525676256772567825679256802568125682256832568425685256862568725688256892569025691256922569325694256952569625697256982569925700257012570225703257042570525706257072570825709257102571125712257132571425715257162571725718257192572025721257222572325724257252572625727257282572925730257312573225733257342573525736257372573825739257402574125742257432574425745257462574725748257492575025751257522575325754257552575625757257582575925760257612576225763257642576525766257672576825769257702577125772257732577425775257762577725778257792578025781257822578325784257852578625787257882578925790257912579225793257942579525796257972579825799258002580125802258032580425805258062580725808258092581025811258122581325814258152581625817258182581925820258212582225823258242582525826258272582825829258302583125832258332583425835258362583725838258392584025841258422584325844258452584625847258482584925850258512585225853258542585525856258572585825859258602586125862258632586425865258662586725868258692587025871258722587325874258752587625877258782587925880258812588225883258842588525886258872588825889258902589125892258932589425895258962589725898258992590025901259022590325904259052590625907259082590925910259112591225913259142591525916259172591825919259202592125922259232592425925259262592725928259292593025931259322593325934259352593625937259382593925940259412594225943259442594525946259472594825949259502595125952259532595425955259562595725958259592596025961259622596325964259652596625967259682596925970259712597225973259742597525976259772597825979259802598125982259832598425985259862598725988259892599025991259922599325994259952599625997259982599926000260012600226003260042600526006260072600826009260102601126012260132601426015260162601726018260192602026021260222602326024260252602626027260282602926030260312603226033260342603526036260372603826039260402604126042260432604426045260462604726048260492605026051260522605326054260552605626057260582605926060260612606226063260642606526066260672606826069260702607126072260732607426075260762607726078260792608026081260822608326084260852608626087260882608926090260912609226093260942609526096260972609826099261002610126102261032610426105261062610726108261092611026111261122611326114261152611626117261182611926120261212612226123261242612526126261272612826129261302613126132261332613426135261362613726138261392614026141261422614326144261452614626147261482614926150261512615226153261542615526156261572615826159261602616126162261632616426165261662616726168261692617026171261722617326174261752617626177261782617926180261812618226183261842618526186261872618826189261902619126192261932619426195261962619726198261992620026201262022620326204262052620626207262082620926210262112621226213262142621526216262172621826219262202622126222262232622426225262262622726228262292623026231262322623326234262352623626237262382623926240262412624226243262442624526246262472624826249262502625126252262532625426255262562625726258262592626026261262622626326264262652626626267262682626926270262712627226273262742627526276262772627826279262802628126282262832628426285262862628726288262892629026291262922629326294262952629626297262982629926300263012630226303263042630526306263072630826309263102631126312263132631426315263162631726318263192632026321263222632326324263252632626327263282632926330263312633226333263342633526336263372633826339263402634126342263432634426345263462634726348263492635026351263522635326354263552635626357263582635926360263612636226363263642636526366263672636826369263702637126372263732637426375263762637726378263792638026381263822638326384263852638626387263882638926390263912639226393263942639526396263972639826399264002640126402264032640426405264062640726408264092641026411264122641326414264152641626417264182641926420264212642226423264242642526426264272642826429264302643126432264332643426435264362643726438264392644026441264422644326444264452644626447264482644926450264512645226453264542645526456264572645826459264602646126462264632646426465264662646726468264692647026471264722647326474264752647626477264782647926480264812648226483264842648526486264872648826489264902649126492264932649426495264962649726498264992650026501265022650326504265052650626507265082650926510265112651226513265142651526516265172651826519265202652126522265232652426525265262652726528265292653026531265322653326534265352653626537265382653926540265412654226543265442654526546265472654826549265502655126552265532655426555265562655726558265592656026561265622656326564265652656626567265682656926570265712657226573265742657526576265772657826579265802658126582265832658426585265862658726588265892659026591265922659326594265952659626597265982659926600266012660226603266042660526606266072660826609266102661126612266132661426615266162661726618266192662026621266222662326624266252662626627266282662926630266312663226633266342663526636266372663826639266402664126642266432664426645266462664726648266492665026651266522665326654266552665626657266582665926660266612666226663266642666526666266672666826669266702667126672266732667426675266762667726678266792668026681266822668326684266852668626687266882668926690266912669226693266942669526696266972669826699267002670126702267032670426705267062670726708267092671026711267122671326714267152671626717267182671926720267212672226723267242672526726267272672826729267302673126732267332673426735267362673726738267392674026741267422674326744267452674626747267482674926750267512675226753267542675526756267572675826759267602676126762267632676426765267662676726768267692677026771267722677326774267752677626777267782677926780267812678226783267842678526786267872678826789267902679126792267932679426795267962679726798267992680026801268022680326804268052680626807268082680926810268112681226813268142681526816268172681826819268202682126822268232682426825268262682726828268292683026831268322683326834268352683626837268382683926840268412684226843268442684526846268472684826849268502685126852268532685426855268562685726858268592686026861268622686326864268652686626867268682686926870268712687226873268742687526876268772687826879268802688126882268832688426885268862688726888268892689026891268922689326894268952689626897268982689926900269012690226903269042690526906269072690826909269102691126912269132691426915269162691726918269192692026921269222692326924269252692626927269282692926930269312693226933269342693526936269372693826939269402694126942269432694426945269462694726948269492695026951269522695326954269552695626957269582695926960269612696226963269642696526966269672696826969269702697126972269732697426975269762697726978269792698026981269822698326984269852698626987269882698926990269912699226993269942699526996269972699826999270002700127002270032700427005270062700727008270092701027011270122701327014270152701627017270182701927020270212702227023270242702527026270272702827029270302703127032270332703427035270362703727038270392704027041270422704327044270452704627047270482704927050270512705227053270542705527056270572705827059270602706127062270632706427065270662706727068270692707027071270722707327074270752707627077270782707927080270812708227083270842708527086270872708827089270902709127092270932709427095270962709727098270992710027101271022710327104271052710627107271082710927110271112711227113271142711527116271172711827119271202712127122271232712427125271262712727128271292713027131271322713327134271352713627137271382713927140271412714227143271442714527146271472714827149271502715127152271532715427155271562715727158271592716027161271622716327164271652716627167271682716927170271712717227173271742717527176271772717827179271802718127182271832718427185271862718727188271892719027191271922719327194271952719627197271982719927200272012720227203272042720527206272072720827209272102721127212272132721427215272162721727218272192722027221272222722327224272252722627227272282722927230272312723227233272342723527236272372723827239272402724127242272432724427245272462724727248272492725027251272522725327254272552725627257272582725927260272612726227263272642726527266272672726827269272702727127272272732727427275272762727727278272792728027281272822728327284272852728627287272882728927290272912729227293272942729527296272972729827299273002730127302273032730427305273062730727308273092731027311273122731327314273152731627317273182731927320273212732227323273242732527326273272732827329273302733127332273332733427335273362733727338273392734027341273422734327344273452734627347273482734927350273512735227353273542735527356273572735827359273602736127362273632736427365273662736727368273692737027371273722737327374273752737627377273782737927380273812738227383273842738527386273872738827389273902739127392273932739427395273962739727398273992740027401274022740327404274052740627407274082740927410274112741227413274142741527416274172741827419274202742127422274232742427425274262742727428274292743027431274322743327434274352743627437274382743927440274412744227443274442744527446274472744827449274502745127452274532745427455274562745727458274592746027461274622746327464274652746627467274682746927470274712747227473274742747527476274772747827479274802748127482274832748427485274862748727488274892749027491274922749327494274952749627497274982749927500275012750227503275042750527506275072750827509275102751127512275132751427515275162751727518275192752027521275222752327524275252752627527275282752927530275312753227533275342753527536275372753827539275402754127542275432754427545275462754727548275492755027551275522755327554275552755627557275582755927560275612756227563275642756527566275672756827569275702757127572275732757427575275762757727578275792758027581275822758327584275852758627587275882758927590275912759227593275942759527596275972759827599276002760127602276032760427605276062760727608276092761027611276122761327614276152761627617276182761927620276212762227623276242762527626276272762827629276302763127632276332763427635276362763727638276392764027641276422764327644276452764627647276482764927650276512765227653276542765527656276572765827659276602766127662276632766427665276662766727668276692767027671276722767327674276752767627677276782767927680276812768227683276842768527686276872768827689276902769127692276932769427695276962769727698276992770027701277022770327704277052770627707277082770927710277112771227713277142771527716277172771827719277202772127722277232772427725277262772727728277292773027731277322773327734277352773627737277382773927740277412774227743277442774527746277472774827749277502775127752277532775427755277562775727758277592776027761277622776327764277652776627767277682776927770277712777227773277742777527776277772777827779277802778127782277832778427785277862778727788277892779027791277922779327794277952779627797277982779927800278012780227803278042780527806278072780827809278102781127812278132781427815278162781727818278192782027821278222782327824278252782627827278282782927830278312783227833278342783527836278372783827839278402784127842278432784427845278462784727848278492785027851278522785327854278552785627857278582785927860278612786227863278642786527866278672786827869278702787127872278732787427875278762787727878278792788027881278822788327884278852788627887278882788927890278912789227893278942789527896278972789827899279002790127902279032790427905279062790727908279092791027911279122791327914279152791627917279182791927920279212792227923279242792527926279272792827929279302793127932279332793427935279362793727938279392794027941279422794327944279452794627947279482794927950279512795227953279542795527956279572795827959279602796127962279632796427965279662796727968279692797027971279722797327974279752797627977279782797927980279812798227983279842798527986279872798827989279902799127992279932799427995279962799727998279992800028001280022800328004280052800628007280082800928010280112801228013280142801528016280172801828019280202802128022280232802428025280262802728028280292803028031280322803328034280352803628037280382803928040280412804228043280442804528046280472804828049280502805128052280532805428055280562805728058280592806028061280622806328064280652806628067280682806928070280712807228073280742807528076280772807828079280802808128082280832808428085280862808728088280892809028091280922809328094280952809628097280982809928100281012810228103281042810528106281072810828109281102811128112281132811428115281162811728118281192812028121281222812328124281252812628127281282812928130281312813228133281342813528136281372813828139281402814128142281432814428145281462814728148281492815028151281522815328154281552815628157281582815928160281612816228163281642816528166281672816828169281702817128172281732817428175281762817728178281792818028181281822818328184281852818628187281882818928190281912819228193281942819528196281972819828199282002820128202282032820428205282062820728208282092821028211282122821328214282152821628217282182821928220282212822228223282242822528226282272822828229282302823128232282332823428235282362823728238282392824028241282422824328244282452824628247282482824928250282512825228253282542825528256282572825828259282602826128262282632826428265282662826728268282692827028271282722827328274282752827628277282782827928280282812828228283282842828528286282872828828289282902829128292282932829428295282962829728298282992830028301283022830328304283052830628307283082830928310283112831228313283142831528316283172831828319283202832128322283232832428325283262832728328283292833028331283322833328334283352833628337283382833928340283412834228343283442834528346283472834828349283502835128352283532835428355283562835728358283592836028361283622836328364283652836628367283682836928370283712837228373283742837528376283772837828379283802838128382283832838428385283862838728388283892839028391283922839328394283952839628397283982839928400284012840228403284042840528406284072840828409284102841128412284132841428415284162841728418284192842028421284222842328424284252842628427284282842928430284312843228433284342843528436284372843828439284402844128442284432844428445284462844728448284492845028451284522845328454284552845628457284582845928460284612846228463284642846528466284672846828469284702847128472284732847428475284762847728478284792848028481284822848328484284852848628487284882848928490284912849228493284942849528496284972849828499285002850128502285032850428505285062850728508285092851028511285122851328514285152851628517285182851928520285212852228523285242852528526285272852828529285302853128532285332853428535285362853728538285392854028541285422854328544285452854628547285482854928550285512855228553285542855528556285572855828559285602856128562285632856428565285662856728568285692857028571285722857328574285752857628577285782857928580285812858228583285842858528586285872858828589285902859128592285932859428595285962859728598285992860028601286022860328604286052860628607286082860928610286112861228613286142861528616286172861828619286202862128622286232862428625286262862728628286292863028631286322863328634286352863628637286382863928640286412864228643286442864528646286472864828649286502865128652286532865428655286562865728658286592866028661286622866328664286652866628667286682866928670286712867228673286742867528676286772867828679286802868128682286832868428685286862868728688286892869028691286922869328694286952869628697286982869928700287012870228703287042870528706287072870828709287102871128712287132871428715287162871728718287192872028721287222872328724287252872628727287282872928730287312873228733287342873528736287372873828739287402874128742287432874428745287462874728748287492875028751287522875328754287552875628757287582875928760287612876228763287642876528766287672876828769287702877128772287732877428775287762877728778287792878028781287822878328784287852878628787287882878928790287912879228793287942879528796287972879828799288002880128802288032880428805288062880728808288092881028811288122881328814288152881628817288182881928820288212882228823288242882528826288272882828829288302883128832288332883428835288362883728838288392884028841288422884328844288452884628847288482884928850288512885228853288542885528856288572885828859288602886128862288632886428865288662886728868288692887028871288722887328874288752887628877288782887928880288812888228883288842888528886288872888828889288902889128892288932889428895288962889728898288992890028901289022890328904289052890628907289082890928910289112891228913289142891528916289172891828919289202892128922289232892428925289262892728928289292893028931289322893328934289352893628937289382893928940289412894228943289442894528946289472894828949289502895128952289532895428955289562895728958289592896028961289622896328964289652896628967289682896928970289712897228973289742897528976289772897828979289802898128982289832898428985289862898728988289892899028991289922899328994289952899628997289982899929000290012900229003290042900529006290072900829009290102901129012290132901429015290162901729018290192902029021290222902329024290252902629027290282902929030290312903229033290342903529036290372903829039290402904129042290432904429045290462904729048290492905029051290522905329054290552905629057290582905929060290612906229063290642906529066290672906829069290702907129072290732907429075290762907729078290792908029081290822908329084290852908629087290882908929090290912909229093290942909529096290972909829099291002910129102291032910429105291062910729108291092911029111291122911329114291152911629117291182911929120291212912229123291242912529126291272912829129291302913129132291332913429135291362913729138291392914029141291422914329144291452914629147291482914929150291512915229153291542915529156291572915829159291602916129162291632916429165291662916729168291692917029171291722917329174291752917629177291782917929180291812918229183291842918529186291872918829189291902919129192291932919429195291962919729198291992920029201292022920329204292052920629207292082920929210292112921229213292142921529216292172921829219292202922129222292232922429225292262922729228292292923029231292322923329234292352923629237292382923929240292412924229243292442924529246292472924829249292502925129252292532925429255292562925729258292592926029261292622926329264292652926629267292682926929270292712927229273292742927529276292772927829279292802928129282292832928429285292862928729288292892929029291292922929329294292952929629297292982929929300293012930229303293042930529306293072930829309293102931129312293132931429315293162931729318293192932029321293222932329324293252932629327293282932929330293312933229333293342933529336293372933829339293402934129342293432934429345293462934729348293492935029351293522935329354293552935629357293582935929360293612936229363293642936529366293672936829369293702937129372293732937429375293762937729378293792938029381293822938329384293852938629387293882938929390293912939229393293942939529396293972939829399294002940129402294032940429405294062940729408294092941029411294122941329414294152941629417294182941929420294212942229423294242942529426294272942829429294302943129432294332943429435294362943729438294392944029441294422944329444294452944629447294482944929450294512945229453294542945529456294572945829459294602946129462294632946429465294662946729468294692947029471294722947329474294752947629477294782947929480294812948229483294842948529486294872948829489294902949129492294932949429495294962949729498294992950029501295022950329504295052950629507295082950929510295112951229513295142951529516295172951829519295202952129522295232952429525295262952729528295292953029531295322953329534295352953629537295382953929540295412954229543295442954529546295472954829549295502955129552295532955429555295562955729558295592956029561295622956329564295652956629567295682956929570295712957229573295742957529576295772957829579295802958129582295832958429585295862958729588295892959029591295922959329594295952959629597295982959929600296012960229603296042960529606296072960829609296102961129612296132961429615296162961729618296192962029621296222962329624296252962629627296282962929630296312963229633296342963529636296372963829639296402964129642296432964429645296462964729648296492965029651296522965329654296552965629657296582965929660296612966229663296642966529666296672966829669296702967129672296732967429675296762967729678296792968029681296822968329684296852968629687296882968929690296912969229693296942969529696296972969829699297002970129702297032970429705297062970729708297092971029711297122971329714297152971629717297182971929720297212972229723297242972529726297272972829729297302973129732297332973429735297362973729738297392974029741297422974329744297452974629747297482974929750297512975229753297542975529756297572975829759297602976129762297632976429765297662976729768297692977029771297722977329774297752977629777297782977929780297812978229783297842978529786297872978829789297902979129792297932979429795297962979729798297992980029801298022980329804298052980629807298082980929810298112981229813298142981529816298172981829819298202982129822298232982429825298262982729828298292983029831298322983329834298352983629837298382983929840298412984229843298442984529846298472984829849298502985129852298532985429855298562985729858298592986029861298622986329864298652986629867298682986929870298712987229873298742987529876298772987829879298802988129882298832988429885298862988729888298892989029891298922989329894298952989629897298982989929900299012990229903299042990529906299072990829909299102991129912299132991429915299162991729918299192992029921299222992329924299252992629927299282992929930299312993229933299342993529936299372993829939299402994129942299432994429945299462994729948299492995029951299522995329954299552995629957299582995929960299612996229963299642996529966299672996829969299702997129972299732997429975299762997729978299792998029981299822998329984299852998629987299882998929990299912999229993299942999529996299972999829999300003000130002300033000430005300063000730008300093001030011300123001330014300153001630017300183001930020300213002230023300243002530026300273002830029300303003130032300333003430035300363003730038300393004030041300423004330044300453004630047300483004930050300513005230053300543005530056300573005830059300603006130062300633006430065300663006730068300693007030071300723007330074300753007630077300783007930080300813008230083300843008530086300873008830089300903009130092300933009430095300963009730098300993010030101301023010330104301053010630107301083010930110301113011230113301143011530116301173011830119301203012130122301233012430125301263012730128301293013030131301323013330134301353013630137301383013930140301413014230143301443014530146301473014830149301503015130152301533015430155301563015730158301593016030161301623016330164301653016630167301683016930170301713017230173301743017530176301773017830179301803018130182301833018430185301863018730188301893019030191301923019330194301953019630197301983019930200302013020230203302043020530206302073020830209302103021130212302133021430215302163021730218302193022030221302223022330224302253022630227302283022930230302313023230233302343023530236302373023830239302403024130242302433024430245302463024730248302493025030251302523025330254302553025630257302583025930260302613026230263302643026530266302673026830269302703027130272302733027430275302763027730278302793028030281302823028330284302853028630287302883028930290302913029230293302943029530296302973029830299303003030130302303033030430305303063030730308303093031030311303123031330314303153031630317303183031930320303213032230323303243032530326303273032830329303303033130332303333033430335303363033730338303393034030341303423034330344303453034630347303483034930350303513035230353303543035530356303573035830359303603036130362303633036430365303663036730368303693037030371303723037330374303753037630377303783037930380303813038230383303843038530386303873038830389303903039130392303933039430395303963039730398303993040030401304023040330404304053040630407304083040930410304113041230413304143041530416304173041830419304203042130422304233042430425304263042730428304293043030431304323043330434304353043630437304383043930440304413044230443304443044530446304473044830449304503045130452304533045430455304563045730458304593046030461304623046330464304653046630467304683046930470304713047230473304743047530476304773047830479304803048130482304833048430485304863048730488304893049030491304923049330494304953049630497304983049930500305013050230503305043050530506305073050830509305103051130512305133051430515305163051730518305193052030521305223052330524305253052630527305283052930530305313053230533305343053530536305373053830539305403054130542305433054430545305463054730548305493055030551305523055330554305553055630557305583055930560305613056230563305643056530566305673056830569305703057130572305733057430575305763057730578305793058030581305823058330584305853058630587305883058930590305913059230593305943059530596305973059830599306003060130602306033060430605306063060730608306093061030611306123061330614306153061630617306183061930620306213062230623306243062530626306273062830629306303063130632306333063430635306363063730638306393064030641306423064330644306453064630647306483064930650306513065230653306543065530656306573065830659306603066130662306633066430665306663066730668306693067030671306723067330674306753067630677306783067930680306813068230683306843068530686306873068830689306903069130692306933069430695306963069730698306993070030701307023070330704307053070630707307083070930710307113071230713307143071530716307173071830719307203072130722307233072430725307263072730728307293073030731307323073330734307353073630737307383073930740307413074230743307443074530746307473074830749307503075130752307533075430755307563075730758307593076030761307623076330764307653076630767307683076930770307713077230773307743077530776307773077830779307803078130782307833078430785307863078730788307893079030791307923079330794307953079630797307983079930800308013080230803308043080530806308073080830809308103081130812308133081430815308163081730818308193082030821308223082330824308253082630827308283082930830308313083230833308343083530836308373083830839308403084130842308433084430845308463084730848308493085030851308523085330854308553085630857308583085930860308613086230863308643086530866308673086830869308703087130872308733087430875308763087730878308793088030881308823088330884308853088630887308883088930890308913089230893308943089530896308973089830899309003090130902309033090430905309063090730908309093091030911309123091330914309153091630917309183091930920309213092230923309243092530926309273092830929309303093130932309333093430935309363093730938309393094030941309423094330944309453094630947309483094930950309513095230953309543095530956309573095830959309603096130962309633096430965309663096730968309693097030971309723097330974309753097630977309783097930980309813098230983309843098530986309873098830989309903099130992309933099430995309963099730998309993100031001310023100331004310053100631007310083100931010310113101231013310143101531016310173101831019310203102131022310233102431025310263102731028310293103031031310323103331034310353103631037310383103931040310413104231043310443104531046310473104831049310503105131052310533105431055310563105731058310593106031061310623106331064310653106631067310683106931070310713107231073310743107531076310773107831079310803108131082310833108431085310863108731088310893109031091310923109331094310953109631097310983109931100311013110231103311043110531106311073110831109311103111131112311133111431115311163111731118311193112031121311223112331124311253112631127311283112931130311313113231133311343113531136311373113831139311403114131142311433114431145311463114731148311493115031151311523115331154311553115631157311583115931160311613116231163311643116531166311673116831169311703117131172311733117431175311763117731178311793118031181311823118331184311853118631187311883118931190311913119231193311943119531196311973119831199312003120131202312033120431205312063120731208312093121031211312123121331214312153121631217312183121931220312213122231223312243122531226312273122831229312303123131232312333123431235312363123731238312393124031241312423124331244312453124631247312483124931250312513125231253312543125531256312573125831259312603126131262312633126431265312663126731268312693127031271312723127331274312753127631277312783127931280312813128231283312843128531286312873128831289312903129131292312933129431295312963129731298312993130031301313023130331304313053130631307313083130931310313113131231313313143131531316313173131831319313203132131322313233132431325313263132731328313293133031331313323133331334313353133631337313383133931340313413134231343313443134531346313473134831349313503135131352313533135431355313563135731358313593136031361313623136331364313653136631367313683136931370313713137231373313743137531376313773137831379313803138131382313833138431385313863138731388313893139031391313923139331394313953139631397313983139931400314013140231403314043140531406314073140831409314103141131412314133141431415314163141731418314193142031421314223142331424314253142631427314283142931430314313143231433314343143531436314373143831439314403144131442314433144431445314463144731448314493145031451314523145331454314553145631457314583145931460314613146231463314643146531466314673146831469314703147131472314733147431475314763147731478314793148031481314823148331484314853148631487314883148931490314913149231493314943149531496314973149831499315003150131502315033150431505315063150731508315093151031511315123151331514315153151631517315183151931520315213152231523315243152531526315273152831529315303153131532315333153431535315363153731538315393154031541315423154331544315453154631547315483154931550315513155231553315543155531556315573155831559315603156131562315633156431565315663156731568315693157031571315723157331574315753157631577315783157931580315813158231583315843158531586315873158831589315903159131592315933159431595315963159731598315993160031601316023160331604316053160631607316083160931610316113161231613316143161531616316173161831619316203162131622316233162431625316263162731628316293163031631316323163331634316353163631637316383163931640316413164231643316443164531646316473164831649316503165131652316533165431655316563165731658316593166031661316623166331664316653166631667316683166931670316713167231673316743167531676316773167831679316803168131682316833168431685316863168731688316893169031691316923169331694316953169631697316983169931700317013170231703317043170531706317073170831709317103171131712317133171431715317163171731718317193172031721317223172331724317253172631727317283172931730317313173231733317343173531736317373173831739317403174131742317433174431745317463174731748317493175031751317523175331754317553175631757317583175931760317613176231763317643176531766317673176831769317703177131772317733177431775317763177731778317793178031781317823178331784317853178631787317883178931790317913179231793317943179531796317973179831799318003180131802318033180431805318063180731808318093181031811318123181331814318153181631817318183181931820318213182231823318243182531826318273182831829318303183131832318333183431835318363183731838318393184031841318423184331844318453184631847318483184931850318513185231853318543185531856318573185831859318603186131862318633186431865318663186731868318693187031871318723187331874318753187631877318783187931880318813188231883318843188531886318873188831889318903189131892318933189431895318963189731898318993190031901319023190331904319053190631907319083190931910319113191231913319143191531916319173191831919319203192131922319233192431925319263192731928319293193031931319323193331934319353193631937319383193931940319413194231943319443194531946319473194831949319503195131952319533195431955319563195731958319593196031961319623196331964319653196631967319683196931970319713197231973319743197531976319773197831979319803198131982319833198431985319863198731988319893199031991319923199331994319953199631997319983199932000320013200232003320043200532006320073200832009320103201132012320133201432015320163201732018320193202032021320223202332024320253202632027320283202932030320313203232033320343203532036320373203832039320403204132042320433204432045320463204732048320493205032051320523205332054320553205632057320583205932060320613206232063320643206532066320673206832069320703207132072320733207432075320763207732078320793208032081320823208332084320853208632087320883208932090320913209232093320943209532096320973209832099321003210132102321033210432105321063210732108321093211032111321123211332114321153211632117321183211932120321213212232123321243212532126321273212832129321303213132132321333213432135321363213732138321393214032141321423214332144321453214632147321483214932150321513215232153321543215532156321573215832159321603216132162321633216432165321663216732168321693217032171321723217332174321753217632177321783217932180321813218232183321843218532186321873218832189321903219132192321933219432195321963219732198321993220032201322023220332204322053220632207322083220932210322113221232213322143221532216322173221832219322203222132222322233222432225322263222732228322293223032231322323223332234322353223632237322383223932240322413224232243322443224532246322473224832249322503225132252322533225432255322563225732258322593226032261322623226332264322653226632267322683226932270322713227232273322743227532276322773227832279322803228132282322833228432285322863228732288322893229032291322923229332294322953229632297322983229932300323013230232303323043230532306323073230832309323103231132312323133231432315323163231732318323193232032321323223232332324323253232632327323283232932330323313233232333323343233532336323373233832339323403234132342323433234432345323463234732348323493235032351323523235332354323553235632357323583235932360323613236232363323643236532366323673236832369323703237132372323733237432375323763237732378323793238032381323823238332384323853238632387323883238932390323913239232393323943239532396323973239832399324003240132402324033240432405324063240732408324093241032411324123241332414324153241632417324183241932420324213242232423324243242532426324273242832429324303243132432324333243432435324363243732438324393244032441324423244332444324453244632447324483244932450324513245232453324543245532456324573245832459324603246132462324633246432465324663246732468324693247032471324723247332474324753247632477324783247932480324813248232483324843248532486324873248832489324903249132492324933249432495324963249732498324993250032501325023250332504325053250632507325083250932510325113251232513325143251532516325173251832519325203252132522325233252432525325263252732528325293253032531325323253332534325353253632537325383253932540325413254232543325443254532546325473254832549325503255132552325533255432555325563255732558325593256032561325623256332564325653256632567325683256932570325713257232573325743257532576325773257832579325803258132582325833258432585325863258732588325893259032591325923259332594325953259632597325983259932600326013260232603326043260532606326073260832609326103261132612326133261432615326163261732618326193262032621326223262332624326253262632627326283262932630326313263232633326343263532636326373263832639326403264132642326433264432645326463264732648326493265032651326523265332654326553265632657326583265932660326613266232663326643266532666326673266832669326703267132672326733267432675326763267732678326793268032681326823268332684326853268632687326883268932690326913269232693326943269532696326973269832699327003270132702327033270432705327063270732708327093271032711327123271332714327153271632717327183271932720327213272232723327243272532726327273272832729327303273132732327333273432735327363273732738327393274032741327423274332744327453274632747327483274932750327513275232753327543275532756327573275832759327603276132762327633276432765327663276732768327693277032771327723277332774327753277632777327783277932780327813278232783327843278532786327873278832789327903279132792327933279432795327963279732798327993280032801328023280332804328053280632807328083280932810328113281232813328143281532816328173281832819328203282132822328233282432825328263282732828328293283032831328323283332834328353283632837328383283932840328413284232843328443284532846328473284832849328503285132852328533285432855328563285732858328593286032861328623286332864328653286632867328683286932870328713287232873328743287532876328773287832879328803288132882328833288432885328863288732888328893289032891328923289332894328953289632897328983289932900329013290232903329043290532906329073290832909329103291132912329133291432915329163291732918329193292032921329223292332924329253292632927329283292932930329313293232933329343293532936329373293832939329403294132942329433294432945329463294732948329493295032951329523295332954329553295632957329583295932960329613296232963329643296532966329673296832969329703297132972329733297432975329763297732978329793298032981329823298332984329853298632987329883298932990329913299232993329943299532996329973299832999330003300133002330033300433005330063300733008330093301033011330123301333014330153301633017330183301933020330213302233023330243302533026330273302833029330303303133032330333303433035330363303733038330393304033041330423304333044330453304633047330483304933050330513305233053330543305533056330573305833059330603306133062330633306433065330663306733068330693307033071330723307333074330753307633077330783307933080330813308233083330843308533086330873308833089330903309133092330933309433095330963309733098330993310033101331023310333104331053310633107331083310933110331113311233113331143311533116331173311833119331203312133122331233312433125331263312733128331293313033131331323313333134331353313633137331383313933140331413314233143331443314533146331473314833149331503315133152331533315433155331563315733158331593316033161331623316333164331653316633167331683316933170331713317233173331743317533176331773317833179331803318133182331833318433185331863318733188331893319033191331923319333194331953319633197331983319933200332013320233203332043320533206332073320833209332103321133212332133321433215332163321733218332193322033221332223322333224332253322633227332283322933230332313323233233332343323533236332373323833239332403324133242332433324433245332463324733248332493325033251332523325333254332553325633257332583325933260332613326233263332643326533266332673326833269332703327133272332733327433275332763327733278332793328033281332823328333284332853328633287332883328933290332913329233293332943329533296332973329833299333003330133302333033330433305333063330733308333093331033311333123331333314333153331633317333183331933320333213332233323333243332533326333273332833329333303333133332333333333433335333363333733338333393334033341333423334333344333453334633347333483334933350333513335233353333543335533356333573335833359333603336133362333633336433365333663336733368333693337033371333723337333374333753337633377333783337933380333813338233383333843338533386333873338833389333903339133392333933339433395333963339733398333993340033401334023340333404334053340633407334083340933410334113341233413334143341533416334173341833419334203342133422334233342433425334263342733428334293343033431334323343333434334353343633437334383343933440334413344233443334443344533446334473344833449334503345133452334533345433455334563345733458334593346033461334623346333464334653346633467334683346933470334713347233473334743347533476334773347833479334803348133482334833348433485334863348733488334893349033491334923349333494334953349633497334983349933500335013350233503335043350533506335073350833509335103351133512335133351433515335163351733518335193352033521335223352333524335253352633527335283352933530335313353233533335343353533536335373353833539335403354133542335433354433545335463354733548335493355033551335523355333554335553355633557335583355933560335613356233563335643356533566335673356833569335703357133572335733357433575335763357733578335793358033581335823358333584335853358633587335883358933590335913359233593335943359533596335973359833599336003360133602336033360433605336063360733608336093361033611336123361333614336153361633617336183361933620336213362233623336243362533626336273362833629336303363133632336333363433635336363363733638336393364033641336423364333644336453364633647336483364933650336513365233653336543365533656336573365833659336603366133662336633366433665336663366733668336693367033671336723367333674336753367633677336783367933680336813368233683336843368533686336873368833689336903369133692336933369433695336963369733698336993370033701337023370333704337053370633707337083370933710337113371233713337143371533716337173371833719337203372133722337233372433725337263372733728337293373033731337323373333734337353373633737337383373933740337413374233743337443374533746337473374833749337503375133752337533375433755337563375733758337593376033761337623376333764337653376633767337683376933770337713377233773337743377533776337773377833779337803378133782337833378433785337863378733788337893379033791337923379333794337953379633797337983379933800338013380233803338043380533806338073380833809338103381133812338133381433815338163381733818338193382033821338223382333824338253382633827338283382933830338313383233833338343383533836338373383833839338403384133842338433384433845338463384733848338493385033851338523385333854338553385633857338583385933860338613386233863338643386533866338673386833869338703387133872338733387433875338763387733878338793388033881338823388333884338853388633887338883388933890338913389233893338943389533896338973389833899339003390133902339033390433905339063390733908339093391033911339123391333914339153391633917339183391933920339213392233923339243392533926339273392833929339303393133932339333393433935339363393733938339393394033941339423394333944339453394633947339483394933950339513395233953339543395533956339573395833959339603396133962339633396433965339663396733968339693397033971339723397333974339753397633977339783397933980339813398233983339843398533986339873398833989339903399133992339933399433995339963399733998339993400034001340023400334004340053400634007340083400934010340113401234013340143401534016340173401834019340203402134022340233402434025340263402734028340293403034031340323403334034340353403634037340383403934040340413404234043340443404534046340473404834049340503405134052340533405434055340563405734058340593406034061340623406334064340653406634067340683406934070340713407234073340743407534076340773407834079340803408134082340833408434085340863408734088340893409034091340923409334094340953409634097340983409934100341013410234103341043410534106341073410834109341103411134112341133411434115341163411734118341193412034121341223412334124341253412634127341283412934130341313413234133341343413534136341373413834139341403414134142341433414434145341463414734148341493415034151341523415334154341553415634157341583415934160341613416234163341643416534166341673416834169341703417134172341733417434175341763417734178341793418034181341823418334184341853418634187341883418934190341913419234193341943419534196341973419834199342003420134202342033420434205342063420734208342093421034211342123421334214342153421634217342183421934220342213422234223342243422534226342273422834229342303423134232342333423434235342363423734238342393424034241342423424334244342453424634247342483424934250342513425234253342543425534256342573425834259342603426134262342633426434265342663426734268342693427034271342723427334274342753427634277342783427934280342813428234283342843428534286342873428834289342903429134292342933429434295342963429734298342993430034301343023430334304343053430634307343083430934310343113431234313343143431534316343173431834319343203432134322343233432434325343263432734328343293433034331343323433334334343353433634337343383433934340343413434234343343443434534346343473434834349343503435134352343533435434355343563435734358343593436034361343623436334364343653436634367343683436934370343713437234373343743437534376343773437834379343803438134382343833438434385343863438734388343893439034391343923439334394343953439634397343983439934400344013440234403344043440534406344073440834409344103441134412344133441434415344163441734418344193442034421344223442334424344253442634427344283442934430344313443234433344343443534436344373443834439344403444134442344433444434445344463444734448344493445034451344523445334454344553445634457344583445934460344613446234463344643446534466344673446834469344703447134472344733447434475344763447734478344793448034481344823448334484344853448634487344883448934490344913449234493344943449534496344973449834499345003450134502345033450434505345063450734508345093451034511345123451334514345153451634517345183451934520345213452234523345243452534526345273452834529345303453134532345333453434535345363453734538345393454034541345423454334544345453454634547345483454934550345513455234553345543455534556345573455834559345603456134562345633456434565345663456734568345693457034571345723457334574345753457634577345783457934580345813458234583345843458534586345873458834589345903459134592345933459434595345963459734598345993460034601346023460334604346053460634607346083460934610346113461234613346143461534616346173461834619346203462134622346233462434625346263462734628346293463034631346323463334634346353463634637346383463934640346413464234643346443464534646346473464834649346503465134652346533465434655346563465734658346593466034661346623466334664346653466634667346683466934670346713467234673346743467534676346773467834679346803468134682346833468434685346863468734688346893469034691346923469334694346953469634697346983469934700347013470234703347043470534706347073470834709347103471134712347133471434715347163471734718347193472034721347223472334724347253472634727347283472934730347313473234733347343473534736347373473834739347403474134742347433474434745347463474734748347493475034751347523475334754347553475634757347583475934760347613476234763347643476534766347673476834769347703477134772347733477434775347763477734778347793478034781347823478334784347853478634787347883478934790347913479234793347943479534796347973479834799348003480134802348033480434805348063480734808348093481034811348123481334814348153481634817348183481934820348213482234823348243482534826348273482834829348303483134832348333483434835348363483734838348393484034841348423484334844348453484634847348483484934850348513485234853348543485534856348573485834859348603486134862348633486434865348663486734868348693487034871348723487334874348753487634877348783487934880348813488234883348843488534886348873488834889348903489134892348933489434895348963489734898348993490034901349023490334904349053490634907349083490934910349113491234913349143491534916349173491834919349203492134922349233492434925349263492734928349293493034931349323493334934349353493634937349383493934940349413494234943349443494534946349473494834949349503495134952349533495434955349563495734958349593496034961349623496334964349653496634967349683496934970349713497234973349743497534976349773497834979349803498134982349833498434985349863498734988349893499034991349923499334994349953499634997349983499935000350013500235003350043500535006350073500835009350103501135012350133501435015350163501735018350193502035021350223502335024350253502635027350283502935030350313503235033350343503535036350373503835039350403504135042350433504435045350463504735048350493505035051350523505335054350553505635057350583505935060350613506235063350643506535066350673506835069350703507135072350733507435075350763507735078350793508035081350823508335084350853508635087350883508935090350913509235093350943509535096350973509835099351003510135102351033510435105351063510735108351093511035111351123511335114351153511635117351183511935120351213512235123351243512535126351273512835129351303513135132351333513435135351363513735138351393514035141351423514335144351453514635147351483514935150351513515235153351543515535156351573515835159351603516135162351633516435165351663516735168351693517035171351723517335174351753517635177351783517935180351813518235183351843518535186351873518835189351903519135192351933519435195351963519735198351993520035201352023520335204352053520635207352083520935210352113521235213352143521535216352173521835219352203522135222352233522435225352263522735228352293523035231352323523335234352353523635237352383523935240352413524235243352443524535246352473524835249352503525135252352533525435255352563525735258352593526035261352623526335264352653526635267352683526935270352713527235273352743527535276352773527835279352803528135282352833528435285352863528735288352893529035291352923529335294352953529635297352983529935300353013530235303353043530535306353073530835309353103531135312353133531435315353163531735318353193532035321353223532335324353253532635327353283532935330353313533235333353343533535336353373533835339353403534135342353433534435345353463534735348353493535035351353523535335354353553535635357353583535935360353613536235363353643536535366353673536835369353703537135372353733537435375353763537735378353793538035381353823538335384353853538635387353883538935390353913539235393353943539535396353973539835399354003540135402354033540435405354063540735408354093541035411354123541335414354153541635417354183541935420354213542235423354243542535426354273542835429354303543135432354333543435435354363543735438354393544035441354423544335444354453544635447354483544935450354513545235453354543545535456354573545835459354603546135462354633546435465354663546735468354693547035471354723547335474354753547635477354783547935480354813548235483354843548535486354873548835489354903549135492354933549435495354963549735498354993550035501355023550335504355053550635507355083550935510355113551235513355143551535516355173551835519355203552135522355233552435525355263552735528355293553035531355323553335534355353553635537355383553935540355413554235543355443554535546355473554835549355503555135552355533555435555355563555735558355593556035561355623556335564355653556635567355683556935570355713557235573355743557535576355773557835579355803558135582355833558435585355863558735588355893559035591355923559335594355953559635597355983559935600356013560235603356043560535606356073560835609356103561135612356133561435615356163561735618356193562035621356223562335624356253562635627356283562935630356313563235633356343563535636356373563835639356403564135642356433564435645356463564735648356493565035651356523565335654356553565635657356583565935660356613566235663356643566535666356673566835669356703567135672356733567435675356763567735678356793568035681356823568335684356853568635687356883568935690356913569235693356943569535696356973569835699357003570135702357033570435705357063570735708357093571035711357123571335714357153571635717357183571935720357213572235723357243572535726357273572835729357303573135732357333573435735357363573735738357393574035741357423574335744357453574635747357483574935750357513575235753357543575535756357573575835759357603576135762357633576435765357663576735768357693577035771357723577335774357753577635777357783577935780357813578235783357843578535786357873578835789357903579135792357933579435795357963579735798357993580035801358023580335804358053580635807358083580935810358113581235813358143581535816358173581835819358203582135822358233582435825358263582735828358293583035831358323583335834358353583635837358383583935840358413584235843358443584535846358473584835849358503585135852358533585435855358563585735858358593586035861358623586335864358653586635867358683586935870358713587235873358743587535876358773587835879358803588135882358833588435885358863588735888358893589035891358923589335894358953589635897358983589935900359013590235903359043590535906359073590835909359103591135912359133591435915359163591735918359193592035921359223592335924359253592635927359283592935930359313593235933359343593535936359373593835939359403594135942359433594435945359463594735948359493595035951359523595335954359553595635957359583595935960359613596235963359643596535966359673596835969359703597135972359733597435975359763597735978359793598035981359823598335984359853598635987359883598935990359913599235993359943599535996359973599835999360003600136002360033600436005360063600736008360093601036011360123601336014360153601636017360183601936020360213602236023360243602536026360273602836029360303603136032360333603436035360363603736038360393604036041360423604336044360453604636047360483604936050360513605236053360543605536056360573605836059360603606136062360633606436065360663606736068360693607036071360723607336074360753607636077360783607936080360813608236083360843608536086360873608836089360903609136092360933609436095360963609736098360993610036101361023610336104361053610636107361083610936110361113611236113361143611536116361173611836119361203612136122361233612436125361263612736128361293613036131361323613336134361353613636137361383613936140361413614236143361443614536146361473614836149361503615136152361533615436155361563615736158361593616036161361623616336164361653616636167361683616936170361713617236173361743617536176361773617836179361803618136182361833618436185361863618736188361893619036191361923619336194361953619636197361983619936200362013620236203362043620536206362073620836209362103621136212362133621436215362163621736218362193622036221362223622336224362253622636227362283622936230362313623236233362343623536236362373623836239362403624136242362433624436245362463624736248362493625036251362523625336254362553625636257362583625936260362613626236263362643626536266362673626836269362703627136272362733627436275362763627736278362793628036281362823628336284362853628636287362883628936290362913629236293362943629536296362973629836299363003630136302363033630436305363063630736308363093631036311363123631336314363153631636317363183631936320363213632236323363243632536326363273632836329363303633136332363333633436335363363633736338363393634036341363423634336344363453634636347363483634936350363513635236353363543635536356363573635836359363603636136362363633636436365363663636736368363693637036371363723637336374363753637636377363783637936380363813638236383363843638536386363873638836389363903639136392363933639436395363963639736398363993640036401364023640336404364053640636407364083640936410364113641236413364143641536416364173641836419364203642136422364233642436425364263642736428364293643036431364323643336434364353643636437364383643936440364413644236443364443644536446364473644836449364503645136452364533645436455364563645736458364593646036461364623646336464364653646636467364683646936470364713647236473364743647536476364773647836479364803648136482364833648436485364863648736488364893649036491364923649336494364953649636497364983649936500365013650236503365043650536506365073650836509365103651136512365133651436515365163651736518365193652036521365223652336524365253652636527365283652936530365313653236533365343653536536365373653836539365403654136542365433654436545365463654736548365493655036551365523655336554365553655636557365583655936560365613656236563365643656536566365673656836569365703657136572365733657436575365763657736578365793658036581365823658336584365853658636587365883658936590365913659236593365943659536596365973659836599366003660136602366033660436605366063660736608366093661036611366123661336614366153661636617366183661936620366213662236623366243662536626366273662836629366303663136632366333663436635366363663736638366393664036641366423664336644366453664636647366483664936650366513665236653366543665536656366573665836659366603666136662366633666436665366663666736668366693667036671366723667336674366753667636677366783667936680366813668236683366843668536686366873668836689366903669136692366933669436695366963669736698366993670036701367023670336704367053670636707367083670936710367113671236713367143671536716367173671836719367203672136722367233672436725367263672736728367293673036731367323673336734367353673636737367383673936740367413674236743367443674536746367473674836749367503675136752367533675436755367563675736758367593676036761367623676336764367653676636767367683676936770367713677236773367743677536776367773677836779367803678136782367833678436785367863678736788367893679036791367923679336794367953679636797367983679936800368013680236803368043680536806368073680836809368103681136812368133681436815368163681736818368193682036821368223682336824368253682636827368283682936830368313683236833368343683536836368373683836839368403684136842368433684436845368463684736848368493685036851368523685336854368553685636857368583685936860368613686236863368643686536866368673686836869368703687136872368733687436875368763687736878368793688036881368823688336884368853688636887368883688936890368913689236893368943689536896368973689836899369003690136902369033690436905369063690736908369093691036911369123691336914369153691636917369183691936920369213692236923369243692536926369273692836929369303693136932369333693436935369363693736938369393694036941369423694336944369453694636947369483694936950369513695236953369543695536956369573695836959369603696136962369633696436965369663696736968369693697036971369723697336974369753697636977369783697936980369813698236983369843698536986369873698836989369903699136992369933699436995369963699736998369993700037001370023700337004370053700637007370083700937010370113701237013370143701537016370173701837019370203702137022370233702437025370263702737028370293703037031370323703337034370353703637037370383703937040370413704237043370443704537046370473704837049370503705137052370533705437055370563705737058370593706037061370623706337064370653706637067370683706937070370713707237073370743707537076370773707837079370803708137082370833708437085370863708737088370893709037091370923709337094370953709637097370983709937100371013710237103371043710537106371073710837109371103711137112371133711437115371163711737118371193712037121371223712337124371253712637127371283712937130371313713237133371343713537136371373713837139371403714137142371433714437145371463714737148371493715037151371523715337154371553715637157371583715937160371613716237163371643716537166371673716837169371703717137172371733717437175371763717737178371793718037181371823718337184371853718637187371883718937190371913719237193371943719537196371973719837199372003720137202372033720437205372063720737208372093721037211372123721337214372153721637217372183721937220372213722237223372243722537226372273722837229372303723137232372333723437235372363723737238372393724037241372423724337244372453724637247372483724937250372513725237253372543725537256372573725837259372603726137262372633726437265372663726737268372693727037271372723727337274372753727637277372783727937280372813728237283372843728537286372873728837289372903729137292372933729437295372963729737298372993730037301373023730337304373053730637307373083730937310373113731237313373143731537316373173731837319373203732137322373233732437325373263732737328373293733037331373323733337334373353733637337373383733937340373413734237343373443734537346373473734837349373503735137352373533735437355373563735737358373593736037361373623736337364373653736637367373683736937370373713737237373373743737537376373773737837379373803738137382373833738437385373863738737388373893739037391373923739337394373953739637397373983739937400374013740237403374043740537406374073740837409374103741137412374133741437415374163741737418374193742037421374223742337424374253742637427374283742937430374313743237433374343743537436374373743837439374403744137442374433744437445374463744737448374493745037451374523745337454374553745637457374583745937460374613746237463374643746537466374673746837469374703747137472374733747437475374763747737478374793748037481374823748337484374853748637487374883748937490374913749237493374943749537496374973749837499375003750137502375033750437505375063750737508375093751037511375123751337514375153751637517375183751937520375213752237523375243752537526375273752837529375303753137532375333753437535375363753737538375393754037541375423754337544375453754637547375483754937550375513755237553375543755537556375573755837559375603756137562375633756437565375663756737568375693757037571375723757337574375753757637577375783757937580375813758237583375843758537586375873758837589375903759137592375933759437595375963759737598375993760037601376023760337604376053760637607376083760937610376113761237613376143761537616376173761837619376203762137622376233762437625376263762737628376293763037631376323763337634376353763637637376383763937640376413764237643376443764537646376473764837649376503765137652376533765437655376563765737658376593766037661376623766337664376653766637667376683766937670376713767237673376743767537676376773767837679376803768137682376833768437685376863768737688376893769037691376923769337694376953769637697376983769937700377013770237703377043770537706377073770837709377103771137712377133771437715377163771737718377193772037721377223772337724377253772637727377283772937730377313773237733377343773537736377373773837739377403774137742377433774437745377463774737748377493775037751377523775337754377553775637757377583775937760377613776237763377643776537766377673776837769377703777137772377733777437775377763777737778377793778037781377823778337784377853778637787377883778937790377913779237793377943779537796377973779837799378003780137802378033780437805378063780737808378093781037811378123781337814378153781637817378183781937820378213782237823378243782537826378273782837829378303783137832378333783437835378363783737838378393784037841378423784337844378453784637847378483784937850378513785237853378543785537856378573785837859378603786137862378633786437865378663786737868378693787037871378723787337874378753787637877378783787937880378813788237883378843788537886378873788837889378903789137892378933789437895378963789737898378993790037901379023790337904379053790637907379083790937910379113791237913379143791537916379173791837919379203792137922379233792437925379263792737928379293793037931379323793337934379353793637937379383793937940379413794237943379443794537946379473794837949379503795137952379533795437955379563795737958379593796037961379623796337964379653796637967379683796937970379713797237973379743797537976379773797837979379803798137982379833798437985379863798737988379893799037991379923799337994379953799637997379983799938000380013800238003380043800538006380073800838009380103801138012380133801438015380163801738018380193802038021380223802338024380253802638027380283802938030380313803238033380343803538036380373803838039380403804138042380433804438045380463804738048380493805038051380523805338054380553805638057380583805938060380613806238063380643806538066380673806838069380703807138072380733807438075380763807738078380793808038081380823808338084380853808638087380883808938090380913809238093380943809538096380973809838099381003810138102381033810438105381063810738108381093811038111381123811338114381153811638117381183811938120381213812238123381243812538126381273812838129381303813138132381333813438135381363813738138381393814038141381423814338144381453814638147381483814938150381513815238153381543815538156381573815838159381603816138162381633816438165381663816738168381693817038171381723817338174381753817638177381783817938180381813818238183381843818538186381873818838189381903819138192381933819438195381963819738198381993820038201382023820338204382053820638207382083820938210382113821238213382143821538216382173821838219382203822138222382233822438225382263822738228382293823038231382323823338234382353823638237382383823938240382413824238243382443824538246382473824838249382503825138252382533825438255382563825738258382593826038261382623826338264382653826638267382683826938270382713827238273382743827538276382773827838279382803828138282382833828438285382863828738288382893829038291382923829338294382953829638297382983829938300383013830238303383043830538306383073830838309383103831138312383133831438315383163831738318383193832038321383223832338324383253832638327383283832938330383313833238333383343833538336383373833838339383403834138342383433834438345383463834738348383493835038351383523835338354383553835638357383583835938360383613836238363383643836538366383673836838369383703837138372383733837438375383763837738378383793838038381383823838338384383853838638387383883838938390383913839238393383943839538396383973839838399384003840138402384033840438405384063840738408384093841038411384123841338414384153841638417384183841938420384213842238423384243842538426384273842838429384303843138432384333843438435384363843738438384393844038441384423844338444384453844638447384483844938450384513845238453384543845538456384573845838459384603846138462384633846438465384663846738468384693847038471384723847338474384753847638477384783847938480384813848238483384843848538486384873848838489384903849138492384933849438495384963849738498384993850038501385023850338504385053850638507385083850938510385113851238513385143851538516385173851838519385203852138522385233852438525385263852738528385293853038531385323853338534385353853638537385383853938540385413854238543385443854538546385473854838549385503855138552385533855438555385563855738558385593856038561385623856338564385653856638567385683856938570385713857238573385743857538576385773857838579385803858138582385833858438585385863858738588385893859038591385923859338594385953859638597385983859938600386013860238603386043860538606386073860838609386103861138612386133861438615386163861738618386193862038621386223862338624386253862638627386283862938630386313863238633386343863538636386373863838639386403864138642386433864438645386463864738648386493865038651386523865338654386553865638657386583865938660386613866238663386643866538666386673866838669386703867138672386733867438675386763867738678386793868038681386823868338684386853868638687386883868938690386913869238693386943869538696386973869838699387003870138702387033870438705387063870738708387093871038711387123871338714387153871638717387183871938720387213872238723387243872538726387273872838729387303873138732387333873438735387363873738738387393874038741387423874338744387453874638747387483874938750387513875238753387543875538756387573875838759387603876138762387633876438765387663876738768387693877038771387723877338774387753877638777387783877938780387813878238783387843878538786387873878838789387903879138792387933879438795387963879738798387993880038801388023880338804388053880638807388083880938810388113881238813388143881538816388173881838819388203882138822388233882438825388263882738828388293883038831388323883338834388353883638837388383883938840388413884238843388443884538846388473884838849388503885138852388533885438855388563885738858388593886038861388623886338864388653886638867388683886938870388713887238873388743887538876388773887838879388803888138882388833888438885388863888738888388893889038891388923889338894388953889638897388983889938900389013890238903389043890538906389073890838909389103891138912389133891438915389163891738918389193892038921389223892338924389253892638927389283892938930389313893238933389343893538936389373893838939389403894138942389433894438945389463894738948389493895038951389523895338954389553895638957389583895938960389613896238963389643896538966389673896838969389703897138972389733897438975389763897738978389793898038981389823898338984389853898638987389883898938990389913899238993389943899538996389973899838999390003900139002390033900439005390063900739008390093901039011390123901339014390153901639017390183901939020390213902239023390243902539026390273902839029390303903139032390333903439035390363903739038390393904039041390423904339044390453904639047390483904939050390513905239053390543905539056390573905839059390603906139062390633906439065390663906739068390693907039071390723907339074390753907639077390783907939080390813908239083390843908539086390873908839089390903909139092390933909439095390963909739098390993910039101391023910339104391053910639107391083910939110391113911239113391143911539116391173911839119391203912139122391233912439125391263912739128391293913039131391323913339134391353913639137391383913939140391413914239143391443914539146391473914839149391503915139152391533915439155391563915739158391593916039161391623916339164391653916639167391683916939170391713917239173391743917539176391773917839179391803918139182391833918439185391863918739188391893919039191391923919339194391953919639197391983919939200392013920239203392043920539206392073920839209392103921139212392133921439215392163921739218392193922039221392223922339224392253922639227392283922939230392313923239233392343923539236392373923839239392403924139242392433924439245392463924739248392493925039251392523925339254392553925639257392583925939260392613926239263392643926539266392673926839269392703927139272392733927439275392763927739278392793928039281392823928339284392853928639287392883928939290392913929239293392943929539296392973929839299393003930139302393033930439305393063930739308393093931039311393123931339314393153931639317393183931939320393213932239323393243932539326393273932839329393303933139332393333933439335393363933739338393393934039341393423934339344393453934639347393483934939350393513935239353393543935539356393573935839359393603936139362393633936439365393663936739368393693937039371393723937339374393753937639377393783937939380393813938239383393843938539386393873938839389393903939139392393933939439395393963939739398393993940039401394023940339404394053940639407394083940939410394113941239413394143941539416394173941839419394203942139422394233942439425394263942739428394293943039431394323943339434394353943639437394383943939440394413944239443394443944539446394473944839449394503945139452394533945439455394563945739458394593946039461394623946339464394653946639467394683946939470394713947239473394743947539476394773947839479394803948139482394833948439485394863948739488394893949039491394923949339494394953949639497394983949939500395013950239503395043950539506395073950839509395103951139512395133951439515395163951739518395193952039521395223952339524395253952639527395283952939530395313953239533395343953539536395373953839539395403954139542395433954439545395463954739548395493955039551395523955339554395553955639557395583955939560395613956239563395643956539566395673956839569395703957139572395733957439575395763957739578395793958039581395823958339584395853958639587395883958939590395913959239593395943959539596395973959839599396003960139602396033960439605396063960739608396093961039611396123961339614396153961639617396183961939620396213962239623396243962539626396273962839629396303963139632396333963439635396363963739638396393964039641396423964339644396453964639647396483964939650396513965239653396543965539656396573965839659396603966139662396633966439665396663966739668396693967039671396723967339674396753967639677396783967939680396813968239683396843968539686396873968839689396903969139692396933969439695396963969739698396993970039701397023970339704397053970639707397083970939710397113971239713397143971539716397173971839719397203972139722397233972439725397263972739728397293973039731397323973339734397353973639737397383973939740397413974239743397443974539746397473974839749397503975139752397533975439755397563975739758397593976039761397623976339764397653976639767397683976939770397713977239773397743977539776397773977839779397803978139782397833978439785397863978739788397893979039791397923979339794397953979639797397983979939800398013980239803398043980539806398073980839809398103981139812398133981439815398163981739818398193982039821398223982339824398253982639827398283982939830398313983239833398343983539836398373983839839398403984139842398433984439845398463984739848398493985039851398523985339854398553985639857398583985939860398613986239863398643986539866398673986839869398703987139872398733987439875398763987739878398793988039881398823988339884398853988639887398883988939890398913989239893398943989539896398973989839899399003990139902399033990439905399063990739908399093991039911399123991339914399153991639917399183991939920399213992239923399243992539926399273992839929399303993139932399333993439935399363993739938399393994039941399423994339944399453994639947399483994939950399513995239953399543995539956399573995839959399603996139962399633996439965399663996739968399693997039971399723997339974399753997639977399783997939980399813998239983399843998539986399873998839989399903999139992399933999439995399963999739998399994000040001400024000340004400054000640007400084000940010400114001240013400144001540016400174001840019400204002140022400234002440025400264002740028400294003040031400324003340034400354003640037400384003940040400414004240043400444004540046400474004840049400504005140052400534005440055400564005740058400594006040061400624006340064400654006640067400684006940070400714007240073400744007540076400774007840079400804008140082400834008440085400864008740088400894009040091400924009340094400954009640097400984009940100401014010240103401044010540106401074010840109401104011140112401134011440115401164011740118401194012040121401224012340124401254012640127401284012940130401314013240133401344013540136401374013840139401404014140142401434014440145401464014740148401494015040151401524015340154401554015640157401584015940160401614016240163401644016540166401674016840169401704017140172401734017440175401764017740178401794018040181401824018340184401854018640187401884018940190401914019240193401944019540196401974019840199402004020140202402034020440205402064020740208402094021040211402124021340214402154021640217402184021940220402214022240223402244022540226402274022840229402304023140232402334023440235402364023740238402394024040241402424024340244402454024640247402484024940250402514025240253402544025540256402574025840259402604026140262402634026440265402664026740268402694027040271402724027340274402754027640277402784027940280402814028240283402844028540286402874028840289402904029140292402934029440295402964029740298402994030040301403024030340304403054030640307403084030940310403114031240313403144031540316403174031840319403204032140322403234032440325403264032740328403294033040331403324033340334403354033640337403384033940340403414034240343403444034540346403474034840349403504035140352403534035440355403564035740358403594036040361403624036340364403654036640367403684036940370403714037240373403744037540376403774037840379403804038140382403834038440385403864038740388403894039040391403924039340394403954039640397403984039940400404014040240403404044040540406404074040840409404104041140412404134041440415404164041740418404194042040421404224042340424404254042640427404284042940430404314043240433404344043540436404374043840439404404044140442404434044440445404464044740448404494045040451404524045340454404554045640457404584045940460404614046240463404644046540466404674046840469404704047140472404734047440475404764047740478404794048040481404824048340484404854048640487404884048940490404914049240493404944049540496404974049840499405004050140502405034050440505405064050740508405094051040511405124051340514405154051640517405184051940520405214052240523405244052540526405274052840529405304053140532405334053440535405364053740538405394054040541405424054340544405454054640547405484054940550405514055240553405544055540556405574055840559405604056140562405634056440565405664056740568405694057040571405724057340574405754057640577405784057940580405814058240583405844058540586405874058840589405904059140592405934059440595405964059740598405994060040601406024060340604406054060640607406084060940610406114061240613406144061540616406174061840619406204062140622406234062440625406264062740628406294063040631406324063340634406354063640637406384063940640406414064240643406444064540646406474064840649406504065140652406534065440655406564065740658406594066040661406624066340664406654066640667406684066940670406714067240673406744067540676406774067840679406804068140682406834068440685406864068740688406894069040691406924069340694406954069640697406984069940700407014070240703407044070540706407074070840709407104071140712407134071440715407164071740718407194072040721407224072340724407254072640727407284072940730407314073240733407344073540736407374073840739407404074140742407434074440745407464074740748407494075040751407524075340754407554075640757407584075940760407614076240763407644076540766407674076840769407704077140772407734077440775407764077740778407794078040781407824078340784407854078640787407884078940790407914079240793407944079540796407974079840799408004080140802408034080440805408064080740808408094081040811408124081340814408154081640817408184081940820408214082240823408244082540826408274082840829408304083140832408334083440835408364083740838408394084040841408424084340844408454084640847408484084940850408514085240853408544085540856408574085840859408604086140862408634086440865408664086740868408694087040871408724087340874408754087640877408784087940880408814088240883408844088540886408874088840889408904089140892408934089440895408964089740898408994090040901409024090340904409054090640907409084090940910409114091240913409144091540916409174091840919409204092140922409234092440925409264092740928409294093040931409324093340934409354093640937409384093940940409414094240943409444094540946409474094840949409504095140952409534095440955409564095740958409594096040961409624096340964409654096640967409684096940970409714097240973409744097540976409774097840979409804098140982409834098440985409864098740988409894099040991409924099340994409954099640997409984099941000410014100241003410044100541006410074100841009410104101141012410134101441015410164101741018410194102041021410224102341024410254102641027410284102941030410314103241033410344103541036410374103841039410404104141042410434104441045410464104741048410494105041051410524105341054410554105641057410584105941060410614106241063410644106541066410674106841069410704107141072410734107441075410764107741078410794108041081410824108341084410854108641087410884108941090410914109241093410944109541096410974109841099411004110141102411034110441105411064110741108411094111041111411124111341114411154111641117411184111941120411214112241123411244112541126411274112841129411304113141132411334113441135411364113741138411394114041141411424114341144411454114641147411484114941150411514115241153411544115541156411574115841159411604116141162411634116441165411664116741168411694117041171411724117341174411754117641177411784117941180411814118241183411844118541186411874118841189411904119141192411934119441195411964119741198411994120041201412024120341204412054120641207412084120941210412114121241213412144121541216412174121841219412204122141222412234122441225412264122741228412294123041231412324123341234412354123641237412384123941240412414124241243412444124541246412474124841249412504125141252412534125441255412564125741258412594126041261412624126341264412654126641267412684126941270412714127241273412744127541276412774127841279412804128141282412834128441285412864128741288412894129041291412924129341294412954129641297412984129941300413014130241303413044130541306413074130841309413104131141312413134131441315413164131741318413194132041321413224132341324413254132641327413284132941330413314133241333413344133541336413374133841339413404134141342413434134441345413464134741348413494135041351413524135341354413554135641357413584135941360413614136241363413644136541366413674136841369413704137141372413734137441375413764137741378413794138041381413824138341384413854138641387413884138941390413914139241393413944139541396413974139841399414004140141402414034140441405414064140741408414094141041411414124141341414414154141641417414184141941420414214142241423414244142541426414274142841429414304143141432414334143441435414364143741438414394144041441414424144341444414454144641447414484144941450414514145241453414544145541456414574145841459414604146141462414634146441465414664146741468414694147041471414724147341474414754147641477414784147941480414814148241483414844148541486414874148841489414904149141492414934149441495414964149741498414994150041501415024150341504415054150641507415084150941510415114151241513415144151541516415174151841519415204152141522415234152441525415264152741528415294153041531415324153341534415354153641537415384153941540415414154241543415444154541546415474154841549415504155141552415534155441555415564155741558415594156041561415624156341564415654156641567415684156941570415714157241573415744157541576415774157841579415804158141582415834158441585415864158741588415894159041591415924159341594415954159641597415984159941600416014160241603416044160541606416074160841609416104161141612416134161441615416164161741618416194162041621416224162341624416254162641627416284162941630416314163241633416344163541636416374163841639416404164141642416434164441645416464164741648416494165041651416524165341654416554165641657416584165941660416614166241663416644166541666416674166841669416704167141672416734167441675416764167741678416794168041681416824168341684416854168641687416884168941690416914169241693416944169541696416974169841699417004170141702417034170441705417064170741708417094171041711417124171341714417154171641717417184171941720417214172241723417244172541726417274172841729417304173141732417334173441735417364173741738417394174041741417424174341744417454174641747417484174941750417514175241753417544175541756417574175841759417604176141762417634176441765417664176741768417694177041771417724177341774417754177641777417784177941780417814178241783417844178541786417874178841789417904179141792417934179441795417964179741798417994180041801418024180341804418054180641807418084180941810418114181241813418144181541816418174181841819418204182141822418234182441825418264182741828418294183041831418324183341834418354183641837418384183941840418414184241843418444184541846418474184841849418504185141852418534185441855418564185741858418594186041861418624186341864418654186641867418684186941870418714187241873418744187541876418774187841879418804188141882418834188441885418864188741888418894189041891418924189341894418954189641897418984189941900419014190241903419044190541906419074190841909419104191141912419134191441915419164191741918419194192041921419224192341924419254192641927419284192941930419314193241933419344193541936419374193841939419404194141942419434194441945419464194741948419494195041951419524195341954419554195641957419584195941960419614196241963419644196541966419674196841969419704197141972419734197441975419764197741978419794198041981419824198341984419854198641987419884198941990419914199241993419944199541996419974199841999420004200142002420034200442005420064200742008420094201042011420124201342014420154201642017420184201942020420214202242023420244202542026420274202842029420304203142032420334203442035420364203742038420394204042041420424204342044420454204642047420484204942050420514205242053420544205542056420574205842059420604206142062420634206442065420664206742068420694207042071420724207342074420754207642077420784207942080420814208242083420844208542086420874208842089420904209142092420934209442095420964209742098420994210042101421024210342104421054210642107421084210942110421114211242113421144211542116421174211842119421204212142122421234212442125421264212742128421294213042131421324213342134421354213642137421384213942140421414214242143421444214542146421474214842149421504215142152421534215442155421564215742158421594216042161421624216342164421654216642167421684216942170421714217242173421744217542176421774217842179421804218142182421834218442185421864218742188421894219042191421924219342194421954219642197421984219942200422014220242203422044220542206422074220842209422104221142212422134221442215422164221742218422194222042221422224222342224422254222642227422284222942230422314223242233422344223542236422374223842239422404224142242422434224442245422464224742248422494225042251422524225342254422554225642257422584225942260422614226242263422644226542266422674226842269422704227142272422734227442275422764227742278422794228042281422824228342284422854228642287422884228942290422914229242293422944229542296422974229842299423004230142302423034230442305423064230742308423094231042311423124231342314423154231642317423184231942320423214232242323423244232542326423274232842329423304233142332423334233442335423364233742338423394234042341423424234342344423454234642347423484234942350423514235242353423544235542356423574235842359423604236142362423634236442365423664236742368423694237042371423724237342374423754237642377423784237942380423814238242383423844238542386423874238842389423904239142392423934239442395423964239742398423994240042401424024240342404424054240642407424084240942410424114241242413424144241542416424174241842419424204242142422424234242442425424264242742428424294243042431424324243342434424354243642437424384243942440424414244242443424444244542446424474244842449424504245142452424534245442455424564245742458424594246042461424624246342464424654246642467424684246942470424714247242473424744247542476424774247842479424804248142482424834248442485424864248742488424894249042491424924249342494424954249642497424984249942500425014250242503425044250542506425074250842509425104251142512425134251442515425164251742518425194252042521425224252342524425254252642527425284252942530425314253242533425344253542536425374253842539425404254142542425434254442545425464254742548425494255042551425524255342554425554255642557425584255942560425614256242563425644256542566425674256842569425704257142572425734257442575425764257742578425794258042581425824258342584425854258642587425884258942590425914259242593425944259542596425974259842599426004260142602426034260442605426064260742608426094261042611426124261342614426154261642617426184261942620426214262242623426244262542626426274262842629426304263142632426334263442635426364263742638426394264042641426424264342644426454264642647426484264942650426514265242653426544265542656426574265842659426604266142662426634266442665426664266742668426694267042671426724267342674426754267642677426784267942680426814268242683426844268542686426874268842689426904269142692426934269442695426964269742698426994270042701427024270342704427054270642707427084270942710427114271242713427144271542716427174271842719427204272142722427234272442725427264272742728427294273042731427324273342734427354273642737427384273942740427414274242743427444274542746427474274842749427504275142752427534275442755427564275742758427594276042761427624276342764427654276642767427684276942770427714277242773427744277542776427774277842779427804278142782427834278442785427864278742788427894279042791427924279342794427954279642797427984279942800428014280242803428044280542806428074280842809428104281142812428134281442815428164281742818428194282042821428224282342824428254282642827428284282942830428314283242833428344283542836428374283842839428404284142842428434284442845428464284742848428494285042851428524285342854428554285642857428584285942860428614286242863428644286542866428674286842869428704287142872428734287442875428764287742878428794288042881428824288342884428854288642887428884288942890428914289242893428944289542896428974289842899429004290142902429034290442905429064290742908429094291042911429124291342914429154291642917429184291942920429214292242923429244292542926429274292842929429304293142932429334293442935429364293742938429394294042941429424294342944429454294642947429484294942950429514295242953429544295542956429574295842959429604296142962429634296442965429664296742968429694297042971429724297342974429754297642977429784297942980429814298242983429844298542986429874298842989429904299142992429934299442995429964299742998429994300043001430024300343004430054300643007430084300943010430114301243013430144301543016430174301843019430204302143022430234302443025430264302743028430294303043031430324303343034430354303643037430384303943040430414304243043430444304543046430474304843049430504305143052430534305443055430564305743058430594306043061430624306343064430654306643067430684306943070430714307243073430744307543076430774307843079430804308143082430834308443085430864308743088430894309043091430924309343094430954309643097430984309943100431014310243103431044310543106431074310843109431104311143112431134311443115431164311743118431194312043121431224312343124431254312643127431284312943130431314313243133431344313543136431374313843139431404314143142431434314443145431464314743148431494315043151431524315343154431554315643157431584315943160431614316243163431644316543166431674316843169431704317143172431734317443175431764317743178431794318043181431824318343184431854318643187431884318943190431914319243193431944319543196431974319843199432004320143202432034320443205432064320743208432094321043211432124321343214432154321643217432184321943220432214322243223432244322543226432274322843229432304323143232432334323443235432364323743238432394324043241432424324343244432454324643247432484324943250432514325243253432544325543256432574325843259432604326143262432634326443265432664326743268432694327043271432724327343274432754327643277432784327943280432814328243283432844328543286432874328843289432904329143292432934329443295432964329743298432994330043301433024330343304433054330643307433084330943310433114331243313433144331543316433174331843319433204332143322433234332443325433264332743328433294333043331433324333343334433354333643337433384333943340433414334243343433444334543346433474334843349433504335143352433534335443355433564335743358433594336043361433624336343364433654336643367433684336943370433714337243373433744337543376433774337843379433804338143382433834338443385433864338743388433894339043391433924339343394433954339643397433984339943400434014340243403434044340543406434074340843409434104341143412434134341443415434164341743418434194342043421434224342343424434254342643427434284342943430434314343243433434344343543436434374343843439434404344143442434434344443445434464344743448434494345043451434524345343454434554345643457434584345943460434614346243463434644346543466434674346843469434704347143472434734347443475434764347743478434794348043481434824348343484434854348643487434884348943490434914349243493434944349543496434974349843499435004350143502435034350443505435064350743508435094351043511435124351343514435154351643517435184351943520435214352243523435244352543526435274352843529435304353143532435334353443535435364353743538435394354043541435424354343544435454354643547435484354943550435514355243553435544355543556435574355843559435604356143562435634356443565435664356743568435694357043571435724357343574435754357643577435784357943580435814358243583435844358543586435874358843589435904359143592435934359443595435964359743598435994360043601436024360343604436054360643607436084360943610436114361243613436144361543616436174361843619436204362143622436234362443625436264362743628436294363043631436324363343634436354363643637436384363943640436414364243643436444364543646436474364843649436504365143652436534365443655436564365743658436594366043661436624366343664436654366643667436684366943670436714367243673436744367543676436774367843679436804368143682436834368443685436864368743688436894369043691436924369343694436954369643697436984369943700437014370243703437044370543706437074370843709437104371143712437134371443715437164371743718437194372043721437224372343724437254372643727437284372943730437314373243733437344373543736437374373843739437404374143742437434374443745437464374743748437494375043751437524375343754437554375643757437584375943760437614376243763437644376543766437674376843769437704377143772437734377443775437764377743778437794378043781437824378343784437854378643787437884378943790437914379243793437944379543796437974379843799438004380143802438034380443805438064380743808438094381043811438124381343814438154381643817438184381943820438214382243823438244382543826438274382843829438304383143832438334383443835438364383743838438394384043841438424384343844438454384643847438484384943850438514385243853438544385543856438574385843859438604386143862438634386443865438664386743868438694387043871438724387343874438754387643877438784387943880438814388243883438844388543886438874388843889438904389143892438934389443895438964389743898438994390043901439024390343904439054390643907439084390943910439114391243913439144391543916439174391843919439204392143922439234392443925439264392743928439294393043931439324393343934439354393643937439384393943940439414394243943439444394543946439474394843949439504395143952439534395443955439564395743958439594396043961439624396343964439654396643967439684396943970439714397243973439744397543976439774397843979439804398143982439834398443985439864398743988439894399043991439924399343994439954399643997439984399944000440014400244003440044400544006440074400844009440104401144012440134401444015440164401744018440194402044021440224402344024440254402644027440284402944030440314403244033440344403544036440374403844039440404404144042440434404444045440464404744048440494405044051440524405344054440554405644057440584405944060440614406244063440644406544066440674406844069440704407144072440734407444075440764407744078440794408044081440824408344084440854408644087440884408944090440914409244093440944409544096440974409844099441004410144102441034410444105441064410744108441094411044111441124411344114441154411644117441184411944120441214412244123441244412544126441274412844129441304413144132441334413444135441364413744138441394414044141441424414344144441454414644147441484414944150441514415244153441544415544156441574415844159441604416144162441634416444165441664416744168441694417044171441724417344174441754417644177441784417944180441814418244183441844418544186441874418844189441904419144192441934419444195441964419744198441994420044201442024420344204442054420644207442084420944210442114421244213442144421544216442174421844219442204422144222442234422444225442264422744228442294423044231442324423344234442354423644237442384423944240442414424244243442444424544246442474424844249442504425144252442534425444255442564425744258442594426044261442624426344264442654426644267442684426944270442714427244273442744427544276442774427844279442804428144282442834428444285442864428744288442894429044291442924429344294442954429644297442984429944300443014430244303443044430544306443074430844309443104431144312443134431444315443164431744318443194432044321443224432344324443254432644327443284432944330443314433244333443344433544336443374433844339443404434144342443434434444345443464434744348443494435044351443524435344354443554435644357443584435944360443614436244363443644436544366443674436844369443704437144372443734437444375443764437744378443794438044381443824438344384443854438644387443884438944390443914439244393443944439544396443974439844399444004440144402444034440444405444064440744408444094441044411444124441344414444154441644417444184441944420444214442244423444244442544426444274442844429444304443144432444334443444435444364443744438444394444044441444424444344444444454444644447444484444944450444514445244453444544445544456444574445844459444604446144462444634446444465444664446744468444694447044471444724447344474444754447644477444784447944480444814448244483444844448544486444874448844489444904449144492444934449444495444964449744498444994450044501445024450344504445054450644507445084450944510445114451244513445144451544516445174451844519445204452144522445234452444525445264452744528445294453044531445324453344534445354453644537445384453944540445414454244543445444454544546445474454844549445504455144552445534455444555445564455744558445594456044561445624456344564445654456644567445684456944570445714457244573445744457544576445774457844579445804458144582445834458444585445864458744588445894459044591445924459344594445954459644597445984459944600446014460244603446044460544606446074460844609446104461144612446134461444615446164461744618446194462044621446224462344624446254462644627446284462944630446314463244633446344463544636446374463844639446404464144642446434464444645446464464744648446494465044651446524465344654446554465644657446584465944660446614466244663446644466544666446674466844669446704467144672446734467444675446764467744678446794468044681446824468344684446854468644687446884468944690446914469244693446944469544696446974469844699447004470144702447034470444705447064470744708447094471044711447124471344714447154471644717447184471944720447214472244723447244472544726447274472844729447304473144732447334473444735447364473744738447394474044741447424474344744447454474644747447484474944750447514475244753447544475544756447574475844759447604476144762447634476444765447664476744768447694477044771447724477344774447754477644777447784477944780447814478244783447844478544786447874478844789447904479144792447934479444795447964479744798447994480044801448024480344804448054480644807448084480944810448114481244813448144481544816448174481844819448204482144822448234482444825448264482744828448294483044831448324483344834448354483644837448384483944840448414484244843448444484544846448474484844849448504485144852448534485444855448564485744858448594486044861448624486344864448654486644867448684486944870448714487244873448744487544876448774487844879448804488144882448834488444885448864488744888448894489044891448924489344894448954489644897448984489944900449014490244903449044490544906449074490844909449104491144912449134491444915449164491744918449194492044921449224492344924449254492644927449284492944930449314493244933449344493544936449374493844939449404494144942449434494444945449464494744948449494495044951449524495344954449554495644957449584495944960449614496244963449644496544966449674496844969449704497144972449734497444975449764497744978449794498044981449824498344984449854498644987449884498944990449914499244993449944499544996449974499844999450004500145002450034500445005450064500745008450094501045011450124501345014450154501645017450184501945020450214502245023450244502545026450274502845029450304503145032450334503445035450364503745038450394504045041450424504345044450454504645047450484504945050450514505245053450544505545056450574505845059450604506145062450634506445065450664506745068450694507045071450724507345074450754507645077450784507945080450814508245083450844508545086450874508845089450904509145092450934509445095450964509745098450994510045101451024510345104451054510645107451084510945110451114511245113451144511545116451174511845119451204512145122451234512445125451264512745128451294513045131451324513345134451354513645137451384513945140451414514245143451444514545146451474514845149451504515145152451534515445155451564515745158451594516045161451624516345164451654516645167451684516945170451714517245173451744517545176451774517845179451804518145182451834518445185451864518745188451894519045191451924519345194451954519645197451984519945200452014520245203452044520545206452074520845209452104521145212452134521445215452164521745218452194522045221452224522345224452254522645227452284522945230452314523245233452344523545236452374523845239452404524145242452434524445245452464524745248452494525045251452524525345254452554525645257452584525945260452614526245263452644526545266452674526845269452704527145272452734527445275452764527745278452794528045281452824528345284452854528645287452884528945290452914529245293452944529545296452974529845299453004530145302453034530445305453064530745308453094531045311453124531345314453154531645317453184531945320453214532245323453244532545326453274532845329453304533145332453334533445335453364533745338453394534045341453424534345344453454534645347453484534945350453514535245353453544535545356453574535845359453604536145362453634536445365453664536745368453694537045371453724537345374453754537645377453784537945380453814538245383453844538545386453874538845389453904539145392453934539445395453964539745398453994540045401454024540345404454054540645407454084540945410454114541245413454144541545416454174541845419454204542145422454234542445425454264542745428454294543045431454324543345434454354543645437454384543945440454414544245443454444544545446454474544845449454504545145452454534545445455454564545745458454594546045461454624546345464454654546645467454684546945470454714547245473454744547545476454774547845479454804548145482454834548445485454864548745488454894549045491454924549345494454954549645497454984549945500455014550245503455044550545506455074550845509455104551145512455134551445515455164551745518455194552045521455224552345524455254552645527455284552945530455314553245533455344553545536455374553845539455404554145542455434554445545455464554745548455494555045551455524555345554455554555645557455584555945560455614556245563455644556545566455674556845569455704557145572455734557445575455764557745578455794558045581455824558345584455854558645587455884558945590455914559245593455944559545596455974559845599456004560145602456034560445605456064560745608456094561045611456124561345614456154561645617456184561945620456214562245623456244562545626456274562845629456304563145632456334563445635456364563745638456394564045641456424564345644456454564645647456484564945650456514565245653456544565545656456574565845659456604566145662456634566445665456664566745668456694567045671456724567345674456754567645677456784567945680456814568245683456844568545686456874568845689456904569145692456934569445695456964569745698456994570045701457024570345704457054570645707457084570945710457114571245713457144571545716457174571845719457204572145722457234572445725457264572745728457294573045731457324573345734457354573645737457384573945740457414574245743457444574545746457474574845749457504575145752457534575445755457564575745758457594576045761457624576345764457654576645767457684576945770457714577245773457744577545776457774577845779457804578145782457834578445785457864578745788457894579045791457924579345794457954579645797457984579945800458014580245803458044580545806458074580845809458104581145812458134581445815458164581745818458194582045821458224582345824458254582645827458284582945830458314583245833458344583545836458374583845839458404584145842458434584445845458464584745848458494585045851458524585345854458554585645857458584585945860458614586245863458644586545866458674586845869458704587145872458734587445875458764587745878458794588045881458824588345884458854588645887458884588945890458914589245893458944589545896458974589845899459004590145902459034590445905459064590745908459094591045911459124591345914459154591645917459184591945920459214592245923459244592545926459274592845929459304593145932459334593445935459364593745938459394594045941459424594345944459454594645947459484594945950459514595245953459544595545956459574595845959459604596145962459634596445965459664596745968459694597045971459724597345974459754597645977459784597945980459814598245983459844598545986459874598845989459904599145992459934599445995459964599745998459994600046001460024600346004460054600646007460084600946010460114601246013460144601546016460174601846019460204602146022460234602446025460264602746028460294603046031460324603346034460354603646037460384603946040460414604246043460444604546046460474604846049460504605146052460534605446055460564605746058460594606046061460624606346064460654606646067460684606946070460714607246073460744607546076460774607846079460804608146082460834608446085460864608746088460894609046091460924609346094460954609646097460984609946100461014610246103461044610546106461074610846109461104611146112461134611446115461164611746118461194612046121461224612346124461254612646127461284612946130461314613246133461344613546136461374613846139461404614146142461434614446145461464614746148461494615046151461524615346154461554615646157461584615946160461614616246163461644616546166461674616846169461704617146172461734617446175461764617746178461794618046181461824618346184461854618646187461884618946190461914619246193461944619546196461974619846199462004620146202462034620446205462064620746208462094621046211462124621346214462154621646217462184621946220462214622246223462244622546226462274622846229462304623146232462334623446235462364623746238462394624046241462424624346244462454624646247462484624946250462514625246253462544625546256462574625846259462604626146262462634626446265462664626746268462694627046271462724627346274462754627646277462784627946280462814628246283462844628546286462874628846289462904629146292462934629446295462964629746298462994630046301463024630346304463054630646307463084630946310463114631246313463144631546316463174631846319463204632146322463234632446325463264632746328463294633046331463324633346334463354633646337463384633946340463414634246343463444634546346463474634846349463504635146352463534635446355463564635746358463594636046361463624636346364463654636646367463684636946370463714637246373463744637546376463774637846379463804638146382463834638446385463864638746388463894639046391463924639346394463954639646397463984639946400464014640246403464044640546406464074640846409464104641146412464134641446415464164641746418464194642046421464224642346424464254642646427464284642946430464314643246433464344643546436464374643846439464404644146442464434644446445464464644746448464494645046451464524645346454464554645646457464584645946460464614646246463464644646546466464674646846469464704647146472464734647446475464764647746478464794648046481464824648346484464854648646487464884648946490464914649246493464944649546496464974649846499465004650146502465034650446505465064650746508465094651046511465124651346514465154651646517465184651946520465214652246523465244652546526465274652846529465304653146532465334653446535465364653746538465394654046541465424654346544465454654646547465484654946550465514655246553465544655546556465574655846559465604656146562465634656446565465664656746568465694657046571465724657346574465754657646577465784657946580465814658246583465844658546586465874658846589465904659146592465934659446595465964659746598465994660046601466024660346604466054660646607466084660946610466114661246613466144661546616466174661846619466204662146622466234662446625466264662746628466294663046631466324663346634466354663646637466384663946640466414664246643466444664546646466474664846649466504665146652466534665446655466564665746658466594666046661466624666346664466654666646667466684666946670466714667246673466744667546676466774667846679466804668146682466834668446685466864668746688466894669046691466924669346694466954669646697466984669946700467014670246703467044670546706467074670846709467104671146712467134671446715467164671746718467194672046721467224672346724467254672646727467284672946730467314673246733467344673546736467374673846739467404674146742467434674446745467464674746748467494675046751467524675346754467554675646757467584675946760467614676246763467644676546766467674676846769467704677146772467734677446775467764677746778467794678046781467824678346784467854678646787467884678946790467914679246793467944679546796467974679846799468004680146802468034680446805468064680746808468094681046811468124681346814468154681646817468184681946820468214682246823468244682546826468274682846829468304683146832468334683446835468364683746838468394684046841468424684346844468454684646847468484684946850468514685246853468544685546856468574685846859468604686146862468634686446865468664686746868468694687046871468724687346874468754687646877468784687946880468814688246883468844688546886468874688846889468904689146892468934689446895468964689746898468994690046901469024690346904469054690646907469084690946910469114691246913469144691546916469174691846919469204692146922469234692446925469264692746928469294693046931469324693346934469354693646937469384693946940469414694246943469444694546946469474694846949469504695146952469534695446955469564695746958469594696046961469624696346964469654696646967469684696946970469714697246973469744697546976469774697846979469804698146982469834698446985469864698746988469894699046991469924699346994469954699646997469984699947000470014700247003470044700547006470074700847009470104701147012470134701447015470164701747018470194702047021470224702347024470254702647027470284702947030470314703247033470344703547036470374703847039470404704147042470434704447045470464704747048470494705047051470524705347054470554705647057470584705947060470614706247063470644706547066470674706847069470704707147072470734707447075470764707747078470794708047081470824708347084470854708647087470884708947090470914709247093470944709547096470974709847099471004710147102471034710447105471064710747108471094711047111471124711347114471154711647117471184711947120471214712247123471244712547126471274712847129471304713147132471334713447135471364713747138471394714047141471424714347144471454714647147471484714947150471514715247153471544715547156471574715847159471604716147162471634716447165471664716747168471694717047171471724717347174471754717647177471784717947180471814718247183471844718547186471874718847189471904719147192471934719447195471964719747198471994720047201472024720347204472054720647207472084720947210472114721247213472144721547216472174721847219472204722147222472234722447225472264722747228472294723047231472324723347234472354723647237472384723947240472414724247243472444724547246472474724847249472504725147252472534725447255472564725747258472594726047261472624726347264472654726647267472684726947270472714727247273472744727547276472774727847279472804728147282472834728447285472864728747288472894729047291472924729347294472954729647297472984729947300473014730247303473044730547306473074730847309473104731147312473134731447315473164731747318473194732047321473224732347324473254732647327473284732947330473314733247333473344733547336473374733847339473404734147342473434734447345473464734747348473494735047351473524735347354473554735647357473584735947360473614736247363473644736547366473674736847369473704737147372473734737447375473764737747378473794738047381473824738347384473854738647387473884738947390473914739247393473944739547396473974739847399474004740147402474034740447405474064740747408474094741047411474124741347414474154741647417474184741947420474214742247423474244742547426474274742847429474304743147432474334743447435474364743747438474394744047441474424744347444474454744647447474484744947450474514745247453474544745547456474574745847459474604746147462474634746447465474664746747468474694747047471474724747347474474754747647477474784747947480474814748247483474844748547486474874748847489474904749147492474934749447495474964749747498474994750047501475024750347504475054750647507475084750947510475114751247513475144751547516475174751847519475204752147522475234752447525475264752747528475294753047531475324753347534475354753647537475384753947540475414754247543475444754547546475474754847549475504755147552475534755447555475564755747558475594756047561475624756347564475654756647567475684756947570475714757247573475744757547576475774757847579475804758147582475834758447585475864758747588475894759047591475924759347594475954759647597475984759947600476014760247603476044760547606476074760847609476104761147612476134761447615476164761747618476194762047621476224762347624476254762647627476284762947630476314763247633476344763547636476374763847639476404764147642476434764447645476464764747648476494765047651476524765347654476554765647657476584765947660476614766247663476644766547666476674766847669476704767147672476734767447675476764767747678476794768047681476824768347684476854768647687476884768947690476914769247693476944769547696476974769847699477004770147702477034770447705477064770747708477094771047711477124771347714477154771647717477184771947720477214772247723477244772547726477274772847729477304773147732477334773447735477364773747738477394774047741477424774347744477454774647747477484774947750477514775247753477544775547756477574775847759477604776147762477634776447765477664776747768477694777047771477724777347774477754777647777477784777947780477814778247783477844778547786477874778847789477904779147792477934779447795477964779747798477994780047801478024780347804478054780647807478084780947810478114781247813478144781547816478174781847819478204782147822478234782447825478264782747828478294783047831478324783347834478354783647837478384783947840478414784247843478444784547846478474784847849478504785147852478534785447855478564785747858478594786047861478624786347864478654786647867478684786947870478714787247873478744787547876478774787847879478804788147882478834788447885478864788747888478894789047891478924789347894478954789647897478984789947900479014790247903479044790547906479074790847909479104791147912479134791447915479164791747918479194792047921479224792347924479254792647927479284792947930479314793247933479344793547936479374793847939479404794147942479434794447945479464794747948479494795047951479524795347954479554795647957479584795947960479614796247963479644796547966479674796847969479704797147972479734797447975479764797747978479794798047981479824798347984479854798647987479884798947990479914799247993479944799547996479974799847999480004800148002480034800448005480064800748008480094801048011480124801348014480154801648017480184801948020480214802248023480244802548026480274802848029480304803148032480334803448035480364803748038480394804048041480424804348044480454804648047480484804948050480514805248053480544805548056480574805848059480604806148062480634806448065480664806748068480694807048071480724807348074480754807648077480784807948080480814808248083480844808548086480874808848089480904809148092480934809448095480964809748098480994810048101481024810348104481054810648107481084810948110481114811248113481144811548116481174811848119481204812148122481234812448125481264812748128481294813048131481324813348134481354813648137481384813948140481414814248143481444814548146481474814848149481504815148152481534815448155481564815748158481594816048161481624816348164481654816648167481684816948170481714817248173481744817548176481774817848179481804818148182481834818448185481864818748188481894819048191481924819348194481954819648197481984819948200482014820248203482044820548206482074820848209482104821148212482134821448215482164821748218482194822048221482224822348224482254822648227482284822948230482314823248233482344823548236482374823848239482404824148242482434824448245482464824748248482494825048251482524825348254482554825648257482584825948260482614826248263482644826548266482674826848269482704827148272482734827448275482764827748278482794828048281482824828348284482854828648287482884828948290482914829248293482944829548296482974829848299483004830148302483034830448305483064830748308483094831048311483124831348314483154831648317483184831948320483214832248323483244832548326483274832848329483304833148332483334833448335483364833748338483394834048341483424834348344483454834648347483484834948350483514835248353483544835548356483574835848359483604836148362483634836448365483664836748368483694837048371483724837348374483754837648377483784837948380483814838248383483844838548386483874838848389483904839148392483934839448395483964839748398483994840048401484024840348404484054840648407484084840948410484114841248413484144841548416484174841848419484204842148422484234842448425484264842748428484294843048431484324843348434484354843648437484384843948440484414844248443484444844548446484474844848449484504845148452484534845448455484564845748458484594846048461484624846348464484654846648467484684846948470484714847248473484744847548476484774847848479484804848148482484834848448485484864848748488484894849048491484924849348494484954849648497484984849948500485014850248503485044850548506485074850848509485104851148512485134851448515485164851748518485194852048521485224852348524485254852648527485284852948530485314853248533485344853548536485374853848539485404854148542485434854448545485464854748548485494855048551485524855348554485554855648557485584855948560485614856248563485644856548566485674856848569485704857148572485734857448575485764857748578485794858048581485824858348584485854858648587485884858948590485914859248593485944859548596485974859848599486004860148602486034860448605486064860748608486094861048611486124861348614486154861648617486184861948620486214862248623486244862548626486274862848629486304863148632486334863448635486364863748638486394864048641486424864348644486454864648647486484864948650486514865248653486544865548656486574865848659486604866148662486634866448665486664866748668486694867048671486724867348674486754867648677486784867948680486814868248683486844868548686486874868848689486904869148692486934869448695486964869748698486994870048701487024870348704487054870648707487084870948710487114871248713487144871548716487174871848719487204872148722487234872448725487264872748728487294873048731487324873348734487354873648737487384873948740487414874248743487444874548746487474874848749487504875148752487534875448755487564875748758487594876048761487624876348764487654876648767487684876948770487714877248773487744877548776487774877848779487804878148782487834878448785487864878748788487894879048791487924879348794487954879648797487984879948800488014880248803488044880548806488074880848809488104881148812488134881448815488164881748818488194882048821488224882348824488254882648827488284882948830488314883248833488344883548836488374883848839488404884148842488434884448845488464884748848488494885048851488524885348854488554885648857488584885948860488614886248863488644886548866488674886848869488704887148872488734887448875488764887748878488794888048881488824888348884488854888648887488884888948890488914889248893488944889548896488974889848899489004890148902489034890448905489064890748908489094891048911489124891348914489154891648917489184891948920489214892248923489244892548926489274892848929489304893148932489334893448935489364893748938489394894048941489424894348944489454894648947489484894948950489514895248953489544895548956489574895848959489604896148962489634896448965489664896748968489694897048971489724897348974489754897648977489784897948980489814898248983489844898548986489874898848989489904899148992489934899448995489964899748998489994900049001490024900349004490054900649007490084900949010490114901249013490144901549016490174901849019490204902149022490234902449025490264902749028490294903049031490324903349034490354903649037490384903949040490414904249043490444904549046490474904849049490504905149052490534905449055490564905749058490594906049061490624906349064490654906649067490684906949070490714907249073490744907549076490774907849079490804908149082490834908449085490864908749088490894909049091490924909349094490954909649097490984909949100491014910249103491044910549106491074910849109491104911149112491134911449115491164911749118491194912049121491224912349124491254912649127491284912949130491314913249133491344913549136491374913849139491404914149142491434914449145491464914749148491494915049151491524915349154491554915649157491584915949160491614916249163491644916549166491674916849169491704917149172491734917449175491764917749178491794918049181491824918349184491854918649187491884918949190491914919249193491944919549196491974919849199492004920149202492034920449205492064920749208492094921049211492124921349214492154921649217492184921949220492214922249223492244922549226492274922849229492304923149232492334923449235492364923749238492394924049241492424924349244492454924649247492484924949250492514925249253492544925549256492574925849259492604926149262492634926449265492664926749268492694927049271492724927349274492754927649277492784927949280492814928249283492844928549286492874928849289492904929149292492934929449295492964929749298492994930049301493024930349304493054930649307493084930949310493114931249313493144931549316493174931849319493204932149322493234932449325493264932749328493294933049331493324933349334493354933649337493384933949340493414934249343493444934549346493474934849349493504935149352493534935449355493564935749358493594936049361493624936349364493654936649367493684936949370493714937249373493744937549376493774937849379493804938149382493834938449385493864938749388493894939049391493924939349394493954939649397493984939949400494014940249403494044940549406494074940849409494104941149412494134941449415494164941749418494194942049421494224942349424494254942649427494284942949430494314943249433494344943549436494374943849439494404944149442494434944449445494464944749448494494945049451494524945349454494554945649457494584945949460494614946249463494644946549466494674946849469494704947149472494734947449475494764947749478494794948049481494824948349484494854948649487494884948949490494914949249493494944949549496494974949849499495004950149502495034950449505495064950749508495094951049511495124951349514495154951649517495184951949520495214952249523495244952549526495274952849529495304953149532495334953449535495364953749538495394954049541495424954349544495454954649547495484954949550495514955249553495544955549556495574955849559495604956149562495634956449565495664956749568495694957049571495724957349574495754957649577495784957949580495814958249583495844958549586495874958849589495904959149592495934959449595495964959749598495994960049601496024960349604496054960649607496084960949610496114961249613496144961549616496174961849619496204962149622496234962449625496264962749628496294963049631496324963349634496354963649637496384963949640496414964249643496444964549646496474964849649496504965149652496534965449655496564965749658496594966049661496624966349664496654966649667496684966949670496714967249673496744967549676496774967849679496804968149682496834968449685496864968749688496894969049691496924969349694496954969649697496984969949700497014970249703497044970549706497074970849709497104971149712497134971449715497164971749718497194972049721497224972349724497254972649727497284972949730497314973249733497344973549736497374973849739497404974149742497434974449745497464974749748497494975049751497524975349754497554975649757497584975949760497614976249763497644976549766497674976849769497704977149772497734977449775497764977749778497794978049781497824978349784497854978649787497884978949790497914979249793497944979549796497974979849799498004980149802498034980449805498064980749808498094981049811498124981349814498154981649817498184981949820498214982249823498244982549826498274982849829498304983149832498334983449835498364983749838498394984049841498424984349844498454984649847498484984949850498514985249853498544985549856498574985849859498604986149862498634986449865498664986749868498694987049871498724987349874498754987649877498784987949880498814988249883498844988549886498874988849889498904989149892498934989449895498964989749898498994990049901499024990349904499054990649907499084990949910499114991249913499144991549916499174991849919499204992149922499234992449925499264992749928499294993049931499324993349934499354993649937499384993949940499414994249943499444994549946499474994849949499504995149952499534995449955499564995749958499594996049961499624996349964499654996649967499684996949970499714997249973499744997549976499774997849979499804998149982499834998449985499864998749988499894999049991499924999349994499954999649997499984999950000500015000250003500045000550006500075000850009500105001150012500135001450015500165001750018500195002050021500225002350024500255002650027500285002950030500315003250033500345003550036500375003850039500405004150042500435004450045500465004750048500495005050051500525005350054500555005650057500585005950060500615006250063500645006550066500675006850069500705007150072500735007450075500765007750078500795008050081500825008350084500855008650087500885008950090500915009250093500945009550096500975009850099501005010150102501035010450105501065010750108501095011050111501125011350114501155011650117501185011950120501215012250123501245012550126501275012850129501305013150132501335013450135501365013750138501395014050141501425014350144501455014650147501485014950150501515015250153501545015550156501575015850159501605016150162501635016450165501665016750168501695017050171501725017350174501755017650177501785017950180501815018250183501845018550186501875018850189501905019150192501935019450195501965019750198501995020050201502025020350204502055020650207502085020950210502115021250213502145021550216502175021850219502205022150222502235022450225502265022750228502295023050231502325023350234502355023650237502385023950240502415024250243502445024550246502475024850249502505025150252502535025450255502565025750258502595026050261502625026350264502655026650267502685026950270502715027250273502745027550276502775027850279502805028150282502835028450285502865028750288502895029050291502925029350294502955029650297502985029950300503015030250303503045030550306503075030850309503105031150312503135031450315503165031750318503195032050321503225032350324503255032650327503285032950330503315033250333503345033550336503375033850339503405034150342503435034450345503465034750348503495035050351503525035350354503555035650357503585035950360503615036250363503645036550366503675036850369503705037150372503735037450375503765037750378503795038050381503825038350384503855038650387503885038950390503915039250393503945039550396503975039850399504005040150402504035040450405504065040750408504095041050411504125041350414504155041650417504185041950420504215042250423504245042550426504275042850429504305043150432504335043450435504365043750438504395044050441504425044350444504455044650447504485044950450504515045250453504545045550456504575045850459504605046150462504635046450465504665046750468504695047050471504725047350474504755047650477504785047950480504815048250483504845048550486504875048850489504905049150492504935049450495504965049750498504995050050501505025050350504505055050650507505085050950510505115051250513505145051550516505175051850519505205052150522505235052450525505265052750528505295053050531505325053350534505355053650537505385053950540505415054250543505445054550546505475054850549505505055150552505535055450555505565055750558505595056050561505625056350564505655056650567505685056950570505715057250573505745057550576505775057850579505805058150582505835058450585505865058750588505895059050591505925059350594505955059650597505985059950600506015060250603506045060550606506075060850609506105061150612506135061450615506165061750618506195062050621506225062350624506255062650627506285062950630506315063250633506345063550636506375063850639506405064150642506435064450645506465064750648506495065050651506525065350654506555065650657506585065950660506615066250663506645066550666506675066850669506705067150672506735067450675506765067750678506795068050681506825068350684506855068650687506885068950690506915069250693506945069550696506975069850699507005070150702507035070450705507065070750708507095071050711507125071350714507155071650717507185071950720507215072250723507245072550726507275072850729507305073150732507335073450735507365073750738507395074050741507425074350744507455074650747507485074950750507515075250753507545075550756507575075850759507605076150762507635076450765507665076750768507695077050771507725077350774507755077650777507785077950780507815078250783507845078550786507875078850789507905079150792507935079450795507965079750798507995080050801508025080350804508055080650807508085080950810508115081250813508145081550816508175081850819508205082150822508235082450825508265082750828508295083050831508325083350834508355083650837508385083950840508415084250843508445084550846508475084850849508505085150852508535085450855508565085750858508595086050861508625086350864508655086650867508685086950870508715087250873508745087550876508775087850879508805088150882508835088450885508865088750888508895089050891508925089350894508955089650897508985089950900509015090250903509045090550906509075090850909509105091150912509135091450915509165091750918509195092050921509225092350924509255092650927509285092950930509315093250933509345093550936509375093850939509405094150942509435094450945509465094750948509495095050951509525095350954509555095650957509585095950960509615096250963509645096550966509675096850969509705097150972509735097450975509765097750978509795098050981509825098350984509855098650987509885098950990509915099250993509945099550996509975099850999510005100151002510035100451005510065100751008510095101051011510125101351014510155101651017510185101951020510215102251023510245102551026510275102851029510305103151032510335103451035510365103751038510395104051041510425104351044510455104651047510485104951050510515105251053510545105551056510575105851059510605106151062510635106451065510665106751068510695107051071510725107351074510755107651077510785107951080510815108251083510845108551086510875108851089510905109151092510935109451095510965109751098510995110051101511025110351104511055110651107511085110951110511115111251113511145111551116511175111851119511205112151122511235112451125511265112751128511295113051131511325113351134511355113651137511385113951140511415114251143511445114551146511475114851149511505115151152511535115451155511565115751158511595116051161511625116351164511655116651167511685116951170511715117251173511745117551176511775117851179511805118151182511835118451185511865118751188511895119051191511925119351194511955119651197511985119951200512015120251203512045120551206512075120851209512105121151212512135121451215512165121751218512195122051221512225122351224512255122651227512285122951230512315123251233512345123551236512375123851239512405124151242512435124451245512465124751248512495125051251512525125351254512555125651257512585125951260512615126251263512645126551266512675126851269512705127151272512735127451275512765127751278512795128051281512825128351284512855128651287512885128951290512915129251293512945129551296512975129851299513005130151302513035130451305513065130751308513095131051311513125131351314513155131651317513185131951320513215132251323513245132551326513275132851329513305133151332513335133451335513365133751338513395134051341513425134351344513455134651347513485134951350513515135251353513545135551356513575135851359513605136151362513635136451365513665136751368513695137051371513725137351374513755137651377513785137951380513815138251383513845138551386513875138851389513905139151392513935139451395513965139751398513995140051401514025140351404514055140651407514085140951410514115141251413514145141551416514175141851419514205142151422514235142451425514265142751428514295143051431514325143351434514355143651437514385143951440514415144251443514445144551446514475144851449514505145151452514535145451455514565145751458514595146051461514625146351464514655146651467514685146951470514715147251473514745147551476514775147851479514805148151482514835148451485514865148751488514895149051491514925149351494514955149651497514985149951500515015150251503515045150551506515075150851509515105151151512515135151451515515165151751518515195152051521515225152351524515255152651527515285152951530515315153251533515345153551536515375153851539515405154151542515435154451545515465154751548515495155051551515525155351554515555155651557515585155951560515615156251563515645156551566515675156851569515705157151572515735157451575515765157751578515795158051581515825158351584515855158651587515885158951590515915159251593515945159551596515975159851599516005160151602516035160451605516065160751608516095161051611516125161351614516155161651617516185161951620516215162251623516245162551626516275162851629516305163151632516335163451635516365163751638516395164051641516425164351644516455164651647516485164951650516515165251653516545165551656516575165851659516605166151662516635166451665516665166751668516695167051671516725167351674516755167651677516785167951680516815168251683516845168551686516875168851689516905169151692516935169451695516965169751698516995170051701517025170351704517055170651707517085170951710517115171251713517145171551716517175171851719517205172151722517235172451725517265172751728517295173051731517325173351734517355173651737517385173951740517415174251743517445174551746517475174851749517505175151752517535175451755517565175751758517595176051761517625176351764517655176651767517685176951770517715177251773517745177551776517775177851779517805178151782517835178451785517865178751788517895179051791517925179351794517955179651797517985179951800518015180251803518045180551806518075180851809518105181151812518135181451815518165181751818518195182051821518225182351824518255182651827518285182951830518315183251833518345183551836518375183851839518405184151842518435184451845518465184751848518495185051851518525185351854518555185651857518585185951860518615186251863518645186551866518675186851869518705187151872518735187451875518765187751878518795188051881518825188351884518855188651887518885188951890518915189251893518945189551896518975189851899519005190151902519035190451905519065190751908519095191051911519125191351914519155191651917519185191951920519215192251923519245192551926519275192851929519305193151932519335193451935519365193751938519395194051941519425194351944519455194651947519485194951950519515195251953519545195551956519575195851959519605196151962519635196451965519665196751968519695197051971519725197351974519755197651977519785197951980519815198251983519845198551986519875198851989519905199151992519935199451995519965199751998519995200052001520025200352004520055200652007520085200952010520115201252013520145201552016520175201852019520205202152022520235202452025520265202752028520295203052031520325203352034520355203652037520385203952040520415204252043520445204552046520475204852049520505205152052520535205452055520565205752058520595206052061520625206352064520655206652067520685206952070520715207252073520745207552076520775207852079520805208152082520835208452085520865208752088520895209052091520925209352094520955209652097520985209952100521015210252103521045210552106521075210852109521105211152112521135211452115521165211752118521195212052121521225212352124521255212652127521285212952130521315213252133521345213552136521375213852139521405214152142521435214452145521465214752148521495215052151521525215352154521555215652157521585215952160521615216252163521645216552166521675216852169521705217152172521735217452175521765217752178521795218052181521825218352184521855218652187521885218952190521915219252193521945219552196521975219852199522005220152202522035220452205522065220752208522095221052211522125221352214522155221652217522185221952220522215222252223522245222552226522275222852229522305223152232522335223452235522365223752238522395224052241522425224352244522455224652247522485224952250522515225252253522545225552256522575225852259522605226152262522635226452265522665226752268522695227052271522725227352274522755227652277522785227952280522815228252283522845228552286522875228852289522905229152292522935229452295522965229752298522995230052301523025230352304523055230652307523085230952310523115231252313523145231552316523175231852319523205232152322523235232452325523265232752328523295233052331523325233352334523355233652337523385233952340523415234252343523445234552346523475234852349523505235152352523535235452355523565235752358523595236052361523625236352364523655236652367523685236952370523715237252373523745237552376523775237852379523805238152382523835238452385523865238752388523895239052391523925239352394523955239652397523985239952400524015240252403524045240552406524075240852409524105241152412524135241452415524165241752418524195242052421524225242352424524255242652427524285242952430524315243252433524345243552436524375243852439524405244152442524435244452445524465244752448524495245052451524525245352454524555245652457524585245952460524615246252463524645246552466524675246852469524705247152472524735247452475524765247752478524795248052481524825248352484524855248652487524885248952490524915249252493524945249552496524975249852499525005250152502525035250452505525065250752508525095251052511525125251352514525155251652517525185251952520525215252252523525245252552526525275252852529525305253152532525335253452535525365253752538525395254052541525425254352544525455254652547525485254952550525515255252553525545255552556525575255852559525605256152562525635256452565525665256752568525695257052571525725257352574525755257652577525785257952580525815258252583525845258552586525875258852589525905259152592525935259452595525965259752598525995260052601526025260352604526055260652607526085260952610526115261252613526145261552616526175261852619526205262152622526235262452625526265262752628526295263052631526325263352634526355263652637526385263952640526415264252643526445264552646526475264852649526505265152652526535265452655526565265752658526595266052661526625266352664526655266652667526685266952670526715267252673526745267552676526775267852679526805268152682526835268452685526865268752688526895269052691526925269352694526955269652697526985269952700527015270252703527045270552706527075270852709527105271152712527135271452715527165271752718527195272052721527225272352724527255272652727527285272952730527315273252733527345273552736527375273852739527405274152742527435274452745527465274752748527495275052751527525275352754527555275652757527585275952760527615276252763527645276552766527675276852769527705277152772527735277452775527765277752778527795278052781527825278352784527855278652787527885278952790527915279252793527945279552796527975279852799528005280152802528035280452805528065280752808528095281052811528125281352814528155281652817528185281952820528215282252823528245282552826528275282852829528305283152832528335283452835528365283752838528395284052841528425284352844528455284652847528485284952850528515285252853528545285552856528575285852859528605286152862528635286452865528665286752868528695287052871528725287352874528755287652877528785287952880528815288252883528845288552886528875288852889528905289152892528935289452895528965289752898528995290052901529025290352904529055290652907529085290952910529115291252913529145291552916529175291852919529205292152922529235292452925529265292752928529295293052931529325293352934529355293652937529385293952940529415294252943529445294552946529475294852949529505295152952529535295452955529565295752958529595296052961529625296352964529655296652967529685296952970529715297252973529745297552976529775297852979529805298152982529835298452985529865298752988529895299052991529925299352994529955299652997529985299953000530015300253003530045300553006530075300853009530105301153012530135301453015530165301753018530195302053021530225302353024530255302653027530285302953030530315303253033530345303553036530375303853039530405304153042530435304453045530465304753048530495305053051530525305353054530555305653057530585305953060530615306253063530645306553066530675306853069530705307153072530735307453075530765307753078530795308053081530825308353084530855308653087530885308953090530915309253093530945309553096530975309853099531005310153102531035310453105531065310753108531095311053111531125311353114531155311653117531185311953120531215312253123531245312553126531275312853129531305313153132531335313453135531365313753138531395314053141531425314353144531455314653147531485314953150531515315253153531545315553156531575315853159531605316153162531635316453165531665316753168531695317053171531725317353174531755317653177531785317953180531815318253183531845318553186531875318853189531905319153192531935319453195531965319753198531995320053201532025320353204532055320653207532085320953210532115321253213532145321553216532175321853219532205322153222532235322453225532265322753228532295323053231532325323353234532355323653237532385323953240532415324253243532445324553246532475324853249532505325153252532535325453255532565325753258532595326053261532625326353264532655326653267532685326953270532715327253273532745327553276532775327853279532805328153282532835328453285532865328753288532895329053291532925329353294532955329653297532985329953300533015330253303533045330553306533075330853309533105331153312533135331453315533165331753318533195332053321533225332353324533255332653327533285332953330533315333253333533345333553336533375333853339533405334153342533435334453345533465334753348533495335053351533525335353354533555335653357533585335953360533615336253363533645336553366533675336853369533705337153372533735337453375533765337753378533795338053381533825338353384533855338653387533885338953390533915339253393533945339553396533975339853399534005340153402534035340453405534065340753408534095341053411534125341353414534155341653417534185341953420534215342253423534245342553426534275342853429534305343153432534335343453435534365343753438534395344053441534425344353444534455344653447534485344953450534515345253453534545345553456534575345853459534605346153462534635346453465534665346753468534695347053471534725347353474534755347653477534785347953480534815348253483534845348553486534875348853489534905349153492534935349453495534965349753498534995350053501535025350353504535055350653507535085350953510535115351253513535145351553516535175351853519535205352153522535235352453525535265352753528535295353053531535325353353534535355353653537535385353953540535415354253543535445354553546535475354853549535505355153552535535355453555535565355753558535595356053561535625356353564535655356653567535685356953570535715357253573535745357553576535775357853579535805358153582535835358453585535865358753588535895359053591535925359353594535955359653597535985359953600536015360253603536045360553606536075360853609536105361153612536135361453615536165361753618536195362053621536225362353624536255362653627536285362953630536315363253633536345363553636536375363853639536405364153642536435364453645536465364753648536495365053651536525365353654536555365653657536585365953660536615366253663536645366553666536675366853669536705367153672536735367453675536765367753678536795368053681536825368353684536855368653687536885368953690536915369253693536945369553696536975369853699537005370153702537035370453705537065370753708537095371053711537125371353714537155371653717537185371953720537215372253723537245372553726537275372853729537305373153732537335373453735537365373753738537395374053741537425374353744537455374653747537485374953750537515375253753537545375553756537575375853759537605376153762537635376453765537665376753768537695377053771537725377353774537755377653777537785377953780537815378253783537845378553786537875378853789537905379153792537935379453795537965379753798537995380053801538025380353804538055380653807538085380953810538115381253813538145381553816538175381853819538205382153822538235382453825538265382753828538295383053831538325383353834538355383653837538385383953840538415384253843538445384553846538475384853849538505385153852538535385453855538565385753858538595386053861538625386353864538655386653867538685386953870538715387253873538745387553876538775387853879538805388153882538835388453885538865388753888538895389053891538925389353894538955389653897538985389953900539015390253903539045390553906539075390853909539105391153912539135391453915539165391753918539195392053921539225392353924539255392653927539285392953930539315393253933539345393553936539375393853939539405394153942539435394453945539465394753948539495395053951539525395353954539555395653957539585395953960539615396253963539645396553966539675396853969539705397153972539735397453975539765397753978539795398053981539825398353984539855398653987539885398953990539915399253993539945399553996539975399853999540005400154002540035400454005540065400754008540095401054011540125401354014540155401654017540185401954020540215402254023540245402554026540275402854029540305403154032540335403454035540365403754038540395404054041540425404354044540455404654047540485404954050540515405254053540545405554056540575405854059540605406154062540635406454065540665406754068540695407054071540725407354074540755407654077540785407954080540815408254083540845408554086540875408854089540905409154092540935409454095540965409754098540995410054101541025410354104541055410654107541085410954110541115411254113541145411554116541175411854119541205412154122541235412454125541265412754128541295413054131541325413354134541355413654137541385413954140541415414254143541445414554146541475414854149541505415154152541535415454155541565415754158541595416054161541625416354164541655416654167541685416954170541715417254173541745417554176541775417854179541805418154182541835418454185541865418754188541895419054191541925419354194541955419654197541985419954200542015420254203542045420554206542075420854209542105421154212542135421454215542165421754218542195422054221542225422354224542255422654227542285422954230542315423254233542345423554236542375423854239542405424154242542435424454245542465424754248542495425054251542525425354254542555425654257542585425954260542615426254263542645426554266542675426854269542705427154272542735427454275542765427754278542795428054281542825428354284542855428654287542885428954290542915429254293542945429554296542975429854299543005430154302543035430454305543065430754308543095431054311543125431354314543155431654317543185431954320543215432254323543245432554326543275432854329543305433154332543335433454335543365433754338543395434054341543425434354344543455434654347543485434954350543515435254353543545435554356543575435854359543605436154362543635436454365543665436754368543695437054371543725437354374543755437654377543785437954380543815438254383543845438554386543875438854389543905439154392543935439454395543965439754398543995440054401544025440354404544055440654407544085440954410544115441254413544145441554416544175441854419544205442154422544235442454425544265442754428544295443054431544325443354434544355443654437544385443954440544415444254443544445444554446544475444854449544505445154452544535445454455544565445754458544595446054461544625446354464544655446654467544685446954470544715447254473544745447554476544775447854479544805448154482544835448454485544865448754488544895449054491544925449354494544955449654497544985449954500545015450254503545045450554506545075450854509545105451154512545135451454515545165451754518545195452054521545225452354524545255452654527545285452954530545315453254533545345453554536545375453854539545405454154542545435454454545545465454754548545495455054551545525455354554545555455654557545585455954560545615456254563545645456554566545675456854569545705457154572545735457454575545765457754578545795458054581545825458354584545855458654587545885458954590545915459254593545945459554596545975459854599546005460154602546035460454605546065460754608546095461054611546125461354614546155461654617546185461954620546215462254623546245462554626546275462854629546305463154632546335463454635546365463754638546395464054641546425464354644546455464654647546485464954650546515465254653546545465554656546575465854659546605466154662546635466454665546665466754668546695467054671546725467354674546755467654677546785467954680546815468254683546845468554686546875468854689546905469154692546935469454695546965469754698546995470054701547025470354704547055470654707547085470954710547115471254713547145471554716547175471854719547205472154722547235472454725547265472754728547295473054731547325473354734547355473654737547385473954740547415474254743547445474554746547475474854749547505475154752547535475454755547565475754758547595476054761547625476354764547655476654767547685476954770547715477254773547745477554776547775477854779547805478154782547835478454785547865478754788547895479054791547925479354794547955479654797547985479954800548015480254803548045480554806548075480854809548105481154812548135481454815548165481754818548195482054821548225482354824548255482654827548285482954830548315483254833548345483554836548375483854839548405484154842548435484454845548465484754848548495485054851548525485354854548555485654857548585485954860548615486254863548645486554866548675486854869548705487154872548735487454875548765487754878548795488054881548825488354884548855488654887548885488954890548915489254893548945489554896548975489854899549005490154902549035490454905549065490754908549095491054911549125491354914549155491654917549185491954920549215492254923549245492554926549275492854929549305493154932549335493454935549365493754938549395494054941549425494354944549455494654947549485494954950549515495254953549545495554956549575495854959549605496154962549635496454965549665496754968549695497054971549725497354974549755497654977549785497954980549815498254983549845498554986549875498854989549905499154992549935499454995549965499754998549995500055001550025500355004550055500655007550085500955010550115501255013550145501555016550175501855019550205502155022550235502455025550265502755028550295503055031550325503355034550355503655037550385503955040550415504255043550445504555046550475504855049550505505155052550535505455055550565505755058550595506055061550625506355064550655506655067550685506955070550715507255073550745507555076550775507855079550805508155082550835508455085550865508755088550895509055091550925509355094550955509655097550985509955100551015510255103551045510555106551075510855109551105511155112551135511455115551165511755118551195512055121551225512355124551255512655127551285512955130551315513255133551345513555136551375513855139551405514155142551435514455145551465514755148551495515055151551525515355154551555515655157551585515955160551615516255163551645516555166551675516855169551705517155172551735517455175551765517755178551795518055181551825518355184551855518655187551885518955190551915519255193551945519555196551975519855199552005520155202552035520455205552065520755208552095521055211552125521355214552155521655217552185521955220552215522255223552245522555226552275522855229552305523155232552335523455235552365523755238552395524055241552425524355244552455524655247552485524955250552515525255253552545525555256552575525855259552605526155262552635526455265552665526755268552695527055271552725527355274552755527655277552785527955280552815528255283552845528555286552875528855289552905529155292552935529455295552965529755298552995530055301553025530355304553055530655307553085530955310553115531255313553145531555316553175531855319553205532155322553235532455325553265532755328553295533055331553325533355334553355533655337553385533955340553415534255343553445534555346553475534855349553505535155352553535535455355553565535755358553595536055361553625536355364553655536655367553685536955370553715537255373553745537555376553775537855379553805538155382553835538455385553865538755388553895539055391553925539355394553955539655397553985539955400554015540255403554045540555406554075540855409554105541155412554135541455415554165541755418554195542055421554225542355424554255542655427554285542955430554315543255433554345543555436554375543855439554405544155442554435544455445554465544755448554495545055451554525545355454554555545655457554585545955460554615546255463554645546555466554675546855469554705547155472554735547455475554765547755478554795548055481554825548355484554855548655487554885548955490554915549255493554945549555496554975549855499555005550155502555035550455505555065550755508555095551055511555125551355514555155551655517555185551955520555215552255523555245552555526555275552855529555305553155532555335553455535555365553755538555395554055541555425554355544555455554655547555485554955550555515555255553555545555555556555575555855559555605556155562555635556455565555665556755568555695557055571555725557355574555755557655577555785557955580555815558255583555845558555586555875558855589555905559155592555935559455595555965559755598555995560055601556025560355604556055560655607556085560955610556115561255613556145561555616556175561855619556205562155622556235562455625556265562755628556295563055631556325563355634556355563655637556385563955640556415564255643556445564555646556475564855649556505565155652556535565455655556565565755658556595566055661556625566355664556655566655667556685566955670556715567255673556745567555676556775567855679556805568155682556835568455685556865568755688556895569055691556925569355694556955569655697556985569955700557015570255703557045570555706557075570855709557105571155712557135571455715557165571755718557195572055721557225572355724557255572655727557285572955730557315573255733557345573555736557375573855739557405574155742557435574455745557465574755748557495575055751557525575355754557555575655757557585575955760557615576255763557645576555766557675576855769557705577155772557735577455775557765577755778557795578055781557825578355784557855578655787557885578955790557915579255793557945579555796557975579855799558005580155802558035580455805558065580755808558095581055811558125581355814558155581655817558185581955820558215582255823558245582555826558275582855829558305583155832558335583455835558365583755838558395584055841558425584355844558455584655847558485584955850558515585255853558545585555856558575585855859558605586155862558635586455865558665586755868558695587055871558725587355874558755587655877558785587955880558815588255883558845588555886558875588855889558905589155892558935589455895558965589755898558995590055901559025590355904559055590655907559085590955910559115591255913559145591555916559175591855919559205592155922559235592455925559265592755928559295593055931559325593355934559355593655937559385593955940559415594255943559445594555946559475594855949559505595155952559535595455955559565595755958559595596055961559625596355964559655596655967559685596955970559715597255973559745597555976559775597855979559805598155982559835598455985559865598755988559895599055991559925599355994559955599655997559985599956000560015600256003560045600556006560075600856009560105601156012560135601456015560165601756018560195602056021560225602356024560255602656027560285602956030560315603256033560345603556036560375603856039560405604156042560435604456045560465604756048560495605056051560525605356054560555605656057560585605956060560615606256063560645606556066560675606856069560705607156072560735607456075560765607756078560795608056081560825608356084560855608656087560885608956090560915609256093560945609556096560975609856099561005610156102561035610456105561065610756108561095611056111561125611356114561155611656117561185611956120561215612256123561245612556126561275612856129561305613156132561335613456135561365613756138561395614056141561425614356144561455614656147561485614956150561515615256153561545615556156561575615856159561605616156162561635616456165561665616756168561695617056171561725617356174561755617656177561785617956180561815618256183561845618556186561875618856189561905619156192561935619456195561965619756198561995620056201562025620356204562055620656207562085620956210562115621256213562145621556216562175621856219562205622156222562235622456225562265622756228562295623056231562325623356234562355623656237562385623956240562415624256243562445624556246562475624856249562505625156252562535625456255562565625756258562595626056261562625626356264562655626656267562685626956270562715627256273562745627556276562775627856279562805628156282562835628456285562865628756288562895629056291562925629356294562955629656297562985629956300563015630256303563045630556306563075630856309563105631156312563135631456315563165631756318563195632056321563225632356324563255632656327563285632956330563315633256333563345633556336563375633856339563405634156342563435634456345563465634756348563495635056351563525635356354563555635656357563585635956360563615636256363563645636556366563675636856369563705637156372563735637456375563765637756378563795638056381563825638356384563855638656387563885638956390563915639256393563945639556396563975639856399564005640156402564035640456405564065640756408564095641056411564125641356414564155641656417564185641956420564215642256423564245642556426564275642856429564305643156432564335643456435564365643756438564395644056441564425644356444564455644656447564485644956450564515645256453564545645556456564575645856459564605646156462564635646456465564665646756468564695647056471564725647356474564755647656477564785647956480564815648256483564845648556486564875648856489564905649156492564935649456495564965649756498564995650056501565025650356504565055650656507565085650956510565115651256513565145651556516565175651856519565205652156522565235652456525565265652756528565295653056531565325653356534565355653656537565385653956540565415654256543565445654556546565475654856549565505655156552565535655456555565565655756558565595656056561565625656356564565655656656567565685656956570565715657256573565745657556576565775657856579565805658156582565835658456585565865658756588565895659056591565925659356594565955659656597565985659956600566015660256603566045660556606566075660856609566105661156612566135661456615566165661756618566195662056621566225662356624566255662656627566285662956630566315663256633566345663556636566375663856639566405664156642566435664456645566465664756648566495665056651566525665356654566555665656657566585665956660566615666256663566645666556666566675666856669566705667156672566735667456675566765667756678566795668056681566825668356684566855668656687566885668956690566915669256693566945669556696566975669856699567005670156702567035670456705567065670756708567095671056711567125671356714567155671656717567185671956720567215672256723567245672556726567275672856729567305673156732567335673456735567365673756738567395674056741567425674356744567455674656747567485674956750567515675256753567545675556756567575675856759567605676156762567635676456765567665676756768567695677056771567725677356774567755677656777567785677956780567815678256783567845678556786567875678856789567905679156792567935679456795567965679756798567995680056801568025680356804568055680656807568085680956810568115681256813568145681556816568175681856819568205682156822568235682456825568265682756828568295683056831568325683356834568355683656837568385683956840568415684256843568445684556846568475684856849568505685156852568535685456855568565685756858568595686056861568625686356864568655686656867568685686956870568715687256873568745687556876568775687856879568805688156882568835688456885568865688756888568895689056891568925689356894568955689656897568985689956900569015690256903569045690556906569075690856909569105691156912569135691456915569165691756918569195692056921569225692356924569255692656927569285692956930569315693256933569345693556936569375693856939569405694156942569435694456945569465694756948569495695056951569525695356954569555695656957569585695956960569615696256963569645696556966569675696856969569705697156972569735697456975569765697756978569795698056981569825698356984569855698656987569885698956990569915699256993569945699556996569975699856999570005700157002570035700457005570065700757008570095701057011570125701357014570155701657017570185701957020570215702257023570245702557026570275702857029570305703157032570335703457035570365703757038570395704057041570425704357044570455704657047570485704957050570515705257053570545705557056570575705857059570605706157062570635706457065570665706757068570695707057071570725707357074570755707657077570785707957080570815708257083570845708557086570875708857089570905709157092570935709457095570965709757098570995710057101571025710357104571055710657107571085710957110571115711257113571145711557116571175711857119571205712157122571235712457125571265712757128571295713057131571325713357134571355713657137571385713957140571415714257143571445714557146571475714857149571505715157152571535715457155571565715757158571595716057161571625716357164571655716657167571685716957170571715717257173571745717557176571775717857179571805718157182571835718457185571865718757188571895719057191571925719357194571955719657197571985719957200572015720257203572045720557206572075720857209572105721157212572135721457215572165721757218572195722057221572225722357224572255722657227572285722957230572315723257233572345723557236572375723857239572405724157242572435724457245572465724757248572495725057251572525725357254572555725657257572585725957260572615726257263572645726557266572675726857269572705727157272572735727457275572765727757278572795728057281572825728357284572855728657287572885728957290572915729257293572945729557296572975729857299573005730157302573035730457305573065730757308573095731057311573125731357314573155731657317573185731957320573215732257323573245732557326573275732857329573305733157332573335733457335573365733757338573395734057341573425734357344573455734657347573485734957350573515735257353573545735557356573575735857359573605736157362573635736457365573665736757368573695737057371573725737357374573755737657377573785737957380573815738257383573845738557386573875738857389573905739157392573935739457395573965739757398573995740057401574025740357404574055740657407574085740957410574115741257413574145741557416574175741857419574205742157422574235742457425574265742757428574295743057431574325743357434574355743657437574385743957440574415744257443574445744557446574475744857449574505745157452574535745457455574565745757458574595746057461574625746357464574655746657467574685746957470574715747257473574745747557476574775747857479574805748157482574835748457485574865748757488574895749057491574925749357494574955749657497574985749957500575015750257503575045750557506575075750857509575105751157512575135751457515575165751757518575195752057521575225752357524575255752657527575285752957530575315753257533575345753557536575375753857539575405754157542575435754457545575465754757548575495755057551575525755357554575555755657557575585755957560575615756257563575645756557566575675756857569575705757157572575735757457575575765757757578575795758057581575825758357584575855758657587575885758957590575915759257593575945759557596575975759857599576005760157602576035760457605576065760757608576095761057611576125761357614576155761657617576185761957620576215762257623576245762557626576275762857629576305763157632576335763457635576365763757638576395764057641576425764357644576455764657647576485764957650576515765257653576545765557656576575765857659576605766157662576635766457665576665766757668576695767057671576725767357674576755767657677576785767957680576815768257683576845768557686576875768857689576905769157692576935769457695576965769757698576995770057701577025770357704577055770657707577085770957710577115771257713577145771557716577175771857719577205772157722577235772457725577265772757728577295773057731577325773357734577355773657737577385773957740577415774257743577445774557746577475774857749577505775157752577535775457755577565775757758577595776057761577625776357764577655776657767577685776957770577715777257773577745777557776577775777857779577805778157782577835778457785577865778757788577895779057791577925779357794577955779657797577985779957800578015780257803578045780557806578075780857809578105781157812578135781457815578165781757818578195782057821578225782357824578255782657827578285782957830578315783257833578345783557836578375783857839578405784157842578435784457845578465784757848578495785057851578525785357854578555785657857578585785957860578615786257863578645786557866578675786857869578705787157872578735787457875578765787757878578795788057881578825788357884578855788657887578885788957890578915789257893578945789557896578975789857899579005790157902579035790457905579065790757908579095791057911579125791357914579155791657917579185791957920579215792257923579245792557926579275792857929579305793157932579335793457935579365793757938579395794057941579425794357944579455794657947579485794957950579515795257953579545795557956579575795857959579605796157962579635796457965579665796757968579695797057971579725797357974579755797657977579785797957980579815798257983579845798557986579875798857989579905799157992579935799457995579965799757998579995800058001580025800358004580055800658007580085800958010580115801258013580145801558016580175801858019580205802158022580235802458025580265802758028580295803058031580325803358034580355803658037580385803958040580415804258043580445804558046580475804858049580505805158052580535805458055580565805758058580595806058061580625806358064580655806658067580685806958070580715807258073580745807558076580775807858079580805808158082580835808458085580865808758088580895809058091580925809358094580955809658097580985809958100581015810258103581045810558106581075810858109581105811158112581135811458115581165811758118581195812058121581225812358124581255812658127581285812958130581315813258133581345813558136581375813858139581405814158142581435814458145581465814758148581495815058151581525815358154581555815658157581585815958160581615816258163581645816558166581675816858169581705817158172581735817458175581765817758178581795818058181581825818358184581855818658187581885818958190581915819258193581945819558196581975819858199582005820158202582035820458205582065820758208582095821058211582125821358214582155821658217582185821958220582215822258223582245822558226582275822858229582305823158232582335823458235582365823758238582395824058241582425824358244582455824658247582485824958250582515825258253582545825558256582575825858259582605826158262582635826458265582665826758268582695827058271582725827358274582755827658277582785827958280582815828258283582845828558286582875828858289582905829158292582935829458295582965829758298582995830058301583025830358304583055830658307583085830958310583115831258313583145831558316583175831858319583205832158322583235832458325583265832758328583295833058331583325833358334583355833658337583385833958340583415834258343583445834558346583475834858349583505835158352583535835458355583565835758358583595836058361583625836358364583655836658367583685836958370583715837258373583745837558376583775837858379583805838158382583835838458385583865838758388583895839058391583925839358394583955839658397583985839958400584015840258403584045840558406584075840858409584105841158412584135841458415584165841758418584195842058421584225842358424584255842658427584285842958430584315843258433584345843558436584375843858439584405844158442584435844458445584465844758448584495845058451584525845358454584555845658457584585845958460584615846258463584645846558466584675846858469584705847158472584735847458475584765847758478584795848058481584825848358484584855848658487584885848958490584915849258493584945849558496584975849858499585005850158502585035850458505585065850758508585095851058511585125851358514585155851658517585185851958520585215852258523585245852558526585275852858529585305853158532585335853458535585365853758538585395854058541585425854358544585455854658547585485854958550585515855258553585545855558556585575855858559585605856158562585635856458565585665856758568585695857058571585725857358574585755857658577585785857958580585815858258583585845858558586585875858858589585905859158592585935859458595585965859758598585995860058601586025860358604586055860658607586085860958610586115861258613586145861558616586175861858619586205862158622586235862458625586265862758628586295863058631586325863358634586355863658637586385863958640586415864258643586445864558646586475864858649586505865158652586535865458655586565865758658586595866058661586625866358664586655866658667586685866958670586715867258673586745867558676586775867858679586805868158682586835868458685586865868758688586895869058691586925869358694586955869658697586985869958700587015870258703587045870558706587075870858709587105871158712587135871458715587165871758718587195872058721587225872358724587255872658727587285872958730587315873258733587345873558736587375873858739587405874158742587435874458745587465874758748587495875058751587525875358754587555875658757587585875958760587615876258763587645876558766587675876858769587705877158772587735877458775587765877758778587795878058781587825878358784587855878658787587885878958790587915879258793587945879558796587975879858799588005880158802588035880458805588065880758808588095881058811588125881358814588155881658817588185881958820588215882258823588245882558826588275882858829588305883158832588335883458835588365883758838588395884058841588425884358844588455884658847588485884958850588515885258853588545885558856588575885858859588605886158862588635886458865588665886758868588695887058871588725887358874588755887658877588785887958880588815888258883588845888558886588875888858889588905889158892588935889458895588965889758898588995890058901589025890358904589055890658907589085890958910589115891258913589145891558916589175891858919589205892158922589235892458925589265892758928589295893058931589325893358934589355893658937589385893958940589415894258943589445894558946589475894858949589505895158952589535895458955589565895758958589595896058961589625896358964589655896658967589685896958970589715897258973589745897558976589775897858979589805898158982589835898458985589865898758988589895899058991589925899358994589955899658997589985899959000590015900259003590045900559006590075900859009590105901159012590135901459015590165901759018590195902059021590225902359024590255902659027590285902959030590315903259033590345903559036590375903859039590405904159042590435904459045590465904759048590495905059051590525905359054590555905659057590585905959060590615906259063590645906559066590675906859069590705907159072590735907459075590765907759078590795908059081590825908359084590855908659087590885908959090590915909259093590945909559096590975909859099591005910159102591035910459105591065910759108591095911059111591125911359114591155911659117591185911959120591215912259123591245912559126591275912859129591305913159132591335913459135591365913759138591395914059141591425914359144591455914659147591485914959150591515915259153591545915559156591575915859159591605916159162591635916459165591665916759168591695917059171591725917359174591755917659177591785917959180591815918259183591845918559186591875918859189591905919159192591935919459195591965919759198591995920059201592025920359204592055920659207592085920959210592115921259213592145921559216592175921859219592205922159222592235922459225592265922759228592295923059231592325923359234592355923659237592385923959240592415924259243592445924559246592475924859249592505925159252592535925459255592565925759258592595926059261592625926359264592655926659267592685926959270592715927259273592745927559276592775927859279592805928159282592835928459285592865928759288592895929059291592925929359294592955929659297592985929959300593015930259303593045930559306593075930859309593105931159312593135931459315593165931759318593195932059321593225932359324593255932659327593285932959330593315933259333593345933559336593375933859339593405934159342593435934459345593465934759348593495935059351593525935359354593555935659357593585935959360593615936259363593645936559366593675936859369593705937159372593735937459375593765937759378593795938059381593825938359384593855938659387593885938959390593915939259393593945939559396593975939859399594005940159402594035940459405594065940759408594095941059411594125941359414594155941659417594185941959420594215942259423594245942559426594275942859429594305943159432594335943459435594365943759438594395944059441594425944359444594455944659447594485944959450594515945259453594545945559456594575945859459594605946159462594635946459465594665946759468594695947059471594725947359474594755947659477594785947959480594815948259483594845948559486594875948859489594905949159492594935949459495594965949759498594995950059501595025950359504595055950659507595085950959510595115951259513595145951559516595175951859519595205952159522595235952459525595265952759528595295953059531595325953359534595355953659537595385953959540595415954259543595445954559546595475954859549595505955159552595535955459555595565955759558595595956059561595625956359564595655956659567595685956959570595715957259573595745957559576595775957859579595805958159582595835958459585595865958759588595895959059591595925959359594595955959659597595985959959600596015960259603596045960559606596075960859609596105961159612596135961459615596165961759618596195962059621596225962359624596255962659627596285962959630596315963259633596345963559636596375963859639596405964159642596435964459645596465964759648596495965059651596525965359654596555965659657596585965959660596615966259663596645966559666596675966859669596705967159672596735967459675596765967759678596795968059681596825968359684596855968659687596885968959690596915969259693596945969559696596975969859699597005970159702597035970459705597065970759708597095971059711597125971359714597155971659717597185971959720597215972259723597245972559726597275972859729597305973159732597335973459735597365973759738597395974059741597425974359744597455974659747597485974959750597515975259753597545975559756597575975859759597605976159762597635976459765597665976759768597695977059771597725977359774597755977659777597785977959780597815978259783597845978559786597875978859789597905979159792597935979459795597965979759798597995980059801598025980359804598055980659807598085980959810598115981259813598145981559816598175981859819598205982159822598235982459825598265982759828598295983059831598325983359834598355983659837598385983959840598415984259843598445984559846598475984859849598505985159852598535985459855598565985759858598595986059861598625986359864598655986659867598685986959870598715987259873598745987559876598775987859879598805988159882598835988459885598865988759888598895989059891598925989359894598955989659897598985989959900599015990259903599045990559906599075990859909599105991159912599135991459915599165991759918599195992059921599225992359924599255992659927599285992959930599315993259933599345993559936599375993859939599405994159942599435994459945599465994759948599495995059951599525995359954599555995659957599585995959960599615996259963599645996559966599675996859969599705997159972599735997459975599765997759978599795998059981599825998359984599855998659987599885998959990599915999259993599945999559996599975999859999600006000160002600036000460005600066000760008600096001060011600126001360014600156001660017600186001960020600216002260023600246002560026600276002860029600306003160032600336003460035600366003760038600396004060041600426004360044600456004660047600486004960050600516005260053600546005560056600576005860059600606006160062600636006460065600666006760068600696007060071600726007360074600756007660077600786007960080600816008260083600846008560086600876008860089600906009160092600936009460095600966009760098600996010060101601026010360104601056010660107601086010960110601116011260113601146011560116601176011860119601206012160122601236012460125601266012760128601296013060131601326013360134601356013660137601386013960140601416014260143601446014560146601476014860149601506015160152601536015460155601566015760158601596016060161601626016360164601656016660167601686016960170601716017260173601746017560176601776017860179601806018160182601836018460185601866018760188601896019060191601926019360194601956019660197601986019960200602016020260203602046020560206602076020860209602106021160212602136021460215602166021760218602196022060221602226022360224602256022660227602286022960230602316023260233602346023560236602376023860239602406024160242602436024460245602466024760248602496025060251602526025360254602556025660257602586025960260602616026260263602646026560266602676026860269602706027160272602736027460275602766027760278602796028060281602826028360284602856028660287602886028960290602916029260293602946029560296602976029860299603006030160302603036030460305603066030760308603096031060311603126031360314603156031660317603186031960320603216032260323603246032560326603276032860329603306033160332603336033460335603366033760338603396034060341603426034360344603456034660347603486034960350603516035260353603546035560356603576035860359603606036160362603636036460365603666036760368603696037060371603726037360374603756037660377603786037960380603816038260383603846038560386603876038860389603906039160392603936039460395603966039760398603996040060401604026040360404604056040660407604086040960410604116041260413604146041560416604176041860419604206042160422604236042460425604266042760428604296043060431604326043360434604356043660437604386043960440604416044260443604446044560446604476044860449604506045160452604536045460455604566045760458604596046060461604626046360464604656046660467604686046960470604716047260473604746047560476604776047860479604806048160482604836048460485604866048760488604896049060491604926049360494604956049660497604986049960500605016050260503605046050560506605076050860509605106051160512605136051460515605166051760518605196052060521605226052360524605256052660527605286052960530605316053260533605346053560536605376053860539605406054160542605436054460545605466054760548605496055060551605526055360554605556055660557605586055960560605616056260563605646056560566605676056860569605706057160572605736057460575605766057760578605796058060581605826058360584605856058660587605886058960590605916059260593605946059560596605976059860599606006060160602606036060460605606066060760608606096061060611606126061360614606156061660617606186061960620606216062260623606246062560626606276062860629606306063160632606336063460635606366063760638606396064060641606426064360644606456064660647606486064960650606516065260653606546065560656606576065860659606606066160662606636066460665606666066760668606696067060671606726067360674606756067660677606786067960680606816068260683606846068560686606876068860689606906069160692606936069460695606966069760698606996070060701607026070360704607056070660707607086070960710607116071260713607146071560716607176071860719607206072160722607236072460725607266072760728607296073060731607326073360734607356073660737607386073960740607416074260743607446074560746607476074860749607506075160752607536075460755607566075760758607596076060761607626076360764607656076660767607686076960770607716077260773607746077560776607776077860779607806078160782607836078460785607866078760788607896079060791607926079360794607956079660797607986079960800608016080260803608046080560806608076080860809608106081160812608136081460815608166081760818608196082060821608226082360824608256082660827608286082960830608316083260833608346083560836608376083860839608406084160842608436084460845608466084760848608496085060851608526085360854608556085660857608586085960860608616086260863608646086560866608676086860869608706087160872608736087460875608766087760878608796088060881608826088360884608856088660887608886088960890608916089260893608946089560896608976089860899609006090160902609036090460905609066090760908609096091060911609126091360914609156091660917609186091960920609216092260923609246092560926609276092860929609306093160932609336093460935609366093760938609396094060941609426094360944609456094660947609486094960950609516095260953609546095560956609576095860959609606096160962609636096460965609666096760968609696097060971609726097360974609756097660977609786097960980609816098260983609846098560986609876098860989609906099160992609936099460995609966099760998609996100061001610026100361004610056100661007610086100961010610116101261013610146101561016610176101861019610206102161022610236102461025610266102761028610296103061031610326103361034610356103661037610386103961040610416104261043610446104561046610476104861049610506105161052610536105461055610566105761058610596106061061610626106361064610656106661067610686106961070610716107261073610746107561076610776107861079610806108161082610836108461085610866108761088610896109061091610926109361094610956109661097610986109961100611016110261103611046110561106611076110861109611106111161112611136111461115611166111761118611196112061121611226112361124611256112661127611286112961130611316113261133611346113561136611376113861139611406114161142611436114461145611466114761148611496115061151611526115361154611556115661157611586115961160611616116261163611646116561166611676116861169611706117161172611736117461175611766117761178611796118061181611826118361184611856118661187611886118961190611916119261193611946119561196611976119861199612006120161202612036120461205612066120761208612096121061211612126121361214612156121661217612186121961220612216122261223612246122561226612276122861229612306123161232612336123461235612366123761238612396124061241612426124361244612456124661247612486124961250612516125261253612546125561256612576125861259612606126161262612636126461265612666126761268612696127061271612726127361274612756127661277612786127961280612816128261283612846128561286612876128861289612906129161292612936129461295612966129761298612996130061301613026130361304613056130661307613086130961310613116131261313613146131561316613176131861319613206132161322613236132461325613266132761328613296133061331613326133361334613356133661337613386133961340613416134261343613446134561346613476134861349613506135161352613536135461355613566135761358613596136061361613626136361364613656136661367613686136961370613716137261373613746137561376613776137861379613806138161382613836138461385613866138761388613896139061391613926139361394613956139661397613986139961400614016140261403614046140561406614076140861409614106141161412614136141461415614166141761418614196142061421614226142361424614256142661427614286142961430614316143261433614346143561436614376143861439614406144161442614436144461445614466144761448614496145061451614526145361454614556145661457614586145961460614616146261463614646146561466614676146861469614706147161472614736147461475614766147761478614796148061481614826148361484614856148661487614886148961490614916149261493614946149561496614976149861499615006150161502615036150461505615066150761508615096151061511615126151361514615156151661517615186151961520615216152261523615246152561526615276152861529615306153161532615336153461535615366153761538615396154061541615426154361544615456154661547615486154961550615516155261553615546155561556615576155861559615606156161562615636156461565615666156761568615696157061571615726157361574615756157661577615786157961580615816158261583615846158561586615876158861589615906159161592615936159461595615966159761598615996160061601616026160361604616056160661607616086160961610616116161261613616146161561616616176161861619616206162161622616236162461625616266162761628616296163061631616326163361634616356163661637616386163961640616416164261643616446164561646616476164861649616506165161652616536165461655616566165761658616596166061661616626166361664616656166661667616686166961670616716167261673616746167561676616776167861679616806168161682616836168461685616866168761688616896169061691616926169361694616956169661697616986169961700617016170261703617046170561706617076170861709617106171161712617136171461715617166171761718617196172061721617226172361724617256172661727617286172961730617316173261733617346173561736617376173861739617406174161742617436174461745617466174761748617496175061751617526175361754617556175661757617586175961760617616176261763617646176561766617676176861769617706177161772617736177461775617766177761778617796178061781617826178361784617856178661787617886178961790617916179261793617946179561796617976179861799618006180161802618036180461805618066180761808618096181061811618126181361814618156181661817618186181961820618216182261823618246182561826618276182861829618306183161832618336183461835618366183761838618396184061841618426184361844618456184661847618486184961850618516185261853618546185561856618576185861859618606186161862618636186461865618666186761868618696187061871618726187361874618756187661877618786187961880618816188261883618846188561886618876188861889618906189161892618936189461895618966189761898618996190061901619026190361904619056190661907619086190961910619116191261913619146191561916619176191861919619206192161922619236192461925619266192761928619296193061931619326193361934619356193661937619386193961940619416194261943619446194561946619476194861949619506195161952619536195461955619566195761958619596196061961619626196361964619656196661967619686196961970619716197261973619746197561976619776197861979619806198161982619836198461985619866198761988619896199061991619926199361994619956199661997619986199962000620016200262003620046200562006620076200862009620106201162012620136201462015620166201762018620196202062021620226202362024620256202662027620286202962030620316203262033620346203562036620376203862039620406204162042620436204462045620466204762048620496205062051620526205362054620556205662057620586205962060620616206262063620646206562066620676206862069620706207162072620736207462075620766207762078620796208062081620826208362084620856208662087620886208962090620916209262093620946209562096620976209862099621006210162102621036210462105621066210762108621096211062111621126211362114621156211662117621186211962120621216212262123621246212562126621276212862129621306213162132621336213462135621366213762138621396214062141621426214362144621456214662147621486214962150621516215262153621546215562156621576215862159621606216162162621636216462165621666216762168621696217062171621726217362174621756217662177621786217962180621816218262183621846218562186621876218862189621906219162192621936219462195621966219762198621996220062201622026220362204622056220662207622086220962210622116221262213622146221562216622176221862219622206222162222622236222462225622266222762228622296223062231622326223362234622356223662237622386223962240622416224262243622446224562246622476224862249622506225162252622536225462255622566225762258622596226062261622626226362264622656226662267622686226962270622716227262273622746227562276622776227862279622806228162282622836228462285622866228762288622896229062291622926229362294622956229662297622986229962300623016230262303623046230562306623076230862309623106231162312623136231462315623166231762318623196232062321623226232362324623256232662327623286232962330623316233262333623346233562336623376233862339623406234162342623436234462345623466234762348623496235062351623526235362354623556235662357623586235962360623616236262363623646236562366623676236862369623706237162372623736237462375623766237762378623796238062381623826238362384623856238662387623886238962390623916239262393623946239562396623976239862399624006240162402624036240462405624066240762408624096241062411624126241362414624156241662417624186241962420624216242262423624246242562426624276242862429624306243162432624336243462435624366243762438624396244062441624426244362444624456244662447624486244962450624516245262453624546245562456624576245862459624606246162462624636246462465624666246762468624696247062471624726247362474624756247662477624786247962480624816248262483624846248562486624876248862489624906249162492624936249462495624966249762498624996250062501625026250362504625056250662507625086250962510625116251262513625146251562516625176251862519625206252162522625236252462525625266252762528625296253062531625326253362534625356253662537625386253962540625416254262543625446254562546625476254862549625506255162552625536255462555625566255762558625596256062561625626256362564625656256662567625686256962570625716257262573625746257562576625776257862579625806258162582625836258462585625866258762588625896259062591625926259362594625956259662597625986259962600626016260262603626046260562606626076260862609626106261162612626136261462615626166261762618626196262062621626226262362624626256262662627626286262962630626316263262633626346263562636626376263862639626406264162642626436264462645626466264762648626496265062651626526265362654626556265662657626586265962660626616266262663626646266562666626676266862669626706267162672626736267462675626766267762678626796268062681626826268362684626856268662687626886268962690626916269262693626946269562696626976269862699627006270162702627036270462705627066270762708627096271062711627126271362714627156271662717627186271962720627216272262723627246272562726627276272862729627306273162732627336273462735627366273762738627396274062741627426274362744627456274662747627486274962750627516275262753627546275562756627576275862759627606276162762627636276462765627666276762768627696277062771627726277362774627756277662777627786277962780627816278262783627846278562786627876278862789627906279162792627936279462795627966279762798627996280062801628026280362804628056280662807628086280962810628116281262813628146281562816628176281862819628206282162822628236282462825628266282762828628296283062831628326283362834628356283662837628386283962840628416284262843628446284562846628476284862849628506285162852628536285462855628566285762858628596286062861628626286362864628656286662867628686286962870628716287262873628746287562876628776287862879628806288162882628836288462885628866288762888628896289062891628926289362894628956289662897628986289962900629016290262903629046290562906629076290862909629106291162912629136291462915629166291762918629196292062921629226292362924629256292662927629286292962930629316293262933629346293562936629376293862939629406294162942629436294462945629466294762948629496295062951629526295362954629556295662957629586295962960629616296262963629646296562966629676296862969629706297162972629736297462975629766297762978629796298062981629826298362984629856298662987629886298962990629916299262993629946299562996629976299862999630006300163002630036300463005630066300763008630096301063011630126301363014630156301663017630186301963020630216302263023630246302563026630276302863029630306303163032630336303463035630366303763038630396304063041630426304363044630456304663047630486304963050630516305263053630546305563056630576305863059630606306163062630636306463065630666306763068630696307063071630726307363074630756307663077630786307963080630816308263083630846308563086630876308863089630906309163092630936309463095630966309763098630996310063101631026310363104631056310663107631086310963110631116311263113631146311563116631176311863119631206312163122631236312463125631266312763128631296313063131631326313363134631356313663137631386313963140631416314263143631446314563146631476314863149631506315163152631536315463155631566315763158631596316063161631626316363164631656316663167631686316963170631716317263173631746317563176631776317863179631806318163182631836318463185631866318763188631896319063191631926319363194631956319663197631986319963200632016320263203632046320563206632076320863209632106321163212632136321463215632166321763218632196322063221632226322363224632256322663227632286322963230632316323263233632346323563236632376323863239632406324163242632436324463245632466324763248632496325063251632526325363254632556325663257632586325963260632616326263263632646326563266632676326863269632706327163272632736327463275632766327763278632796328063281632826328363284632856328663287632886328963290632916329263293632946329563296632976329863299633006330163302633036330463305633066330763308633096331063311633126331363314633156331663317633186331963320633216332263323633246332563326633276332863329633306333163332633336333463335633366333763338633396334063341633426334363344633456334663347633486334963350633516335263353633546335563356633576335863359633606336163362633636336463365633666336763368633696337063371633726337363374633756337663377633786337963380633816338263383633846338563386633876338863389633906339163392633936339463395633966339763398633996340063401634026340363404634056340663407634086340963410634116341263413634146341563416634176341863419634206342163422634236342463425634266342763428634296343063431634326343363434634356343663437634386343963440634416344263443634446344563446634476344863449634506345163452634536345463455634566345763458634596346063461634626346363464634656346663467634686346963470634716347263473634746347563476634776347863479634806348163482634836348463485634866348763488634896349063491634926349363494634956349663497634986349963500635016350263503635046350563506635076350863509635106351163512635136351463515635166351763518635196352063521635226352363524635256352663527635286352963530635316353263533635346353563536635376353863539635406354163542635436354463545635466354763548635496355063551635526355363554635556355663557635586355963560635616356263563635646356563566635676356863569635706357163572635736357463575635766357763578635796358063581635826358363584635856358663587635886358963590635916359263593635946359563596635976359863599636006360163602636036360463605636066360763608636096361063611636126361363614636156361663617636186361963620636216362263623636246362563626636276362863629636306363163632636336363463635636366363763638636396364063641636426364363644636456364663647636486364963650636516365263653636546365563656636576365863659636606366163662636636366463665636666366763668636696367063671636726367363674636756367663677636786367963680636816368263683636846368563686636876368863689636906369163692636936369463695636966369763698636996370063701637026370363704637056370663707637086370963710637116371263713637146371563716637176371863719637206372163722637236372463725637266372763728637296373063731637326373363734637356373663737637386373963740637416374263743637446374563746637476374863749637506375163752637536375463755637566375763758637596376063761637626376363764637656376663767637686376963770637716377263773637746377563776637776377863779637806378163782637836378463785637866378763788637896379063791637926379363794637956379663797637986379963800638016380263803638046380563806638076380863809638106381163812638136381463815638166381763818638196382063821638226382363824638256382663827638286382963830638316383263833638346383563836638376383863839638406384163842638436384463845638466384763848638496385063851638526385363854638556385663857638586385963860638616386263863638646386563866638676386863869638706387163872638736387463875638766387763878638796388063881638826388363884638856388663887638886388963890638916389263893638946389563896638976389863899639006390163902639036390463905639066390763908639096391063911639126391363914639156391663917639186391963920639216392263923639246392563926639276392863929639306393163932639336393463935639366393763938639396394063941639426394363944639456394663947639486394963950639516395263953639546395563956639576395863959639606396163962639636396463965639666396763968639696397063971639726397363974639756397663977639786397963980639816398263983639846398563986639876398863989639906399163992639936399463995639966399763998639996400064001640026400364004640056400664007640086400964010640116401264013640146401564016640176401864019640206402164022640236402464025640266402764028640296403064031640326403364034640356403664037640386403964040640416404264043640446404564046640476404864049640506405164052640536405464055640566405764058640596406064061640626406364064640656406664067640686406964070640716407264073640746407564076640776407864079640806408164082640836408464085640866408764088640896409064091640926409364094640956409664097640986409964100641016410264103641046410564106641076410864109641106411164112641136411464115641166411764118641196412064121641226412364124641256412664127641286412964130641316413264133641346413564136641376413864139641406414164142641436414464145641466414764148641496415064151641526415364154641556415664157641586415964160641616416264163641646416564166641676416864169641706417164172641736417464175641766417764178641796418064181641826418364184641856418664187641886418964190641916419264193641946419564196641976419864199642006420164202642036420464205642066420764208642096421064211642126421364214642156421664217642186421964220642216422264223642246422564226642276422864229642306423164232642336423464235642366423764238642396424064241642426424364244642456424664247642486424964250642516425264253642546425564256642576425864259642606426164262642636426464265642666426764268642696427064271642726427364274642756427664277642786427964280642816428264283642846428564286642876428864289642906429164292642936429464295642966429764298642996430064301643026430364304643056430664307643086430964310643116431264313643146431564316643176431864319643206432164322643236432464325643266432764328643296433064331643326433364334643356433664337643386433964340643416434264343643446434564346643476434864349643506435164352643536435464355643566435764358643596436064361643626436364364643656436664367643686436964370643716437264373643746437564376643776437864379643806438164382643836438464385643866438764388643896439064391643926439364394643956439664397643986439964400644016440264403644046440564406644076440864409644106441164412644136441464415644166441764418644196442064421644226442364424644256442664427644286442964430644316443264433644346443564436644376443864439644406444164442644436444464445644466444764448644496445064451644526445364454644556445664457644586445964460644616446264463644646446564466644676446864469644706447164472644736447464475644766447764478644796448064481644826448364484644856448664487644886448964490644916449264493644946449564496644976449864499645006450164502645036450464505645066450764508645096451064511645126451364514645156451664517645186451964520645216452264523645246452564526645276452864529645306453164532645336453464535645366453764538645396454064541645426454364544645456454664547645486454964550645516455264553645546455564556645576455864559645606456164562645636456464565645666456764568645696457064571645726457364574645756457664577645786457964580645816458264583645846458564586645876458864589645906459164592645936459464595645966459764598645996460064601646026460364604646056460664607646086460964610646116461264613646146461564616646176461864619646206462164622646236462464625646266462764628646296463064631646326463364634646356463664637646386463964640646416464264643646446464564646646476464864649646506465164652646536465464655646566465764658646596466064661646626466364664646656466664667646686466964670646716467264673646746467564676646776467864679646806468164682646836468464685646866468764688646896469064691646926469364694646956469664697646986469964700647016470264703647046470564706647076470864709647106471164712647136471464715647166471764718647196472064721647226472364724647256472664727647286472964730647316473264733647346473564736647376473864739647406474164742647436474464745647466474764748647496475064751647526475364754647556475664757647586475964760647616476264763647646476564766647676476864769647706477164772647736477464775647766477764778647796478064781647826478364784647856478664787647886478964790647916479264793647946479564796647976479864799648006480164802648036480464805648066480764808648096481064811648126481364814648156481664817648186481964820648216482264823648246482564826648276482864829648306483164832648336483464835648366483764838648396484064841648426484364844648456484664847648486484964850648516485264853648546485564856648576485864859648606486164862648636486464865648666486764868648696487064871648726487364874648756487664877648786487964880648816488264883648846488564886648876488864889648906489164892648936489464895648966489764898648996490064901649026490364904649056490664907649086490964910649116491264913649146491564916649176491864919649206492164922649236492464925649266492764928649296493064931649326493364934649356493664937649386493964940649416494264943649446494564946649476494864949649506495164952649536495464955649566495764958649596496064961649626496364964649656496664967649686496964970649716497264973649746497564976649776497864979649806498164982649836498464985649866498764988649896499064991649926499364994649956499664997649986499965000650016500265003650046500565006650076500865009650106501165012650136501465015650166501765018650196502065021650226502365024650256502665027650286502965030650316503265033650346503565036650376503865039650406504165042650436504465045650466504765048650496505065051650526505365054650556505665057650586505965060650616506265063650646506565066650676506865069650706507165072650736507465075650766507765078650796508065081650826508365084650856508665087650886508965090650916509265093650946509565096650976509865099651006510165102651036510465105651066510765108651096511065111651126511365114651156511665117651186511965120651216512265123651246512565126651276512865129651306513165132651336513465135651366513765138651396514065141651426514365144651456514665147651486514965150651516515265153651546515565156651576515865159651606516165162651636516465165651666516765168651696517065171651726517365174651756517665177651786517965180651816518265183651846518565186651876518865189651906519165192651936519465195651966519765198651996520065201652026520365204652056520665207652086520965210652116521265213652146521565216652176521865219652206522165222652236522465225652266522765228652296523065231652326523365234652356523665237652386523965240652416524265243652446524565246652476524865249652506525165252652536525465255652566525765258652596526065261652626526365264652656526665267652686526965270652716527265273652746527565276652776527865279652806528165282652836528465285652866528765288652896529065291652926529365294652956529665297652986529965300653016530265303653046530565306653076530865309653106531165312653136531465315653166531765318653196532065321653226532365324653256532665327653286532965330653316533265333653346533565336653376533865339653406534165342653436534465345653466534765348653496535065351653526535365354653556535665357653586535965360653616536265363653646536565366653676536865369653706537165372653736537465375653766537765378653796538065381653826538365384653856538665387653886538965390653916539265393653946539565396653976539865399654006540165402654036540465405654066540765408654096541065411654126541365414654156541665417654186541965420654216542265423654246542565426654276542865429654306543165432654336543465435654366543765438654396544065441654426544365444654456544665447654486544965450654516545265453654546545565456654576545865459654606546165462654636546465465654666546765468654696547065471654726547365474654756547665477654786547965480654816548265483654846548565486654876548865489654906549165492654936549465495654966549765498654996550065501655026550365504655056550665507655086550965510655116551265513655146551565516655176551865519655206552165522655236552465525655266552765528655296553065531655326553365534655356553665537655386553965540655416554265543655446554565546655476554865549655506555165552655536555465555655566555765558655596556065561655626556365564655656556665567655686556965570655716557265573655746557565576655776557865579655806558165582655836558465585655866558765588655896559065591655926559365594655956559665597655986559965600656016560265603656046560565606656076560865609656106561165612656136561465615656166561765618656196562065621656226562365624656256562665627656286562965630656316563265633656346563565636656376563865639656406564165642656436564465645656466564765648656496565065651656526565365654656556565665657656586565965660656616566265663656646566565666656676566865669656706567165672656736567465675656766567765678656796568065681656826568365684656856568665687656886568965690656916569265693656946569565696656976569865699657006570165702657036570465705657066570765708657096571065711657126571365714657156571665717657186571965720657216572265723657246572565726657276572865729657306573165732657336573465735657366573765738657396574065741657426574365744657456574665747657486574965750657516575265753657546575565756657576575865759657606576165762657636576465765657666576765768657696577065771657726577365774657756577665777657786577965780657816578265783657846578565786657876578865789657906579165792657936579465795657966579765798657996580065801658026580365804658056580665807658086580965810658116581265813658146581565816658176581865819658206582165822658236582465825658266582765828658296583065831658326583365834658356583665837658386583965840658416584265843658446584565846658476584865849658506585165852658536585465855658566585765858658596586065861658626586365864658656586665867658686586965870658716587265873658746587565876658776587865879658806588165882658836588465885658866588765888658896589065891658926589365894658956589665897658986589965900659016590265903659046590565906659076590865909659106591165912659136591465915659166591765918659196592065921659226592365924659256592665927659286592965930659316593265933659346593565936659376593865939659406594165942659436594465945659466594765948659496595065951659526595365954659556595665957659586595965960659616596265963659646596565966659676596865969659706597165972659736597465975659766597765978659796598065981659826598365984659856598665987659886598965990659916599265993659946599565996659976599865999660006600166002660036600466005660066600766008660096601066011660126601366014660156601666017660186601966020660216602266023660246602566026660276602866029660306603166032660336603466035660366603766038660396604066041660426604366044660456604666047660486604966050660516605266053660546605566056660576605866059660606606166062660636606466065660666606766068660696607066071660726607366074660756607666077660786607966080660816608266083660846608566086660876608866089660906609166092660936609466095660966609766098660996610066101661026610366104661056610666107661086610966110661116611266113661146611566116661176611866119661206612166122661236612466125661266612766128661296613066131661326613366134661356613666137661386613966140661416614266143661446614566146661476614866149661506615166152661536615466155661566615766158661596616066161661626616366164661656616666167661686616966170661716617266173661746617566176661776617866179661806618166182661836618466185661866618766188661896619066191661926619366194661956619666197661986619966200662016620266203662046620566206662076620866209662106621166212662136621466215662166621766218662196622066221662226622366224662256622666227662286622966230662316623266233662346623566236662376623866239662406624166242662436624466245662466624766248662496625066251662526625366254662556625666257662586625966260662616626266263662646626566266662676626866269662706627166272662736627466275662766627766278662796628066281662826628366284662856628666287662886628966290662916629266293662946629566296662976629866299663006630166302663036630466305663066630766308663096631066311663126631366314663156631666317663186631966320663216632266323663246632566326663276632866329663306633166332663336633466335663366633766338663396634066341663426634366344663456634666347663486634966350663516635266353663546635566356663576635866359663606636166362663636636466365663666636766368663696637066371663726637366374663756637666377663786637966380663816638266383663846638566386663876638866389663906639166392663936639466395663966639766398663996640066401664026640366404664056640666407664086640966410664116641266413664146641566416664176641866419664206642166422664236642466425664266642766428664296643066431664326643366434664356643666437664386643966440664416644266443664446644566446664476644866449664506645166452664536645466455664566645766458664596646066461664626646366464664656646666467664686646966470664716647266473664746647566476664776647866479664806648166482664836648466485664866648766488664896649066491664926649366494664956649666497664986649966500665016650266503665046650566506665076650866509665106651166512665136651466515665166651766518665196652066521665226652366524665256652666527665286652966530665316653266533665346653566536665376653866539665406654166542665436654466545665466654766548665496655066551665526655366554665556655666557665586655966560665616656266563665646656566566665676656866569665706657166572665736657466575665766657766578665796658066581665826658366584665856658666587665886658966590665916659266593665946659566596665976659866599666006660166602666036660466605666066660766608666096661066611666126661366614666156661666617666186661966620666216662266623666246662566626666276662866629666306663166632666336663466635666366663766638666396664066641666426664366644666456664666647666486664966650666516665266653666546665566656666576665866659666606666166662666636666466665666666666766668666696667066671666726667366674666756667666677666786667966680666816668266683666846668566686666876668866689666906669166692666936669466695666966669766698666996670066701667026670366704667056670666707667086670966710667116671266713667146671566716667176671866719667206672166722667236672466725667266672766728667296673066731667326673366734667356673666737667386673966740667416674266743667446674566746667476674866749667506675166752667536675466755667566675766758667596676066761667626676366764667656676666767667686676966770667716677266773667746677566776667776677866779667806678166782667836678466785667866678766788667896679066791667926679366794667956679666797667986679966800668016680266803668046680566806668076680866809668106681166812668136681466815668166681766818668196682066821668226682366824668256682666827668286682966830668316683266833668346683566836668376683866839668406684166842668436684466845668466684766848668496685066851668526685366854668556685666857668586685966860668616686266863668646686566866668676686866869668706687166872668736687466875668766687766878668796688066881668826688366884668856688666887668886688966890668916689266893668946689566896668976689866899669006690166902669036690466905669066690766908669096691066911669126691366914669156691666917669186691966920669216692266923669246692566926669276692866929669306693166932669336693466935669366693766938669396694066941669426694366944669456694666947669486694966950669516695266953669546695566956669576695866959669606696166962669636696466965669666696766968669696697066971669726697366974669756697666977669786697966980669816698266983669846698566986669876698866989669906699166992669936699466995669966699766998669996700067001670026700367004670056700667007670086700967010670116701267013670146701567016670176701867019670206702167022670236702467025670266702767028670296703067031670326703367034670356703667037670386703967040670416704267043670446704567046670476704867049670506705167052670536705467055670566705767058670596706067061670626706367064670656706667067670686706967070670716707267073670746707567076670776707867079670806708167082670836708467085670866708767088670896709067091670926709367094670956709667097670986709967100671016710267103671046710567106671076710867109671106711167112671136711467115671166711767118671196712067121671226712367124671256712667127671286712967130671316713267133671346713567136671376713867139671406714167142671436714467145671466714767148671496715067151671526715367154671556715667157671586715967160671616716267163671646716567166671676716867169671706717167172671736717467175671766717767178671796718067181671826718367184671856718667187671886718967190671916719267193671946719567196671976719867199672006720167202672036720467205672066720767208672096721067211672126721367214672156721667217672186721967220672216722267223672246722567226672276722867229672306723167232672336723467235672366723767238672396724067241672426724367244672456724667247672486724967250672516725267253672546725567256672576725867259672606726167262672636726467265672666726767268672696727067271672726727367274672756727667277672786727967280672816728267283672846728567286672876728867289672906729167292672936729467295672966729767298672996730067301673026730367304673056730667307673086730967310673116731267313673146731567316673176731867319673206732167322673236732467325673266732767328673296733067331673326733367334673356733667337673386733967340673416734267343673446734567346673476734867349673506735167352673536735467355673566735767358673596736067361673626736367364673656736667367673686736967370673716737267373673746737567376673776737867379673806738167382673836738467385673866738767388673896739067391673926739367394673956739667397673986739967400674016740267403674046740567406674076740867409674106741167412674136741467415674166741767418674196742067421674226742367424674256742667427674286742967430674316743267433674346743567436674376743867439674406744167442674436744467445674466744767448674496745067451674526745367454674556745667457674586745967460674616746267463674646746567466674676746867469674706747167472674736747467475674766747767478674796748067481674826748367484674856748667487674886748967490674916749267493674946749567496674976749867499675006750167502675036750467505675066750767508675096751067511675126751367514675156751667517675186751967520675216752267523675246752567526675276752867529675306753167532675336753467535675366753767538675396754067541675426754367544675456754667547675486754967550675516755267553675546755567556675576755867559675606756167562675636756467565675666756767568675696757067571675726757367574675756757667577675786757967580675816758267583675846758567586675876758867589675906759167592675936759467595675966759767598675996760067601676026760367604676056760667607676086760967610676116761267613676146761567616676176761867619676206762167622676236762467625676266762767628676296763067631676326763367634676356763667637676386763967640676416764267643676446764567646676476764867649676506765167652676536765467655676566765767658676596766067661676626766367664676656766667667676686766967670676716767267673676746767567676676776767867679676806768167682676836768467685676866768767688676896769067691676926769367694676956769667697676986769967700677016770267703677046770567706677076770867709677106771167712677136771467715677166771767718677196772067721677226772367724677256772667727677286772967730677316773267733677346773567736677376773867739677406774167742677436774467745677466774767748677496775067751677526775367754677556775667757677586775967760677616776267763677646776567766 |
- /*
- #
- # File : CImg.h
- # ( C++ header file )
- #
- # Description : C++ Template Image Processing Toolkit.
- # This file is the main component of the CImg Library project.
- # ( http://cimg.eu )
- #
- # Project manager : David Tschumperlé
- # ( http://tschumperle.users.greyc.fr/ )
- #
- # A complete list of contributors is available in file 'README.txt'
- # distributed within the CImg package.
- #
- # Licenses : This file is 'dual-licensed', you have to choose one
- # of the two licenses below to apply.
- #
- # CeCILL-C
- # The CeCILL-C license is close to the GNU LGPL.
- # ( http://cecill.info/licences/Licence_CeCILL-C_V1-en.html )
- #
- # or CeCILL v2.1
- # The CeCILL license is compatible with the GNU GPL.
- # ( http://cecill.info/licences/Licence_CeCILL_V2.1-en.html )
- #
- # This software is governed either by the CeCILL or the CeCILL-C license
- # under French law and abiding by the rules of distribution of free software.
- # You can use, modify and or redistribute the software under the terms of
- # the CeCILL or CeCILL-C licenses as circulated by CEA, CNRS and INRIA
- # at the following URL: "http://cecill.info".
- #
- # As a counterpart to the access to the source code and rights to copy,
- # modify and redistribute granted by the license, users are provided only
- # with a limited warranty and the software's author, the holder of the
- # economic rights, and the successive licensors have only limited
- # liability.
- #
- # In this respect, the user's attention is drawn to the risks associated
- # with loading, using, modifying and/or developing or reproducing the
- # software by the user in light of its specific status of free software,
- # that may mean that it is complicated to manipulate, and that also
- # therefore means that it is reserved for developers and experienced
- # professionals having in-depth computer knowledge. Users are therefore
- # encouraged to load and test the software's suitability as regards their
- # requirements in conditions enabling the security of their systems and/or
- # data to be ensured and, more generally, to use and operate it in the
- # same conditions as regards security.
- #
- # The fact that you are presently reading this means that you have had
- # knowledge of the CeCILL and CeCILL-C licenses and that you accept its terms.
- #
- */
- // Set version number of the library.
- #ifndef cimg_version
- #define cimg_version 333
- /*-----------------------------------------------------------
- #
- # Test and possibly auto-set CImg configuration variables
- # and include required headers.
- #
- # If you find that the default configuration variables are
- # not adapted to your system, you can override their values
- # before including the header file "CImg.h"
- # (use the #define directive).
- #
- ------------------------------------------------------------*/
- // Include standard C++ headers.
- // This is the minimal set of required headers to make CImg-based codes compile.
- #include <cstdio>
- #include <cstdlib>
- #include <cstdarg>
- #include <cstring>
- #include <cmath>
- #include <cfloat>
- #include <climits>
- #include <ctime>
- #include <exception>
- #include <algorithm>
- #define cimg_str(x) #x
- #define cimg_str2(x) cimg_str(x)
- // Detect/configure OS variables.
- //
- // Define 'cimg_OS' to: '0' for an unknown OS (will try to minize library dependencies).
- // '1' for a Unix-like OS (Linux, Solaris, BSD, MacOSX, Irix, ...).
- // '2' for Microsoft Windows.
- // (auto-detection is performed if 'cimg_OS' is not set by the user).
- #ifndef cimg_OS
- #if defined(unix) || defined(__unix) || defined(__unix__) \
- || defined(linux) || defined(__linux) || defined(__linux__) \
- || defined(sun) || defined(__sun) \
- || defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) \
- || defined(__FreeBSD__) || defined (__DragonFly__) \
- || defined(sgi) || defined(__sgi) \
- || defined(__OSX__) || defined(__MACOSX__) || defined(__APPLE__) \
- || defined(__CYGWIN__)
- #define cimg_OS 1
- #elif defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) \
- || defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
- #define cimg_OS 2
- #else
- #define cimg_OS 0
- #endif
- #elif !(cimg_OS==0 || cimg_OS==1 || cimg_OS==2)
- #error CImg Library: Invalid configuration variable 'cimg_OS'.
- #error (correct values are '0 = unknown OS', '1 = Unix-like OS', '2 = Microsoft Windows').
- #endif
- #ifndef cimg_date
- #define cimg_date __DATE__
- #endif
- #ifndef cimg_time
- #define cimg_time __TIME__
- #endif
- // Disable silly warnings on some Microsoft VC++ compilers.
- #ifdef _MSC_VER
- #pragma warning(push)
- #pragma warning(disable:4127)
- #pragma warning(disable:4244)
- #pragma warning(disable:4307)
- #pragma warning(disable:4311)
- #pragma warning(disable:4312)
- #pragma warning(disable:4319)
- #pragma warning(disable:4512)
- #pragma warning(disable:4571)
- #pragma warning(disable:4640)
- #pragma warning(disable:4706)
- #pragma warning(disable:4710)
- #pragma warning(disable:4800)
- #pragma warning(disable:4804)
- #pragma warning(disable:4820)
- #pragma warning(disable:4995)
- #pragma warning(disable:4996)
- #ifndef _CRT_SECURE_NO_DEPRECATE
- #define _CRT_SECURE_NO_DEPRECATE 1
- #endif
- #ifndef _CRT_SECURE_NO_WARNINGS
- #define _CRT_SECURE_NO_WARNINGS 1
- #endif
- #ifndef _CRT_NONSTDC_NO_DEPRECATE
- #define _CRT_NONSTDC_NO_DEPRECATE 1
- #endif
- #endif
- // Define correct string functions for each compiler and OS.
- #if cimg_OS==2 && defined(_MSC_VER)
- #define cimg_sscanf std::sscanf
- #define cimg_snprintf cimg::_snprintf
- #define cimg_vsnprintf cimg::_vsnprintf
- #else
- #include <stdio.h>
- #if defined(__MACOSX__) || defined(__APPLE__)
- #define cimg_sscanf cimg::_sscanf
- #define cimg_snprintf cimg::_snprintf
- #define cimg_vsnprintf cimg::_vsnprintf
- #else
- #define cimg_sscanf std::sscanf
- #define cimg_snprintf snprintf
- #define cimg_vsnprintf vsnprintf
- #endif
- #endif
- // Include OS-specific headers.
- #if cimg_OS==1
- #include <sys/types.h>
- #include <sys/time.h>
- #include <sys/stat.h>
- #include <unistd.h>
- #include <dirent.h>
- #include <fnmatch.h>
- #elif cimg_OS==2
- #ifndef NOMINMAX
- #define NOMINMAX
- #endif
- #ifndef WIN32_LEAN_AND_MEAN
- #define WIN32_LEAN_AND_MEAN
- #endif
- #include <windows.h>
- #ifndef _WIN32_IE
- #define _WIN32_IE 0x0400
- #endif
- #include <shlobj.h>
- #include <process.h>
- #include <io.h>
- enum {FALSE_WIN = 0};
- #endif
- // Look for C++11 features.
- #ifndef cimg_use_cpp11
- #if __cplusplus>201100
- #define cimg_use_cpp11 1
- #else
- #define cimg_use_cpp11 0
- #endif
- #endif
- #if cimg_use_cpp11==1
- #include <initializer_list>
- #include <utility>
- #endif
- // Convenient macro to define pragma
- #ifdef _MSC_VER
- #define cimg_pragma(x) __pragma(x)
- #else
- #define cimg_pragma(x) _Pragma(#x)
- #endif
- // Define own datatypes to ensure portability.
- // ( 'sizeof(cimg_ulong/cimg_long) = sizeof(void*)' ).
- #define cimg_uint8 unsigned char
- #if defined(CHAR_MAX) && CHAR_MAX==255
- #define cimg_int8 signed char
- #else
- #define cimg_int8 char
- #endif
- #define cimg_uint16 unsigned short
- #define cimg_int16 short
- #define cimg_uint32 unsigned int
- #define cimg_int32 int
- #define cimg_float32 float
- #define cimg_float64 double
- #if cimg_OS==2
- #define cimg_uint64 unsigned __int64
- #define cimg_int64 __int64
- #define cimg_ulong UINT_PTR
- #define cimg_long INT_PTR
- #ifdef _MSC_VER
- #define cimg_fuint64 "%I64u"
- #define cimg_fint64 "%I64d"
- #else
- #define cimg_fuint64 "%llu"
- #define cimg_fint64 "%lld"
- #endif
- #else
- #if UINTPTR_MAX==0xffffffff || defined(__arm__) || defined(_M_ARM) || ((ULONG_MAX)==(UINT_MAX))
- #define cimg_uint64 unsigned long long
- #define cimg_int64 long long
- #define cimg_fuint64 "%llu"
- #define cimg_fint64 "%lld"
- #else
- #define cimg_uint64 unsigned long
- #define cimg_int64 long
- #define cimg_fuint64 "%lu"
- #define cimg_fint64 "%ld"
- #endif
- #if defined(__arm__) || defined(_M_ARM)
- #define cimg_ulong unsigned long long
- #define cimg_long long long
- #else
- #define cimg_ulong unsigned long
- #define cimg_long long
- #endif
- #endif
- #ifndef cimg_max_buf_size
- #if UINTPTR_MAX==0xffffffff
- #define cimg_max_buf_size ((cimg_ulong)3*1024*1024*1024)
- #else
- #define cimg_max_buf_size ((cimg_ulong)16*1024*1024*1024)
- #endif
- #endif
- // Configure filename separator.
- //
- // Filename separator is set by default to '/', except for Windows where it is '\'.
- #ifndef cimg_file_separator
- #if cimg_OS==2
- #define cimg_file_separator '\\'
- #else
- #define cimg_file_separator '/'
- #endif
- #endif
- // Configure verbosity of output messages.
- //
- // Define 'cimg_verbosity' to: '0' to hide library messages (quiet mode).
- // '1' to output library messages on the console.
- // '2' to output library messages on a basic dialog window (default behavior).
- // '3' to do as '1' + add extra warnings (may slow down the code!).
- // '4' to do as '2' + add extra warnings (may slow down the code!).
- //
- // Define 'cimg_strict_warnings' to replace warning messages by exception throwns.
- //
- // Define 'cimg_use_vt100' to allow output of color messages on VT100-compatible terminals.
- #ifndef cimg_verbosity
- #if cimg_OS==2
- #define cimg_verbosity 2
- #else
- #define cimg_verbosity 1
- #endif
- #elif !(cimg_verbosity==0 || cimg_verbosity==1 || cimg_verbosity==2 || cimg_verbosity==3 || cimg_verbosity==4)
- #error CImg Library: Configuration variable 'cimg_verbosity' is badly defined.
- #error (should be { 0=quiet | 1=console | 2=dialog | 3=console+warnings | 4=dialog+warnings }).
- #endif
- // Configure OpenMP support.
- // (http://www.openmp.org)
- //
- // Define 'cimg_use_openmp' to enable OpenMP support (requires OpenMP 3.0+).
- //
- // OpenMP directives are used in many CImg functions to get
- // advantages of multi-core CPUs.
- #if !defined(cimg_use_openmp)
- #ifdef _OPENMP
- #define cimg_use_openmp 1
- #else
- #define cimg_use_openmp 0
- #endif
- #else
- #undef cimg_use_openmp
- #define cimg_use_openmp 1
- #endif
- #if cimg_use_openmp!=0
- #include <omp.h>
- #define cimg_pragma_openmp(p) cimg_pragma(omp p)
- #else
- #define cimg_pragma_openmp(p)
- #endif
- // Configure the 'abort' signal handler (does nothing by default).
- // A typical signal handler can be defined in your own source like this:
- // #define cimg_abort_test if (is_abort) throw CImgAbortException("")
- //
- // where 'is_abort' is a boolean variable defined somewhere in your code and reachable in the method.
- // 'cimg_abort_test2' does the same but is called more often (in inner loops).
- #if defined(cimg_abort_test) && cimg_use_openmp!=0
- // Define abort macros to be used with OpenMP.
- #ifndef _cimg_abort_init_openmp
- #define _cimg_abort_init_openmp unsigned int _cimg_abort_go_openmp = 1; cimg::unused(_cimg_abort_go_openmp)
- #endif
- #ifndef _cimg_abort_try_openmp
- #define _cimg_abort_try_openmp if (_cimg_abort_go_openmp) try
- #endif
- #ifndef _cimg_abort_catch_openmp
- #define _cimg_abort_catch_openmp catch (CImgAbortException&) { \
- cimg_pragma_openmp(atomic) _cimg_abort_go_openmp&=0; \
- }
- #endif
- #ifndef _cimg_abort_catch_fill_openmp
- #define _cimg_abort_catch_fill_openmp \
- catch (CImgException& e) { cimg_pragma(omp critical(abort)) CImg<charT>::string(e._message).move_to(is_error_expr); \
- cimg_pragma_openmp(atomic) _cimg_abort_go_openmp&=0; }
- #endif
- #ifdef cimg_abort_test2
- #ifndef _cimg_abort_try_openmp2
- #define _cimg_abort_try_openmp2 _cimg_abort_try_openmp
- #endif
- #ifndef _cimg_abort_catch_openmp2
- #define _cimg_abort_catch_openmp2 _cimg_abort_catch_openmp
- #endif
- #endif
- #endif
- #ifndef _cimg_abort_init_openmp
- #define _cimg_abort_init_openmp
- #endif
- #ifndef _cimg_abort_try_openmp
- #define _cimg_abort_try_openmp
- #endif
- #ifndef _cimg_abort_catch_openmp
- #define _cimg_abort_catch_openmp
- #endif
- #ifndef _cimg_abort_try_openmp2
- #define _cimg_abort_try_openmp2
- #endif
- #ifndef _cimg_abort_catch_openmp2
- #define _cimg_abort_catch_openmp2
- #endif
- #ifndef _cimg_abort_catch_fill_openmp
- #define _cimg_abort_catch_fill_openmp
- #endif
- #ifndef cimg_abort_init
- #define cimg_abort_init
- #endif
- #ifndef cimg_abort_test
- #define cimg_abort_test
- #endif
- #ifndef cimg_abort_test2
- #define cimg_abort_test2
- #endif
- // Configure display framework.
- //
- // Define 'cimg_display' to: '0' to disable display capabilities.
- // '1' to use the X-Window framework (X11).
- // '2' to use the Microsoft GDI32 framework.
- #ifndef cimg_display
- #if cimg_OS==0
- #define cimg_display 0
- #elif cimg_OS==1
- #define cimg_display 1
- #elif cimg_OS==2
- #define cimg_display 2
- #endif
- #elif !(cimg_display==0 || cimg_display==1 || cimg_display==2)
- #error CImg Library: Configuration variable 'cimg_display' is badly defined.
- #error (should be { 0=none | 1=X-Window (X11) | 2=Microsoft GDI32 }).
- #endif
- // Include display-specific headers.
- #if cimg_display==1
- #include <X11/Xlib.h>
- #include <X11/Xutil.h>
- #include <X11/keysym.h>
- #include <pthread.h>
- #ifdef cimg_use_xshm
- #include <sys/ipc.h>
- #include <sys/shm.h>
- #include <X11/extensions/XShm.h>
- #endif
- #ifdef cimg_use_xrandr
- #include <X11/extensions/Xrandr.h>
- #endif
- #endif
- #ifndef cimg_appname
- #define cimg_appname "CImg"
- #endif
- // Configure OpenCV support.
- // (http://opencv.willowgarage.com/wiki/)
- //
- // Define 'cimg_use_opencv' to enable OpenCV support.
- //
- // OpenCV library may be used to access images from cameras
- // (see method 'CImg<T>::load_camera()').
- #ifdef cimg_use_opencv
- #ifdef True
- #undef True
- #define _cimg_redefine_True
- #endif
- #ifdef False
- #undef False
- #define _cimg_redefine_False
- #endif
- #ifdef Status
- #undef Status
- #define _cimg_redefine_Status
- #endif
- #include <cstddef>
- #include <opencv2/opencv.hpp>
- #if CV_MAJOR_VERSION>=3
- #define _cimg_fourcc cv::VideoWriter::fourcc
- #define _cimg_cap_prop_frame_width cv::VideoCaptureProperties::CAP_PROP_FRAME_WIDTH
- #define _cimg_cap_prop_frame_height cv::VideoCaptureProperties::CAP_PROP_FRAME_HEIGHT
- #define _cimg_cap_prop_frame_count cv::VideoCaptureProperties::CAP_PROP_FRAME_COUNT
- #else
- #define _cimg_fourcc CV_FOURCC
- #define _cimg_cap_prop_frame_width CV_CAP_PROP_FRAME_WIDTH
- #define _cimg_cap_prop_frame_height CV_CAP_PROP_FRAME_HEIGHT
- #define _cimg_cap_prop_frame_count CV_CAP_PROP_FRAME_COUNT
- #endif
- #endif
- // Configure LibPNG support.
- // (http://www.libpng.org)
- //
- // Define 'cimg_use_png' to enable LibPNG support.
- //
- // PNG library may be used to get a native support of '.png' files.
- // (see methods 'CImg<T>::{load,save}_png()'.
- #ifdef cimg_use_png
- extern "C" {
- #include "png.h"
- }
- #endif
- // Configure LibJPEG support.
- // (http://en.wikipedia.org/wiki/Libjpeg)
- //
- // Define 'cimg_use_jpeg' to enable LibJPEG support.
- //
- // JPEG library may be used to get a native support of '.jpg' files.
- // (see methods 'CImg<T>::{load,save}_jpeg()').
- #ifdef cimg_use_jpeg
- extern "C" {
- #include "jpeglib.h"
- #include "setjmp.h"
- }
- #endif
- // Configure LibTIFF support.
- // (http://www.libtiff.org)
- //
- // Define 'cimg_use_tiff' to enable LibTIFF support.
- //
- // TIFF library may be used to get a native support of '.tif' files.
- // (see methods 'CImg[List]<T>::{load,save}_tiff()').
- #ifdef cimg_use_tiff
- extern "C" {
- #define uint64 uint64_hack_
- #define int64 int64_hack_
- #include "tiffio.h"
- #undef uint64
- #undef int64
- }
- #endif
- // Configure HEIF support
- // (https://github.com/strukturag/libheif)
- //
- // Define 'cimg_use_heif' to enable HEIF support.
- //
- // HEIF library may be used to get a native support of '.heic' and '.avif' files.
- // (see method 'CImg<T>::load_heif()').
- #ifdef cimg_use_heif
- #include <libheif/heif_cxx.h>
- #endif
- // Configure LibMINC2 support.
- // (http://en.wikibooks.org/wiki/MINC/Reference/MINC2.0_File_Format_Reference)
- //
- // Define 'cimg_use_minc2' to enable LibMINC2 support.
- //
- // MINC2 library may be used to get a native support of '.mnc' files.
- // (see methods 'CImg<T>::{load,save}_minc2()').
- #ifdef cimg_use_minc2
- #include "minc_io_simple_volume.h"
- #include "minc_1_simple.h"
- #include "minc_1_simple_rw.h"
- #endif
- // Configure Zlib support.
- // (http://www.zlib.net)
- //
- // Define 'cimg_use_zlib' to enable Zlib support.
- //
- // Zlib library may be used to allow compressed data in '.cimgz' files
- // (see methods 'CImg[List]<T>::{load,save}_cimg()').
- #ifdef cimg_use_zlib
- extern "C" {
- #include "zlib.h"
- }
- #endif
- // Configure libcurl support.
- // (http://curl.haxx.se/libcurl/)
- //
- // Define 'cimg_use_curl' to enable libcurl support.
- //
- // Libcurl may be used to get a native support of file downloading from the network.
- // (see method 'cimg::load_network()'.)
- #ifdef cimg_use_curl
- #include "curl/curl.h"
- #endif
- // Configure Magick++ support.
- // (http://www.imagemagick.org/Magick++)
- //
- // Define 'cimg_use_magick' to enable Magick++ support.
- //
- // Magick++ library may be used to get a native support of various image file formats.
- // (see methods 'CImg<T>::{load,save}()').
- #ifdef cimg_use_magick
- #include "Magick++.h"
- #endif
- // Configure FFTW3 support.
- // (http://www.fftw.org)
- //
- // Define 'cimg_use_fftw3' to enable libFFTW3 support.
- //
- // FFTW3 library may be used to efficiently compute the Fast Fourier Transform
- // of image data, without restriction on the image size.
- // (see method 'CImg[List]<T>::FFT()').
- #ifdef cimg_use_fftw3
- extern "C" {
- #include "fftw3.h"
- }
- #endif
- // Configure LibBoard support.
- // (http://libboard.sourceforge.net/)
- //
- // Define 'cimg_use_board' to enable Board support.
- //
- // Board library may be used to draw 3D objects in vector-graphics canvas
- // that can be saved as '.ps' or '.svg' files afterwards.
- // (see method 'CImg<T>::draw_object3d()').
- #ifdef cimg_use_board
- #include "Board.h"
- #endif
- // Configure OpenEXR support.
- // (http://www.openexr.com/)
- //
- // Define 'cimg_use_openexr' to enable OpenEXR support.
- //
- // OpenEXR library may be used to get a native support of '.exr' files.
- // (see methods 'CImg<T>::{load,save}_exr()').
- #ifdef cimg_use_openexr
- #if __GNUC__>=5
- #pragma GCC diagnostic push
- #pragma GCC diagnostic ignored "-Wdeprecated"
- #pragma GCC diagnostic ignored "-Wdeprecated-copy"
- #pragma GCC diagnostic ignored "-Wshadow"
- #endif
- #include "ImfRgbaFile.h"
- #include "ImfInputFile.h"
- #include "ImfChannelList.h"
- #include "ImfMatrixAttribute.h"
- #include "ImfArray.h"
- #if __GNUC__>=5
- #pragma GCC diagnostic pop
- #endif
- #endif
- // Configure TinyEXR support.
- // (https://github.com/syoyo/tinyexr)
- //
- // Define 'cimg_use_tinyexr' to enable TinyEXR support.
- //
- // TinyEXR is a small, single header-only library to load and save OpenEXR(.exr) images.
- #ifdef cimg_use_tinyexr
- #ifndef TINYEXR_IMPLEMENTATION
- #define TINYEXR_IMPLEMENTATION
- #endif
- #include "tinyexr.h"
- #endif
- // Lapack configuration.
- // (http://www.netlib.org/lapack)
- //
- // Define 'cimg_use_lapack' to enable LAPACK support.
- //
- // Lapack library may be used in several CImg methods to speed up
- // matrix computations (eigenvalues, inverse, ...).
- #ifdef cimg_use_lapack
- extern "C" {
- extern void sgetrf_(int*, int*, float*, int*, int*, int*);
- extern void sgetri_(int*, float*, int*, int*, float*, int*, int*);
- extern void sgetrs_(char*, int*, int*, float*, int*, int*, float*, int*, int*);
- extern void sgesvd_(char*, char*, int*, int*, float*, int*, float*, float*, int*, float*, int*, float*, int*, int*);
- extern void ssyev_(char*, char*, int*, float*, int*, float*, float*, int*, int*);
- extern void dgetrf_(int*, int*, double*, int*, int*, int*);
- extern void dgetri_(int*, double*, int*, int*, double*, int*, int*);
- extern void dgetrs_(char*, int*, int*, double*, int*, int*, double*, int*, int*);
- extern void dgesvd_(char*, char*, int*, int*, double*, int*, double*, double*,
- int*, double*, int*, double*, int*, int*);
- extern void dsyev_(char*, char*, int*, double*, int*, double*, double*, int*, int*);
- extern void dgels_(char*, int*,int*,int*,double*,int*,double*,int*,double*,int*,int*);
- extern void sgels_(char*, int*,int*,int*,float*,int*,float*,int*,float*,int*,int*);
- }
- #endif
- // Check if min/max/PI macros are defined.
- //
- // CImg does not compile if macros 'min', 'max' or 'PI' are defined,
- // because it redefines functions min(), max() and const variable PI in the cimg:: namespace.
- // so it '#undef' these macros if necessary, and restore them to reasonable
- // values at the end of this file.
- #ifdef min
- #undef min
- #define _cimg_redefine_min
- #endif
- #ifdef max
- #undef max
- #define _cimg_redefine_max
- #endif
- #ifdef PI
- #undef PI
- #define _cimg_redefine_PI
- #endif
- /*------------------------------------------------------------------------------
- #
- # Define user-friendly macros.
- #
- # These CImg macros are prefixed by 'cimg_' and can be used safely in your own
- # code. They are useful to parse command line options, or to write image loops.
- #
- ------------------------------------------------------------------------------*/
- // Macros to define program usage, and retrieve command line arguments.
- #define cimg_usage(usage) cimg_library::cimg::option((char*)0,argc,argv,(char*)0,usage,false)
- #define cimg_help(str) cimg_library::cimg::option((char*)0,argc,argv,str,(char*)0)
- #define cimg_option(name,_default,usage) cimg_library::cimg::option(name,argc,argv,_default,usage)
- // Macros to define and manipulate local neighborhoods.
- #define CImg_2x2(I,T) T I[4]; \
- T& I##cc = I[0]; T& I##nc = I[1]; \
- T& I##cn = I[2]; T& I##nn = I[3]; \
- I##cc = I##nc = \
- I##cn = I##nn = 0
- #define CImg_3x3(I,T) T I[9]; \
- T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; \
- T& I##pc = I[3]; T& I##cc = I[4]; T& I##nc = I[5]; \
- T& I##pn = I[6]; T& I##cn = I[7]; T& I##nn = I[8]; \
- I##pp = I##cp = I##np = \
- I##pc = I##cc = I##nc = \
- I##pn = I##cn = I##nn = 0
- #define CImg_4x4(I,T) T I[16]; \
- T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; T& I##ap = I[3]; \
- T& I##pc = I[4]; T& I##cc = I[5]; T& I##nc = I[6]; T& I##ac = I[7]; \
- T& I##pn = I[8]; T& I##cn = I[9]; T& I##nn = I[10]; T& I##an = I[11]; \
- T& I##pa = I[12]; T& I##ca = I[13]; T& I##na = I[14]; T& I##aa = I[15]; \
- I##pp = I##cp = I##np = I##ap = \
- I##pc = I##cc = I##nc = I##ac = \
- I##pn = I##cn = I##nn = I##an = \
- I##pa = I##ca = I##na = I##aa = 0
- #define CImg_5x5(I,T) T I[25]; \
- T& I##bb = I[0]; T& I##pb = I[1]; T& I##cb = I[2]; T& I##nb = I[3]; T& I##ab = I[4]; \
- T& I##bp = I[5]; T& I##pp = I[6]; T& I##cp = I[7]; T& I##np = I[8]; T& I##ap = I[9]; \
- T& I##bc = I[10]; T& I##pc = I[11]; T& I##cc = I[12]; T& I##nc = I[13]; T& I##ac = I[14]; \
- T& I##bn = I[15]; T& I##pn = I[16]; T& I##cn = I[17]; T& I##nn = I[18]; T& I##an = I[19]; \
- T& I##ba = I[20]; T& I##pa = I[21]; T& I##ca = I[22]; T& I##na = I[23]; T& I##aa = I[24]; \
- I##bb = I##pb = I##cb = I##nb = I##ab = \
- I##bp = I##pp = I##cp = I##np = I##ap = \
- I##bc = I##pc = I##cc = I##nc = I##ac = \
- I##bn = I##pn = I##cn = I##nn = I##an = \
- I##ba = I##pa = I##ca = I##na = I##aa = 0
- #define CImg_2x2x2(I,T) T I[8]; \
- T& I##ccc = I[0]; T& I##ncc = I[1]; \
- T& I##cnc = I[2]; T& I##nnc = I[3]; \
- T& I##ccn = I[4]; T& I##ncn = I[5]; \
- T& I##cnn = I[6]; T& I##nnn = I[7]; \
- I##ccc = I##ncc = \
- I##cnc = I##nnc = \
- I##ccn = I##ncn = \
- I##cnn = I##nnn = 0
- #define CImg_3x3x3(I,T) T I[27]; \
- T& I##ppp = I[0]; T& I##cpp = I[1]; T& I##npp = I[2]; \
- T& I##pcp = I[3]; T& I##ccp = I[4]; T& I##ncp = I[5]; \
- T& I##pnp = I[6]; T& I##cnp = I[7]; T& I##nnp = I[8]; \
- T& I##ppc = I[9]; T& I##cpc = I[10]; T& I##npc = I[11]; \
- T& I##pcc = I[12]; T& I##ccc = I[13]; T& I##ncc = I[14]; \
- T& I##pnc = I[15]; T& I##cnc = I[16]; T& I##nnc = I[17]; \
- T& I##ppn = I[18]; T& I##cpn = I[19]; T& I##npn = I[20]; \
- T& I##pcn = I[21]; T& I##ccn = I[22]; T& I##ncn = I[23]; \
- T& I##pnn = I[24]; T& I##cnn = I[25]; T& I##nnn = I[26]; \
- I##ppp = I##cpp = I##npp = \
- I##pcp = I##ccp = I##ncp = \
- I##pnp = I##cnp = I##nnp = \
- I##ppc = I##cpc = I##npc = \
- I##pcc = I##ccc = I##ncc = \
- I##pnc = I##cnc = I##nnc = \
- I##ppn = I##cpn = I##npn = \
- I##pcn = I##ccn = I##ncn = \
- I##pnn = I##cnn = I##nnn = 0
- #define cimg_def2x2(img,x,y) \
- int _n1##x = x<(img).width() - 1?x + 1:(img).width() - 1, \
- _n1##y = y<(img).height() - 1?y + 1:(img).height() - 1
- #define cimg_def3x3(img,x,y) \
- cimg_def2x2(img,x,y); \
- int _p1##x = x>1?x - 1:0, \
- _p1##y = y>1?y - 1:0
- #define cimg_def4x4(img,x,y) \
- cimg_def3x3(img,x,y); \
- int _n2##x = x<(img).width() - 2?x + 2:(img).width() - 1, \
- _n2##y = y<(img).height() - 2?y + 2:(img).height() - 1
- #define cimg_def5x5(img,x,y) \
- cimg_def4x4(img,x,y); \
- int _p2##x = x>2?x - 2:0, \
- _p2##y = y>2?y - 2:0
- #define cimg_def6x6(img,x,y) \
- cimg_def5x5(img,x,y); \
- int _n3##x = x<(img).width() - 3?x + 3:(img).width() - 1, \
- _n3##y = y<(img).height() - 3?y + 3:(img).height() - 1
- #define cimg_def7x7(img,x,y) \
- cimg_def6x6(img,x,y); \
- int _p3##x = x>3?x - 3:0, \
- _p3##y = y>3?y - 3:0
- #define cimg_def8x8(img,x,y) \
- cimg_def7x7(img,x,y); \
- int _n4##x = x<(img).width() - 4?x + 4:(img).width() - 1, \
- _n4##y = y<(img).height() - 4?y + 4:(img).height() - 1
- #define cimg_def9x9(img,x,y) \
- cimg_def8x8(img,x,y); \
- int _p4##x = x>4?x - 4:0, \
- _p4##y = y>4?y - 4:0
- #define cimg_def2x2x2(img,x,y,z) \
- cimg_def2x2(img,x,y); \
- int _n1##z = z<(img).depth() - 1?z + 1:(img).depth() - 1
- #define cimg_def3x3x3(img,x,y,z) \
- cimg_def2x2x2(img,x,y,z); \
- int _p1##x = x>1?x - 1:0, \
- _p1##y = y>1?y - 1:0, \
- _p1##z = z>1?z - 1:0
- #define cimg_get2x2(img,x,y,z,c,I,T) \
- I[0] = (T)(img)(x,y,z,c), I[1] = (T)(img)(_n1##x,y,z,c), I[2] = (T)(img)(x,_n1##y,z,c), \
- I[3] = (T)(img)(_n1##x,_n1##y,z,c)
- #define cimg_get3x3(img,x,y,z,c,I,T) \
- I[0] = (T)(img)(_p1##x,_p1##y,z,c), I[1] = (T)(img)(x,_p1##y,z,c), I[2] = (T)(img)(_n1##x,_p1##y,z,c), \
- I[3] = (T)(img)(_p1##x,y,z,c), I[4] = (T)(img)(x,y,z,c), I[5] = (T)(img)(_n1##x,y,z,c), \
- I[6] = (T)(img)(_p1##x,_n1##y,z,c), I[7] = (T)(img)(x,_n1##y,z,c), I[8] = (T)(img)(_n1##x,_n1##y,z,c)
- #define cimg_get4x4(img,x,y,z,c,I,T) \
- I[0] = (T)(img)(_p1##x,_p1##y,z,c), I[1] = (T)(img)(x,_p1##y,z,c), I[2] = (T)(img)(_n1##x,_p1##y,z,c), \
- I[3] = (T)(img)(_n2##x,_p1##y,z,c), I[4] = (T)(img)(_p1##x,y,z,c), I[5] = (T)(img)(x,y,z,c), \
- I[6] = (T)(img)(_n1##x,y,z,c), I[7] = (T)(img)(_n2##x,y,z,c), I[8] = (T)(img)(_p1##x,_n1##y,z,c), \
- I[9] = (T)(img)(x,_n1##y,z,c), I[10] = (T)(img)(_n1##x,_n1##y,z,c), I[11] = (T)(img)(_n2##x,_n1##y,z,c), \
- I[12] = (T)(img)(_p1##x,_n2##y,z,c), I[13] = (T)(img)(x,_n2##y,z,c), I[14] = (T)(img)(_n1##x,_n2##y,z,c), \
- I[15] = (T)(img)(_n2##x,_n2##y,z,c)
- #define cimg_get5x5(img,x,y,z,c,I,T) \
- I[0] = (T)(img)(_p2##x,_p2##y,z,c), I[1] = (T)(img)(_p1##x,_p2##y,z,c), I[2] = (T)(img)(x,_p2##y,z,c), \
- I[3] = (T)(img)(_n1##x,_p2##y,z,c), I[4] = (T)(img)(_n2##x,_p2##y,z,c), I[5] = (T)(img)(_p2##x,_p1##y,z,c), \
- I[6] = (T)(img)(_p1##x,_p1##y,z,c), I[7] = (T)(img)(x,_p1##y,z,c), I[8] = (T)(img)(_n1##x,_p1##y,z,c), \
- I[9] = (T)(img)(_n2##x,_p1##y,z,c), I[10] = (T)(img)(_p2##x,y,z,c), I[11] = (T)(img)(_p1##x,y,z,c), \
- I[12] = (T)(img)(x,y,z,c), I[13] = (T)(img)(_n1##x,y,z,c), I[14] = (T)(img)(_n2##x,y,z,c), \
- I[15] = (T)(img)(_p2##x,_n1##y,z,c), I[16] = (T)(img)(_p1##x,_n1##y,z,c), I[17] = (T)(img)(x,_n1##y,z,c), \
- I[18] = (T)(img)(_n1##x,_n1##y,z,c), I[19] = (T)(img)(_n2##x,_n1##y,z,c), I[20] = (T)(img)(_p2##x,_n2##y,z,c), \
- I[21] = (T)(img)(_p1##x,_n2##y,z,c), I[22] = (T)(img)(x,_n2##y,z,c), I[23] = (T)(img)(_n1##x,_n2##y,z,c), \
- I[24] = (T)(img)(_n2##x,_n2##y,z,c)
- #define cimg_get6x6(img,x,y,z,c,I,T) \
- I[0] = (T)(img)(_p2##x,_p2##y,z,c), I[1] = (T)(img)(_p1##x,_p2##y,z,c), I[2] = (T)(img)(x,_p2##y,z,c), \
- I[3] = (T)(img)(_n1##x,_p2##y,z,c), I[4] = (T)(img)(_n2##x,_p2##y,z,c), I[5] = (T)(img)(_n3##x,_p2##y,z,c), \
- I[6] = (T)(img)(_p2##x,_p1##y,z,c), I[7] = (T)(img)(_p1##x,_p1##y,z,c), I[8] = (T)(img)(x,_p1##y,z,c), \
- I[9] = (T)(img)(_n1##x,_p1##y,z,c), I[10] = (T)(img)(_n2##x,_p1##y,z,c), I[11] = (T)(img)(_n3##x,_p1##y,z,c), \
- I[12] = (T)(img)(_p2##x,y,z,c), I[13] = (T)(img)(_p1##x,y,z,c), I[14] = (T)(img)(x,y,z,c), \
- I[15] = (T)(img)(_n1##x,y,z,c), I[16] = (T)(img)(_n2##x,y,z,c), I[17] = (T)(img)(_n3##x,y,z,c), \
- I[18] = (T)(img)(_p2##x,_n1##y,z,c), I[19] = (T)(img)(_p1##x,_n1##y,z,c), I[20] = (T)(img)(x,_n1##y,z,c), \
- I[21] = (T)(img)(_n1##x,_n1##y,z,c), I[22] = (T)(img)(_n2##x,_n1##y,z,c), I[23] = (T)(img)(_n3##x,_n1##y,z,c), \
- I[24] = (T)(img)(_p2##x,_n2##y,z,c), I[25] = (T)(img)(_p1##x,_n2##y,z,c), I[26] = (T)(img)(x,_n2##y,z,c), \
- I[27] = (T)(img)(_n1##x,_n2##y,z,c), I[28] = (T)(img)(_n2##x,_n2##y,z,c), I[29] = (T)(img)(_n3##x,_n2##y,z,c), \
- I[30] = (T)(img)(_p2##x,_n3##y,z,c), I[31] = (T)(img)(_p1##x,_n3##y,z,c), I[32] = (T)(img)(x,_n3##y,z,c), \
- I[33] = (T)(img)(_n1##x,_n3##y,z,c), I[34] = (T)(img)(_n2##x,_n3##y,z,c), I[35] = (T)(img)(_n3##x,_n3##y,z,c)
- #define cimg_get7x7(img,x,y,z,c,I,T) \
- I[0] = (T)(img)(_p3##x,_p3##y,z,c), I[1] = (T)(img)(_p2##x,_p3##y,z,c), I[2] = (T)(img)(_p1##x,_p3##y,z,c), \
- I[3] = (T)(img)(x,_p3##y,z,c), I[4] = (T)(img)(_n1##x,_p3##y,z,c), I[5] = (T)(img)(_n2##x,_p3##y,z,c), \
- I[6] = (T)(img)(_n3##x,_p3##y,z,c), I[7] = (T)(img)(_p3##x,_p2##y,z,c), I[8] = (T)(img)(_p2##x,_p2##y,z,c), \
- I[9] = (T)(img)(_p1##x,_p2##y,z,c), I[10] = (T)(img)(x,_p2##y,z,c), I[11] = (T)(img)(_n1##x,_p2##y,z,c), \
- I[12] = (T)(img)(_n2##x,_p2##y,z,c), I[13] = (T)(img)(_n3##x,_p2##y,z,c), I[14] = (T)(img)(_p3##x,_p1##y,z,c), \
- I[15] = (T)(img)(_p2##x,_p1##y,z,c), I[16] = (T)(img)(_p1##x,_p1##y,z,c), I[17] = (T)(img)(x,_p1##y,z,c), \
- I[18] = (T)(img)(_n1##x,_p1##y,z,c), I[19] = (T)(img)(_n2##x,_p1##y,z,c), I[20] = (T)(img)(_n3##x,_p1##y,z,c), \
- I[21] = (T)(img)(_p3##x,y,z,c), I[22] = (T)(img)(_p2##x,y,z,c), I[23] = (T)(img)(_p1##x,y,z,c), \
- I[24] = (T)(img)(x,y,z,c), I[25] = (T)(img)(_n1##x,y,z,c), I[26] = (T)(img)(_n2##x,y,z,c), \
- I[27] = (T)(img)(_n3##x,y,z,c), I[28] = (T)(img)(_p3##x,_n1##y,z,c), I[29] = (T)(img)(_p2##x,_n1##y,z,c), \
- I[30] = (T)(img)(_p1##x,_n1##y,z,c), I[31] = (T)(img)(x,_n1##y,z,c), I[32] = (T)(img)(_n1##x,_n1##y,z,c), \
- I[33] = (T)(img)(_n2##x,_n1##y,z,c), I[34] = (T)(img)(_n3##x,_n1##y,z,c), I[35] = (T)(img)(_p3##x,_n2##y,z,c), \
- I[36] = (T)(img)(_p2##x,_n2##y,z,c), I[37] = (T)(img)(_p1##x,_n2##y,z,c), I[38] = (T)(img)(x,_n2##y,z,c), \
- I[39] = (T)(img)(_n1##x,_n2##y,z,c), I[40] = (T)(img)(_n2##x,_n2##y,z,c), I[41] = (T)(img)(_n3##x,_n2##y,z,c), \
- I[42] = (T)(img)(_p3##x,_n3##y,z,c), I[43] = (T)(img)(_p2##x,_n3##y,z,c), I[44] = (T)(img)(_p1##x,_n3##y,z,c), \
- I[45] = (T)(img)(x,_n3##y,z,c), I[46] = (T)(img)(_n1##x,_n3##y,z,c), I[47] = (T)(img)(_n2##x,_n3##y,z,c), \
- I[48] = (T)(img)(_n3##x,_n3##y,z,c)
- #define cimg_get8x8(img,x,y,z,c,I,T) \
- I[0] = (T)(img)(_p3##x,_p3##y,z,c), I[1] = (T)(img)(_p2##x,_p3##y,z,c), I[2] = (T)(img)(_p1##x,_p3##y,z,c), \
- I[3] = (T)(img)(x,_p3##y,z,c), I[4] = (T)(img)(_n1##x,_p3##y,z,c), I[5] = (T)(img)(_n2##x,_p3##y,z,c), \
- I[6] = (T)(img)(_n3##x,_p3##y,z,c), I[7] = (T)(img)(_n4##x,_p3##y,z,c), I[8] = (T)(img)(_p3##x,_p2##y,z,c), \
- I[9] = (T)(img)(_p2##x,_p2##y,z,c), I[10] = (T)(img)(_p1##x,_p2##y,z,c), I[11] = (T)(img)(x,_p2##y,z,c), \
- I[12] = (T)(img)(_n1##x,_p2##y,z,c), I[13] = (T)(img)(_n2##x,_p2##y,z,c), I[14] = (T)(img)(_n3##x,_p2##y,z,c), \
- I[15] = (T)(img)(_n4##x,_p2##y,z,c), I[16] = (T)(img)(_p3##x,_p1##y,z,c), I[17] = (T)(img)(_p2##x,_p1##y,z,c), \
- I[18] = (T)(img)(_p1##x,_p1##y,z,c), I[19] = (T)(img)(x,_p1##y,z,c), I[20] = (T)(img)(_n1##x,_p1##y,z,c), \
- I[21] = (T)(img)(_n2##x,_p1##y,z,c), I[22] = (T)(img)(_n3##x,_p1##y,z,c), I[23] = (T)(img)(_n4##x,_p1##y,z,c), \
- I[24] = (T)(img)(_p3##x,y,z,c), I[25] = (T)(img)(_p2##x,y,z,c), I[26] = (T)(img)(_p1##x,y,z,c), \
- I[27] = (T)(img)(x,y,z,c), I[28] = (T)(img)(_n1##x,y,z,c), I[29] = (T)(img)(_n2##x,y,z,c), \
- I[30] = (T)(img)(_n3##x,y,z,c), I[31] = (T)(img)(_n4##x,y,z,c), I[32] = (T)(img)(_p3##x,_n1##y,z,c), \
- I[33] = (T)(img)(_p2##x,_n1##y,z,c), I[34] = (T)(img)(_p1##x,_n1##y,z,c), I[35] = (T)(img)(x,_n1##y,z,c), \
- I[36] = (T)(img)(_n1##x,_n1##y,z,c), I[37] = (T)(img)(_n2##x,_n1##y,z,c), I[38] = (T)(img)(_n3##x,_n1##y,z,c), \
- I[39] = (T)(img)(_n4##x,_n1##y,z,c), I[40] = (T)(img)(_p3##x,_n2##y,z,c), I[41] = (T)(img)(_p2##x,_n2##y,z,c), \
- I[42] = (T)(img)(_p1##x,_n2##y,z,c), I[43] = (T)(img)(x,_n2##y,z,c), I[44] = (T)(img)(_n1##x,_n2##y,z,c), \
- I[45] = (T)(img)(_n2##x,_n2##y,z,c), I[46] = (T)(img)(_n3##x,_n2##y,z,c), I[47] = (T)(img)(_n4##x,_n2##y,z,c), \
- I[48] = (T)(img)(_p3##x,_n3##y,z,c), I[49] = (T)(img)(_p2##x,_n3##y,z,c), I[50] = (T)(img)(_p1##x,_n3##y,z,c), \
- I[51] = (T)(img)(x,_n3##y,z,c), I[52] = (T)(img)(_n1##x,_n3##y,z,c), I[53] = (T)(img)(_n2##x,_n3##y,z,c), \
- I[54] = (T)(img)(_n3##x,_n3##y,z,c), I[55] = (T)(img)(_n4##x,_n3##y,z,c), I[56] = (T)(img)(_p3##x,_n4##y,z,c), \
- I[57] = (T)(img)(_p2##x,_n4##y,z,c), I[58] = (T)(img)(_p1##x,_n4##y,z,c), I[59] = (T)(img)(x,_n4##y,z,c), \
- I[60] = (T)(img)(_n1##x,_n4##y,z,c), I[61] = (T)(img)(_n2##x,_n4##y,z,c), I[62] = (T)(img)(_n3##x,_n4##y,z,c), \
- I[63] = (T)(img)(_n4##x,_n4##y,z,c);
- #define cimg_get9x9(img,x,y,z,c,I,T) \
- I[0] = (T)(img)(_p4##x,_p4##y,z,c), I[1] = (T)(img)(_p3##x,_p4##y,z,c), I[2] = (T)(img)(_p2##x,_p4##y,z,c), \
- I[3] = (T)(img)(_p1##x,_p4##y,z,c), I[4] = (T)(img)(x,_p4##y,z,c), I[5] = (T)(img)(_n1##x,_p4##y,z,c), \
- I[6] = (T)(img)(_n2##x,_p4##y,z,c), I[7] = (T)(img)(_n3##x,_p4##y,z,c), I[8] = (T)(img)(_n4##x,_p4##y,z,c), \
- I[9] = (T)(img)(_p4##x,_p3##y,z,c), I[10] = (T)(img)(_p3##x,_p3##y,z,c), I[11] = (T)(img)(_p2##x,_p3##y,z,c), \
- I[12] = (T)(img)(_p1##x,_p3##y,z,c), I[13] = (T)(img)(x,_p3##y,z,c), I[14] = (T)(img)(_n1##x,_p3##y,z,c), \
- I[15] = (T)(img)(_n2##x,_p3##y,z,c), I[16] = (T)(img)(_n3##x,_p3##y,z,c), I[17] = (T)(img)(_n4##x,_p3##y,z,c), \
- I[18] = (T)(img)(_p4##x,_p2##y,z,c), I[19] = (T)(img)(_p3##x,_p2##y,z,c), I[20] = (T)(img)(_p2##x,_p2##y,z,c), \
- I[21] = (T)(img)(_p1##x,_p2##y,z,c), I[22] = (T)(img)(x,_p2##y,z,c), I[23] = (T)(img)(_n1##x,_p2##y,z,c), \
- I[24] = (T)(img)(_n2##x,_p2##y,z,c), I[25] = (T)(img)(_n3##x,_p2##y,z,c), I[26] = (T)(img)(_n4##x,_p2##y,z,c), \
- I[27] = (T)(img)(_p4##x,_p1##y,z,c), I[28] = (T)(img)(_p3##x,_p1##y,z,c), I[29] = (T)(img)(_p2##x,_p1##y,z,c), \
- I[30] = (T)(img)(_p1##x,_p1##y,z,c), I[31] = (T)(img)(x,_p1##y,z,c), I[32] = (T)(img)(_n1##x,_p1##y,z,c), \
- I[33] = (T)(img)(_n2##x,_p1##y,z,c), I[34] = (T)(img)(_n3##x,_p1##y,z,c), I[35] = (T)(img)(_n4##x,_p1##y,z,c), \
- I[36] = (T)(img)(_p4##x,y,z,c), I[37] = (T)(img)(_p3##x,y,z,c), I[38] = (T)(img)(_p2##x,y,z,c), \
- I[39] = (T)(img)(_p1##x,y,z,c), I[40] = (T)(img)(x,y,z,c), I[41] = (T)(img)(_n1##x,y,z,c), \
- I[42] = (T)(img)(_n2##x,y,z,c), I[43] = (T)(img)(_n3##x,y,z,c), I[44] = (T)(img)(_n4##x,y,z,c), \
- I[45] = (T)(img)(_p4##x,_n1##y,z,c), I[46] = (T)(img)(_p3##x,_n1##y,z,c), I[47] = (T)(img)(_p2##x,_n1##y,z,c), \
- I[48] = (T)(img)(_p1##x,_n1##y,z,c), I[49] = (T)(img)(x,_n1##y,z,c), I[50] = (T)(img)(_n1##x,_n1##y,z,c), \
- I[51] = (T)(img)(_n2##x,_n1##y,z,c), I[52] = (T)(img)(_n3##x,_n1##y,z,c), I[53] = (T)(img)(_n4##x,_n1##y,z,c), \
- I[54] = (T)(img)(_p4##x,_n2##y,z,c), I[55] = (T)(img)(_p3##x,_n2##y,z,c), I[56] = (T)(img)(_p2##x,_n2##y,z,c), \
- I[57] = (T)(img)(_p1##x,_n2##y,z,c), I[58] = (T)(img)(x,_n2##y,z,c), I[59] = (T)(img)(_n1##x,_n2##y,z,c), \
- I[60] = (T)(img)(_n2##x,_n2##y,z,c), I[61] = (T)(img)(_n3##x,_n2##y,z,c), I[62] = (T)(img)(_n4##x,_n2##y,z,c), \
- I[63] = (T)(img)(_p4##x,_n3##y,z,c), I[64] = (T)(img)(_p3##x,_n3##y,z,c), I[65] = (T)(img)(_p2##x,_n3##y,z,c), \
- I[66] = (T)(img)(_p1##x,_n3##y,z,c), I[67] = (T)(img)(x,_n3##y,z,c), I[68] = (T)(img)(_n1##x,_n3##y,z,c), \
- I[69] = (T)(img)(_n2##x,_n3##y,z,c), I[70] = (T)(img)(_n3##x,_n3##y,z,c), I[71] = (T)(img)(_n4##x,_n3##y,z,c), \
- I[72] = (T)(img)(_p4##x,_n4##y,z,c), I[73] = (T)(img)(_p3##x,_n4##y,z,c), I[74] = (T)(img)(_p2##x,_n4##y,z,c), \
- I[75] = (T)(img)(_p1##x,_n4##y,z,c), I[76] = (T)(img)(x,_n4##y,z,c), I[77] = (T)(img)(_n1##x,_n4##y,z,c), \
- I[78] = (T)(img)(_n2##x,_n4##y,z,c), I[79] = (T)(img)(_n3##x,_n4##y,z,c), I[80] = (T)(img)(_n4##x,_n4##y,z,c)
- #define cimg_get2x2x2(img,x,y,z,c,I,T) \
- I[0] = (T)(img)(x,y,z,c), I[1] = (T)(img)(_n1##x,y,z,c), I[2] = (T)(img)(x,_n1##y,z,c), \
- I[3] = (T)(img)(_n1##x,_n1##y,z,c), I[4] = (T)(img)(x,y,_n1##z,c), I[5] = (T)(img)(_n1##x,y,_n1##z,c), \
- I[6] = (T)(img)(x,_n1##y,_n1##z,c), I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)
- #define cimg_get3x3x3(img,x,y,z,c,I,T) \
- I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c), I[1] = (T)(img)(x,_p1##y,_p1##z,c), \
- I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c), I[3] = (T)(img)(_p1##x,y,_p1##z,c), I[4] = (T)(img)(x,y,_p1##z,c), \
- I[5] = (T)(img)(_n1##x,y,_p1##z,c), I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c), I[7] = (T)(img)(x,_n1##y,_p1##z,c), \
- I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c), I[9] = (T)(img)(_p1##x,_p1##y,z,c), I[10] = (T)(img)(x,_p1##y,z,c), \
- I[11] = (T)(img)(_n1##x,_p1##y,z,c), I[12] = (T)(img)(_p1##x,y,z,c), I[13] = (T)(img)(x,y,z,c), \
- I[14] = (T)(img)(_n1##x,y,z,c), I[15] = (T)(img)(_p1##x,_n1##y,z,c), I[16] = (T)(img)(x,_n1##y,z,c), \
- I[17] = (T)(img)(_n1##x,_n1##y,z,c), I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c), I[19] = (T)(img)(x,_p1##y,_n1##z,c), \
- I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c), I[21] = (T)(img)(_p1##x,y,_n1##z,c), I[22] = (T)(img)(x,y,_n1##z,c), \
- I[23] = (T)(img)(_n1##x,y,_n1##z,c), I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c), I[25] = (T)(img)(x,_n1##y,_n1##z,c), \
- I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)
- // Macros to perform various image loops.
- //
- // These macros are simpler to use than loops with C++ iterators.
- #define cimg_for(img,ptrs,T_ptrs) \
- for (T_ptrs *ptrs = (img)._data, *_max##ptrs = (img)._data + (img).size(); ptrs<_max##ptrs; ++ptrs)
- #define cimg_rof(img,ptrs,T_ptrs) for (T_ptrs *ptrs = (img)._data + (img).size() - 1; ptrs>=(img)._data; --ptrs)
- #define cimg_foroff(img,off) for (cimg_ulong off = 0, _max##off = (img).size(); off<_max##off; ++off)
- #define cimg_rofoff(img,off) for (cimg_long off = (cimg_long)((img).size() - 1); off>=0; --off)
- #define cimg_for1(bound,i) for (int i = 0; i<(int)(bound); ++i)
- #define cimg_forX(img,x) cimg_for1((img)._width,x)
- #define cimg_forY(img,y) cimg_for1((img)._height,y)
- #define cimg_forZ(img,z) cimg_for1((img)._depth,z)
- #define cimg_forC(img,c) cimg_for1((img)._spectrum,c)
- #define cimg_forXY(img,x,y) cimg_forY(img,y) cimg_forX(img,x)
- #define cimg_forXZ(img,x,z) cimg_forZ(img,z) cimg_forX(img,x)
- #define cimg_forYZ(img,y,z) cimg_forZ(img,z) cimg_forY(img,y)
- #define cimg_forXC(img,x,c) cimg_forC(img,c) cimg_forX(img,x)
- #define cimg_forYC(img,y,c) cimg_forC(img,c) cimg_forY(img,y)
- #define cimg_forZC(img,z,c) cimg_forC(img,c) cimg_forZ(img,z)
- #define cimg_forXYZ(img,x,y,z) cimg_forZ(img,z) cimg_forXY(img,x,y)
- #define cimg_forXYC(img,x,y,c) cimg_forC(img,c) cimg_forXY(img,x,y)
- #define cimg_forXZC(img,x,z,c) cimg_forC(img,c) cimg_forXZ(img,x,z)
- #define cimg_forYZC(img,y,z,c) cimg_forC(img,c) cimg_forYZ(img,y,z)
- #define cimg_forXYZC(img,x,y,z,c) cimg_forC(img,c) cimg_forXYZ(img,x,y,z)
- #define cimg_rof1(bound,i) for (int i = (int)(bound) - 1; i>=0; --i)
- #define cimg_rofX(img,x) cimg_rof1((img)._width,x)
- #define cimg_rofY(img,y) cimg_rof1((img)._height,y)
- #define cimg_rofZ(img,z) cimg_rof1((img)._depth,z)
- #define cimg_rofC(img,c) cimg_rof1((img)._spectrum,c)
- #define cimg_rofXY(img,x,y) cimg_rofY(img,y) cimg_rofX(img,x)
- #define cimg_rofXZ(img,x,z) cimg_rofZ(img,z) cimg_rofX(img,x)
- #define cimg_rofYZ(img,y,z) cimg_rofZ(img,z) cimg_rofY(img,y)
- #define cimg_rofXC(img,x,c) cimg_rofC(img,c) cimg_rofX(img,x)
- #define cimg_rofYC(img,y,c) cimg_rofC(img,c) cimg_rofY(img,y)
- #define cimg_rofZC(img,z,c) cimg_rofC(img,c) cimg_rofZ(img,z)
- #define cimg_rofXYZ(img,x,y,z) cimg_rofZ(img,z) cimg_rofXY(img,x,y)
- #define cimg_rofXYC(img,x,y,c) cimg_rofC(img,c) cimg_rofXY(img,x,y)
- #define cimg_rofXZC(img,x,z,c) cimg_rofC(img,c) cimg_rofXZ(img,x,z)
- #define cimg_rofYZC(img,y,z,c) cimg_rofC(img,c) cimg_rofYZ(img,y,z)
- #define cimg_rofXYZC(img,x,y,z,c) cimg_rofC(img,c) cimg_rofXYZ(img,x,y,z)
- #define cimg_for_in1(bound,i0,i1,i) \
- for (int i = (int)(i0)<0?0:(int)(i0), _max##i = (int)(i1)<(int)(bound)?(int)(i1):(int)(bound) - 1; i<=_max##i; ++i)
- #define cimg_for_inX(img,x0,x1,x) cimg_for_in1((img)._width,x0,x1,x)
- #define cimg_for_inY(img,y0,y1,y) cimg_for_in1((img)._height,y0,y1,y)
- #define cimg_for_inZ(img,z0,z1,z) cimg_for_in1((img)._depth,z0,z1,z)
- #define cimg_for_inC(img,c0,c1,c) cimg_for_in1((img)._spectrum,c0,c1,c)
- #define cimg_for_inXY(img,x0,y0,x1,y1,x,y) cimg_for_inY(img,y0,y1,y) cimg_for_inX(img,x0,x1,x)
- #define cimg_for_inXZ(img,x0,z0,x1,z1,x,z) cimg_for_inZ(img,z0,z1,z) cimg_for_inX(img,x0,x1,x)
- #define cimg_for_inXC(img,x0,c0,x1,c1,x,c) cimg_for_inC(img,c0,c1,c) cimg_for_inX(img,x0,x1,x)
- #define cimg_for_inYZ(img,y0,z0,y1,z1,y,z) cimg_for_inZ(img,x0,z1,z) cimg_for_inY(img,y0,y1,y)
- #define cimg_for_inYC(img,y0,c0,y1,c1,y,c) cimg_for_inC(img,c0,c1,c) cimg_for_inY(img,y0,y1,y)
- #define cimg_for_inZC(img,z0,c0,z1,c1,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inZ(img,z0,z1,z)
- #define cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_inZ(img,z0,z1,z) cimg_for_inXY(img,x0,y0,x1,y1,x,y)
- #define cimg_for_inXYC(img,x0,y0,c0,x1,y1,c1,x,y,c) cimg_for_inC(img,c0,c1,c) cimg_for_inXY(img,x0,y0,x1,y1,x,y)
- #define cimg_for_inXZC(img,x0,z0,c0,x1,z1,c1,x,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inXZ(img,x0,z0,x1,z1,x,z)
- #define cimg_for_inYZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_inC(img,c0,c1,c) cimg_for_inYZ(img,y0,z0,y1,z1,y,z)
- #define cimg_for_inXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
- cimg_for_inC(img,c0,c1,c) cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
- #define cimg_for_insideX(img,x,n) cimg_for_inX(img,n,(img)._width - 1 - (n),x)
- #define cimg_for_insideY(img,y,n) cimg_for_inY(img,n,(img)._height - 1 - (n),y)
- #define cimg_for_insideZ(img,z,n) cimg_for_inZ(img,n,(img)._depth - 1 - (n),z)
- #define cimg_for_insideC(img,c,n) cimg_for_inC(img,n,(img)._spectrum - 1 - (n),c)
- #define cimg_for_insideXY(img,x,y,n) cimg_for_inXY(img,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),x,y)
- #define cimg_for_insideXYZ(img,x,y,z,n) \
- cimg_for_inXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z)
- #define cimg_for_insideXYZC(img,x,y,z,c,n) \
- cimg_for_inXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z)
- #define cimg_for_out1(boundi,i0,i1,i) \
- for (int i = (int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); ++i, i = i==(int)(i0)?(int)(i1) + 1:i)
- #define cimg_for_out2(boundi,boundj,i0,j0,i1,j1,i,j) \
- for (int j = 0; j<(int)(boundj); ++j) \
- for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j?0:(int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); \
- ++i, i = _n1j?i:(i==(int)(i0)?(int)(i1) + 1:i))
- #define cimg_for_out3(boundi,boundj,boundk,i0,j0,k0,i1,j1,k1,i,j,k) \
- for (int k = 0; k<(int)(boundk); ++k) \
- for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \
- for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k?0:(int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); \
- ++i, i = _n1j || _n1k?i:(i==(int)(i0)?(int)(i1) + 1:i))
- #define cimg_for_out4(boundi,boundj,boundk,boundl,i0,j0,k0,l0,i1,j1,k1,l1,i,j,k,l) \
- for (int l = 0; l<(int)(boundl); ++l) \
- for (int _n1l = (int)(l<(int)(l0) || l>(int)(l1)), k = 0; k<(int)(boundk); ++k) \
- for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \
- for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k || _n1l?0:(int)(i0)>0?0:(int)(i1) + 1; \
- i<(int)(boundi); ++i, i = _n1j || _n1k || _n1l?i:(i==(int)(i0)?(int)(i1) + 1:i))
- #define cimg_for_outX(img,x0,x1,x) cimg_for_out1((img)._width,x0,x1,x)
- #define cimg_for_outY(img,y0,y1,y) cimg_for_out1((img)._height,y0,y1,y)
- #define cimg_for_outZ(img,z0,z1,z) cimg_for_out1((img)._depth,z0,z1,z)
- #define cimg_for_outC(img,c0,c1,c) cimg_for_out1((img)._spectrum,c0,c1,c)
- #define cimg_for_outXY(img,x0,y0,x1,y1,x,y) cimg_for_out2((img)._width,(img)._height,x0,y0,x1,y1,x,y)
- #define cimg_for_outXZ(img,x0,z0,x1,z1,x,z) cimg_for_out2((img)._width,(img)._depth,x0,z0,x1,z1,x,z)
- #define cimg_for_outXC(img,x0,c0,x1,c1,x,c) cimg_for_out2((img)._width,(img)._spectrum,x0,c0,x1,c1,x,c)
- #define cimg_for_outYZ(img,y0,z0,y1,z1,y,z) cimg_for_out2((img)._height,(img)._depth,y0,z0,y1,z1,y,z)
- #define cimg_for_outYC(img,y0,c0,y1,c1,y,c) cimg_for_out2((img)._height,(img)._spectrum,y0,c0,y1,c1,y,c)
- #define cimg_for_outZC(img,z0,c0,z1,c1,z,c) cimg_for_out2((img)._depth,(img)._spectrum,z0,c0,z1,c1,z,c)
- #define cimg_for_outXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) \
- cimg_for_out3((img)._width,(img)._height,(img)._depth,x0,y0,z0,x1,y1,z1,x,y,z)
- #define cimg_for_outXYC(img,x0,y0,c0,x1,y1,c1,x,y,c) \
- cimg_for_out3((img)._width,(img)._height,(img)._spectrum,x0,y0,c0,x1,y1,c1,x,y,c)
- #define cimg_for_outXZC(img,x0,z0,c0,x1,z1,c1,x,z,c) \
- cimg_for_out3((img)._width,(img)._depth,(img)._spectrum,x0,z0,c0,x1,z1,c1,x,z,c)
- #define cimg_for_outYZC(img,y0,z0,c0,y1,z1,c1,y,z,c) \
- cimg_for_out3((img)._height,(img)._depth,(img)._spectrum,y0,z0,c0,y1,z1,c1,y,z,c)
- #define cimg_for_outXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
- cimg_for_out4((img)._width,(img)._height,(img)._depth,(img)._spectrum,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c)
- #define cimg_for_borderX(img,x,n) cimg_for_outX(img,n,(img)._width - 1 - (n),x)
- #define cimg_for_borderY(img,y,n) cimg_for_outY(img,n,(img)._height - 1 - (n),y)
- #define cimg_for_borderZ(img,z,n) cimg_for_outZ(img,n,(img)._depth - 1 - (n),z)
- #define cimg_for_borderC(img,c,n) cimg_for_outC(img,n,(img)._spectrum - 1 - (n),c)
- #define cimg_for_borderXY(img,x,y,n) cimg_for_outXY(img,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),x,y)
- #define cimg_for_borderXYZ(img,x,y,z,n) \
- cimg_for_outXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z)
- #define cimg_for_borderXYZC(img,x,y,z,c,n) \
- cimg_for_outXYZC(img,n,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n), \
- (img)._depth - 1 - (n),(img)._spectrum - 1 - (n),x,y,z,c)
- #define cimg_for_spiralXY(img,x,y) \
- for (int x = 0, y = 0, _n1##x = 1, _n1##y = (img).width()*(img).height(); _n1##y; \
- --_n1##y, _n1##x+=(_n1##x>>2) - ((!(_n1##x&3)?--y:((_n1##x&3)==1?(img)._width - 1 - ++x:\
- ((_n1##x&3)==2?(img)._height - 1 - ++y:--x))))?0:1)
- #define cimg_for_lineXY(x,y,x0,y0,x1,y1) \
- for (int x = (int)(x0), y = (int)(y0), _sx = 1, _sy = 1, _steep = 0, \
- _dx=(x1)>(x0)?(int)(x1) - (int)(x0):(_sx=-1,(int)(x0) - (int)(x1)), \
- _dy=(y1)>(y0)?(int)(y1) - (int)(y0):(_sy=-1,(int)(y0) - (int)(y1)), \
- _counter = _dx, \
- _err = _dx>_dy?(_dy>>1):((_steep=1),(_counter=_dy),(_dx>>1)); \
- _counter>=0; \
- --_counter, x+=_steep? \
- (y+=_sy,(_err-=_dx)<0?_err+=_dy,_sx:0): \
- (y+=(_err-=_dy)<0?_err+=_dx,_sy:0,_sx))
- #define cimg_for2(bound,i) \
- for (int i = 0, _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
- _n1##i<(int)(bound) || i==--_n1##i; \
- ++i, ++_n1##i)
- #define cimg_for2X(img,x) cimg_for2((img)._width,x)
- #define cimg_for2Y(img,y) cimg_for2((img)._height,y)
- #define cimg_for2Z(img,z) cimg_for2((img)._depth,z)
- #define cimg_for2C(img,c) cimg_for2((img)._spectrum,c)
- #define cimg_for2XY(img,x,y) cimg_for2Y(img,y) cimg_for2X(img,x)
- #define cimg_for2XZ(img,x,z) cimg_for2Z(img,z) cimg_for2X(img,x)
- #define cimg_for2XC(img,x,c) cimg_for2C(img,c) cimg_for2X(img,x)
- #define cimg_for2YZ(img,y,z) cimg_for2Z(img,z) cimg_for2Y(img,y)
- #define cimg_for2YC(img,y,c) cimg_for2C(img,c) cimg_for2Y(img,y)
- #define cimg_for2ZC(img,z,c) cimg_for2C(img,c) cimg_for2Z(img,z)
- #define cimg_for2XYZ(img,x,y,z) cimg_for2Z(img,z) cimg_for2XY(img,x,y)
- #define cimg_for2XZC(img,x,z,c) cimg_for2C(img,c) cimg_for2XZ(img,x,z)
- #define cimg_for2YZC(img,y,z,c) cimg_for2C(img,c) cimg_for2YZ(img,y,z)
- #define cimg_for2XYZC(img,x,y,z,c) cimg_for2C(img,c) cimg_for2XYZ(img,x,y,z)
- #define cimg_for_in2(bound,i0,i1,i) \
- for (int i = (int)(i0)<0?0:(int)(i0), \
- _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1; \
- i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \
- ++i, ++_n1##i)
- #define cimg_for_in2X(img,x0,x1,x) cimg_for_in2((img)._width,x0,x1,x)
- #define cimg_for_in2Y(img,y0,y1,y) cimg_for_in2((img)._height,y0,y1,y)
- #define cimg_for_in2Z(img,z0,z1,z) cimg_for_in2((img)._depth,z0,z1,z)
- #define cimg_for_in2C(img,c0,c1,c) cimg_for_in2((img)._spectrum,c0,c1,c)
- #define cimg_for_in2XY(img,x0,y0,x1,y1,x,y) cimg_for_in2Y(img,y0,y1,y) cimg_for_in2X(img,x0,x1,x)
- #define cimg_for_in2XZ(img,x0,z0,x1,z1,x,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2X(img,x0,x1,x)
- #define cimg_for_in2XC(img,x0,c0,x1,c1,x,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2X(img,x0,x1,x)
- #define cimg_for_in2YZ(img,y0,z0,y1,z1,y,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2Y(img,y0,y1,y)
- #define cimg_for_in2YC(img,y0,c0,y1,c1,y,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2Y(img,y0,y1,y)
- #define cimg_for_in2ZC(img,z0,c0,z1,c1,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2Z(img,z0,z1,z)
- #define cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in2Z(img,z0,z1,z) cimg_for_in2XY(img,x0,y0,x1,y1,x,y)
- #define cimg_for_in2XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2XZ(img,x0,y0,x1,y1,x,z)
- #define cimg_for_in2YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in2C(img,c0,c1,c) cimg_for_in2YZ(img,y0,z0,y1,z1,y,z)
- #define cimg_for_in2XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
- cimg_for_in2C(img,c0,c1,c) cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
- #define cimg_for3(bound,i) \
- for (int i = 0, _p1##i = 0, \
- _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
- _n1##i<(int)(bound) || i==--_n1##i; \
- _p1##i = i++, ++_n1##i)
- #define cimg_for3X(img,x) cimg_for3((img)._width,x)
- #define cimg_for3Y(img,y) cimg_for3((img)._height,y)
- #define cimg_for3Z(img,z) cimg_for3((img)._depth,z)
- #define cimg_for3C(img,c) cimg_for3((img)._spectrum,c)
- #define cimg_for3XY(img,x,y) cimg_for3Y(img,y) cimg_for3X(img,x)
- #define cimg_for3XZ(img,x,z) cimg_for3Z(img,z) cimg_for3X(img,x)
- #define cimg_for3XC(img,x,c) cimg_for3C(img,c) cimg_for3X(img,x)
- #define cimg_for3YZ(img,y,z) cimg_for3Z(img,z) cimg_for3Y(img,y)
- #define cimg_for3YC(img,y,c) cimg_for3C(img,c) cimg_for3Y(img,y)
- #define cimg_for3ZC(img,z,c) cimg_for3C(img,c) cimg_for3Z(img,z)
- #define cimg_for3XYZ(img,x,y,z) cimg_for3Z(img,z) cimg_for3XY(img,x,y)
- #define cimg_for3XZC(img,x,z,c) cimg_for3C(img,c) cimg_for3XZ(img,x,z)
- #define cimg_for3YZC(img,y,z,c) cimg_for3C(img,c) cimg_for3YZ(img,y,z)
- #define cimg_for3XYZC(img,x,y,z,c) cimg_for3C(img,c) cimg_for3XYZ(img,x,y,z)
- #define cimg_for_in3(bound,i0,i1,i) \
- for (int i = (int)(i0)<0?0:(int)(i0), \
- _p1##i = i - 1<0?0:i - 1, \
- _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1; \
- i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \
- _p1##i = i++, ++_n1##i)
- #define cimg_for_in3X(img,x0,x1,x) cimg_for_in3((img)._width,x0,x1,x)
- #define cimg_for_in3Y(img,y0,y1,y) cimg_for_in3((img)._height,y0,y1,y)
- #define cimg_for_in3Z(img,z0,z1,z) cimg_for_in3((img)._depth,z0,z1,z)
- #define cimg_for_in3C(img,c0,c1,c) cimg_for_in3((img)._spectrum,c0,c1,c)
- #define cimg_for_in3XY(img,x0,y0,x1,y1,x,y) cimg_for_in3Y(img,y0,y1,y) cimg_for_in3X(img,x0,x1,x)
- #define cimg_for_in3XZ(img,x0,z0,x1,z1,x,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3X(img,x0,x1,x)
- #define cimg_for_in3XC(img,x0,c0,x1,c1,x,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3X(img,x0,x1,x)
- #define cimg_for_in3YZ(img,y0,z0,y1,z1,y,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3Y(img,y0,y1,y)
- #define cimg_for_in3YC(img,y0,c0,y1,c1,y,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3Y(img,y0,y1,y)
- #define cimg_for_in3ZC(img,z0,c0,z1,c1,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3Z(img,z0,z1,z)
- #define cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in3Z(img,z0,z1,z) cimg_for_in3XY(img,x0,y0,x1,y1,x,y)
- #define cimg_for_in3XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3XZ(img,x0,y0,x1,y1,x,z)
- #define cimg_for_in3YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in3C(img,c0,c1,c) cimg_for_in3YZ(img,y0,z0,y1,z1,y,z)
- #define cimg_for_in3XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
- cimg_for_in3C(img,c0,c1,c) cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
- #define cimg_for4(bound,i) \
- for (int i = 0, _p1##i = 0, _n1##i = 1>=(bound)?(int)(bound) - 1:1, \
- _n2##i = 2>=(bound)?(int)(bound) - 1:2; \
- _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \
- _p1##i = i++, ++_n1##i, ++_n2##i)
- #define cimg_for4X(img,x) cimg_for4((img)._width,x)
- #define cimg_for4Y(img,y) cimg_for4((img)._height,y)
- #define cimg_for4Z(img,z) cimg_for4((img)._depth,z)
- #define cimg_for4C(img,c) cimg_for4((img)._spectrum,c)
- #define cimg_for4XY(img,x,y) cimg_for4Y(img,y) cimg_for4X(img,x)
- #define cimg_for4XZ(img,x,z) cimg_for4Z(img,z) cimg_for4X(img,x)
- #define cimg_for4XC(img,x,c) cimg_for4C(img,c) cimg_for4X(img,x)
- #define cimg_for4YZ(img,y,z) cimg_for4Z(img,z) cimg_for4Y(img,y)
- #define cimg_for4YC(img,y,c) cimg_for4C(img,c) cimg_for4Y(img,y)
- #define cimg_for4ZC(img,z,c) cimg_for4C(img,c) cimg_for4Z(img,z)
- #define cimg_for4XYZ(img,x,y,z) cimg_for4Z(img,z) cimg_for4XY(img,x,y)
- #define cimg_for4XZC(img,x,z,c) cimg_for4C(img,c) cimg_for4XZ(img,x,z)
- #define cimg_for4YZC(img,y,z,c) cimg_for4C(img,c) cimg_for4YZ(img,y,z)
- #define cimg_for4XYZC(img,x,y,z,c) cimg_for4C(img,c) cimg_for4XYZ(img,x,y,z)
- #define cimg_for_in4(bound,i0,i1,i) \
- for (int i = (int)(i0)<0?0:(int)(i0), \
- _p1##i = i - 1<0?0:i - 1, \
- _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
- _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2; \
- i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \
- _p1##i = i++, ++_n1##i, ++_n2##i)
- #define cimg_for_in4X(img,x0,x1,x) cimg_for_in4((img)._width,x0,x1,x)
- #define cimg_for_in4Y(img,y0,y1,y) cimg_for_in4((img)._height,y0,y1,y)
- #define cimg_for_in4Z(img,z0,z1,z) cimg_for_in4((img)._depth,z0,z1,z)
- #define cimg_for_in4C(img,c0,c1,c) cimg_for_in4((img)._spectrum,c0,c1,c)
- #define cimg_for_in4XY(img,x0,y0,x1,y1,x,y) cimg_for_in4Y(img,y0,y1,y) cimg_for_in4X(img,x0,x1,x)
- #define cimg_for_in4XZ(img,x0,z0,x1,z1,x,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4X(img,x0,x1,x)
- #define cimg_for_in4XC(img,x0,c0,x1,c1,x,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4X(img,x0,x1,x)
- #define cimg_for_in4YZ(img,y0,z0,y1,z1,y,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4Y(img,y0,y1,y)
- #define cimg_for_in4YC(img,y0,c0,y1,c1,y,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4Y(img,y0,y1,y)
- #define cimg_for_in4ZC(img,z0,c0,z1,c1,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4Z(img,z0,z1,z)
- #define cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in4Z(img,z0,z1,z) cimg_for_in4XY(img,x0,y0,x1,y1,x,y)
- #define cimg_for_in4XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4XZ(img,x0,y0,x1,y1,x,z)
- #define cimg_for_in4YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in4C(img,c0,c1,c) cimg_for_in4YZ(img,y0,z0,y1,z1,y,z)
- #define cimg_for_in4XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
- cimg_for_in4C(img,c0,c1,c) cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
- #define cimg_for5(bound,i) \
- for (int i = 0, _p2##i = 0, _p1##i = 0, \
- _n1##i = 1>=(bound)?(int)(bound) - 1:1, \
- _n2##i = 2>=(bound)?(int)(bound) - 1:2; \
- _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \
- _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i)
- #define cimg_for5X(img,x) cimg_for5((img)._width,x)
- #define cimg_for5Y(img,y) cimg_for5((img)._height,y)
- #define cimg_for5Z(img,z) cimg_for5((img)._depth,z)
- #define cimg_for5C(img,c) cimg_for5((img)._spectrum,c)
- #define cimg_for5XY(img,x,y) cimg_for5Y(img,y) cimg_for5X(img,x)
- #define cimg_for5XZ(img,x,z) cimg_for5Z(img,z) cimg_for5X(img,x)
- #define cimg_for5XC(img,x,c) cimg_for5C(img,c) cimg_for5X(img,x)
- #define cimg_for5YZ(img,y,z) cimg_for5Z(img,z) cimg_for5Y(img,y)
- #define cimg_for5YC(img,y,c) cimg_for5C(img,c) cimg_for5Y(img,y)
- #define cimg_for5ZC(img,z,c) cimg_for5C(img,c) cimg_for5Z(img,z)
- #define cimg_for5XYZ(img,x,y,z) cimg_for5Z(img,z) cimg_for5XY(img,x,y)
- #define cimg_for5XZC(img,x,z,c) cimg_for5C(img,c) cimg_for5XZ(img,x,z)
- #define cimg_for5YZC(img,y,z,c) cimg_for5C(img,c) cimg_for5YZ(img,y,z)
- #define cimg_for5XYZC(img,x,y,z,c) cimg_for5C(img,c) cimg_for5XYZ(img,x,y,z)
- #define cimg_for_in5(bound,i0,i1,i) \
- for (int i = (int)(i0)<0?0:(int)(i0), \
- _p2##i = i - 2<0?0:i - 2, \
- _p1##i = i - 1<0?0:i - 1, \
- _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
- _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2; \
- i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \
- _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i)
- #define cimg_for_in5X(img,x0,x1,x) cimg_for_in5((img)._width,x0,x1,x)
- #define cimg_for_in5Y(img,y0,y1,y) cimg_for_in5((img)._height,y0,y1,y)
- #define cimg_for_in5Z(img,z0,z1,z) cimg_for_in5((img)._depth,z0,z1,z)
- #define cimg_for_in5C(img,c0,c1,c) cimg_for_in5((img)._spectrum,c0,c1,c)
- #define cimg_for_in5XY(img,x0,y0,x1,y1,x,y) cimg_for_in5Y(img,y0,y1,y) cimg_for_in5X(img,x0,x1,x)
- #define cimg_for_in5XZ(img,x0,z0,x1,z1,x,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5X(img,x0,x1,x)
- #define cimg_for_in5XC(img,x0,c0,x1,c1,x,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5X(img,x0,x1,x)
- #define cimg_for_in5YZ(img,y0,z0,y1,z1,y,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5Y(img,y0,y1,y)
- #define cimg_for_in5YC(img,y0,c0,y1,c1,y,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5Y(img,y0,y1,y)
- #define cimg_for_in5ZC(img,z0,c0,z1,c1,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5Z(img,z0,z1,z)
- #define cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in5Z(img,z0,z1,z) cimg_for_in5XY(img,x0,y0,x1,y1,x,y)
- #define cimg_for_in5XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5XZ(img,x0,y0,x1,y1,x,z)
- #define cimg_for_in5YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in5C(img,c0,c1,c) cimg_for_in5YZ(img,y0,z0,y1,z1,y,z)
- #define cimg_for_in5XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
- cimg_for_in5C(img,c0,c1,c) cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
- #define cimg_for6(bound,i) \
- for (int i = 0, _p2##i = 0, _p1##i = 0, \
- _n1##i = 1>=(bound)?(int)(bound) - 1:1, \
- _n2##i = 2>=(bound)?(int)(bound) - 1:2, \
- _n3##i = 3>=(bound)?(int)(bound) - 1:3; \
- _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \
- _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
- #define cimg_for6X(img,x) cimg_for6((img)._width,x)
- #define cimg_for6Y(img,y) cimg_for6((img)._height,y)
- #define cimg_for6Z(img,z) cimg_for6((img)._depth,z)
- #define cimg_for6C(img,c) cimg_for6((img)._spectrum,c)
- #define cimg_for6XY(img,x,y) cimg_for6Y(img,y) cimg_for6X(img,x)
- #define cimg_for6XZ(img,x,z) cimg_for6Z(img,z) cimg_for6X(img,x)
- #define cimg_for6XC(img,x,c) cimg_for6C(img,c) cimg_for6X(img,x)
- #define cimg_for6YZ(img,y,z) cimg_for6Z(img,z) cimg_for6Y(img,y)
- #define cimg_for6YC(img,y,c) cimg_for6C(img,c) cimg_for6Y(img,y)
- #define cimg_for6ZC(img,z,c) cimg_for6C(img,c) cimg_for6Z(img,z)
- #define cimg_for6XYZ(img,x,y,z) cimg_for6Z(img,z) cimg_for6XY(img,x,y)
- #define cimg_for6XZC(img,x,z,c) cimg_for6C(img,c) cimg_for6XZ(img,x,z)
- #define cimg_for6YZC(img,y,z,c) cimg_for6C(img,c) cimg_for6YZ(img,y,z)
- #define cimg_for6XYZC(img,x,y,z,c) cimg_for6C(img,c) cimg_for6XYZ(img,x,y,z)
- #define cimg_for_in6(bound,i0,i1,i) \
- for (int i = (int)(i0)<0?0:(int)(i0), \
- _p2##i = i - 2<0?0:i - 2, \
- _p1##i = i - 1<0?0:i - 1, \
- _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
- _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \
- _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3; \
- i<=(int)(i1) && \
- (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \
- _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
- #define cimg_for_in6X(img,x0,x1,x) cimg_for_in6((img)._width,x0,x1,x)
- #define cimg_for_in6Y(img,y0,y1,y) cimg_for_in6((img)._height,y0,y1,y)
- #define cimg_for_in6Z(img,z0,z1,z) cimg_for_in6((img)._depth,z0,z1,z)
- #define cimg_for_in6C(img,c0,c1,c) cimg_for_in6((img)._spectrum,c0,c1,c)
- #define cimg_for_in6XY(img,x0,y0,x1,y1,x,y) cimg_for_in6Y(img,y0,y1,y) cimg_for_in6X(img,x0,x1,x)
- #define cimg_for_in6XZ(img,x0,z0,x1,z1,x,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6X(img,x0,x1,x)
- #define cimg_for_in6XC(img,x0,c0,x1,c1,x,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6X(img,x0,x1,x)
- #define cimg_for_in6YZ(img,y0,z0,y1,z1,y,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6Y(img,y0,y1,y)
- #define cimg_for_in6YC(img,y0,c0,y1,c1,y,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6Y(img,y0,y1,y)
- #define cimg_for_in6ZC(img,z0,c0,z1,c1,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6Z(img,z0,z1,z)
- #define cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in6Z(img,z0,z1,z) cimg_for_in6XY(img,x0,y0,x1,y1,x,y)
- #define cimg_for_in6XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6XZ(img,x0,y0,x1,y1,x,z)
- #define cimg_for_in6YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in6C(img,c0,c1,c) cimg_for_in6YZ(img,y0,z0,y1,z1,y,z)
- #define cimg_for_in6XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
- cimg_for_in6C(img,c0,c1,c) cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
- #define cimg_for7(bound,i) \
- for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
- _n1##i = 1>=(bound)?(int)(bound) - 1:1, \
- _n2##i = 2>=(bound)?(int)(bound) - 1:2, \
- _n3##i = 3>=(bound)?(int)(bound) - 1:3; \
- _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \
- _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
- #define cimg_for7X(img,x) cimg_for7((img)._width,x)
- #define cimg_for7Y(img,y) cimg_for7((img)._height,y)
- #define cimg_for7Z(img,z) cimg_for7((img)._depth,z)
- #define cimg_for7C(img,c) cimg_for7((img)._spectrum,c)
- #define cimg_for7XY(img,x,y) cimg_for7Y(img,y) cimg_for7X(img,x)
- #define cimg_for7XZ(img,x,z) cimg_for7Z(img,z) cimg_for7X(img,x)
- #define cimg_for7XC(img,x,c) cimg_for7C(img,c) cimg_for7X(img,x)
- #define cimg_for7YZ(img,y,z) cimg_for7Z(img,z) cimg_for7Y(img,y)
- #define cimg_for7YC(img,y,c) cimg_for7C(img,c) cimg_for7Y(img,y)
- #define cimg_for7ZC(img,z,c) cimg_for7C(img,c) cimg_for7Z(img,z)
- #define cimg_for7XYZ(img,x,y,z) cimg_for7Z(img,z) cimg_for7XY(img,x,y)
- #define cimg_for7XZC(img,x,z,c) cimg_for7C(img,c) cimg_for7XZ(img,x,z)
- #define cimg_for7YZC(img,y,z,c) cimg_for7C(img,c) cimg_for7YZ(img,y,z)
- #define cimg_for7XYZC(img,x,y,z,c) cimg_for7C(img,c) cimg_for7XYZ(img,x,y,z)
- #define cimg_for_in7(bound,i0,i1,i) \
- for (int i = (int)(i0)<0?0:(int)(i0), \
- _p3##i = i - 3<0?0:i - 3, \
- _p2##i = i - 2<0?0:i - 2, \
- _p1##i = i - 1<0?0:i - 1, \
- _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
- _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \
- _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3; \
- i<=(int)(i1) && \
- (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \
- _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
- #define cimg_for_in7X(img,x0,x1,x) cimg_for_in7((img)._width,x0,x1,x)
- #define cimg_for_in7Y(img,y0,y1,y) cimg_for_in7((img)._height,y0,y1,y)
- #define cimg_for_in7Z(img,z0,z1,z) cimg_for_in7((img)._depth,z0,z1,z)
- #define cimg_for_in7C(img,c0,c1,c) cimg_for_in7((img)._spectrum,c0,c1,c)
- #define cimg_for_in7XY(img,x0,y0,x1,y1,x,y) cimg_for_in7Y(img,y0,y1,y) cimg_for_in7X(img,x0,x1,x)
- #define cimg_for_in7XZ(img,x0,z0,x1,z1,x,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7X(img,x0,x1,x)
- #define cimg_for_in7XC(img,x0,c0,x1,c1,x,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7X(img,x0,x1,x)
- #define cimg_for_in7YZ(img,y0,z0,y1,z1,y,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7Y(img,y0,y1,y)
- #define cimg_for_in7YC(img,y0,c0,y1,c1,y,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7Y(img,y0,y1,y)
- #define cimg_for_in7ZC(img,z0,c0,z1,c1,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7Z(img,z0,z1,z)
- #define cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in7Z(img,z0,z1,z) cimg_for_in7XY(img,x0,y0,x1,y1,x,y)
- #define cimg_for_in7XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7XZ(img,x0,y0,x1,y1,x,z)
- #define cimg_for_in7YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in7C(img,c0,c1,c) cimg_for_in7YZ(img,y0,z0,y1,z1,y,z)
- #define cimg_for_in7XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
- cimg_for_in7C(img,c0,c1,c) cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
- #define cimg_for8(bound,i) \
- for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
- _n1##i = 1>=(bound)?(int)(bound) - 1:1, \
- _n2##i = 2>=(bound)?(int)(bound) - 1:2, \
- _n3##i = 3>=(bound)?(int)(bound) - 1:3, \
- _n4##i = 4>=(bound)?(int)(bound) - 1:4; \
- _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
- i==(_n4##i = _n3##i = _n2##i = --_n1##i); \
- _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
- #define cimg_for8X(img,x) cimg_for8((img)._width,x)
- #define cimg_for8Y(img,y) cimg_for8((img)._height,y)
- #define cimg_for8Z(img,z) cimg_for8((img)._depth,z)
- #define cimg_for8C(img,c) cimg_for8((img)._spectrum,c)
- #define cimg_for8XY(img,x,y) cimg_for8Y(img,y) cimg_for8X(img,x)
- #define cimg_for8XZ(img,x,z) cimg_for8Z(img,z) cimg_for8X(img,x)
- #define cimg_for8XC(img,x,c) cimg_for8C(img,c) cimg_for8X(img,x)
- #define cimg_for8YZ(img,y,z) cimg_for8Z(img,z) cimg_for8Y(img,y)
- #define cimg_for8YC(img,y,c) cimg_for8C(img,c) cimg_for8Y(img,y)
- #define cimg_for8ZC(img,z,c) cimg_for8C(img,c) cimg_for8Z(img,z)
- #define cimg_for8XYZ(img,x,y,z) cimg_for8Z(img,z) cimg_for8XY(img,x,y)
- #define cimg_for8XZC(img,x,z,c) cimg_for8C(img,c) cimg_for8XZ(img,x,z)
- #define cimg_for8YZC(img,y,z,c) cimg_for8C(img,c) cimg_for8YZ(img,y,z)
- #define cimg_for8XYZC(img,x,y,z,c) cimg_for8C(img,c) cimg_for8XYZ(img,x,y,z)
- #define cimg_for_in8(bound,i0,i1,i) \
- for (int i = (int)(i0)<0?0:(int)(i0), \
- _p3##i = i - 3<0?0:i - 3, \
- _p2##i = i - 2<0?0:i - 2, \
- _p1##i = i - 1<0?0:i - 1, \
- _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
- _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \
- _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3, \
- _n4##i = i + 4>=(int)(bound)?(int)(bound) - 1:i + 4; \
- i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
- i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \
- _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
- #define cimg_for_in8X(img,x0,x1,x) cimg_for_in8((img)._width,x0,x1,x)
- #define cimg_for_in8Y(img,y0,y1,y) cimg_for_in8((img)._height,y0,y1,y)
- #define cimg_for_in8Z(img,z0,z1,z) cimg_for_in8((img)._depth,z0,z1,z)
- #define cimg_for_in8C(img,c0,c1,c) cimg_for_in8((img)._spectrum,c0,c1,c)
- #define cimg_for_in8XY(img,x0,y0,x1,y1,x,y) cimg_for_in8Y(img,y0,y1,y) cimg_for_in8X(img,x0,x1,x)
- #define cimg_for_in8XZ(img,x0,z0,x1,z1,x,z) cimg_for_in8Z(img,z0,z1,z) cimg_for_in8X(img,x0,x1,x)
- #define cimg_for_in8XC(img,x0,c0,x1,c1,x,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8X(img,x0,x1,x)
- #define cimg_for_in8YZ(img,y0,z0,y1,z1,y,z) cimg_for_in8Z(img,z0,z1,z) cimg_for_in8Y(img,y0,y1,y)
- #define cimg_for_in8YC(img,y0,c0,y1,c1,y,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8Y(img,y0,y1,y)
- #define cimg_for_in8ZC(img,z0,c0,z1,c1,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8Z(img,z0,z1,z)
- #define cimg_for_in8XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in8Z(img,z0,z1,z) cimg_for_in8XY(img,x0,y0,x1,y1,x,y)
- #define cimg_for_in8XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8XZ(img,x0,y0,x1,y1,x,z)
- #define cimg_for_in8YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in8C(img,c0,c1,c) cimg_for_in8YZ(img,y0,z0,y1,z1,y,z)
- #define cimg_for_in8XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
- cimg_for_in8C(img,c0,c1,c) cimg_for_in8XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
- #define cimg_for9(bound,i) \
- for (int i = 0, _p4##i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
- _n1##i = 1>=(int)(bound)?(int)(bound) - 1:1, \
- _n2##i = 2>=(int)(bound)?(int)(bound) - 1:2, \
- _n3##i = 3>=(int)(bound)?(int)(bound) - 1:3, \
- _n4##i = 4>=(int)(bound)?(int)(bound) - 1:4; \
- _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
- i==(_n4##i = _n3##i = _n2##i = --_n1##i); \
- _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
- #define cimg_for9X(img,x) cimg_for9((img)._width,x)
- #define cimg_for9Y(img,y) cimg_for9((img)._height,y)
- #define cimg_for9Z(img,z) cimg_for9((img)._depth,z)
- #define cimg_for9C(img,c) cimg_for9((img)._spectrum,c)
- #define cimg_for9XY(img,x,y) cimg_for9Y(img,y) cimg_for9X(img,x)
- #define cimg_for9XZ(img,x,z) cimg_for9Z(img,z) cimg_for9X(img,x)
- #define cimg_for9XC(img,x,c) cimg_for9C(img,c) cimg_for9X(img,x)
- #define cimg_for9YZ(img,y,z) cimg_for9Z(img,z) cimg_for9Y(img,y)
- #define cimg_for9YC(img,y,c) cimg_for9C(img,c) cimg_for9Y(img,y)
- #define cimg_for9ZC(img,z,c) cimg_for9C(img,c) cimg_for9Z(img,z)
- #define cimg_for9XYZ(img,x,y,z) cimg_for9Z(img,z) cimg_for9XY(img,x,y)
- #define cimg_for9XZC(img,x,z,c) cimg_for9C(img,c) cimg_for9XZ(img,x,z)
- #define cimg_for9YZC(img,y,z,c) cimg_for9C(img,c) cimg_for9YZ(img,y,z)
- #define cimg_for9XYZC(img,x,y,z,c) cimg_for9C(img,c) cimg_for9XYZ(img,x,y,z)
- #define cimg_for_in9(bound,i0,i1,i) \
- for (int i = (int)(i0)<0?0:(int)(i0), \
- _p4##i = i - 4<0?0:i - 4, \
- _p3##i = i - 3<0?0:i - 3, \
- _p2##i = i - 2<0?0:i - 2, \
- _p1##i = i - 1<0?0:i - 1, \
- _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
- _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \
- _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3, \
- _n4##i = i + 4>=(int)(bound)?(int)(bound) - 1:i + 4; \
- i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
- i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \
- _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
- #define cimg_for_in9X(img,x0,x1,x) cimg_for_in9((img)._width,x0,x1,x)
- #define cimg_for_in9Y(img,y0,y1,y) cimg_for_in9((img)._height,y0,y1,y)
- #define cimg_for_in9Z(img,z0,z1,z) cimg_for_in9((img)._depth,z0,z1,z)
- #define cimg_for_in9C(img,c0,c1,c) cimg_for_in9((img)._spectrum,c0,c1,c)
- #define cimg_for_in9XY(img,x0,y0,x1,y1,x,y) cimg_for_in9Y(img,y0,y1,y) cimg_for_in9X(img,x0,x1,x)
- #define cimg_for_in9XZ(img,x0,z0,x1,z1,x,z) cimg_for_in9Z(img,z0,z1,z) cimg_for_in9X(img,x0,x1,x)
- #define cimg_for_in9XC(img,x0,c0,x1,c1,x,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9X(img,x0,x1,x)
- #define cimg_for_in9YZ(img,y0,z0,y1,z1,y,z) cimg_for_in9Z(img,z0,z1,z) cimg_for_in9Y(img,y0,y1,y)
- #define cimg_for_in9YC(img,y0,c0,y1,c1,y,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9Y(img,y0,y1,y)
- #define cimg_for_in9ZC(img,z0,c0,z1,c1,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9Z(img,z0,z1,z)
- #define cimg_for_in9XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) cimg_for_in9Z(img,z0,z1,z) cimg_for_in9XY(img,x0,y0,x1,y1,x,y)
- #define cimg_for_in9XZC(img,x0,z0,c0,x1,y1,c1,x,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9XZ(img,x0,y0,x1,y1,x,z)
- #define cimg_for_in9YZC(img,y0,z0,c0,y1,z1,c1,y,z,c) cimg_for_in9C(img,c0,c1,c) cimg_for_in9YZ(img,y0,z0,y1,z1,y,z)
- #define cimg_for_in9XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
- cimg_for_in9C(img,c0,c1,c) cimg_for_in9XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
- #define cimg_for2x2(img,x,y,z,c,I,T) \
- cimg_for2((img)._height,y) for (int x = 0, \
- _n1##x = (int)( \
- (I[0] = (T)(img)(0,y,z,c)), \
- (I[2] = (T)(img)(0,_n1##y,z,c)), \
- 1>=(img)._width?(img).width() - 1:1); \
- (_n1##x<(img).width() && ( \
- (I[1] = (T)(img)(_n1##x,y,z,c)), \
- (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
- x==--_n1##x; \
- I[0] = I[1], \
- I[2] = I[3], \
- ++x, ++_n1##x)
- #define cimg_for_in2x2(img,x0,y0,x1,y1,x,y,z,c,I,T) \
- cimg_for_in2((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
- _n1##x = (int)( \
- (I[0] = (T)(img)(x,y,z,c)), \
- (I[2] = (T)(img)(x,_n1##y,z,c)), \
- x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \
- x<=(int)(x1) && ((_n1##x<(img).width() && ( \
- (I[1] = (T)(img)(_n1##x,y,z,c)), \
- (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
- x==--_n1##x); \
- I[0] = I[1], \
- I[2] = I[3], \
- ++x, ++_n1##x)
- #define cimg_for3x3(img,x,y,z,c,I,T) \
- cimg_for3((img)._height,y) for (int x = 0, \
- _p1##x = 0, \
- _n1##x = (int)( \
- (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
- (I[3] = I[4] = (T)(img)(0,y,z,c)), \
- (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)), \
- 1>=(img)._width?(img).width() - 1:1); \
- (_n1##x<(img).width() && ( \
- (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
- (I[5] = (T)(img)(_n1##x,y,z,c)), \
- (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
- x==--_n1##x; \
- I[0] = I[1], I[1] = I[2], \
- I[3] = I[4], I[4] = I[5], \
- I[6] = I[7], I[7] = I[8], \
- _p1##x = x++, ++_n1##x)
- #define cimg_for_in3x3(img,x0,y0,x1,y1,x,y,z,c,I,T) \
- cimg_for_in3((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
- _p1##x = x - 1<0?0:x - 1, \
- _n1##x = (int)( \
- (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \
- (I[3] = (T)(img)(_p1##x,y,z,c)), \
- (I[6] = (T)(img)(_p1##x,_n1##y,z,c)), \
- (I[1] = (T)(img)(x,_p1##y,z,c)), \
- (I[4] = (T)(img)(x,y,z,c)), \
- (I[7] = (T)(img)(x,_n1##y,z,c)), \
- x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \
- x<=(int)(x1) && ((_n1##x<(img).width() && ( \
- (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
- (I[5] = (T)(img)(_n1##x,y,z,c)), \
- (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
- x==--_n1##x); \
- I[0] = I[1], I[1] = I[2], \
- I[3] = I[4], I[4] = I[5], \
- I[6] = I[7], I[7] = I[8], \
- _p1##x = x++, ++_n1##x)
- #define cimg_for4x4(img,x,y,z,c,I,T) \
- cimg_for4((img)._height,y) for (int x = 0, \
- _p1##x = 0, \
- _n1##x = 1>=(img)._width?(img).width() - 1:1, \
- _n2##x = (int)( \
- (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
- (I[4] = I[5] = (T)(img)(0,y,z,c)), \
- (I[8] = I[9] = (T)(img)(0,_n1##y,z,c)), \
- (I[12] = I[13] = (T)(img)(0,_n2##y,z,c)), \
- (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
- (I[6] = (T)(img)(_n1##x,y,z,c)), \
- (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \
- (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \
- 2>=(img)._width?(img).width() - 1:2); \
- (_n2##x<(img).width() && ( \
- (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \
- (I[7] = (T)(img)(_n2##x,y,z,c)), \
- (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \
- (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
- _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \
- I[0] = I[1], I[1] = I[2], I[2] = I[3], \
- I[4] = I[5], I[5] = I[6], I[6] = I[7], \
- I[8] = I[9], I[9] = I[10], I[10] = I[11], \
- I[12] = I[13], I[13] = I[14], I[14] = I[15], \
- _p1##x = x++, ++_n1##x, ++_n2##x)
- #define cimg_for_in4x4(img,x0,y0,x1,y1,x,y,z,c,I,T) \
- cimg_for_in4((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
- _p1##x = x - 1<0?0:x - 1, \
- _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \
- _n2##x = (int)( \
- (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \
- (I[4] = (T)(img)(_p1##x,y,z,c)), \
- (I[8] = (T)(img)(_p1##x,_n1##y,z,c)), \
- (I[12] = (T)(img)(_p1##x,_n2##y,z,c)), \
- (I[1] = (T)(img)(x,_p1##y,z,c)), \
- (I[5] = (T)(img)(x,y,z,c)), \
- (I[9] = (T)(img)(x,_n1##y,z,c)), \
- (I[13] = (T)(img)(x,_n2##y,z,c)), \
- (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
- (I[6] = (T)(img)(_n1##x,y,z,c)), \
- (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \
- (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \
- x + 2>=(int)(img)._width?(img).width() - 1:x + 2); \
- x<=(int)(x1) && ((_n2##x<(img).width() && ( \
- (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \
- (I[7] = (T)(img)(_n2##x,y,z,c)), \
- (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \
- (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
- _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \
- I[0] = I[1], I[1] = I[2], I[2] = I[3], \
- I[4] = I[5], I[5] = I[6], I[6] = I[7], \
- I[8] = I[9], I[9] = I[10], I[10] = I[11], \
- I[12] = I[13], I[13] = I[14], I[14] = I[15], \
- _p1##x = x++, ++_n1##x, ++_n2##x)
- #define cimg_for5x5(img,x,y,z,c,I,T) \
- cimg_for5((img)._height,y) for (int x = 0, \
- _p2##x = 0, _p1##x = 0, \
- _n1##x = 1>=(img)._width?(img).width() - 1:1, \
- _n2##x = (int)( \
- (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \
- (I[5] = I[6] = I[7] = (T)(img)(0,_p1##y,z,c)), \
- (I[10] = I[11] = I[12] = (T)(img)(0,y,z,c)), \
- (I[15] = I[16] = I[17] = (T)(img)(0,_n1##y,z,c)), \
- (I[20] = I[21] = I[22] = (T)(img)(0,_n2##y,z,c)), \
- (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
- (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \
- (I[13] = (T)(img)(_n1##x,y,z,c)), \
- (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \
- (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \
- 2>=(img)._width?(img).width() - 1:2); \
- (_n2##x<(img).width() && ( \
- (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
- (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \
- (I[14] = (T)(img)(_n2##x,y,z,c)), \
- (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \
- (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
- _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \
- I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \
- I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \
- I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \
- I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \
- I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
- _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x)
- #define cimg_for_in5x5(img,x0,y0,x1,y1,x,y,z,c,I,T) \
- cimg_for_in5((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
- _p2##x = x - 2<0?0:x - 2, \
- _p1##x = x - 1<0?0:x - 1, \
- _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \
- _n2##x = (int)( \
- (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \
- (I[5] = (T)(img)(_p2##x,_p1##y,z,c)), \
- (I[10] = (T)(img)(_p2##x,y,z,c)), \
- (I[15] = (T)(img)(_p2##x,_n1##y,z,c)), \
- (I[20] = (T)(img)(_p2##x,_n2##y,z,c)), \
- (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \
- (I[6] = (T)(img)(_p1##x,_p1##y,z,c)), \
- (I[11] = (T)(img)(_p1##x,y,z,c)), \
- (I[16] = (T)(img)(_p1##x,_n1##y,z,c)), \
- (I[21] = (T)(img)(_p1##x,_n2##y,z,c)), \
- (I[2] = (T)(img)(x,_p2##y,z,c)), \
- (I[7] = (T)(img)(x,_p1##y,z,c)), \
- (I[12] = (T)(img)(x,y,z,c)), \
- (I[17] = (T)(img)(x,_n1##y,z,c)), \
- (I[22] = (T)(img)(x,_n2##y,z,c)), \
- (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
- (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \
- (I[13] = (T)(img)(_n1##x,y,z,c)), \
- (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \
- (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \
- x + 2>=(int)(img)._width?(img).width() - 1:x + 2); \
- x<=(int)(x1) && ((_n2##x<(img).width() && ( \
- (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
- (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \
- (I[14] = (T)(img)(_n2##x,y,z,c)), \
- (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \
- (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
- _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \
- I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \
- I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \
- I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \
- I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \
- I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
- _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x)
- #define cimg_for6x6(img,x,y,z,c,I,T) \
- cimg_for6((img)._height,y) for (int x = 0, \
- _p2##x = 0, _p1##x = 0, \
- _n1##x = 1>=(img)._width?(img).width() - 1:1, \
- _n2##x = 2>=(img)._width?(img).width() - 1:2, \
- _n3##x = (int)( \
- (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \
- (I[6] = I[7] = I[8] = (T)(img)(0,_p1##y,z,c)), \
- (I[12] = I[13] = I[14] = (T)(img)(0,y,z,c)), \
- (I[18] = I[19] = I[20] = (T)(img)(0,_n1##y,z,c)), \
- (I[24] = I[25] = I[26] = (T)(img)(0,_n2##y,z,c)), \
- (I[30] = I[31] = I[32] = (T)(img)(0,_n3##y,z,c)), \
- (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
- (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \
- (I[15] = (T)(img)(_n1##x,y,z,c)), \
- (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \
- (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \
- (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \
- (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
- (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \
- (I[16] = (T)(img)(_n2##x,y,z,c)), \
- (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \
- (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \
- (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \
- 3>=(img)._width?(img).width() - 1:3); \
- (_n3##x<(img).width() && ( \
- (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \
- (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \
- (I[17] = (T)(img)(_n3##x,y,z,c)), \
- (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \
- (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \
- (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
- _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x); \
- I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \
- I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \
- I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \
- I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
- I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \
- I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
- _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
- #define cimg_for_in6x6(img,x0,y0,x1,y1,x,y,z,c,I,T) \
- cimg_for_in6((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)x0, \
- _p2##x = x - 2<0?0:x - 2, \
- _p1##x = x - 1<0?0:x - 1, \
- _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \
- _n2##x = x + 2>=(int)(img)._width?(img).width() - 1:x + 2, \
- _n3##x = (int)( \
- (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \
- (I[6] = (T)(img)(_p2##x,_p1##y,z,c)), \
- (I[12] = (T)(img)(_p2##x,y,z,c)), \
- (I[18] = (T)(img)(_p2##x,_n1##y,z,c)), \
- (I[24] = (T)(img)(_p2##x,_n2##y,z,c)), \
- (I[30] = (T)(img)(_p2##x,_n3##y,z,c)), \
- (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \
- (I[7] = (T)(img)(_p1##x,_p1##y,z,c)), \
- (I[13] = (T)(img)(_p1##x,y,z,c)), \
- (I[19] = (T)(img)(_p1##x,_n1##y,z,c)), \
- (I[25] = (T)(img)(_p1##x,_n2##y,z,c)), \
- (I[31] = (T)(img)(_p1##x,_n3##y,z,c)), \
- (I[2] = (T)(img)(x,_p2##y,z,c)), \
- (I[8] = (T)(img)(x,_p1##y,z,c)), \
- (I[14] = (T)(img)(x,y,z,c)), \
- (I[20] = (T)(img)(x,_n1##y,z,c)), \
- (I[26] = (T)(img)(x,_n2##y,z,c)), \
- (I[32] = (T)(img)(x,_n3##y,z,c)), \
- (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
- (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \
- (I[15] = (T)(img)(_n1##x,y,z,c)), \
- (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \
- (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \
- (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \
- (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
- (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \
- (I[16] = (T)(img)(_n2##x,y,z,c)), \
- (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \
- (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \
- (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \
- x + 3>=(int)(img)._width?(img).width() - 1:x + 3); \
- x<=(int)(x1) && ((_n3##x<(img).width() && ( \
- (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \
- (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \
- (I[17] = (T)(img)(_n3##x,y,z,c)), \
- (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \
- (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \
- (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
- _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x)); \
- I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \
- I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \
- I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \
- I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
- I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \
- I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
- _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
- #define cimg_for7x7(img,x,y,z,c,I,T) \
- cimg_for7((img)._height,y) for (int x = 0, \
- _p3##x = 0, _p2##x = 0, _p1##x = 0, \
- _n1##x = 1>=(img)._width?(img).width() - 1:1, \
- _n2##x = 2>=(img)._width?(img).width() - 1:2, \
- _n3##x = (int)( \
- (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \
- (I[7] = I[8] = I[9] = I[10] = (T)(img)(0,_p2##y,z,c)), \
- (I[14] = I[15] = I[16] = I[17] = (T)(img)(0,_p1##y,z,c)), \
- (I[21] = I[22] = I[23] = I[24] = (T)(img)(0,y,z,c)), \
- (I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_n1##y,z,c)), \
- (I[35] = I[36] = I[37] = I[38] = (T)(img)(0,_n2##y,z,c)), \
- (I[42] = I[43] = I[44] = I[45] = (T)(img)(0,_n3##y,z,c)), \
- (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
- (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \
- (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \
- (I[25] = (T)(img)(_n1##x,y,z,c)), \
- (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \
- (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \
- (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \
- (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
- (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \
- (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \
- (I[26] = (T)(img)(_n2##x,y,z,c)), \
- (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \
- (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \
- (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \
- 3>=(img)._width?(img).width() - 1:3); \
- (_n3##x<(img).width() && ( \
- (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
- (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \
- (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \
- (I[27] = (T)(img)(_n3##x,y,z,c)), \
- (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \
- (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \
- (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
- _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x); \
- I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \
- I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \
- I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \
- I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \
- I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \
- I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \
- I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
- _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
- #define cimg_for_in7x7(img,x0,y0,x1,y1,x,y,z,c,I,T) \
- cimg_for_in7((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
- _p3##x = x - 3<0?0:x - 3, \
- _p2##x = x - 2<0?0:x - 2, \
- _p1##x = x - 1<0?0:x - 1, \
- _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \
- _n2##x = x + 2>=(int)(img)._width?(img).width() - 1:x + 2, \
- _n3##x = (int)( \
- (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \
- (I[7] = (T)(img)(_p3##x,_p2##y,z,c)), \
- (I[14] = (T)(img)(_p3##x,_p1##y,z,c)), \
- (I[21] = (T)(img)(_p3##x,y,z,c)), \
- (I[28] = (T)(img)(_p3##x,_n1##y,z,c)), \
- (I[35] = (T)(img)(_p3##x,_n2##y,z,c)), \
- (I[42] = (T)(img)(_p3##x,_n3##y,z,c)), \
- (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \
- (I[8] = (T)(img)(_p2##x,_p2##y,z,c)), \
- (I[15] = (T)(img)(_p2##x,_p1##y,z,c)), \
- (I[22] = (T)(img)(_p2##x,y,z,c)), \
- (I[29] = (T)(img)(_p2##x,_n1##y,z,c)), \
- (I[36] = (T)(img)(_p2##x,_n2##y,z,c)), \
- (I[43] = (T)(img)(_p2##x,_n3##y,z,c)), \
- (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \
- (I[9] = (T)(img)(_p1##x,_p2##y,z,c)), \
- (I[16] = (T)(img)(_p1##x,_p1##y,z,c)), \
- (I[23] = (T)(img)(_p1##x,y,z,c)), \
- (I[30] = (T)(img)(_p1##x,_n1##y,z,c)), \
- (I[37] = (T)(img)(_p1##x,_n2##y,z,c)), \
- (I[44] = (T)(img)(_p1##x,_n3##y,z,c)), \
- (I[3] = (T)(img)(x,_p3##y,z,c)), \
- (I[10] = (T)(img)(x,_p2##y,z,c)), \
- (I[17] = (T)(img)(x,_p1##y,z,c)), \
- (I[24] = (T)(img)(x,y,z,c)), \
- (I[31] = (T)(img)(x,_n1##y,z,c)), \
- (I[38] = (T)(img)(x,_n2##y,z,c)), \
- (I[45] = (T)(img)(x,_n3##y,z,c)), \
- (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
- (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \
- (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \
- (I[25] = (T)(img)(_n1##x,y,z,c)), \
- (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \
- (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \
- (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \
- (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
- (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \
- (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \
- (I[26] = (T)(img)(_n2##x,y,z,c)), \
- (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \
- (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \
- (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \
- x + 3>=(int)(img)._width?(img).width() - 1:x + 3); \
- x<=(int)(x1) && ((_n3##x<(img).width() && ( \
- (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
- (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \
- (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \
- (I[27] = (T)(img)(_n3##x,y,z,c)), \
- (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \
- (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \
- (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
- _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x)); \
- I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \
- I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \
- I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \
- I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \
- I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \
- I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \
- I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
- _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
- #define cimg_for8x8(img,x,y,z,c,I,T) \
- cimg_for8((img)._height,y) for (int x = 0, \
- _p3##x = 0, _p2##x = 0, _p1##x = 0, \
- _n1##x = 1>=((img)._width)?(img).width() - 1:1, \
- _n2##x = 2>=((img)._width)?(img).width() - 1:2, \
- _n3##x = 3>=((img)._width)?(img).width() - 1:3, \
- _n4##x = (int)( \
- (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \
- (I[8] = I[9] = I[10] = I[11] = (T)(img)(0,_p2##y,z,c)), \
- (I[16] = I[17] = I[18] = I[19] = (T)(img)(0,_p1##y,z,c)), \
- (I[24] = I[25] = I[26] = I[27] = (T)(img)(0,y,z,c)), \
- (I[32] = I[33] = I[34] = I[35] = (T)(img)(0,_n1##y,z,c)), \
- (I[40] = I[41] = I[42] = I[43] = (T)(img)(0,_n2##y,z,c)), \
- (I[48] = I[49] = I[50] = I[51] = (T)(img)(0,_n3##y,z,c)), \
- (I[56] = I[57] = I[58] = I[59] = (T)(img)(0,_n4##y,z,c)), \
- (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
- (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \
- (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \
- (I[28] = (T)(img)(_n1##x,y,z,c)), \
- (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \
- (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \
- (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \
- (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \
- (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
- (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \
- (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \
- (I[29] = (T)(img)(_n2##x,y,z,c)), \
- (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \
- (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \
- (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \
- (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \
- (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
- (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \
- (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \
- (I[30] = (T)(img)(_n3##x,y,z,c)), \
- (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \
- (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \
- (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \
- (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \
- 4>=((img)._width)?(img).width() - 1:4); \
- (_n4##x<(img).width() && ( \
- (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \
- (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \
- (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \
- (I[31] = (T)(img)(_n4##x,y,z,c)), \
- (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \
- (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \
- (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \
- (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
- _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \
- I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], \
- I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], \
- I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
- I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], \
- I[32] = I[33], I[33] = I[34], I[34] = I[35], I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], \
- I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], \
- I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[53] = I[54], I[54] = I[55], \
- I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[62] = I[63], \
- _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
- #define cimg_for_in8x8(img,x0,y0,x1,y1,x,y,z,c,I,T) \
- cimg_for_in8((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
- _p3##x = x - 3<0?0:x - 3, \
- _p2##x = x - 2<0?0:x - 2, \
- _p1##x = x - 1<0?0:x - 1, \
- _n1##x = x + 1>=(img).width()?(img).width() - 1:x + 1, \
- _n2##x = x + 2>=(img).width()?(img).width() - 1:x + 2, \
- _n3##x = x + 3>=(img).width()?(img).width() - 1:x + 3, \
- _n4##x = (int)( \
- (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \
- (I[8] = (T)(img)(_p3##x,_p2##y,z,c)), \
- (I[16] = (T)(img)(_p3##x,_p1##y,z,c)), \
- (I[24] = (T)(img)(_p3##x,y,z,c)), \
- (I[32] = (T)(img)(_p3##x,_n1##y,z,c)), \
- (I[40] = (T)(img)(_p3##x,_n2##y,z,c)), \
- (I[48] = (T)(img)(_p3##x,_n3##y,z,c)), \
- (I[56] = (T)(img)(_p3##x,_n4##y,z,c)), \
- (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \
- (I[9] = (T)(img)(_p2##x,_p2##y,z,c)), \
- (I[17] = (T)(img)(_p2##x,_p1##y,z,c)), \
- (I[25] = (T)(img)(_p2##x,y,z,c)), \
- (I[33] = (T)(img)(_p2##x,_n1##y,z,c)), \
- (I[41] = (T)(img)(_p2##x,_n2##y,z,c)), \
- (I[49] = (T)(img)(_p2##x,_n3##y,z,c)), \
- (I[57] = (T)(img)(_p2##x,_n4##y,z,c)), \
- (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \
- (I[10] = (T)(img)(_p1##x,_p2##y,z,c)), \
- (I[18] = (T)(img)(_p1##x,_p1##y,z,c)), \
- (I[26] = (T)(img)(_p1##x,y,z,c)), \
- (I[34] = (T)(img)(_p1##x,_n1##y,z,c)), \
- (I[42] = (T)(img)(_p1##x,_n2##y,z,c)), \
- (I[50] = (T)(img)(_p1##x,_n3##y,z,c)), \
- (I[58] = (T)(img)(_p1##x,_n4##y,z,c)), \
- (I[3] = (T)(img)(x,_p3##y,z,c)), \
- (I[11] = (T)(img)(x,_p2##y,z,c)), \
- (I[19] = (T)(img)(x,_p1##y,z,c)), \
- (I[27] = (T)(img)(x,y,z,c)), \
- (I[35] = (T)(img)(x,_n1##y,z,c)), \
- (I[43] = (T)(img)(x,_n2##y,z,c)), \
- (I[51] = (T)(img)(x,_n3##y,z,c)), \
- (I[59] = (T)(img)(x,_n4##y,z,c)), \
- (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
- (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \
- (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \
- (I[28] = (T)(img)(_n1##x,y,z,c)), \
- (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \
- (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \
- (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \
- (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \
- (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
- (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \
- (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \
- (I[29] = (T)(img)(_n2##x,y,z,c)), \
- (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \
- (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \
- (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \
- (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \
- (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
- (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \
- (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \
- (I[30] = (T)(img)(_n3##x,y,z,c)), \
- (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \
- (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \
- (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \
- (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \
- x + 4>=(img).width()?(img).width() - 1:x + 4); \
- x<=(int)(x1) && ((_n4##x<(img).width() && ( \
- (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \
- (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \
- (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \
- (I[31] = (T)(img)(_n4##x,y,z,c)), \
- (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \
- (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \
- (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \
- (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
- _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \
- I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], \
- I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], \
- I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
- I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], \
- I[32] = I[33], I[33] = I[34], I[34] = I[35], I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], \
- I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], \
- I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[53] = I[54], I[54] = I[55], \
- I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[62] = I[63], \
- _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
- #define cimg_for9x9(img,x,y,z,c,I,T) \
- cimg_for9((img)._height,y) for (int x = 0, \
- _p4##x = 0, _p3##x = 0, _p2##x = 0, _p1##x = 0, \
- _n1##x = 1>=((img)._width)?(img).width() - 1:1, \
- _n2##x = 2>=((img)._width)?(img).width() - 1:2, \
- _n3##x = 3>=((img)._width)?(img).width() - 1:3, \
- _n4##x = (int)( \
- (I[0] = I[1] = I[2] = I[3] = I[4] = (T)(img)(_p4##x,_p4##y,z,c)), \
- (I[9] = I[10] = I[11] = I[12] = I[13] = (T)(img)(0,_p3##y,z,c)), \
- (I[18] = I[19] = I[20] = I[21] = I[22] = (T)(img)(0,_p2##y,z,c)), \
- (I[27] = I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_p1##y,z,c)), \
- (I[36] = I[37] = I[38] = I[39] = I[40] = (T)(img)(0,y,z,c)), \
- (I[45] = I[46] = I[47] = I[48] = I[49] = (T)(img)(0,_n1##y,z,c)), \
- (I[54] = I[55] = I[56] = I[57] = I[58] = (T)(img)(0,_n2##y,z,c)), \
- (I[63] = I[64] = I[65] = I[66] = I[67] = (T)(img)(0,_n3##y,z,c)), \
- (I[72] = I[73] = I[74] = I[75] = I[76] = (T)(img)(0,_n4##y,z,c)), \
- (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \
- (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \
- (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \
- (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \
- (I[41] = (T)(img)(_n1##x,y,z,c)), \
- (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \
- (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \
- (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \
- (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \
- (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \
- (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \
- (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \
- (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \
- (I[42] = (T)(img)(_n2##x,y,z,c)), \
- (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \
- (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \
- (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \
- (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \
- (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \
- (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \
- (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \
- (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \
- (I[43] = (T)(img)(_n3##x,y,z,c)), \
- (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \
- (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \
- (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \
- (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \
- 4>=((img)._width)?(img).width() - 1:4); \
- (_n4##x<(img).width() && ( \
- (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \
- (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \
- (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \
- (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \
- (I[44] = (T)(img)(_n4##x,y,z,c)), \
- (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \
- (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \
- (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \
- (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
- _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \
- I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], I[7] = I[8], \
- I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], \
- I[16] = I[17], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
- I[24] = I[25], I[25] = I[26], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], \
- I[32] = I[33], I[33] = I[34], I[34] = I[35], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], \
- I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
- I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[54] = I[55], I[55] = I[56], \
- I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[63] = I[64], \
- I[64] = I[65], I[65] = I[66], I[66] = I[67], I[67] = I[68], I[68] = I[69], I[69] = I[70], I[70] = I[71], \
- I[72] = I[73], I[73] = I[74], I[74] = I[75], I[75] = I[76], I[76] = I[77], I[77] = I[78], I[78] = I[79], \
- I[79] = I[80], \
- _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
- #define cimg_for_in9x9(img,x0,y0,x1,y1,x,y,z,c,I,T) \
- cimg_for_in9((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
- _p4##x = x - 4<0?0:x - 4, \
- _p3##x = x - 3<0?0:x - 3, \
- _p2##x = x - 2<0?0:x - 2, \
- _p1##x = x - 1<0?0:x - 1, \
- _n1##x = x + 1>=(img).width()?(img).width() - 1:x + 1, \
- _n2##x = x + 2>=(img).width()?(img).width() - 1:x + 2, \
- _n3##x = x + 3>=(img).width()?(img).width() - 1:x + 3, \
- _n4##x = (int)( \
- (I[0] = (T)(img)(_p4##x,_p4##y,z,c)), \
- (I[9] = (T)(img)(_p4##x,_p3##y,z,c)), \
- (I[18] = (T)(img)(_p4##x,_p2##y,z,c)), \
- (I[27] = (T)(img)(_p4##x,_p1##y,z,c)), \
- (I[36] = (T)(img)(_p4##x,y,z,c)), \
- (I[45] = (T)(img)(_p4##x,_n1##y,z,c)), \
- (I[54] = (T)(img)(_p4##x,_n2##y,z,c)), \
- (I[63] = (T)(img)(_p4##x,_n3##y,z,c)), \
- (I[72] = (T)(img)(_p4##x,_n4##y,z,c)), \
- (I[1] = (T)(img)(_p3##x,_p4##y,z,c)), \
- (I[10] = (T)(img)(_p3##x,_p3##y,z,c)), \
- (I[19] = (T)(img)(_p3##x,_p2##y,z,c)), \
- (I[28] = (T)(img)(_p3##x,_p1##y,z,c)), \
- (I[37] = (T)(img)(_p3##x,y,z,c)), \
- (I[46] = (T)(img)(_p3##x,_n1##y,z,c)), \
- (I[55] = (T)(img)(_p3##x,_n2##y,z,c)), \
- (I[64] = (T)(img)(_p3##x,_n3##y,z,c)), \
- (I[73] = (T)(img)(_p3##x,_n4##y,z,c)), \
- (I[2] = (T)(img)(_p2##x,_p4##y,z,c)), \
- (I[11] = (T)(img)(_p2##x,_p3##y,z,c)), \
- (I[20] = (T)(img)(_p2##x,_p2##y,z,c)), \
- (I[29] = (T)(img)(_p2##x,_p1##y,z,c)), \
- (I[38] = (T)(img)(_p2##x,y,z,c)), \
- (I[47] = (T)(img)(_p2##x,_n1##y,z,c)), \
- (I[56] = (T)(img)(_p2##x,_n2##y,z,c)), \
- (I[65] = (T)(img)(_p2##x,_n3##y,z,c)), \
- (I[74] = (T)(img)(_p2##x,_n4##y,z,c)), \
- (I[3] = (T)(img)(_p1##x,_p4##y,z,c)), \
- (I[12] = (T)(img)(_p1##x,_p3##y,z,c)), \
- (I[21] = (T)(img)(_p1##x,_p2##y,z,c)), \
- (I[30] = (T)(img)(_p1##x,_p1##y,z,c)), \
- (I[39] = (T)(img)(_p1##x,y,z,c)), \
- (I[48] = (T)(img)(_p1##x,_n1##y,z,c)), \
- (I[57] = (T)(img)(_p1##x,_n2##y,z,c)), \
- (I[66] = (T)(img)(_p1##x,_n3##y,z,c)), \
- (I[75] = (T)(img)(_p1##x,_n4##y,z,c)), \
- (I[4] = (T)(img)(x,_p4##y,z,c)), \
- (I[13] = (T)(img)(x,_p3##y,z,c)), \
- (I[22] = (T)(img)(x,_p2##y,z,c)), \
- (I[31] = (T)(img)(x,_p1##y,z,c)), \
- (I[40] = (T)(img)(x,y,z,c)), \
- (I[49] = (T)(img)(x,_n1##y,z,c)), \
- (I[58] = (T)(img)(x,_n2##y,z,c)), \
- (I[67] = (T)(img)(x,_n3##y,z,c)), \
- (I[76] = (T)(img)(x,_n4##y,z,c)), \
- (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \
- (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \
- (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \
- (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \
- (I[41] = (T)(img)(_n1##x,y,z,c)), \
- (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \
- (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \
- (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \
- (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \
- (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \
- (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \
- (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \
- (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \
- (I[42] = (T)(img)(_n2##x,y,z,c)), \
- (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \
- (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \
- (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \
- (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \
- (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \
- (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \
- (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \
- (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \
- (I[43] = (T)(img)(_n3##x,y,z,c)), \
- (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \
- (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \
- (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \
- (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \
- x + 4>=(img).width()?(img).width() - 1:x + 4); \
- x<=(int)(x1) && ((_n4##x<(img).width() && ( \
- (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \
- (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \
- (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \
- (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \
- (I[44] = (T)(img)(_n4##x,y,z,c)), \
- (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \
- (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \
- (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \
- (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
- _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \
- I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], I[6] = I[7], I[7] = I[8], \
- I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], \
- I[16] = I[17], I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
- I[24] = I[25], I[25] = I[26], I[27] = I[28], I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], \
- I[32] = I[33], I[33] = I[34], I[34] = I[35], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], \
- I[40] = I[41], I[41] = I[42], I[42] = I[43], I[43] = I[44], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
- I[48] = I[49], I[49] = I[50], I[50] = I[51], I[51] = I[52], I[52] = I[53], I[54] = I[55], I[55] = I[56], \
- I[56] = I[57], I[57] = I[58], I[58] = I[59], I[59] = I[60], I[60] = I[61], I[61] = I[62], I[63] = I[64], \
- I[64] = I[65], I[65] = I[66], I[66] = I[67], I[67] = I[68], I[68] = I[69], I[69] = I[70], I[70] = I[71], \
- I[72] = I[73], I[73] = I[74], I[74] = I[75], I[75] = I[76], I[76] = I[77], I[77] = I[78], I[78] = I[79], \
- I[79] = I[80], \
- _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
- #define cimg_for2x2x2(img,x,y,z,c,I,T) \
- cimg_for2((img)._depth,z) cimg_for2((img)._height,y) for (int x = 0, \
- _n1##x = (int)( \
- (I[0] = (T)(img)(0,y,z,c)), \
- (I[2] = (T)(img)(0,_n1##y,z,c)), \
- (I[4] = (T)(img)(0,y,_n1##z,c)), \
- (I[6] = (T)(img)(0,_n1##y,_n1##z,c)), \
- 1>=(img)._width?(img).width() - 1:1); \
- (_n1##x<(img).width() && ( \
- (I[1] = (T)(img)(_n1##x,y,z,c)), \
- (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \
- (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \
- (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
- x==--_n1##x; \
- I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \
- ++x, ++_n1##x)
- #define cimg_for_in2x2x2(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \
- cimg_for_in2((img)._depth,z0,z1,z) cimg_for_in2((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
- _n1##x = (int)( \
- (I[0] = (T)(img)(x,y,z,c)), \
- (I[2] = (T)(img)(x,_n1##y,z,c)), \
- (I[4] = (T)(img)(x,y,_n1##z,c)), \
- (I[6] = (T)(img)(x,_n1##y,_n1##z,c)), \
- x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \
- x<=(int)(x1) && ((_n1##x<(img).width() && ( \
- (I[1] = (T)(img)(_n1##x,y,z,c)), \
- (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \
- (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \
- (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
- x==--_n1##x); \
- I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \
- ++x, ++_n1##x)
- #define cimg_for3x3x3(img,x,y,z,c,I,T) \
- cimg_for3((img)._depth,z) cimg_for3((img)._height,y) for (int x = 0, \
- _p1##x = 0, \
- _n1##x = (int)( \
- (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \
- (I[3] = I[4] = (T)(img)(0,y,_p1##z,c)), \
- (I[6] = I[7] = (T)(img)(0,_n1##y,_p1##z,c)), \
- (I[9] = I[10] = (T)(img)(0,_p1##y,z,c)), \
- (I[12] = I[13] = (T)(img)(0,y,z,c)), \
- (I[15] = I[16] = (T)(img)(0,_n1##y,z,c)), \
- (I[18] = I[19] = (T)(img)(0,_p1##y,_n1##z,c)), \
- (I[21] = I[22] = (T)(img)(0,y,_n1##z,c)), \
- (I[24] = I[25] = (T)(img)(0,_n1##y,_n1##z,c)), \
- 1>=(img)._width?(img).width() - 1:1); \
- (_n1##x<(img).width() && ( \
- (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \
- (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \
- (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \
- (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \
- (I[14] = (T)(img)(_n1##x,y,z,c)), \
- (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \
- (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \
- (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \
- (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
- x==--_n1##x; \
- I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \
- I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \
- I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \
- _p1##x = x++, ++_n1##x)
- #define cimg_for_in3x3x3(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \
- cimg_for_in3((img)._depth,z0,z1,z) cimg_for_in3((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
- _p1##x = x - 1<0?0:x - 1, \
- _n1##x = (int)( \
- (I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \
- (I[3] = (T)(img)(_p1##x,y,_p1##z,c)), \
- (I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c)), \
- (I[9] = (T)(img)(_p1##x,_p1##y,z,c)), \
- (I[12] = (T)(img)(_p1##x,y,z,c)), \
- (I[15] = (T)(img)(_p1##x,_n1##y,z,c)), \
- (I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c)), \
- (I[21] = (T)(img)(_p1##x,y,_n1##z,c)), \
- (I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c)), \
- (I[1] = (T)(img)(x,_p1##y,_p1##z,c)), \
- (I[4] = (T)(img)(x,y,_p1##z,c)), \
- (I[7] = (T)(img)(x,_n1##y,_p1##z,c)), \
- (I[10] = (T)(img)(x,_p1##y,z,c)), \
- (I[13] = (T)(img)(x,y,z,c)), \
- (I[16] = (T)(img)(x,_n1##y,z,c)), \
- (I[19] = (T)(img)(x,_p1##y,_n1##z,c)), \
- (I[22] = (T)(img)(x,y,_n1##z,c)), \
- (I[25] = (T)(img)(x,_n1##y,_n1##z,c)), \
- x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \
- x<=(int)(x1) && ((_n1##x<(img).width() && ( \
- (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \
- (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \
- (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \
- (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \
- (I[14] = (T)(img)(_n1##x,y,z,c)), \
- (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \
- (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \
- (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \
- (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
- x==--_n1##x); \
- I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \
- I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \
- I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \
- _p1##x = x++, ++_n1##x)
- #define cimglist_for(list,l) for (int l = 0; l<(int)(list)._width; ++l)
- #define cimglist_rof(list,l) for (int l = (int)(list)._width - 1; l>=0; --l)
- #define cimglist_for_in(list,l0,l1,l) \
- for (int l = (int)(l0)<0?0:(int)(l0), _max##l = (unsigned int)l1<(list)._width?(int)(l1):(int)(list)._width - 1; \
- l<=_max##l; ++l)
- #define cimglist_apply(list,fn) cimglist_for(list,__##fn) (list)[__##fn].fn
- // Macros used to display error messages when exceptions are thrown.
- // You should not use these macros is your own code.
- #define _cimgdisplay_instance "[instance(%u,%u,%u,%c%s%c)] CImgDisplay::"
- #define cimgdisplay_instance _width,_height,_normalization,_title?'\"':'[',_title?_title:"untitled",_title?'\"':']'
- #define _cimg_instance "[instance(%u,%u,%u,%u,%p,%sshared)] CImg<%s>::"
- #define cimg_instance _width,_height,_depth,_spectrum,_data,_is_shared?"":"non-",pixel_type()
- #define _cimglist_instance "[instance(%u,%u,%p)] CImgList<%s>::"
- #define cimglist_instance _width,_allocated_width,_data,pixel_type()
- /*------------------------------------------------
- #
- #
- # Define cimg_library:: namespace
- #
- #
- -------------------------------------------------*/
- //! Contains <i>all classes and functions</i> of the \CImg library.
- /**
- This namespace is defined to avoid functions and class names collisions
- that could happen with the inclusion of other C++ header files.
- Anyway, it should not happen often and you should reasonably start most of your
- \CImg-based programs with
- \code
- #include "CImg.h"
- using namespace cimg_library;
- \endcode
- to simplify the declaration of \CImg Library objects afterwards.
- **/
- namespace cimg_library {
- // Declare the four classes of the CImg Library.
- template<typename T=float> struct CImg;
- template<typename T=float> struct CImgList;
- struct CImgDisplay;
- struct CImgException;
- // Declare cimg:: namespace.
- // This is an incomplete namespace definition here. It only contains some
- // necessary stuff to ensure a correct declaration order of the classes and functions
- // defined afterwards.
- namespace cimg {
- // Define character sequences for colored terminal output.
- #ifdef cimg_use_vt100
- static const char t_normal[] = { 0x1b, '[', '0', ';', '0', ';', '0', 'm', 0 };
- static const char t_black[] = { 0x1b, '[', '0', ';', '3', '0', ';', '5', '9', 'm', 0 };
- static const char t_red[] = { 0x1b, '[', '0', ';', '3', '1', ';', '5', '9', 'm', 0 };
- static const char t_green[] = { 0x1b, '[', '0', ';', '3', '2', ';', '5', '9', 'm', 0 };
- static const char t_yellow[] = { 0x1b, '[', '0', ';', '3', '3', ';', '5', '9', 'm', 0 };
- static const char t_blue[] = { 0x1b, '[', '0', ';', '3', '4', ';', '5', '9', 'm', 0 };
- static const char t_magenta[] = { 0x1b, '[', '0', ';', '3', '5', ';', '5', '9', 'm', 0 };
- static const char t_cyan[] = { 0x1b, '[', '0', ';', '3', '6', ';', '5', '9', 'm', 0 };
- static const char t_white[] = { 0x1b, '[', '0', ';', '3', '7', ';', '5', '9', 'm', 0 };
- static const char t_bold[] = { 0x1b, '[', '1', 'm', 0 };
- static const char t_underscore[] = { 0x1b, '[', '4', 'm', 0 };
- #else
- static const char t_normal[] = { 0 };
- static const char *const t_black = cimg::t_normal,
- *const t_red = cimg::t_normal,
- *const t_green = cimg::t_normal,
- *const t_yellow = cimg::t_normal,
- *const t_blue = cimg::t_normal,
- *const t_magenta = cimg::t_normal,
- *const t_cyan = cimg::t_normal,
- *const t_white = cimg::t_normal,
- *const t_bold = cimg::t_normal,
- *const t_underscore = cimg::t_normal;
- #endif
- inline std::FILE* output(std::FILE *file=0);
- inline void info();
- //! Avoid warning messages due to unused parameters. Do nothing actually.
- template<typename T>
- inline void unused(const T&, ...) {}
- // [internal] Lock/unlock a mutex for managing concurrent threads.
- // 'lock_mode' can be { 0=unlock | 1=lock | 2=trylock }.
- // 'n' can be in [0,31] but mutex range [0,15] is reserved by CImg.
- inline int mutex(const unsigned int n, const int lock_mode=1);
- inline unsigned int& exception_mode(const unsigned int value, const bool is_set) {
- static unsigned int mode = cimg_verbosity;
- if (is_set) { cimg::mutex(0); mode = value<4?value:4; cimg::mutex(0,0); }
- return mode;
- }
- // Functions to return standard streams 'stdin', 'stdout' and 'stderr'.
- inline FILE* _stdin(const bool throw_exception=true);
- inline FILE* _stdout(const bool throw_exception=true);
- inline FILE* _stderr(const bool throw_exception=true);
- // Mandatory because Microsoft's _snprintf() and _vsnprintf() do not add the '\0' character
- // at the end of the string.
- #if cimg_OS==2 && defined(_MSC_VER)
- inline int _snprintf(char *const s, const size_t size, const char *const format, ...) {
- va_list ap;
- va_start(ap,format);
- const int result = _vsnprintf(s,size,format,ap);
- va_end(ap);
- return result;
- }
- inline int _vsnprintf(char *const s, const size_t size, const char *const format, va_list ap) {
- int result = -1;
- cimg::mutex(6);
- if (size) result = _vsnprintf_s(s,size,_TRUNCATE,format,ap);
- if (result==-1) result = _vscprintf(format,ap);
- cimg::mutex(6,0);
- return result;
- }
- // Mutex-protected version of sscanf, snprintf and vnsprintf.
- // Used only MacOSX, as it seems those functions are not re-entrant on MacOSX.
- #elif defined(__MACOSX__) || defined(__APPLE__)
- inline int _sscanf(const char *const s, const char *const format, ...) {
- cimg::mutex(6);
- va_list args;
- va_start(args,format);
- const int result = std::vsscanf(s,format,args);
- va_end(args);
- cimg::mutex(6,0);
- return result;
- }
- inline int _snprintf(char *const s, const size_t n, const char *const format, ...) {
- cimg::mutex(6);
- va_list args;
- va_start(args,format);
- const int result = std::vsnprintf(s,n,format,args);
- va_end(args);
- cimg::mutex(6,0);
- return result;
- }
- inline int _vsnprintf(char *const s, const size_t size, const char* format, va_list ap) {
- cimg::mutex(6);
- const int result = std::vsnprintf(s,size,format,ap);
- cimg::mutex(6,0);
- return result;
- }
- #endif
- //! Set current \CImg exception mode.
- /**
- The way error messages are handled by \CImg can be changed dynamically, using this function.
- \param mode Desired exception mode. Possible values are:
- - \c 0: Hide library messages (quiet mode).
- - \c 1: Print library messages on the console.
- - \c 2: Display library messages on a dialog window.
- - \c 3: Do as \c 1 + add extra debug warnings (slow down the code!).
- - \c 4: Do as \c 2 + add extra debug warnings (slow down the code!).
- **/
- inline unsigned int& exception_mode(const unsigned int mode) {
- return exception_mode(mode,true);
- }
- //! Return current \CImg exception mode.
- /**
- \note By default, return the value of configuration macro \c cimg_verbosity
- **/
- inline unsigned int& exception_mode() {
- return exception_mode(0,false);
- }
- inline unsigned int openmp_mode(const unsigned int value, const bool is_set) {
- #if cimg_use_openmp!=0
- static unsigned int mode = 2;
- if (is_set) { cimg::mutex(0); mode = value<2?value:2; cimg::mutex(0,0); }
- return mode;
- #else
- cimg::unused(value,is_set);
- return 0;
- #endif
- }
- //! Set current \CImg openmp mode.
- /**
- The way openmp-based methods are handled by \CImg can be changed dynamically, using this function.
- \param mode Desired openmp mode. Possible values are:
- - \c 0: Never parallelize.
- - \c 1: Always parallelize.
- - \c 2: Adaptive parallelization mode (default behavior).
- **/
- inline unsigned int openmp_mode(const unsigned int mode) {
- return openmp_mode(mode,true);
- }
- //! Return current \CImg openmp mode.
- inline unsigned int openmp_mode() {
- return openmp_mode(0,false);
- }
- #ifndef cimg_openmp_sizefactor
- #define cimg_openmp_sizefactor 1
- #endif
- #define cimg_openmp_if(cond) if ((cimg::openmp_mode()==1 || (cimg::openmp_mode()>1 && (cond))))
- #define cimg_openmp_if_size(size,min_size) cimg_openmp_if((size)>=(cimg_openmp_sizefactor)*(min_size))
- #ifdef _MSC_VER
- // Disable 'collapse()' directive for MSVC (supports only OpenMP 2.0).
- #define cimg_openmp_collapse(k)
- #else
- #define cimg_openmp_collapse(k) collapse(k)
- #endif
- #if cimg_OS==2
- // Disable parallelization of simple loops on Windows, due to noticed performance drop.
- #define cimg_openmp_for(instance,expr,min_size) cimg_rof((instance),ptr,T) *ptr = (T)(expr);
- #else
- #define cimg_openmp_for(instance,expr,min_size) \
- cimg_pragma_openmp(parallel for cimg_openmp_if_size((instance).size(),min_size)) \
- cimg_rof((instance),ptr,T) *ptr = (T)(expr);
- #endif
- // Display a simple dialog box, and wait for the user's response.
- inline int dialog(const char *const title, const char *const msg,
- const char *const button1_label="OK", const char *const button2_label=0,
- const char *const button3_label=0, const char *const button4_label=0,
- const char *const button5_label=0, const char *const button6_label=0,
- const bool centering=false);
- // Evaluate math expression.
- inline double eval(const char *const expression,
- const double x=0, const double y=0, const double z=0, const double c=0);
- } // namespace cimg { ...
- /*---------------------------------------
- #
- # Define the CImgException structures
- #
- --------------------------------------*/
- //! Instances of \c CImgException are thrown when errors are encountered in a \CImg function call.
- /**
- \par Overview
- CImgException is the base class of all exceptions thrown by \CImg (except \b CImgAbortException).
- CImgException is never thrown itself. Derived classes that specify the type of errord are thrown instead.
- These classes can be:
- - \b CImgAbortException: Thrown when a computationally-intensive function is aborted by an external signal.
- This is the only \c non-derived exception class.
- - \b CImgArgumentException: Thrown when one argument of a called \CImg function is invalid.
- This is probably one of the most thrown exception by \CImg.
- For instance, the following example throws a \c CImgArgumentException:
- \code
- CImg<float> img(100,100,1,3); // Define a 100x100 color image with float-valued pixels
- img.mirror('e'); // Try to mirror image along the (non-existing) 'e'-axis
- \endcode
- - \b CImgDisplayException: Thrown when something went wrong during the display of images in CImgDisplay instances.
- - \b CImgInstanceException: Thrown when an instance associated to a called \CImg method does not fit
- the function requirements. For instance, the following example throws a \c CImgInstanceException:
- \code
- const CImg<float> img; // Define an empty image
- const float value = img.at(0); // Try to read first pixel value (does not exist)
- \endcode
- - \b CImgIOException: Thrown when an error occurred when trying to load or save image files.
- This happens when trying to read files that do not exist or with invalid formats.
- For instance, the following example throws a \c CImgIOException:
- \code
- const CImg<float> img("missing_file.jpg"); // Try to load a file that does not exist
- \endcode
- - \b CImgWarningException: Thrown only if configuration macro \c cimg_strict_warnings is set, and
- when a \CImg function has to display a warning message (see cimg::warn()).
- It is not recommended to throw CImgException instances by yourself,
- since they are expected to be thrown only by \CImg.
- When an error occurs in a library function call, \CImg may display error messages on the screen or on the
- standard output, depending on the current \CImg exception mode.
- The \CImg exception mode can be get and set by functions cimg::exception_mode() and
- cimg::exception_mode(unsigned int).
- \par Exceptions handling
- In all cases, when an error occurs in \CImg, an instance of the corresponding exception class is thrown.
- This may lead the program to break (this is the default behavior), but you can bypass this behavior by
- handling the exceptions by yourself,
- using a usual <tt>try { ... } catch () { ... }</tt> block, as in the following example:
- \code
- #define "CImg.h"
- using namespace cimg_library;
- int main() {
- cimg::exception_mode(0); // Enable quiet exception mode
- try {
- ... // Here, do what you want to stress CImg
- } catch (CImgException& e) { // You succeeded: something went wrong!
- std::fprintf(stderr,"CImg Library Error: %s",e.what()); // Display your custom error message
- ... // Do what you want now to save the ship!
- }
- }
- \endcode
- **/
- struct CImgException : public std::exception {
- #define _cimg_exception_err(etype,disp_flag) \
- std::va_list ap, ap2; \
- va_start(ap,format); va_start(ap2,format); \
- int size = cimg_vsnprintf(0,0,format,ap2); \
- if (size++>=0) { \
- delete[] _message; \
- _message = new char[(size_t)size]; \
- cimg_vsnprintf(_message,(size_t)size,format,ap); \
- if (cimg::exception_mode()) { \
- std::fprintf(cimg::output(),"\n%s[CImg] *** %s ***%s %s\n",cimg::t_red,etype,cimg::t_normal,_message); \
- if (cimg_display && disp_flag && !(cimg::exception_mode()%2)) try { cimg::dialog(etype,_message,"Abort"); } \
- catch (CImgException&) {} \
- if (cimg::exception_mode()>=3) cimg_library::cimg::info(); \
- } \
- } \
- va_end(ap); va_end(ap2);
- char *_message;
- CImgException() { _message = new char[1]; *_message = 0; }
- CImgException(const char *const format, ...):_message(0) { _cimg_exception_err("CImgException",true); }
- CImgException(const CImgException& e):std::exception(e) {
- const size_t size = std::strlen(e._message);
- _message = new char[size + 1];
- std::strncpy(_message,e._message,size);
- _message[size] = 0;
- }
- ~CImgException() throw() { delete[] _message; }
- CImgException& operator=(const CImgException& e) {
- const size_t size = std::strlen(e._message);
- _message = new char[size + 1];
- std::strncpy(_message,e._message,size);
- _message[size] = 0;
- return *this;
- }
- //! Return a C-string containing the error message associated to the thrown exception.
- const char *what() const throw() { return _message; }
- }; // struct CImgException { ...
- // The CImgAbortException class is used to throw an exception when
- // a computationally-intensive function has been aborted by an external signal.
- struct CImgAbortException : public std::exception {
- char *_message;
- CImgAbortException() { _message = new char[1]; *_message = 0; }
- CImgAbortException(const char *const format, ...):_message(0) { _cimg_exception_err("CImgAbortException",true); }
- CImgAbortException(const CImgAbortException& e):std::exception(e) {
- const size_t size = std::strlen(e._message);
- _message = new char[size + 1];
- std::strncpy(_message,e._message,size);
- _message[size] = 0;
- }
- ~CImgAbortException() throw() { delete[] _message; }
- CImgAbortException& operator=(const CImgAbortException& e) {
- const size_t size = std::strlen(e._message);
- _message = new char[size + 1];
- std::strncpy(_message,e._message,size);
- _message[size] = 0;
- return *this;
- }
- //! Return a C-string containing the error message associated to the thrown exception.
- const char *what() const throw() { return _message; }
- }; // struct CImgAbortException { ...
- // The CImgArgumentException class is used to throw an exception related
- // to invalid arguments encountered in a library function call.
- struct CImgArgumentException : public CImgException {
- CImgArgumentException(const char *const format, ...) { _cimg_exception_err("CImgArgumentException",true); }
- }; // struct CImgArgumentException { ...
- // The CImgDisplayException class is used to throw an exception related
- // to display problems encountered in a library function call.
- struct CImgDisplayException : public CImgException {
- CImgDisplayException(const char *const format, ...) { _cimg_exception_err("CImgDisplayException",false); }
- }; // struct CImgDisplayException { ...
- // The CImgInstanceException class is used to throw an exception related
- // to an invalid instance encountered in a library function call.
- struct CImgInstanceException : public CImgException {
- CImgInstanceException(const char *const format, ...) { _cimg_exception_err("CImgInstanceException",true); }
- }; // struct CImgInstanceException { ...
- // The CImgIOException class is used to throw an exception related
- // to input/output file problems encountered in a library function call.
- struct CImgIOException : public CImgException {
- CImgIOException(const char *const format, ...) { _cimg_exception_err("CImgIOException",true); }
- }; // struct CImgIOException { ...
- // The CImgWarningException class is used to throw an exception for warnings
- // encountered in a library function call.
- struct CImgWarningException : public CImgException {
- CImgWarningException(const char *const format, ...) { _cimg_exception_err("CImgWarningException",false); }
- }; // struct CImgWarningException { ...
- /*-------------------------------------
- #
- # Define cimg:: namespace
- #
- -----------------------------------*/
- //! Contains \a low-level functions and variables of the \CImg Library.
- /**
- Most of the functions and variables within this namespace are used by the \CImg library for low-level operations.
- You may use them to access specific const values or environment variables internally used by \CImg.
- \warning Never write <tt>using namespace cimg_library::cimg;</tt> in your source code. Lot of functions in the
- <tt>cimg:: namespace</tt> have the same names as standard C functions that may be defined in the global
- namespace <tt>::</tt>.
- **/
- namespace cimg {
- // Define traits that will be used to determine the best data type to work in CImg functions.
- //
- template<typename T> struct type {
- static const char* string() {
- static const char* s[] = { "unknown", "unknown8", "unknown16", "unknown24",
- "unknown32", "unknown40", "unknown48", "unknown56",
- "unknown64", "unknown72", "unknown80", "unknown88",
- "unknown96", "unknown104", "unknown112", "unknown120",
- "unknown128" };
- return s[(sizeof(T)<17)?sizeof(T):0];
- }
- static bool is_float() { return false; }
- static bool is_inf(const T) { return false; }
- static bool is_nan(const T) { return false; }
- static bool is_finite(const T) { return true; }
- static T min() { return ~max(); }
- static T max() { return (T)1<<(8*sizeof(T) - 1); }
- static T inf() { return max(); }
- static T nan() { return inf(); }
- static T cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(T)val; }
- static const char* format() { return "%s"; }
- static const char* format_s() { return "%s"; }
- static const char* format(const T& val) { static const char *const s = "unknown"; cimg::unused(val); return s; }
- };
- template<> struct type<bool> {
- static const char* string() {
- static const char *const s = "bool";
- return s;
- }
- static bool is_float() { return false; }
- static bool is_inf(const bool) { return false; }
- static bool is_nan(const bool) { return false; }
- static bool is_finite(const bool) { return true; }
- static bool min() { return false; }
- static bool max() { return true; }
- static bool inf() { return max(); }
- static bool nan() { return inf(); }
- static bool is_inf() { return false; }
- static bool cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(bool)val; }
- static const char* format() { return "%s"; }
- static const char* format_s() { return "%s"; }
- static const char* format(const bool val) { static const char* s[] = { "false", "true" }; return s[val?1:0]; }
- };
- template<> struct type<unsigned char> {
- static const char* string() { static const char *const s = "uint8"; return s; }
- static bool is_float() { return false; }
- static bool is_inf(const unsigned char) { return false; }
- static bool is_nan(const unsigned char) { return false; }
- static bool is_finite(const unsigned char) { return true; }
- static unsigned char min() { return 0; }
- static unsigned char max() { return (unsigned char)-1; }
- static unsigned char inf() { return max(); }
- static unsigned char nan() { return inf(); }
- static unsigned char cut(const double val) {
- return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; }
- static const char* format() { return "%u"; }
- static const char* format_s() { return "%u"; }
- static unsigned int format(const unsigned char val) { return (unsigned int)val; }
- };
- #if defined(CHAR_MAX) && CHAR_MAX==255
- template<> struct type<char> {
- static const char* string() { static const char *const s = "uint8"; return s; }
- static bool is_float() { return false; }
- static bool is_inf(const char) { return false; }
- static bool is_nan(const char) { return false; }
- static bool is_finite(const char) { return true; }
- static char min() { return 0; }
- static char max() { return (char)-1; }
- static char inf() { return max(); }
- static char nan() { return inf(); }
- static char cut(const double val) {
- return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; }
- static const char* format() { return "%u"; }
- static const char* format_s() { return "%u"; }
- static unsigned int format(const char val) { return (unsigned int)val; }
- };
- #else
- template<> struct type<char> {
- static const char* string() { static const char *const s = "int8"; return s; }
- static bool is_float() { return false; }
- static bool is_inf(const char) { return false; }
- static bool is_nan(const char) { return false; }
- static bool is_finite(const char) { return true; }
- static char min() { return ~max(); }
- static char max() { return (char)((unsigned char)-1>>1); }
- static char inf() { return max(); }
- static char nan() { return inf(); }
- static char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(char)val; }
- static const char* format() { return "%d"; }
- static const char* format_s() { return "%d"; }
- static int format(const char val) { return (int)val; }
- };
- #endif
- template<> struct type<signed char> {
- static const char* string() { static const char *const s = "int8"; return s; }
- static bool is_float() { return false; }
- static bool is_inf(const signed char) { return false; }
- static bool is_nan(const signed char) { return false; }
- static bool is_finite(const signed char) { return true; }
- static signed char min() { return ~max(); }
- static signed char max() { return (signed char)((unsigned char)-1>>1); }
- static signed char inf() { return max(); }
- static signed char nan() { return inf(); }
- static signed char cut(const double val) {
- return val<(double)min()?min():val>(double)max()?max():(signed char)val; }
- static const char* format() { return "%d"; }
- static const char* format_s() { return "%d"; }
- static int format(const signed char val) { return (int)val; }
- };
- template<> struct type<unsigned short> {
- static const char* string() { static const char *const s = "uint16"; return s; }
- static bool is_float() { return false; }
- static bool is_inf(const unsigned short) { return false; }
- static bool is_nan(const unsigned short) { return false; }
- static bool is_finite(const unsigned short) { return true; }
- static unsigned short min() { return 0; }
- static unsigned short max() { return (unsigned short)-1; }
- static unsigned short inf() { return max(); }
- static unsigned short nan() { return inf(); }
- static unsigned short cut(const double val) {
- return val<(double)min()?min():val>(double)max()?max():(unsigned short)val; }
- static const char* format() { return "%u"; }
- static const char* format_s() { return "%u"; }
- static unsigned int format(const unsigned short val) { return (unsigned int)val; }
- };
- template<> struct type<short> {
- static const char* string() { static const char *const s = "int16"; return s; }
- static bool is_float() { return false; }
- static bool is_inf(const short) { return false; }
- static bool is_nan(const short) { return false; }
- static bool is_finite(const short) { return true; }
- static short min() { return ~max(); }
- static short max() { return (short)((unsigned short)-1>>1); }
- static short inf() { return max(); }
- static short nan() { return inf(); }
- static short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(short)val; }
- static const char* format() { return "%d"; }
- static const char* format_s() { return "%d"; }
- static int format(const short val) { return (int)val; }
- };
- template<> struct type<unsigned int> {
- static const char* string() { static const char *const s = "uint32"; return s; }
- static bool is_float() { return false; }
- static bool is_inf(const unsigned int) { return false; }
- static bool is_nan(const unsigned int) { return false; }
- static bool is_finite(const unsigned int) { return true; }
- static unsigned int min() { return 0; }
- static unsigned int max() { return (unsigned int)-1; }
- static unsigned int inf() { return max(); }
- static unsigned int nan() { return inf(); }
- static unsigned int cut(const double val) {
- return val<(double)min()?min():val>(double)max()?max():(unsigned int)val; }
- static const char* format() { return "%u"; }
- static const char* format_s() { return "%u"; }
- static unsigned int format(const unsigned int val) { return val; }
- };
- template<> struct type<int> {
- static const char* string() { static const char *const s = "int32"; return s; }
- static bool is_float() { return false; }
- static bool is_inf(const int) { return false; }
- static bool is_nan(const int) { return false; }
- static bool is_finite(const int) { return true; }
- static int min() { return ~max(); }
- static int max() { return (int)(~0U>>1); }
- static int inf() { return max(); }
- static int nan() { return inf(); }
- static int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(int)val; }
- static const char* format() { return "%d"; }
- static const char* format_s() { return "%d"; }
- static int format(const int val) { return val; }
- };
- template<> struct type<cimg_uint64> {
- static const char* string() { static const char *const s = "uint64"; return s; }
- static bool is_float() { return false; }
- static bool is_inf(const cimg_uint64) { return false; }
- static bool is_nan(const cimg_uint64) { return false; }
- static bool is_finite(const cimg_uint64) { return true; }
- static cimg_uint64 min() { return 0; }
- static cimg_uint64 max() { return (cimg_uint64)-1; }
- static cimg_uint64 inf() { return max(); }
- static cimg_uint64 nan() { return inf(); }
- static cimg_uint64 cut(const double val) {
- return val<(double)min()?min():val>(double)max()?max():(cimg_uint64)val; }
- static const char* format() { return cimg_fuint64; }
- static const char* format_s() { return cimg_fuint64; }
- static cimg_uint64 format(const cimg_uint64 val) { return val; }
- };
- template<> struct type<cimg_int64> {
- static const char* string() { static const char *const s = "int64"; return s; }
- static bool is_float() { return false; }
- static bool is_inf(const cimg_int64) { return false; }
- static bool is_nan(const cimg_int64) { return false; }
- static bool is_finite(const cimg_int64) { return true; }
- static cimg_int64 min() { return ~max(); }
- static cimg_int64 max() { return (cimg_int64)((cimg_uint64)-1>>1); }
- static cimg_int64 inf() { return max(); }
- static cimg_int64 nan() { return inf(); }
- static cimg_int64 cut(const double val) {
- return val<(double)min()?min():val>(double)max()?max():(cimg_int64)val;
- }
- static const char* format() { return cimg_fint64; }
- static const char* format_s() { return cimg_fint64; }
- static long format(const long val) { return (long)val; }
- };
- #if cimg_use_cpp11==1 && \
- (!(UINTPTR_MAX==0xffffffff || defined(__arm__) || defined(_M_ARM) || ((ULONG_MAX)==(UINT_MAX))))
- template<> struct type<unsigned long long> {
- static const char* string() { static const char *const s = "uint64"; return s; }
- static bool is_float() { return false; }
- static bool is_inf(const cimg_uint64) { return false; }
- static bool is_nan(const cimg_uint64) { return false; }
- static bool is_finite(const cimg_uint64) { return true; }
- static cimg_uint64 min() { return 0; }
- static cimg_uint64 max() { return (cimg_uint64)-1; }
- static cimg_uint64 inf() { return max(); }
- static cimg_uint64 nan() { return inf(); }
- static cimg_uint64 cut(const double val) {
- return val<(double)min()?min():val>(double)max()?max():(cimg_uint64)val; }
- static const char* format() { return cimg_fuint64; }
- static const char* format_s() { return cimg_fuint64; }
- static cimg_uint64 format(const cimg_uint64 val) { return val; }
- };
- template<> struct type<long long> {
- static const char* string() { static const char *const s = "int64"; return s; }
- static bool is_float() { return false; }
- static bool is_inf(const cimg_int64) { return false; }
- static bool is_nan(const cimg_int64) { return false; }
- static bool is_finite(const cimg_int64) { return true; }
- static long long min() { return ~max(); }
- static long long max() { return (cimg_int64)((cimg_uint64)-1>>1); }
- static long long inf() { return max(); }
- static long long nan() { return max(); }
- static long long cut(const double val) {
- return val<(double)min()?min():val>(double)max()?max():(cimg_int64)val;
- }
- static const char* format() { return cimg_fint64; }
- static const char* format_s() { return cimg_fint64; }
- static long format(const long val) { return (long)val; }
- };
- #endif
- template<> struct type<double> {
- static const char* string() { static const char *const s = "float64"; return s; }
- static bool is_float() { return true; }
- static bool is_inf(const double val) {
- #ifdef isinf
- return (bool)isinf(val);
- #else
- return !is_nan(val) && (val<cimg::type<double>::min() || val>cimg::type<double>::max());
- #endif
- }
- static bool is_nan(const double val) { // Custom version that works with '-ffast-math'
- if (sizeof(double)==8) {
- cimg_uint64 u;
- std::memcpy(&u,&val,sizeof(double));
- return ((unsigned int)(u>>32)&0x7fffffff) + ((unsigned int)u!=0)>0x7ff00000;
- }
- #ifdef isnan
- return (bool)isnan(val);
- #else
- return !(val==val);
- #endif
- }
- static bool is_finite(const double val) {
- #ifdef isfinite
- return (bool)isfinite(val);
- #else
- return !is_nan(val) && !is_inf(val);
- #endif
- }
- static double min() { return -DBL_MAX; }
- static double max() { return DBL_MAX; }
- static double inf() {
- #ifdef INFINITY
- return (double)INFINITY;
- #else
- return max()*max();
- #endif
- }
- static double nan() {
- #ifdef NAN
- return (double)NAN;
- #else
- const double val_nan = -std::sqrt(-1.); return val_nan;
- #endif
- }
- static double cut(const double val) { return val; }
- static const char* format() { return "%.17g"; }
- static const char* format_s() { return "%g"; }
- static double format(const double val) { return val; }
- };
- template<> struct type<float> {
- static const char* string() { static const char *const s = "float32"; return s; }
- static bool is_float() { return true; }
- static bool is_inf(const float val) {
- #ifdef isinf
- return (bool)isinf(val);
- #else
- return !is_nan(val) && (val<cimg::type<float>::min() || val>cimg::type<float>::max());
- #endif
- }
- static bool is_nan(const float val) { // Custom version that works with '-ffast-math'
- if (sizeof(float)==4) {
- unsigned int u;
- std::memcpy(&u,&val,sizeof(float));
- return (u&0x7fffffff)>0x7f800000;
- }
- #ifdef isnan
- return (bool)isnan(val);
- #else
- return !(val==val);
- #endif
- }
- static bool is_finite(const float val) {
- #ifdef isfinite
- return (bool)isfinite(val);
- #else
- return !is_nan(val) && !is_inf(val);
- #endif
- }
- static float min() { return -FLT_MAX; }
- static float max() { return FLT_MAX; }
- static float inf() { return (float)cimg::type<double>::inf(); }
- static float nan() { return (float)cimg::type<double>::nan(); }
- static float cut(const double val) { return (float)val; }
- static float cut(const float val) { return (float)val; }
- static const char* format() { return "%.9g"; }
- static const char* format_s() { return "%g"; }
- static double format(const float val) { return (double)val; }
- };
- template<> struct type<long double> {
- static const char* string() { static const char *const s = "float128"; return s; }
- static bool is_float() { return true; }
- static bool is_inf(const long double val) {
- #ifdef isinf
- return (bool)isinf(val);
- #else
- return !is_nan(val) && (val<cimg::type<long double>::min() || val>cimg::type<long double>::max());
- #endif
- }
- static bool is_nan(const long double val) {
- #ifdef isnan
- return (bool)isnan(val);
- #else
- return !(val==val);
- #endif
- }
- static bool is_finite(const long double val) {
- #ifdef isfinite
- return (bool)isfinite(val);
- #else
- return !is_nan(val) && !is_inf(val);
- #endif
- }
- static long double min() { return -LDBL_MAX; }
- static long double max() { return LDBL_MAX; }
- static long double inf() { return max()*max(); }
- static long double nan() { const long double val_nan = -std::sqrt(-1.L); return val_nan; }
- static long double cut(const long double val) { return val; }
- static const char* format() { return "%.17g"; }
- static const char* format_s() { return "%g"; }
- static double format(const long double val) { return (double)val; }
- };
- #ifdef cimg_use_half
- template<> struct type<half> {
- static const char* string() { static const char *const s = "float16"; return s; }
- static bool is_float() { return true; }
- static bool is_inf(const long double val) {
- #ifdef isinf
- return (bool)isinf(val);
- #else
- return !is_nan(val) && (val<cimg::type<half>::min() || val>cimg::type<half>::max());
- #endif
- }
- static bool is_nan(const half val) { // Custom version that works with '-ffast-math'
- if (sizeof(half)==2) {
- short u;
- std::memcpy(&u,&val,sizeof(short));
- return (bool)((u&0x7fff)>0x7c00);
- }
- return cimg::type<float>::is_nan((float)val);
- }
- static bool is_finite(const half val) {
- #ifdef isfinite
- return (bool)isfinite(val);
- #else
- return !is_nan(val) && !is_inf(val);
- #endif
- }
- static half min() { return (half)-65504; }
- static half max() { return (half)65504; }
- static half inf() { return max()*max(); }
- static half nan() { const half val_nan = (half)-std::sqrt(-1.); return val_nan; }
- static half cut(const double val) { return (half)val; }
- static const char* format() { return "%.9g"; }
- static const char* format_s() { return "%g"; }
- static double format(const half val) { return (double)val; }
- };
- #endif
- template<typename T, typename t> struct superset { typedef T type; };
- template<> struct superset<bool,unsigned char> { typedef unsigned char type; };
- template<> struct superset<bool,char> { typedef char type; };
- template<> struct superset<bool,signed char> { typedef signed char type; };
- template<> struct superset<bool,unsigned short> { typedef unsigned short type; };
- template<> struct superset<bool,short> { typedef short type; };
- template<> struct superset<bool,unsigned int> { typedef unsigned int type; };
- template<> struct superset<bool,int> { typedef int type; };
- template<> struct superset<bool,cimg_uint64> { typedef cimg_uint64 type; };
- template<> struct superset<bool,cimg_int64> { typedef cimg_int64 type; };
- template<> struct superset<bool,float> { typedef float type; };
- template<> struct superset<bool,double> { typedef double type; };
- template<> struct superset<unsigned char,char> { typedef short type; };
- template<> struct superset<unsigned char,signed char> { typedef short type; };
- template<> struct superset<unsigned char,unsigned short> { typedef unsigned short type; };
- template<> struct superset<unsigned char,short> { typedef short type; };
- template<> struct superset<unsigned char,unsigned int> { typedef unsigned int type; };
- template<> struct superset<unsigned char,int> { typedef int type; };
- template<> struct superset<unsigned char,cimg_uint64> { typedef cimg_uint64 type; };
- template<> struct superset<unsigned char,cimg_int64> { typedef cimg_int64 type; };
- template<> struct superset<unsigned char,float> { typedef float type; };
- template<> struct superset<unsigned char,double> { typedef double type; };
- template<> struct superset<signed char,unsigned char> { typedef short type; };
- template<> struct superset<signed char,char> { typedef short type; };
- template<> struct superset<signed char,unsigned short> { typedef int type; };
- template<> struct superset<signed char,short> { typedef short type; };
- template<> struct superset<signed char,unsigned int> { typedef cimg_int64 type; };
- template<> struct superset<signed char,int> { typedef int type; };
- template<> struct superset<signed char,cimg_uint64> { typedef cimg_int64 type; };
- template<> struct superset<signed char,cimg_int64> { typedef cimg_int64 type; };
- template<> struct superset<signed char,float> { typedef float type; };
- template<> struct superset<signed char,double> { typedef double type; };
- template<> struct superset<char,unsigned char> { typedef short type; };
- template<> struct superset<char,signed char> { typedef short type; };
- template<> struct superset<char,unsigned short> { typedef int type; };
- template<> struct superset<char,short> { typedef short type; };
- template<> struct superset<char,unsigned int> { typedef cimg_int64 type; };
- template<> struct superset<char,int> { typedef int type; };
- template<> struct superset<char,cimg_uint64> { typedef cimg_int64 type; };
- template<> struct superset<char,cimg_int64> { typedef cimg_int64 type; };
- template<> struct superset<char,float> { typedef float type; };
- template<> struct superset<char,double> { typedef double type; };
- template<> struct superset<unsigned short,char> { typedef int type; };
- template<> struct superset<unsigned short,signed char> { typedef int type; };
- template<> struct superset<unsigned short,short> { typedef int type; };
- template<> struct superset<unsigned short,unsigned int> { typedef unsigned int type; };
- template<> struct superset<unsigned short,int> { typedef int type; };
- template<> struct superset<unsigned short,cimg_uint64> { typedef cimg_uint64 type; };
- template<> struct superset<unsigned short,cimg_int64> { typedef cimg_int64 type; };
- template<> struct superset<unsigned short,float> { typedef float type; };
- template<> struct superset<unsigned short,double> { typedef double type; };
- template<> struct superset<short,unsigned short> { typedef int type; };
- template<> struct superset<short,unsigned int> { typedef cimg_int64 type; };
- template<> struct superset<short,int> { typedef int type; };
- template<> struct superset<short,cimg_uint64> { typedef cimg_int64 type; };
- template<> struct superset<short,cimg_int64> { typedef cimg_int64 type; };
- template<> struct superset<short,float> { typedef float type; };
- template<> struct superset<short,double> { typedef double type; };
- template<> struct superset<unsigned int,char> { typedef cimg_int64 type; };
- template<> struct superset<unsigned int,signed char> { typedef cimg_int64 type; };
- template<> struct superset<unsigned int,short> { typedef cimg_int64 type; };
- template<> struct superset<unsigned int,int> { typedef cimg_int64 type; };
- template<> struct superset<unsigned int,cimg_uint64> { typedef cimg_uint64 type; };
- template<> struct superset<unsigned int,cimg_int64> { typedef cimg_int64 type; };
- template<> struct superset<unsigned int,float> { typedef float type; };
- template<> struct superset<unsigned int,double> { typedef double type; };
- template<> struct superset<int,unsigned int> { typedef cimg_int64 type; };
- template<> struct superset<int,cimg_uint64> { typedef cimg_int64 type; };
- template<> struct superset<int,cimg_int64> { typedef cimg_int64 type; };
- template<> struct superset<int,float> { typedef float type; };
- template<> struct superset<int,double> { typedef double type; };
- template<> struct superset<cimg_uint64,char> { typedef cimg_int64 type; };
- template<> struct superset<cimg_uint64,signed char> { typedef cimg_int64 type; };
- template<> struct superset<cimg_uint64,short> { typedef cimg_int64 type; };
- template<> struct superset<cimg_uint64,int> { typedef cimg_int64 type; };
- template<> struct superset<cimg_uint64,cimg_int64> { typedef cimg_int64 type; };
- template<> struct superset<cimg_uint64,float> { typedef double type; };
- template<> struct superset<cimg_uint64,double> { typedef double type; };
- template<> struct superset<cimg_int64,float> { typedef double type; };
- template<> struct superset<cimg_int64,double> { typedef double type; };
- #if cimg_use_cpp11==1 && \
- (!(UINTPTR_MAX==0xffffffff || defined(__arm__) || defined(_M_ARM) || ((ULONG_MAX)==(UINT_MAX))))
- template<> struct superset<unsigned long long,char> { typedef cimg_int64 type; };
- template<> struct superset<unsigned long long,signed char> { typedef cimg_int64 type; };
- template<> struct superset<unsigned long long,short> { typedef cimg_int64 type; };
- template<> struct superset<unsigned long long,int> { typedef cimg_int64 type; };
- template<> struct superset<unsigned long long,cimg_int64> { typedef cimg_int64 type; };
- template<> struct superset<unsigned long long,float> { typedef double type; };
- template<> struct superset<unsigned long long,double> { typedef double type; };
- template<> struct superset<long long,float> { typedef double type; };
- template<> struct superset<long long,double> { typedef double type; };
- #endif
- template<> struct superset<float,cimg_uint64> { typedef double type; };
- template<> struct superset<float,cimg_int64> { typedef double type; };
- template<> struct superset<float,double> { typedef double type; };
- #ifdef cimg_use_half
- template<> struct superset<half,unsigned short> { typedef float type; };
- template<> struct superset<half,short> { typedef float type; };
- template<> struct superset<half,unsigned int> { typedef float type; };
- template<> struct superset<half,int> { typedef float type; };
- template<> struct superset<half,cimg_uint64> { typedef float type; };
- template<> struct superset<half,cimg_int64> { typedef float type; };
- template<> struct superset<half,float> { typedef float type; };
- template<> struct superset<half,double> { typedef double type; };
- #endif
- template<typename t1, typename t2, typename t3> struct superset2 {
- typedef typename superset<t1, typename superset<t2,t3>::type>::type type;
- };
- template<typename t1, typename t2, typename t3, typename t4> struct superset3 {
- typedef typename superset<t1, typename superset2<t2,t3,t4>::type>::type type;
- };
- template<typename t1, typename t2> struct last { typedef t2 type; };
- #define _cimg_Tt typename cimg::superset<T,t>::type
- #define _cimg_Tfloat typename cimg::superset<T,float>::type
- #define _cimg_tfloat typename cimg::superset<t,float>::type
- #define _cimg_Ttfloat typename cimg::superset2<T,t,float>::type
- #define _cimg_Ttdouble typename cimg::superset2<T,t,double>::type
- // Define variables used internally by CImg.
- #if cimg_display==1
- struct X11_static {
- unsigned int nb_wins;
- pthread_t *events_thread;
- pthread_cond_t wait_event;
- pthread_mutex_t wait_event_mutex;
- CImgDisplay **wins;
- Display *display;
- unsigned int nb_bits;
- bool is_blue_first;
- bool is_shm_enabled;
- bool byte_order;
- #ifdef cimg_use_xrandr
- XRRScreenSize *resolutions;
- Rotation curr_rotation;
- unsigned int curr_resolution;
- unsigned int nb_resolutions;
- #endif
- X11_static():nb_wins(0),events_thread(0),display(0),
- nb_bits(0),is_blue_first(false),is_shm_enabled(false),byte_order(false) {
- #ifdef __FreeBSD__
- XInitThreads();
- #endif
- wins = new CImgDisplay*[1024];
- pthread_mutex_init(&wait_event_mutex,0);
- pthread_cond_init(&wait_event,0);
- #ifdef cimg_use_xrandr
- resolutions = 0;
- curr_rotation = 0;
- curr_resolution = nb_resolutions = 0;
- #endif
- }
- ~X11_static() {
- delete[] wins;
- /*
- if (events_thread) {
- pthread_cancel(*events_thread);
- delete events_thread;
- }
- if (display) { } // XCloseDisplay(display); }
- pthread_cond_destroy(&wait_event);
- pthread_mutex_unlock(&wait_event_mutex);
- pthread_mutex_destroy(&wait_event_mutex);
- */
- }
- }; // struct X11_static { ...
- #if defined(cimg_module)
- X11_static& X11_attr();
- #elif defined(cimg_main)
- X11_static& X11_attr() { static X11_static val; return val; }
- #else
- inline X11_static& X11_attr() { static X11_static val; return val; }
- #endif
- #elif cimg_display==2
- struct Win32_static {
- HANDLE wait_event;
- Win32_static() { wait_event = CreateEvent(0,FALSE_WIN,FALSE_WIN,0); }
- }; // struct Win32_static { ...
- #if defined(cimg_module)
- Win32_static& Win32_attr();
- #elif defined(cimg_main)
- Win32_static& Win32_attr() { static Win32_static val; return val; }
- #else
- inline Win32_static& Win32_attr() { static Win32_static val; return val; }
- #endif
- #endif
- #define cimg_lock_display() cimg::mutex(15)
- #define cimg_unlock_display() cimg::mutex(15,0)
- struct Mutex_static {
- #if cimg_OS==1 && (defined(cimg_use_pthread) || cimg_display==1)
- pthread_mutex_t mutex[32];
- Mutex_static() { for (unsigned int i = 0; i<32; ++i) pthread_mutex_init(&mutex[i],0); }
- void lock(const unsigned int n) { pthread_mutex_lock(&mutex[n]); }
- void unlock(const unsigned int n) { pthread_mutex_unlock(&mutex[n]); }
- int trylock(const unsigned int n) { return pthread_mutex_trylock(&mutex[n]); }
- #elif cimg_OS==2
- HANDLE mutex[32];
- Mutex_static() { for (unsigned int i = 0; i<32; ++i) mutex[i] = CreateMutex(0,FALSE_WIN,0); }
- void lock(const unsigned int n) { WaitForSingleObject(mutex[n],INFINITE); }
- void unlock(const unsigned int n) { ReleaseMutex(mutex[n]); }
- int trylock(const unsigned int) { return 0; }
- #else
- Mutex_static() {}
- void lock(const unsigned int) {}
- void unlock(const unsigned int) {}
- int trylock(const unsigned int) { return 0; }
- #endif
- }; // struct Mutex_static { ...
- #if defined(cimg_module)
- Mutex_static& Mutex_attr();
- #elif defined(cimg_main)
- Mutex_static& Mutex_attr() { static Mutex_static val; return val; }
- #else
- inline Mutex_static& Mutex_attr() { static Mutex_static val; return val; }
- #endif
- #if defined(cimg_use_magick)
- struct Magick_static {
- Magick_static() {
- Magick::InitializeMagick("");
- }
- }; // struct Magick_static { ...
- static Magick_static _Magick_static;
- #endif
- #if defined(cimg_use_fftw3) && !defined(cimg_use_fftw3_singlethread)
- struct FFTW3_static {
- FFTW3_static() {
- fftw_init_threads();
- }
- }; // struct FFTW3_static { ...
- static FFTW3_static _FFTW3_static;
- #endif
- #if cimg_display==1
- // Define keycodes for X11-based graphical systems.
- const unsigned int keyESC = XK_Escape;
- const unsigned int keyF1 = XK_F1;
- const unsigned int keyF2 = XK_F2;
- const unsigned int keyF3 = XK_F3;
- const unsigned int keyF4 = XK_F4;
- const unsigned int keyF5 = XK_F5;
- const unsigned int keyF6 = XK_F6;
- const unsigned int keyF7 = XK_F7;
- const unsigned int keyF8 = XK_F8;
- const unsigned int keyF9 = XK_F9;
- const unsigned int keyF10 = XK_F10;
- const unsigned int keyF11 = XK_F11;
- const unsigned int keyF12 = XK_F12;
- const unsigned int keyPAUSE = XK_Pause;
- const unsigned int key1 = XK_1;
- const unsigned int key2 = XK_2;
- const unsigned int key3 = XK_3;
- const unsigned int key4 = XK_4;
- const unsigned int key5 = XK_5;
- const unsigned int key6 = XK_6;
- const unsigned int key7 = XK_7;
- const unsigned int key8 = XK_8;
- const unsigned int key9 = XK_9;
- const unsigned int key0 = XK_0;
- const unsigned int keyBACKSPACE = XK_BackSpace;
- const unsigned int keyINSERT = XK_Insert;
- const unsigned int keyHOME = XK_Home;
- const unsigned int keyPAGEUP = XK_Page_Up;
- const unsigned int keyTAB = XK_Tab;
- const unsigned int keyQ = XK_q;
- const unsigned int keyW = XK_w;
- const unsigned int keyE = XK_e;
- const unsigned int keyR = XK_r;
- const unsigned int keyT = XK_t;
- const unsigned int keyY = XK_y;
- const unsigned int keyU = XK_u;
- const unsigned int keyI = XK_i;
- const unsigned int keyO = XK_o;
- const unsigned int keyP = XK_p;
- const unsigned int keyDELETE = XK_Delete;
- const unsigned int keyEND = XK_End;
- const unsigned int keyPAGEDOWN = XK_Page_Down;
- const unsigned int keyCAPSLOCK = XK_Caps_Lock;
- const unsigned int keyA = XK_a;
- const unsigned int keyS = XK_s;
- const unsigned int keyD = XK_d;
- const unsigned int keyF = XK_f;
- const unsigned int keyG = XK_g;
- const unsigned int keyH = XK_h;
- const unsigned int keyJ = XK_j;
- const unsigned int keyK = XK_k;
- const unsigned int keyL = XK_l;
- const unsigned int keyENTER = XK_Return;
- const unsigned int keySHIFTLEFT = XK_Shift_L;
- const unsigned int keyZ = XK_z;
- const unsigned int keyX = XK_x;
- const unsigned int keyC = XK_c;
- const unsigned int keyV = XK_v;
- const unsigned int keyB = XK_b;
- const unsigned int keyN = XK_n;
- const unsigned int keyM = XK_m;
- const unsigned int keySHIFTRIGHT = XK_Shift_R;
- const unsigned int keyARROWUP = XK_Up;
- const unsigned int keyCTRLLEFT = XK_Control_L;
- const unsigned int keyAPPLEFT = XK_Super_L;
- const unsigned int keyALT = XK_Alt_L;
- const unsigned int keySPACE = XK_space;
- const unsigned int keyALTGR = XK_Alt_R;
- const unsigned int keyAPPRIGHT = XK_Super_R;
- const unsigned int keyMENU = XK_Menu;
- const unsigned int keyCTRLRIGHT = XK_Control_R;
- const unsigned int keyARROWLEFT = XK_Left;
- const unsigned int keyARROWDOWN = XK_Down;
- const unsigned int keyARROWRIGHT = XK_Right;
- const unsigned int keyPAD0 = XK_KP_0;
- const unsigned int keyPAD1 = XK_KP_1;
- const unsigned int keyPAD2 = XK_KP_2;
- const unsigned int keyPAD3 = XK_KP_3;
- const unsigned int keyPAD4 = XK_KP_4;
- const unsigned int keyPAD5 = XK_KP_5;
- const unsigned int keyPAD6 = XK_KP_6;
- const unsigned int keyPAD7 = XK_KP_7;
- const unsigned int keyPAD8 = XK_KP_8;
- const unsigned int keyPAD9 = XK_KP_9;
- const unsigned int keyPADADD = XK_KP_Add;
- const unsigned int keyPADSUB = XK_KP_Subtract;
- const unsigned int keyPADMUL = XK_KP_Multiply;
- const unsigned int keyPADDIV = XK_KP_Divide;
- #elif cimg_display==2
- // Define keycodes for Windows.
- const unsigned int keyESC = VK_ESCAPE;
- const unsigned int keyF1 = VK_F1;
- const unsigned int keyF2 = VK_F2;
- const unsigned int keyF3 = VK_F3;
- const unsigned int keyF4 = VK_F4;
- const unsigned int keyF5 = VK_F5;
- const unsigned int keyF6 = VK_F6;
- const unsigned int keyF7 = VK_F7;
- const unsigned int keyF8 = VK_F8;
- const unsigned int keyF9 = VK_F9;
- const unsigned int keyF10 = VK_F10;
- const unsigned int keyF11 = VK_F11;
- const unsigned int keyF12 = VK_F12;
- const unsigned int keyPAUSE = VK_PAUSE;
- const unsigned int key1 = '1';
- const unsigned int key2 = '2';
- const unsigned int key3 = '3';
- const unsigned int key4 = '4';
- const unsigned int key5 = '5';
- const unsigned int key6 = '6';
- const unsigned int key7 = '7';
- const unsigned int key8 = '8';
- const unsigned int key9 = '9';
- const unsigned int key0 = '0';
- const unsigned int keyBACKSPACE = VK_BACK;
- const unsigned int keyINSERT = VK_INSERT;
- const unsigned int keyHOME = VK_HOME;
- const unsigned int keyPAGEUP = VK_PRIOR;
- const unsigned int keyTAB = VK_TAB;
- const unsigned int keyQ = 'Q';
- const unsigned int keyW = 'W';
- const unsigned int keyE = 'E';
- const unsigned int keyR = 'R';
- const unsigned int keyT = 'T';
- const unsigned int keyY = 'Y';
- const unsigned int keyU = 'U';
- const unsigned int keyI = 'I';
- const unsigned int keyO = 'O';
- const unsigned int keyP = 'P';
- const unsigned int keyDELETE = VK_DELETE;
- const unsigned int keyEND = VK_END;
- const unsigned int keyPAGEDOWN = VK_NEXT;
- const unsigned int keyCAPSLOCK = VK_CAPITAL;
- const unsigned int keyA = 'A';
- const unsigned int keyS = 'S';
- const unsigned int keyD = 'D';
- const unsigned int keyF = 'F';
- const unsigned int keyG = 'G';
- const unsigned int keyH = 'H';
- const unsigned int keyJ = 'J';
- const unsigned int keyK = 'K';
- const unsigned int keyL = 'L';
- const unsigned int keyENTER = VK_RETURN;
- const unsigned int keySHIFTLEFT = VK_SHIFT;
- const unsigned int keyZ = 'Z';
- const unsigned int keyX = 'X';
- const unsigned int keyC = 'C';
- const unsigned int keyV = 'V';
- const unsigned int keyB = 'B';
- const unsigned int keyN = 'N';
- const unsigned int keyM = 'M';
- const unsigned int keySHIFTRIGHT = VK_SHIFT;
- const unsigned int keyARROWUP = VK_UP;
- const unsigned int keyCTRLLEFT = VK_CONTROL;
- const unsigned int keyAPPLEFT = VK_LWIN;
- const unsigned int keyALT = VK_LMENU;
- const unsigned int keySPACE = VK_SPACE;
- const unsigned int keyALTGR = VK_CONTROL;
- const unsigned int keyAPPRIGHT = VK_RWIN;
- const unsigned int keyMENU = VK_APPS;
- const unsigned int keyCTRLRIGHT = VK_CONTROL;
- const unsigned int keyARROWLEFT = VK_LEFT;
- const unsigned int keyARROWDOWN = VK_DOWN;
- const unsigned int keyARROWRIGHT = VK_RIGHT;
- const unsigned int keyPAD0 = 0x60;
- const unsigned int keyPAD1 = 0x61;
- const unsigned int keyPAD2 = 0x62;
- const unsigned int keyPAD3 = 0x63;
- const unsigned int keyPAD4 = 0x64;
- const unsigned int keyPAD5 = 0x65;
- const unsigned int keyPAD6 = 0x66;
- const unsigned int keyPAD7 = 0x67;
- const unsigned int keyPAD8 = 0x68;
- const unsigned int keyPAD9 = 0x69;
- const unsigned int keyPADADD = VK_ADD;
- const unsigned int keyPADSUB = VK_SUBTRACT;
- const unsigned int keyPADMUL = VK_MULTIPLY;
- const unsigned int keyPADDIV = VK_DIVIDE;
- #else
- // Define random keycodes when no display is available.
- // (should rarely be used then!).
- const unsigned int keyESC = 1U; //!< Keycode for the \c ESC key (architecture-dependent)
- const unsigned int keyF1 = 2U; //!< Keycode for the \c F1 key (architecture-dependent)
- const unsigned int keyF2 = 3U; //!< Keycode for the \c F2 key (architecture-dependent)
- const unsigned int keyF3 = 4U; //!< Keycode for the \c F3 key (architecture-dependent)
- const unsigned int keyF4 = 5U; //!< Keycode for the \c F4 key (architecture-dependent)
- const unsigned int keyF5 = 6U; //!< Keycode for the \c F5 key (architecture-dependent)
- const unsigned int keyF6 = 7U; //!< Keycode for the \c F6 key (architecture-dependent)
- const unsigned int keyF7 = 8U; //!< Keycode for the \c F7 key (architecture-dependent)
- const unsigned int keyF8 = 9U; //!< Keycode for the \c F8 key (architecture-dependent)
- const unsigned int keyF9 = 10U; //!< Keycode for the \c F9 key (architecture-dependent)
- const unsigned int keyF10 = 11U; //!< Keycode for the \c F10 key (architecture-dependent)
- const unsigned int keyF11 = 12U; //!< Keycode for the \c F11 key (architecture-dependent)
- const unsigned int keyF12 = 13U; //!< Keycode for the \c F12 key (architecture-dependent)
- const unsigned int keyPAUSE = 14U; //!< Keycode for the \c PAUSE key (architecture-dependent)
- const unsigned int key1 = 15U; //!< Keycode for the \c 1 key (architecture-dependent)
- const unsigned int key2 = 16U; //!< Keycode for the \c 2 key (architecture-dependent)
- const unsigned int key3 = 17U; //!< Keycode for the \c 3 key (architecture-dependent)
- const unsigned int key4 = 18U; //!< Keycode for the \c 4 key (architecture-dependent)
- const unsigned int key5 = 19U; //!< Keycode for the \c 5 key (architecture-dependent)
- const unsigned int key6 = 20U; //!< Keycode for the \c 6 key (architecture-dependent)
- const unsigned int key7 = 21U; //!< Keycode for the \c 7 key (architecture-dependent)
- const unsigned int key8 = 22U; //!< Keycode for the \c 8 key (architecture-dependent)
- const unsigned int key9 = 23U; //!< Keycode for the \c 9 key (architecture-dependent)
- const unsigned int key0 = 24U; //!< Keycode for the \c 0 key (architecture-dependent)
- const unsigned int keyBACKSPACE = 25U; //!< Keycode for the \c BACKSPACE key (architecture-dependent)
- const unsigned int keyINSERT = 26U; //!< Keycode for the \c INSERT key (architecture-dependent)
- const unsigned int keyHOME = 27U; //!< Keycode for the \c HOME key (architecture-dependent)
- const unsigned int keyPAGEUP = 28U; //!< Keycode for the \c PAGEUP key (architecture-dependent)
- const unsigned int keyTAB = 29U; //!< Keycode for the \c TAB key (architecture-dependent)
- const unsigned int keyQ = 30U; //!< Keycode for the \c Q key (architecture-dependent)
- const unsigned int keyW = 31U; //!< Keycode for the \c W key (architecture-dependent)
- const unsigned int keyE = 32U; //!< Keycode for the \c E key (architecture-dependent)
- const unsigned int keyR = 33U; //!< Keycode for the \c R key (architecture-dependent)
- const unsigned int keyT = 34U; //!< Keycode for the \c T key (architecture-dependent)
- const unsigned int keyY = 35U; //!< Keycode for the \c Y key (architecture-dependent)
- const unsigned int keyU = 36U; //!< Keycode for the \c U key (architecture-dependent)
- const unsigned int keyI = 37U; //!< Keycode for the \c I key (architecture-dependent)
- const unsigned int keyO = 38U; //!< Keycode for the \c O key (architecture-dependent)
- const unsigned int keyP = 39U; //!< Keycode for the \c P key (architecture-dependent)
- const unsigned int keyDELETE = 40U; //!< Keycode for the \c DELETE key (architecture-dependent)
- const unsigned int keyEND = 41U; //!< Keycode for the \c END key (architecture-dependent)
- const unsigned int keyPAGEDOWN = 42U; //!< Keycode for the \c PAGEDOWN key (architecture-dependent)
- const unsigned int keyCAPSLOCK = 43U; //!< Keycode for the \c CAPSLOCK key (architecture-dependent)
- const unsigned int keyA = 44U; //!< Keycode for the \c A key (architecture-dependent)
- const unsigned int keyS = 45U; //!< Keycode for the \c S key (architecture-dependent)
- const unsigned int keyD = 46U; //!< Keycode for the \c D key (architecture-dependent)
- const unsigned int keyF = 47U; //!< Keycode for the \c F key (architecture-dependent)
- const unsigned int keyG = 48U; //!< Keycode for the \c G key (architecture-dependent)
- const unsigned int keyH = 49U; //!< Keycode for the \c H key (architecture-dependent)
- const unsigned int keyJ = 50U; //!< Keycode for the \c J key (architecture-dependent)
- const unsigned int keyK = 51U; //!< Keycode for the \c K key (architecture-dependent)
- const unsigned int keyL = 52U; //!< Keycode for the \c L key (architecture-dependent)
- const unsigned int keyENTER = 53U; //!< Keycode for the \c ENTER key (architecture-dependent)
- const unsigned int keySHIFTLEFT = 54U; //!< Keycode for the \c SHIFTLEFT key (architecture-dependent)
- const unsigned int keyZ = 55U; //!< Keycode for the \c Z key (architecture-dependent)
- const unsigned int keyX = 56U; //!< Keycode for the \c X key (architecture-dependent)
- const unsigned int keyC = 57U; //!< Keycode for the \c C key (architecture-dependent)
- const unsigned int keyV = 58U; //!< Keycode for the \c V key (architecture-dependent)
- const unsigned int keyB = 59U; //!< Keycode for the \c B key (architecture-dependent)
- const unsigned int keyN = 60U; //!< Keycode for the \c N key (architecture-dependent)
- const unsigned int keyM = 61U; //!< Keycode for the \c M key (architecture-dependent)
- const unsigned int keySHIFTRIGHT = 62U; //!< Keycode for the \c SHIFTRIGHT key (architecture-dependent)
- const unsigned int keyARROWUP = 63U; //!< Keycode for the \c ARROWUP key (architecture-dependent)
- const unsigned int keyCTRLLEFT = 64U; //!< Keycode for the \c CTRLLEFT key (architecture-dependent)
- const unsigned int keyAPPLEFT = 65U; //!< Keycode for the \c APPLEFT key (architecture-dependent)
- const unsigned int keyALT = 66U; //!< Keycode for the \c ALT key (architecture-dependent)
- const unsigned int keySPACE = 67U; //!< Keycode for the \c SPACE key (architecture-dependent)
- const unsigned int keyALTGR = 68U; //!< Keycode for the \c ALTGR key (architecture-dependent)
- const unsigned int keyAPPRIGHT = 69U; //!< Keycode for the \c APPRIGHT key (architecture-dependent)
- const unsigned int keyMENU = 70U; //!< Keycode for the \c MENU key (architecture-dependent)
- const unsigned int keyCTRLRIGHT = 71U; //!< Keycode for the \c CTRLRIGHT key (architecture-dependent)
- const unsigned int keyARROWLEFT = 72U; //!< Keycode for the \c ARROWLEFT key (architecture-dependent)
- const unsigned int keyARROWDOWN = 73U; //!< Keycode for the \c ARROWDOWN key (architecture-dependent)
- const unsigned int keyARROWRIGHT = 74U; //!< Keycode for the \c ARROWRIGHT key (architecture-dependent)
- const unsigned int keyPAD0 = 75U; //!< Keycode for the \c PAD0 key (architecture-dependent)
- const unsigned int keyPAD1 = 76U; //!< Keycode for the \c PAD1 key (architecture-dependent)
- const unsigned int keyPAD2 = 77U; //!< Keycode for the \c PAD2 key (architecture-dependent)
- const unsigned int keyPAD3 = 78U; //!< Keycode for the \c PAD3 key (architecture-dependent)
- const unsigned int keyPAD4 = 79U; //!< Keycode for the \c PAD4 key (architecture-dependent)
- const unsigned int keyPAD5 = 80U; //!< Keycode for the \c PAD5 key (architecture-dependent)
- const unsigned int keyPAD6 = 81U; //!< Keycode for the \c PAD6 key (architecture-dependent)
- const unsigned int keyPAD7 = 82U; //!< Keycode for the \c PAD7 key (architecture-dependent)
- const unsigned int keyPAD8 = 83U; //!< Keycode for the \c PAD8 key (architecture-dependent)
- const unsigned int keyPAD9 = 84U; //!< Keycode for the \c PAD9 key (architecture-dependent)
- const unsigned int keyPADADD = 85U; //!< Keycode for the \c PADADD key (architecture-dependent)
- const unsigned int keyPADSUB = 86U; //!< Keycode for the \c PADSUB key (architecture-dependent)
- const unsigned int keyPADMUL = 87U; //!< Keycode for the \c PADMUL key (architecture-dependent)
- const unsigned int keyPADDIV = 88U; //!< Keycode for the \c PADDDIV key (architecture-dependent)
- #endif
- const double PI = 3.14159265358979323846; //!< Value of the mathematical constant PI
- // Define a 10x13 binary font (small sans).
- static const char *const data_font_small[] = {
- " UwlwnwoyuwHwlwmwcwlwnw[xuwowlwmwoyuwRwlwnxcw Mw (wnwnwuwpwuypwuwoy"
- "ZwnwmwuwowuwmwnwnwuwowuwfwuxnwnwmwuwpwuypwuwZwnwnwtwpwtwow'y Hw cwnw >{ jw %xdxZwdw_wexfwYwkw 7yowoyFx=w "
- "ry qw %wuw !xnwkwnwoyuwfwuw[wkwnwcwowrwpwdwuwoxuwpwkwnwoyuwRwkwnwbwpwNyoyoyoyoy;wdwnxpxtxowG|!ydwnwuwowtwow"
- "pxswqxlwnxnxmwDwoyoxnyoymwp{oyq{pyoy>ypwqwpwp{oyqzo{q{pzrwrwowlwqwswpwnwqwsxswpypzoyqzozq}swrwrwqwtwswswtxsxswq"
- "ws}qwnwkwnydwew_wfwdwkwmwowkw(w0wmwmwGwtwdxQw swuwnwo{q{pynwp|rwtwtwqydwcwcwcwmwmxgwqwpwnzpwuwpzoyRzoyoyexnynwd"
- "z\\xnxgxrwsxrwsyswowmwmwmwmwmwmwo}ryp{q{q{q{nwmwnwmwozqxswpyoyoyoyoyeyuwswrwrwrwrwrwrwrwrwqwrwmwtwnwmwnwuwpwuyp"
- "wuwoyZwmwnwuwowuwmwqwkwuwowuwoxnwuxowmwnwuwpwuypwuwZwmwnwuwowuwnwowmwtw\\wuwuwqwswqwswqwswqwswEwqwtweypzr~qyIw "
- "rwswewnwuwowuwozswtwuwqwtwmwnwlwowuwuwowOxpxuxqwuwowswqwswoxpwlwjwqwswqwsw<wrwowrwuwqwrwqwswrwswpwmwmwrwswrwowl"
- "wqwtwownxsxsxswqwswqwswqwswrwswqwrwowpwrwrwqwtwswswswswqwswmwpwmwlwoxuxSw_wfwdwYwkw(w0wmwmwGwtwoxnwNw uwswpwuwp"
- "wmwmwswq{rwrwrwtwtwrwswfydwdyZwnwtwrwqwrwswowowdwrwqxuwSwrwfwuwnwlwnw[yuw[wowtwgwswqwswqwswewuwowuwowuwowuwowuw"
- "nwowuwowswqwmwmwmwjwmwnwmwowswrxswqwswqwswqwswqwswqwswrwrwqwswrwrwrwrwrwrwrwrwqwswqzpwtw #w DwPwtwtwswqwswuwuwu"
- "wswswuwswqwGwqxtwf{qzr~r{qzqwrwpxowtwrw rzcwnwuwq}rwuwqwtwuwqwtwmwnwlwnynwOwowswowkwmwpwuwpwmwjwpwswqwswowmwjwi"
- "wjxswsytwrwuwqwrwrwmwrwqwmwnwmwrwowlwqwuwnwnxsxswuwtwrwqwrwswrwqwswswqwjwpwrwqwswrwtwtwqwuwowuwmwowmwlwpxsx]ypz"
- "oyozpypzozqznwmwowtwnwqzuyrzoypzozqwuxoypzpwswrwrwrwtwtwswrwrwrwq{owmwmwQyuwqwtwmwoxnypzqxswowowswqwswqwtxr|rwt"
- "wtwqyp{q{qwswpwuwownwnwqwsxuwuxswrwrwtwtwswqwrwmwuwuwnwnwowtwpwuwuwewnzpwn{pwuwnwnxgwtxtwrwtwowtw_wuytwgynwmwlw"
- "gwswpyuw[wowtwqwtwpwtwpwtwowuwmwnwuwowuwowuwowuwowuwowuwqxuwpwlwmwmwmwjwmwnwmwowrwswuwtwrwqwswqwswqwswqwswqwrwt"
- "wqwswuwswrwrwrwrwrwrwrwpwuwpwswqwuwnyoyoyoyoyoyqyuyqyoyoyoyoymwqwjwmwnypzoyoyoyoyoynwnzqwswqwswqwswqwswrwrwqzqw"
- "rw^}s}swtwtwswtwtwswtwtwK}rwuwe{s~t~s}rwtwqwrwpxowtwrw qwawewtwpwuwpxuwpycwlwnynwOwowswowkwpypwtwpzpzmwoypwsw[y"
- "r}rymwrwtwtwtwrwuwq{qwmwrwq{q{rwm|owlwqxmwnwuwuwuwswuwtwrwqwrwswrwqwswswqylwpwrwqwswrwuwuwuwpwmwmwnwmwlwMwqwswq"
- "wmwswqwswpwnwswqwswowmwowuwmwqwswswswswqwswqwswqwswqxnwswpwnwswrwrwrwtwtwrwtwqwrwmwqxlwlx]xuxrwtyqwuwlwpwtwpwmw"
- "swqwtxpxowswrwqwswtwuxrwtwqwtwtwrwswrwswnwo{pwuwnxpwnwqwswtwtwswrwrwtwtwswuyuwswjwkwowpwrwowcwowuwnwnwswqxuxowo"
- "wtwhwuwrwrzpwtwq}jwuwtwuw_}qyoxfwswpyuwowdyoxowtwryuwqyuwqyuwmwnwuwowuwowuwowuwowuwowuwqwt{twl{q{q{q{nwmwnwmwpz"
- "twswuwtwrwqwswqwswqwswqwswqwqxpwtwtwswrwrwrwrwrwrwrwowowswqwuwkwmwmwmwmwmwowswswmwswqwswqwswqwswnwqwjwmwowswqws"
- "wqwswqwswqwswqwswqwswgwtxqwswqwswqwswqwswrwrwqwswrwrw^wtwtwswqwswuwuwuwswuwswswqwHwowuwf}t~s|r}swrwrwrwqwtwpwtw"
- "r~#zcwewtwoynwuxtwtwswgwlwowuwuwr}gyexowswowlwlwrwswlwqwswowowswpz^yayqwqwtwtwuwrwswrwrwrwmwrwqwmwnwsyswrwowlwq"
- "wuwnwnwuwuwuwswtwuwrwqwrzqwqwszmyowpwrwpwuwqwuwuwuwpwmwmwnwlwmwPzqwswqwmwswq{pwnwswqwswowmwoxlwqwswswswswqwswqw"
- "swqwswqwlxnwnwswqwtwqwuwuwuwqxowtwmwnwmwmwoytwiwtwtwswswpwtxqzpwswpxowswpwuwowuwpwswrwtwtwswtwtwrwtwqwtwtwrwswr"
- "wswnwowswqwswowownwqwswtwtwswrwqwuwuwrwuyuwt~pwq~pwq~pwcwowuwozpwswowewswiwuwrwiwtwjwjwuytw\\wRwswoxuwHwtwpwswq"
- "wtxqwswqxowswqwswqwswqwswqwswqwswrwtwpwlwmwmwmwjwmwnwmwowrwswtwuwrwqwswqwswqwswqwswqwqxpwtwtwswrwrwrwrwrwrwrwow"
- "owswqwtwozpzpzpzpzpzr~swm{q{q{q{nwqwjwmwowswqwswqwswqwswqwswqwswqwswr}rwuwuwqwswqwswqwswqwswqwtwpwswqwtw\\wuwuw"
- "qwswqwswqwswqwswJ}qxf}t~rzp{rwrwrwrwqwtwpwtwrw qwawg}owuwpwuwtwuwswuwfwlwmwmwPwnwswowmwkwr|mwqwswowowswmw^yo}oy"
- "qwqwszq{rwrwrwmwrwqwmwnwqwswrwowlwqwtwownwtwtwswtwuwrwqwrwnwqwswtwkwowpwrwpwuwqwuwuwuwqwuwnwnwmwlwmwQwswqwswqwm"
- "wswqwlwnwswqwswowmwowuwmwqwswswswswqwswqwswqwswqwjwownwswqwtwqwuwuwuwqxowtwnwmwmwmwpwtyhwtwtwswswpwswqwtwpwswqw"
- "mwswpwuwpwtwpwswrwtwtwswtwtwrwtwqwtwtwrwswrwswnwowswqwswpwnwnwqwsxuwuxswrwpyqwqwswjwkwqwuwuwrwrwqwuwuwewowuwnwn"
- "wswq{ownxuwiwtxtwrzpwtwkwjwuwtwuw\\wRwswnwuwSzpwtwowtxqwrwrwtxrxn{q{q{q{q{q{s{pwlwmwmwmwjwmwnwmwowrwswtwuwrwqws"
- "wqwswqwswqwswqwrwtwqwuwswswrwrwrwrwrwrwrwowozpwswqwswqwswqwswqwswqwswqwswswswowmwmwmwmwjwqwjwmwowswqwswqwswqwsw"
- "qwswqwswqwswgwuwuwqwswqwswqwswqwswqwtwpwswqwtw[yoyoyoyoyGwmwdwuwuwpxnxnyqwrwqwtwpwtwoxpw rwswSwuwmwuwpwuwtwuxsw"
- "ewlwcwPwnxuxownwnwswnwlwqwswowowswnwZygygwkwswrwrwqwswrwswpwmwmwrwswrwowlwqwswpwnwqwswsxqwswqwmwswrwswqwrwowpxt"
- "xowowswqwswowowlwlwmwQwswqwswqwmwswqwswpwnwswqwswowmwowtwnwqwswswswswqwswqwswqwswqwmwswpwnwswpxowswqwtwoxnwlwmw"
- "mw[xuxrwtxpwswqwtwpwswqwmwswpypwtwpwswrwtwtwsxuwuxrwtwqwtwtwrwswrwswnwnwuwpwswqwmwmwswq{rwrwowowswqwkwlwoypwtwo"
- "ydwowuwnwn{owmwlwgwrwfwtw^wrw6wswnwuwJwtwowtzswrwrwtzswmwswqwswqwswqwswqwswqwswswswowswqwmwmwmwjwmwnwmwowswrwsx"
- "qwswqwswqwswqwswqwswrwrwqwswrxtxrxtxrxtxrxtxowowmwswqwswqwswqwswqwswqwswqwswswtxowmwswqwswqwswqwswnwqwjwmwowswq"
- "wswqwswqwswqwswqwswqwswowoxtwqwswqwswqwswqwswpxowswpx Wwlwbwnxcwpwrwqzpwtwoxo|!ydwfwtwozpwsxszuxgxnxcwmwcwoxmyp"
- "{q{pymwpzoyowmypymwmwjwiwkwowrwrwqws{oyqzo{qwlzrwrwowlwqwrwq{rwqwswsxpypwlyqwrwqznwoznwowswrxsxpwp}qwkwnwPzqzoy"
- "ozpyowmzqwswowmwowswowqwswswswswpypzozqwlynxozpxowswrwrwpwn{owmwmwQxuxqzoxnyoypwswowpwrwqzpxuxq{qwtxq{qzpylwoyq"
- "}r{qwnyuypwpwrwownydwcwcwcwnzq{rwqwpwmwkwgzHz]}U|owuw@wqwswrytwqwqyqwqwswqwswqwswqwswqwswqwuwr{ryp{q{q{q{nwmwnw"
- "mwozqwsxpyoyoyoyoygwuypzpzpzpznwowmwuypzpzpzpzpzpzryuzryoyoyoyoymwqwjwmwnypwswpyoyoyoyoyfzozpzpzpzpwnzow \\w"
- "OwnwXw[w SwGz kx0x lxdx gw[w=wiw*wbyowoyGwKwowewawcwow YwOwoz Ewjwuwdw 7w 9w Iwnwlw \\w 0|*y[x=wiw,"
- "xWw=wKwowewawcwow Yw hwVx 8w 9w Jxmwnxp" };
- // Define a 26x32 font (normal sans).
- static const char *const data_font_normal[] = {
- " #{}~}a{|y~f{|y~}f{|}|x{}|j{|y}y{|y}g{}y~}|2y~|a{}~}f{}y~|"
- "gy}|yy}|i{}~}a{}~}f{}y~}gy}|yx}N{|}|x{}|hy~|ay~|fx~|g{}y~|y{}~j{|y~|yy~}5{}~}a{}~}f{}y~}gy~}yy~}e{|y~ "
- " 2{}~}c{|y~f{|y~}~}h{}w~y}~|j{}y~y{}y~h{}~y}y~|2y~|c{}~}f{"
- "}~y}~|hy~}yy~}hy~|c{|~}f{|~y}~|hy~}y{}y~O{}w~y}~|gy~|cy~|fy~|}~|i{|~y}y~}~}j{|y~|yy~}4{}~|c{}~}f{}~|}~}hy~}yy~}"
- "ey~| g{|}y~} J{}~|dy~|fy~y{}~|i{~}{|}y~}i{}y~y{}y"
- "~i{|~}x{~}2{|y~d{|~}f{|~}yy~hy~}yy~}gy~cy~f{|~}y{}~|iy~}y{}y~P{|~}{|}y~|ey~d{}~|fy~x{}~|j{}~y{|}~}i{|y}|yy}|3{}"
- "~|e{}~}f{}~|y{|~|iy}|yx}f{}~| fy~y}~} k{|y~| /{|y~| y{}"
- "~} Xy|e{|}|f{|}wy|5{|~|x{}~1{|}|ey|ey|wy|M{|}|e{|}|fy|wy| g{|}|3{|y~|_{}~}g{|y~2{}~|y{}~|5{|y~^y~}g{}y~N{|"
- "}|^{|}|g{|}| s{}~}_{|y~|gy~} Z{}~}_{|y~|gy~} )y}| -{|y~ Jy}|yy}| "
- "X{}y~ 4{|~}y{|~} P{| n{|y~`{|y~fx~}3{}~x{|~|4{}~}`{}~}g{|x~}N{}~}`{|y~|gx~| sy~|`y~|g{}x~| Z{}~}`y~}g{}"
- "x~|I{}y~ 1{|x~|oi| r{|~|O{|d{|y}|j{|y}|u{|y}|h{| \"{|}x~}|Ny~}g{|y~y{|~}g{|~}x{|~}i{|~l{|}y~}|s{|~}l{|}x~}|e"
- "{|y~by~g{}~}b{~} S{|y~i{|}x~}|i{|y}x~|i{|y}x~y}i{|y}w~}|d{}x~kq~|i{|}w~}|m{}o~k{|}x~y}h{|}x~}| B{|}w~}L{|x~j{|s"
- "~y}g{|}w~}|o{|s~y}|k{|o~n{|p~j{|}w~y}|o{|y~|ry~}k{|y~|e{|y~|j{|y~|t{|x~n{|y~|i{|x~}r{|x~}s{|x~|sy~}l{|}x~y}|l{|"
- "s~}|i{|}x~y}|m{|s~}|hy}w~y}|ok~}r{}y~r{|y~|r{}y~|p{}y~vy~}t{|x~sy~}ux~rx~q{}y~|r{}y~|r{|l~l{}v~hy~|c{|v~|f{|}|L"
- "{}~}M{}y~@{}~}O{|}w~R{}y~`{|y~d{|y~h{}y~`{|y~ ay}y~}h{}~}h{}y~y} Wy}x~}|O{|y}w~}| xx~} I{|}x~}f{|x~i{|o~m{|"
- "o~m{|y}x~}|f{}y~k{|m~}r{|y~|w{}y~vy~}n{|}x~y}|My}Iy}|J{}~| q{|}x~y}T{}y~r{}~}R{}w~}|j{|y~}yy~}O{|}w~} \\{|t~}h{"
- "|}y~}M{|}x~}|h{|}x~}|e{|y~L{|}t~|7y}y~}f{|}x~}Uy|y}|py}p{|n{|t{|}w~}r{|y~P{|x~e{|x~e{|x~e{|x~e{|x~f{}v~|jk~|o{|"
- "}w~}|m{|o~n{|o~n{|o~n{|o~j{|y~|e{|y~|e{|y~|e{|y~|k{|s~y}|m{|x~|sy~}l{|}x~y}|i{|}x~y}|i{|}x~y}|i{|}x~y}|i{|}x~y}"
- "|O{|}x~y}|y{|~}s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~|r{}y~|p{|y~|b{|}x~}|h{}~}b{|y~|g{}~|}~|h{|y~}|"
- "{}~iy~}yy~}i{}~|y{}~|3{}~}b{|~}fy~{}~|i{|y~|{|y~}iy~|ay~}g{}~}y~gy~}yy~}i{|y~}wy|j{|y~}y{}~hy~|b{}~}g{|~}|~}h{|"
- "}y~}{|~}j{}y~y{}y~|4y~|b{}~}g{|~}{y~h{}y~y{|y~|f{|y~|k{}y~by~}y{}y~ ev~o{}k~} r{}~O{|~e{}v~l{}v~w{}w~}j{}~ Y{}"
- "o~ S{|s~}Oy~}g{|y~y{|~}g{}~|x{}~|i{|~m{|y~y}y~|ty~l{}t~}f{|y~c{}~}fy~b{~} S{}~}j{}t~}kt~|j{}r~|l{|r~}f{|w~kq~|"
- "j{}s~|n{}p~}m{|r~|l{|s~| D{}s~|i{|y}y~y}|hw~|k{|p~|k{|q~}q{|p~}|m{|o~n{|p~l{|p~}q{|y~|ry~}k{|y~|e{|y~|j{|y~|u{|"
- "x~m{|y~|i{|w~|sw~}s{|w~sy~}n{}r~}m{|q~}l{}r~}n{|q~}k{|q~|pk~}r{}y~r{|y~|r{|y~}py~}v{}y~t{}x~|u{|y~|u{|y~}t{}y~|"
- "py~}s{|y~}q{|l~l{}w~}h{}~}c{|v~|gw~}L{}~|N{}y~@{}~}P{|u~R{}y~`{|y~d{|y~h{}y~`{|y~ e{}y~ {}w~}h{}~}h{}v~ Ys~}Q"
- "{|r~| yv~ K{}t~|hw~|j{|o~m{|o~n{}r~|h{}y~k{|m~}r{|y~|w{}y~vy~}p{}r~}O{}x~Jy~|K{}x~|/{~|f{}t~}Ty~|t{|y~|Ss~j{|y"
- "~}yy~}i{|}v~}|j{}~w}y~ v{|}v~}|k{|t~}i{|y~}x~N{}~}|}y~|i{|y}y|}~}fy~|N{|u~y}y~|8{|~y}~}g{|y~x}y~W{|w~}q{}~}s{}x"
- "~}q{|y~t{|}x|}~}s{}~|Pw~|fw~|fw~|fw~|fw~|fw~|j{|k~|q{|q~}o{|o~n{|o~n{|o~n{|o~j{|y~|e{|y~|e{|y~|e{|y~|k{|o~|o{|w"
- "~sy~}n{}r~}l{}r~}l{}r~}l{}r~}l{}r~}R{}r~}|y~|s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|py~}s{|y~}o{|y~|cs~}h{"
- "}~}cy~|g{|~}yy~i{|y~}w~}iy~}yy~}hy~y}~}1y~|d{|y~f{}~|{|~}i{|y~|{|y~}i{|y~b{}~}g{|~}{|~}hy~}yy~}h{|y~y}y~}|k{|y~"
- "}y~}~}h{|y~c{|~}fy~y{|~|i{}~}v~|j{}y~y{}y~|4{|y~c{|~}fy~y{|~}i{}y~y{|y~|fy~|j{}y~by~}y{}y~ f{|y~{}~|p{|k~| r{~"
- "}Oy~}g{}u~}n{}t~y{}u~}l{}y~} \\{}m~ T{|x~}|{y|y~|Py~}g{|y~y{|~}gy~|xy~|i{|~my~|y{|y~u{}~}m{}y~}|y{|y}f{|y~d{|y"
- "~e{}~}hy|x{~}x{| Wy~|k{|y~}|{|}y~}lx~y}y~|jx~}x|}x~|m{|~}v|x~}gv~ky~s|j{}x~w|}~|nr|}y~|mx~}|{y|x~|mx~y|{|}y~| E"
- "y~}x|}x~k{}q~|k{|w~}k{|y~u|}x~l{}x~}v|}y~|r{|y~u|y}x~}n{|y~q|n{|y~r|m{}x~}v|}x~|r{|y~|ry~}k{|y~|e{|y~|j{|y~|v{|"
- "x~l{|y~|i{|w~}t{|w~}s{|w~}ty~}o{}x~}|{y|}x~n{|y~v|}x~|n{}x~}|{y|}x~o{|y~v|}x~}m{|x~}v|}~|pt|y~}u|q{}y~r{|y~|qx~"
- "q{|y~|v{|y~|u{}x~|u{}y~|t{}y~|v{|y~}o{|y~|tx~op|}y~}l{}~}e{|y~`{|y~|h{}v~}L{}~|O{}y~@{}~}Py~}|O{}y~`{|y~d{|y~h{"
- "}y~`{|y~ e{}y~ !{|y~}e{}~}e{|y~| [{}y~|x{}y~|jy}~y}|ix~|w{|}| w{}y~| M{}y~|y{|}y~i{|w~}ix~r|m{|y~q|p{|w~}x|}x"
- "~}l{|y}x~y}|n{|y~q|y~}r{|y~|w{}y~vy~}q{}x~}|{y|}x~Q{}v~Ky~|L{}v~|0{~|g{|y~}|y{|y}T{}y~t{}~}i{}~}h{}y~|x{|}P{}~y"
- "}x|y}~}k{|v{}~| x{}~y}x|y}~}Qy~x{|~}J{|y~cy~g{}~|Mt~y{}~|5{|~}gy~|x{}~}U{|~}r{|y~r{}y|~}qy~|ny~t{|~}P{|w~}g{|w~"
- "}g{|w~}g{|w~}g{|w~}fw~}j{}y~y|y~}r|q{}x~}v|}y~|p{|y~q|n{|y~q|n{|y~q|n{|y~q|j{|y~|e{|y~|e{|y~|e{|y~|k{|y~}u|}x~}"
- "p{|w~}ty~}o{}x~}|{y|}x~n{}x~}|{y|}x~n{}x~}|{y|}x~n{}x~}|{y|}x~n{}x~}|{y|}x~T{}x~}|{y|v~|r{}y~r{|y~|q{}y~r{|y~|q"
- "{}y~r{|y~|q{}y~r{|y~|p{|y~|tx~n{|y~|d{}y~|x{}y~|h{}~|e{}~|f{~}x{|~}j{|}xx}hy}|yy}|h{|}y~}/y~dy~|g{|~}x{|~|j{|y}"
- "|yy}|h{|~}d{|~}f{}~x{}~|iy}|yy}|iy|w~|h{}~y{|}~}f{|~}e{|~}f{}~|x{}~|j{}|y{|y}|i{|y}y{|y}2{|~}dy~f{}~|x{}~|j{|y}"
- "y{|y}|g{}~|i{}y~by}|y{|y}5{|}w~}|i{|}w~}|i{|}w~}|i{|}w~}|i{|}w~}|f{}~}{y|ny~}q{}y~ r{|~O{}x~|hs~|p{|s~y|s~|n{|w"
- "~} ^{}y~}| Ix~|u{}|Py~}g{|y~y{|~}gy~wy~k{|}v~y}|s{|y~w{}~|w{|y~ly~}_{|y~d{}~}dy~|iy~}y{~}{|y~|hy}| o{|y~jx~v{}"
- "y~l{|x{|y~|j{}|u{|x~d{}y~|i{}~|}y~ky~|d{|y~}]{}y~m{|y~|v{|y~}n{}y~|v{}y~ E{}u{}y~|n{|x~}|w{|}y~}|m{}y~}y~k{|y~|"
- "u{|y~}n{}y~}s{|~|r{|y~|t{|x~|o{|y~|e{|y~|f{}y~}r{}~|r{|y~|ry~}k{|y~|e{|y~|j{|y~|w{}y~}k{|y~|i{|y~}y~t{}~}y~}s{|"
- "v~ty~}p{}y~}t{}y~}o{|y~|v{|x~o{}y~}t{}y~}p{|y~|v{|x~mx~r{|iy~}k{}y~r{|y~|q{|y~|r{}y~u{|y~|uy~}~}u{}y~rx~vx~m{}y"
- "~u{}y~|e{|y~}k{}~}dy~|a{|y~|i{}y~|{}y~| y{}y~@{}~}Py~|N{}y~0{}y~`{|y~ e{}y~ !{|y~d{}~}dy~} [y~}v{}~}ju~}jy~| n"
- "{}y~ N{|y~|v{}~}j{}y~}y~i{|y~}e{|y~|gx~}t{}y~}o{|}q~|p{|y~|ry~}r{|y~|w{}y~vy~}r{}y~}t{}y~}S{}t~Ly~|M{}t~|1{~|g"
- "{}y~Ly~|v{|y~|i{}~}hy~}L{|y~|t{|y~|g{|~} {{|y~|t{|y~|T{|~|wy~f{}~|ay~ey|y~7{}t~y{}~|5{|~}h{|~}vy~U{|~}r{}~|p{|~"
- "}r{|~}my~ty~O{}y~}y~g{}y~}y~g{}y~}y~g{}y~}y~g{}y~}y~g{|y~}y~jy~}yy~}i{}y~}s{|~|p{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{"
- "|y~|e{|y~|e{|y~|k{|y~|t{|x~}q{|v~ty~}p{}y~}t{}y~}p{}y~}t{}y~}p{}y~}t{}y~}p{}y~}t{}y~}p{}y~}t{}y~}V{}y~}t{}x~q{}"
- "y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|o{}y~u{}y~|n{|y~|e{|y~|v{}~} A{|}|ey|e{}|wy|Py~}y|y~} ?{}~}h{}y~ p"
- "{}r~|l{}r~|l{}r~|l{}r~|l{}r~|h{}~}k{}y~qy~}1{|~}dy}P{}v~|is~|p{|r~}s~}nu~|h{|w}|k{|y}sy}|jx}|j{|y}t{|y}o{}y~| "
- "H{|y~|Gy~}g{|y~y{|~}h{|~}x{|~}l{}r~}s{|~}w{}~}w{}~|ly~}_{|y~dy~|d{}~}h{|y~|~y}~}|g{}~| o{}~}k{|y~|uy~}i{|y~|a{}"
- "y~|e{|y~}j{|~}{}y~ky~|dy~}]{|y~}m{}y~tx~ny~}u{|y~|,{|X{|X{|y~|o{}y~|q{}y~my~}|y~|l{|y~|ty~}o{|x~p{|r{|y~|s{|x~o"
- "{|y~|e{|y~|g{|x~p{|q{|y~|ry~}k{|y~|e{|y~|j{|y~|x{}y~}j{|y~|i{|y~|y~|uy~|y~}s{|y~|y~}uy~}q{|x~r{}y~|p{|y~|u{}y~|"
- "q{|x~r{}y~|q{|y~|u{}y~|ny~}_y~}k{}y~r{|y~|py~}s{|y~}ty~}v{|y~|y~uy~}r{|y~}x{}y~|ly~}w{|y~}e{|x~j{}~}d{}~}a{|y~|"
- "j{}y~|x{}y~| {{}y~@{}~}Py~|N{}y~0{}y~`{|y~ e{}y~ !{}y~d{}~}d{}~} \\{|y~u{}y~j{}x|}y~|kx~| o{|y~| O{}~}u{|y~jy"
- "~}|y~|i{|y~}f{|y~|h{}y~|rx~|q{|w~y}y~y}x~}q{|y~|ry~}r{|y~|w{}y~vy~}s{|x~r{}y~|U{}y~|y~}x~My~|N{}y~|y~|x~|2{~|gy"
- "~}g{|p{}m{}y~v{}~}h{}~}h{}~}L{~}xy|}y|x{}~l{|}u~ {{~}p{}~T{|~|wy~f{}~|b{}~}g{}w~|7{}t~y{}~|5{|~}h{}~|v{}~|V{|~}"
- "s{|~}o{|~}ry~n{|}~|u{}~}Oy~}|y~|hy~}|y~|hy~}|y~|hy~}|y~|hy~}|y~|hy~}|y~|l{}y~|yy~}j{|x~p{|p{|y~|e{|y~|e{|y~|e{|"
- "y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|rx~q{|y~|y~}uy~}q{|x~r{}y~|r{|x~r{}y~|r{|x~r{}y~|r{|x~r{}y~|r{|x~r{}y~|q{|}q{|"
- "}p{|x~s{}x~|r{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|ny~}w{|y~}m{|s~}|l{|y~u{|y~ 8{|w{|y~} _{} G{}y~ r{|"
- "x~|w{|}y~|o{|x~|w{|}y~|o{|x~|w{|}y~|o{|x~|w{|}y~|o{|x~|w{|}y~|i{}~|jy~|s{|y~|1y~}d{~|Q{|t~is~|p{}i~}os~j{|s~|m{"
- "|y~sy~|jw~j{|y~|u{}y~p{|y~| Gx~Fy~}g{|y~y{|~}h{}~}x{}~}m{|y~}{|~x{|}s{|~}w{}~}x{|~}ky~}_{|y~e{|y~c{|y~f{}x~}|e"
- "{}~| oy~|k{}y~t{}y~i{|y~|a{|y~|dy~|jy~|{}y~ky~|e{|y~|]{}y~|m{}y~ty~}o{|y~|ty~}/{|}~}Xy~}|[{|y~|p{}y~|o{}y~o{|y~"
- "|{y~}l{|y~|ty~}o{}y~|f{|y~|r{}y~|p{|y~|e{|y~|g{}y~|e{|y~|ry~}k{|y~|e{|y~|j{|y~|y{}y~}i{|y~|i{|y~|}~}v{|y~{y~}s{"
- "|y~|}y~uy~}q{}y~|qx~p{|y~|u{}y~|q{}y~|qx~q{|y~|u{}y~|o{|y~|_y~}k{}y~r{|y~|p{}y~s{}y~|t{}y~v{|~}{y~|w{|y~|q{}y~|"
- "{|y~}k{|y~|xx~dx~|j{}~}d{|y~a{|y~|k{}y~|v{}y~|9{|y}x~y}j{}y~y{}x~}|h{|}x~y}|j{}x~}|{}~}k{|}x~}|j{|s~|i{}x~}|{}~"
- "}n{}y~y{}x~}|h{|y~d{|y~h{}y~u{|y~}j{|y~m{}y~y{}x~}w{|}y~}|p{}y~y{}x~}|i{|}x~}|k{}y~y{}x~}|i{}x~}|{}~}k{}y~y{}x~"
- "k{|}w~y}|k{|r~l{}~}t{}~}oy~}s{}y~r{}~}v{}y~}v{}~}r{|y~|u{|y~|oy~}s{}y~n{}p~h{}y~d{}~}d{}~} t{}x~}|y{|~}n{|y~u{}"
- "~}e{}y~k{|w~y}|g{|}w~y}l{}y~y{}x~}|n{}~}|s{}y~iy~}i{}~}t{}~}p{|y~|r{}y~n{|y}y{}y~}lm~p{}y~x{|y~x{|y~|k{}w~}|j{|"
- "}q~|q{}n~ny~|ty~|l{|y~|{y~}h{|y~}g{|y~|hy~}q{|y~}qx~}y{}y~y{|x~|r{|y~|ry~}r{|y~|w{}y~vy~}s{}y~|qx~V{|y~|{y~y|y~"
- "}Ny~|O{|y~|{y~|{y~}Ny~}e{|}w~}|jy~}h{|y~r{}~}my~|x{|y~|h{}~}h{|y~}Ny}x{}u~|yy}n{}y~w}y~ y}y{}v~}|xy}T{~}x{|~}f{"
- "}~|c{|~}fx|y}|Q{}~}t{}~}ns~y{}~|5{|~}h{}~|v{}~|V{|~}sy~n{|~}s{}~|p{}x~}u{|y~f{|y~|h{|y~|{y~}i{|y~|{y~}i{|y~|{y~"
- "}i{|y~|{y~}i{|y~|{y~}i{|y~|{y~}ly~}xy~}j{}y~|d{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|r{|y~}r{|y~|"
- "}y~uy~}q{}y~|qx~r{}y~|qx~r{}y~|qx~r{}y~|qx~r{}y~|qx~qy~}s{|y~}q{}y~|t{}~}x~r{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}"
- "y~r{|y~|n{|y~|xx~l{|q~}m{}y~w{|w~l{|y}x~y}i{|y}x~y}i{|y}x~y}i{|y}x~y}i{|y}x~y}i{|y}x~y}n{|y}x~y}w{|}x~}|l{|}x~y"
- "}|j{|}x~}|h{|}x~}|h{|}x~}|h{|}x~}|g{|y~d{|y~d{|y~d{|y~e{|}v~|l{}y~y{}x~}|i{|}x~}|h{|}x~}|h{|}x~}|h{|}x~}|h{|}x~"
- "}|g{|x~f{|}x~}|{}~|o{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}oy~}s{}y~n{}y~y{}x~}|my~}s{}y~;y~}xy~|y{|y~|py~}s{|y"
- "~|py~}s{|y~|py~}s{|y~|py~}s{|y~|j{}~|j{}y~sy~}1y~}d{|~Q{|s~}j{}t~o{|i~}p{}s~}kx~}y|}x~m{|y~sy~|k{|w~|jy~}uy~}py"
- "~} Fy~}Fy~}g{|y~y{|~}m{|k~}q{}y~y{|~n{|~}w{}~|xy~j{}y~|`{|y~e{}~}by~|g{|x~}d{}~| p{|y~jy~}t{}y~i{|y~|a{|y~|e{|"
- "y~|k{}~}y{}y~ky~|{|g{}y~\\x~l{|y~|v{|y~|o{|y~|tx~i{}y~d{}y~a{|}w~}Xv~}|^x~p{|y~l{}~}p{}y~y{|y~|m{|y~|ty~}ox~e{|"
- "y~|qy~}p{|y~|e{|y~|gx~d{|y~|ry~}k{|y~|e{|y~|j{|y~|{}y~}h{|y~|i{|y~y|y~v{}~}{y~}s{|y~|{y~}vy~}qx~p{}y~|q{|y~|u{|"
- "y~|qx~p{}y~|r{|y~|u{}y~|ny~}_y~}k{}y~r{|y~|p{|y~|ty~}s{}y~|w{}~|{}~|w{|y~|py~}{x~i{}y~y{}y~|e{}y~|i{}~}cy~|b{|y"
- "~|l{}y~|t{}y~|;{|r~|l{}y~|t~|j{}s~|m{|t~|}~}l{}s~|l{|s~|k{|t~|}~}n{}y~|t~|i{|y~d{|y~h{}y~v{}y~}i{|y~m{}y~|t~y{}"
- "u~}q{}y~|t~|l{|s~}l{}y~|t~|l{|t~|}~}k{}y~|v~l{|r~k{|s~}l{}~}t{}~}o{}y~sy~}r{|y~v{}x~vy~}q{}y~|w{|y~}n{}y~sy~}n{"
- "|p~h{}y~d{}~}d{}~} v{|t~|{}~|n{|y~u{}~}e{|y~|k{|t~|j{}s~|m{}y~|t~|o{}x~|ty~}ix~i{}~}t{}~}py~}q{|y~|p{|y~}{}v~|n"
- "m~p{}y~x{|y~x{|y~|ls~|l{|o~|q{}n~o{|y~|t{}~}l{}y~y{}y~|h{}y~}h{|y~|i{|y~|p{}y~r{}y~|x{}y~x{|x~r{|y~|ry~}r{|y~|w"
- "{}y~vy~}sx~p{}y~|p{}b{}|yy~|{|}b{}|hy~|i{|}s{}|ly|yy~|y{}My~}g{|r~k{|y~|gx~|}x~}|}y~|m{}y~xy~}g{}~}g{}x~|Q{|~|y"
- "y~}|yx|y{|~|p{|~}w{|y~gy|w{|<{|~|y{}~}x|y~|y{|~|U{}y~y}y~e{}~|d{|y~a{}~Q{}~}t{}~}n{}t~y{}~|5{|~}h{}~|v{}~|m{|v{"
- "|k{|~}t{}~|n{|~}t{|~}ox|}y~v{}~|f{|y~|h{}y~y{|y~|j{}y~y{|y~|j{}y~y{|y~|j{}y~y{|y~|j{}y~y{|y~|j{}y~y{}y~m{|y~|xy"
- "~}jx~c{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|qx~r{|y~|{y~}vy~}qx~p{}y~|sx~p{}y~|sx~p{}y~|sx~p{}y~"
- "|sx~p{}y~|r{|y~}u{|x~px~t{}~}{}y~|s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|m{}y~y{}y~|l{|y~v|}x~}n{}y~x{}y~|"
- "k{|r~|l{|r~|l{|r~|l{|r~|l{|r~|l{|r~|q{|r~|{}s~n{}s~|l{}s~|k{}s~|k{}s~|k{}s~|i{|y~d{|y~d{|y~d{|y~g{|r~l{}y~|t~|l"
- "{|s~}k{|s~}k{|s~}k{|s~}k{|s~}h{|x~h{|q~|n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}o{}y~sy~}n{}y~|t~|n{}y~sy~}<{}~"
- "}wy~|x{|y~q{}~}q{|y~q{}~}{~}w{|~y|y~q{}~}t{|~y|y~q{}~}q{|y~j{}~|j{|y~|u{|y~|<q|}y~w|p{|y}uy}Qq~}k{|u~}o{|i~|q{|"
- "q~}m{}y~uy~}n{|y~sy~|k{}w~|j{}y~v{|y~|q{|y~ H{}o~My~}fy|xy|m{|k~}q{}~}y{|~n{|y~wy~|y{}~|ix~_y|ey~|by~}i{|}~}~}"
- "y~}f{}~| p{|~}jy~}t{|y~|j{|y~|a{}y~f{|}y~}k{|y~x{}y~kt~}|ky~}{|w~}|e{|y~|kx~|x{|y~}n{|y~|tx~i{}y~d{}y~d{|}v~}|q"
- "k|p{|}w~}|a{}y~|p{}~|w{|}y~}|{y|xy~|qy~}xy~}m{|y~|u{|y~|p{|y~}e{|y~|qx~p{|y~|e{|y~|h{|y~}d{|y~|ry~}k{|y~|e{|y~|"
- "j{|y~|}y~}g{|y~|i{|y~|{y~|wy~|{y~}s{|y~|{}y~vy~}r{|y~}p{|y~|q{|y~|u{}y~|r{|y~}p{|y~|r{|y~|u{}y~mx~}`y~}k{}y~r{|"
- "y~|oy~}u{|y~|s{|y~|wy~|{|~}w{}y~o{|v~|hy~}|y~}e{}y~}h{}~}c{}~}b{|y~|m{}y~|r{|y~|<{|}y|x{|}y~l{}w~|y{|x~|lx~}|yy"
- "|}|mx~|y{|}x~}mx~y|y{|x~iy~|h{|x~|y{|}x~}n{}w~|y{|x~i{|y~d{|y~h{}y~w{}y~|h{|y~m{}w~|y{|y~}|~}|{|}y~|r{}w~|y{|x~"
- "lx~|y{|}y~}m{}w~|y{|x~|mx~|y{|}x~}k{}w~w|ly~}|xy|}i{}y~g{}~}t{}~}o{|y~|u{|y~|r{|y~|ww~vy~|px~wx~m{|y~|u{|y~|f{|"
- "y~}h{}y~d{}~}d{}~}6{|}x~|x{}x~|o{|y~}|{|}y~{y~m{|y~v{|y~|dy~|l{}~}x{|x~|l{}y~}|yy|}|m{}w~|y{|x~n{|}~}u{|y~|j{|x"
- "~|j{}~}t{}~}q{|y~|py~}q{|x~y|y~y|x~ny|y~}w|}y~}|p{}y~x{|y~x{|y~|mx~|y{|x~|n{|x~|x{}x~y|pu|y~}v|o{|y~s{}y~ly~}xy"
- "~}g{}y~|i{|y~|i{}y~o{}y~|sx~w{}y~w{}y~|s{|y~|ry~}r{|y~|w{}y~w{|y~}t{|y~}p{|y~|qy~}_y~|`{|y~|iy~|j{|y~}u{|y~|iy~"
- "|Jy~}h{|x~y|~|{|}k{|y~|fp~|ky~}{|y~f{}~}h{}~y}y~}|Sy}y{}~}qy}p{|~}w{|y~h{|~|x{}~<y}x{}~}x{|~}xy}T{|}y~}d{}~|e{|"
- "~}`{}~|R{}~}t{}~}n{}t~y{}~|5{|~}h{|~}vy~l{|~|xy}l{|~}u{|~}m{|~}ty~|k{}~|x{|~}e{|y~|hy~}xy~}jy~}xy~}jy~}xy~}jy~}"
- "xy~}jy~}xy~}jy~}xy~|n{}y~wy~}k{|y~}c{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|q{}y~r{|y~|{}y~vy~}r{|"
- "y~}p{|y~|t{|y~}p{|y~|t{|y~}p{|y~|t{|y~}p{|y~|t{|y~}p{|y~|q{|y~}w{|x~p{|y~}u{|~}y{|y~|s{}y~r{|y~|q{}y~r{|y~|q{}y"
- "~r{|y~|q{}y~r{|y~|ly~}|y~}k{|y~|ux~n{}y~y{|y~|j{|}y|x{|}y~l{|}y|x{|}y~l{|}y|x{|}y~l{|}y|x{|}y~l{|}y|x{|}y~l{|}y"
- "|x{|}y~q{|}y|x{|}v~|x{|y~}px~}|yy|}|mx~y|y{|x~lx~y|y{|x~lx~y|y{|x~lx~y|y{|x~i{|y~d{|y~d{|y~d{|y~gx~|x{|y~}m{}w~"
- "|y{|x~lx~|y{|}y~}lx~|y{|}y~}lx~|y{|}y~}lx~|y{|}y~}lx~|y{|}y~}i{|x~hx~|y{|}y~}m{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~"
- "}t{}~}o{|y~|u{|y~|n{}w~|y{|x~|o{|y~|u{|y~|={|y~vy~|w{}~|s{|y~o{}~|s{|y~{}y~}y{|y~}{}~|s{|y~t{|y~}{}~|s{|y~o{}~|"
- "ky~|iy~}u{}y~;k~}qw~u{~|R{}p~|k{}w~}mi~q{|o~|ny~|u{}y~n{|y~sy~|ky~}y~}j{|y~|w{}y~p{}~} Hx}y~t}|My~}M{|y~x{|~}l"
- "{}y~y{|~m{}~}y{}~}y{|~}i{|w~I{|y~|b{}y~j{}~}|{~}{|y~|h{}~| p{}~|jy~}t{|y~|j{|y~|b{|y~}j{}u~}jy~|x{}y~kr~}ly~y}t"
- "~}f{}y~i{}t~|ly~}u{|x~|j{}y~d{}y~f{}v~}|nk~}n{|}w~}|e{}y~|p{|~}w{|u~y}~x{|~}r{|y~|x{}y~m{|y~|xy|}y~}o{|y~|e{|y~"
- "|q{}y~p{|y~r|m{|y~s|o{|y~|d{|y~q|y~}k{|y~|e{|y~|j{|v~}f{|y~|i{|y~|{}~}x{|~}yy~}s{|y~|yy~}wy~}r{|y~|p{|y~}q{|y~|"
- "ux~q{|y~|p{|y~}r{|y~|v{|y~}m{|v~}y|ey~}k{}y~r{|y~|o{}y~u{}y~qy~}x{|y~y{|y~wy~}n{}x~}g{|v~e{|y~}g{}~}c{|y~b{|y~|"
- " o{}~}m{}x~ux~m{}y~|f{}y~u{}y~}n{}y~|uy~}jy~|h{}y~u{}y~}n{}x~v{|y~|j{|y~d{|y~h{}y~x{}y~|g{|y~m{}x~v{}x~|vy~}r{}"
- "x~v{|y~|n{}y~|v{}y~|n{}x~ux~n{}y~u{}y~}k{}x~h{|y~a{}y~g{}~}t{}~}ny~}u{}y~py~|x{|y~}~|x{|y~o{|y~}y{}y~|l{}~}u{}~"
- "}ex~g{}y~d{}~}d{}~}6y~y}y~|{}y~}y~}p{}y~vy~}y~m{|y~x{|}x~c{}~}m{|y~u{}y~l{}~}e{}x~v{|y~|n{|y~u{}~}i{}x~}j{}~}t{"
- "}~}q{}y~o{}y~q{}y~|{|y~y{|y~}my~|w{|y~|o{}y~x{|y~x{|y~|n{}y~ux~n{}y~u{}y~|iy~|j{|n~m{|y~|x{}y~f{}y~|j{|y~|i{}y~"
- "o{|y~|t{|y~}w{}y~w{|y~}s{|y~|ry~}qy~}w{}y~w{|y~|t{|y~|{r~y|y~}rx~|_y~|_{}y~|jy~|k{|x~s{}y~|jy~|Jx|h{}y~|y{~|h{|"
- "y~|f{|y~}|y{}y~|n{|u~{u~|j{}~}i{|~}y{|}y~}T{~|yy~p{|~p{|~}wx~i{|y~|y{}y~ok|X{~|x{}~}x{|~}x{|~?k~}m{}~}_y~|R{}~}"
- "t{}~}mt~y{}~|ix|J{|~}gy~|x{}~}l{|y~|y{}~}m{|~}uy~|u{|t{|~}u{}~}j{}~|xy~cy|h{|y~|x{}y~k{|y~|x{}y~k{|y~|x{}y~k{|y"
- "~|x{}y~k{|y~|x{}y~k{|y~|x{}y~ny~}wy~}r|t{|y~|c{|y~r|m{|y~r|m{|y~r|m{|y~r|i{|y~|e{|y~|e{|y~|e{|y~|k{|y~|q{}y~|s{"
- "|y~|yy~}wy~}r{|y~|p{|y~}t{|y~|p{|y~}t{|y~|p{|y~}t{|y~|p{|y~}t{|y~|p{|y~}p{|y~}y{|x~o{|y~|v{|y~wy~}s{}y~r{|y~|q{"
- "}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|l{|v~j{|y~|u{}y~|o{}y~y{|y~`{}~}d{}~}d{}~}d{}~}d{}~}d{}~}i{}x~u{|y~|r{}y~|f{}y~|"
- "uy~}n{}y~|uy~}n{}y~|uy~}n{}y~|uy~}j{|y~d{|y~d{|y~d{|y~h{}y~|v{|y~|n{}x~v{|y~|n{}y~|v{}y~|n{}y~|v{}y~|n{}y~|v{}y"
- "~|n{}y~|v{}y~|n{}y~|v{}y~|ix|i{}y~|w{|x~|n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}u{}~}m{}x~ux~n{}~}u{}~}<{"
- "|~}vy~|w{|~}s{|~}o{|~}s{|~}y{}y~}|y~}y{|~}s{|~}u{|y~}y{|~}s{|~}o{|~}ky~|i{}y~uy~};k~}q{|{y~|w{}~R{|n~o{}o~}|r{|"
- "k~}qm~|oy~|u{|y~n{|y~sy~|l{|y~|}y~iy~}wy~}p{}~} F{|y~|Fy~}M{}~}x{}~}l{|y~}y|~lu~xy~|xx}|q{|y~|x~ty|S{|y~a{}y~j"
- "{}|x{~}x{}|h{}~| py~j{|y~|t{|y~|j{|y~|bx~i{}v~}|k{}~}w{}y~k{}y|x{|x~}mw~}|y{|x~|gy~}h{}u~}l{}y~|v{}x~|jx|dx|i{|"
- "}w~}|kk~}l{|}v~}|i{}y~|o{}~|wy~}x{}x~wy~rx~w{|y~|n{|q~|n{|y~|e{|y~|q{}y~|q{|p~}n{|q~o{|y~|tu|q{|m~}k{|y~|e{|y~|"
- "j{|v~e{|y~|i{|y~|yy~xy~|yy~}s{|y~|y{}y~|xy~}r{|y~|oy~}q{|y~|w{|x~}q{|y~|oy~}r{|y~v|}y~}k{|}t~}gy~}k{}y~r{|y~|o{"
- "|y~}vy~}q{}y~x{|~}xy~|y{|y~|n{|x~e{}x~|ex~f{}~}by~|c{|y~| o{|y~m{}y~|u{|y~|ny~}ey~}u{|y~}ny~|t{}y~jy~|hy~}u{|y~"
- "}n{}y~|u{}~}j{|y~d{|y~h{}y~y{}y~|f{|y~m{}y~|v{|x~u{}y~r{}y~|u{}~}ny~}ty~}n{}y~|u{|y~|oy~}u{|y~}k{}y~|h{|y~a{}y~"
- "g{}~}t{}~}n{}y~uy~}p{}~}x{}~}|~}x{}~}n{|y~y|y~}k{|y~uy~|f{}y~f{}~}d{}~}d{}y~7{}~x{|y~|~}x{}~py~}v{}x~}m{|y~{|v~"
- "|i{}y~}|{}~}my~|ty~}m{}~}e{}y~|u{}~}my~|vy~|j{|y~}y~j{}~}t{}~}qx~o{|y~|ry~}y{|y~x{}y~my~|w{|y~|o{}y~x{|y~x{|y~|"
- "ny~|u{|y~|oy~}ty~}iy~|j{|n~mx~w{|y~|g{|y~}j{|y~|i{}y~o{|y~|t{|y~|w{}y~vy~}s{|y~|ry~}qx~w{}y~w{}y~|t{|y~|{r~|{y~"
- "}sx~|^y~|^{}y~|ky~|l{|x~q{}y~|ky~|5{|y~|x{~|h{|y~|f{}~}v{|~}mw}v~w}|j{}~}i{}~|w{|y~}U{~y{|y~p{|~ox~y}~}y~j{}y~|"
- "y{}y~nk~}Y{~w{}~}y{|}~|x{|~?k~}n{}y~v|ix}|}y~}Q{}~}t{}~}m{|u~y{}~|iy~}Ly|}~}y|i{|y~x}y~j{}y~|y{}y~n{|~}v{|~}v{|"
- "y~}u{|~}v{|y~y{}w~}wx|{|}y~x{}~|uy~}Wx~w{|y~|lx~w{|y~|lx~w{|y~|lx~w{|y~|lx~w{|y~|l{}y~w{|y~|p{}y~|wo~t{|y~|c{|p"
- "~}n{|p~}n{|p~}n{|p~}j{|y~|e{|y~|e{|y~|e{|y~|mq~u{}y~|s{|y~|y{}y~|xy~}r{|y~|oy~}t{|y~|oy~}t{|y~|oy~}t{|y~|oy~}t{"
- "|y~|oy~}o{|y~}|x~n{|y~|vy~|wy~}s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|k{}x~|j{|y~|u{|y~|o{}y~y{|y~|a{|y~d{"
- "|y~d{|y~d{|y~d{|y~d{|y~i{|y~|t{}~}ry~}ey~|t{}y~ny~|t{}y~ny~|t{}y~ny~|t{}y~j{|y~d{|y~d{|y~d{|y~hy~}ty~}n{}y~|u{}"
- "~}ny~}ty~}ny~}ty~}ny~}ty~}ny~}ty~}ny~}ty~}Ty~}w{|~}y~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{|y~uy~|m{}y~|u{"
- "|y~|o{|y~uy~|<{}~u|y~}w|{y~s{}~n|{y~s{}~|x{}w~}wy~s{}~|v{|y~}wy~s{}~|vy~}vy~ky~|i{|y~|w{|y~|4{|y~}i{}~}w{~}Rm~}"
- "qk~|r{}l~q{|m~|p{|y~sy~|o{|y~sy~|l{}y~{|y~|j{}y~x{|y~|p{}i~ V{|y~|F{}~}M{}~|x{}~|k{}v~}|m{|y}|x{}~}y{|v~}s{|y~"
- "|{|x~v{|y~S{}y~a{|y~e{~}jt|y~}u| w{|~}j{|y~|t{|y~|j{|y~|cx~|hw|}y~|m{|y~v{}y~cx~|nx~}ux~h{|y~|j{|y~}|{y|x~m{|x~"
- "|y{|}w~|={}w~}|E{|}v~|l{|y~|ny~w{}~}v{}y~wy~s{|y~|vy~}n{|q~}|o{|y~|e{|y~|q{}y~p{|p~}n{|q~o{|y~|u{|u~}r{|m~}k{|y"
- "~|e{|y~|j{|y~}x~f{|y~|i{|y~|y{}~|{|y~xy~}s{|y~|xy~}xy~}r{|y~|oy~}q{|q~}p{|y~|oy~}r{|r~}h{|y}u~|iy~}k{}y~r{|y~|n"
- "x~w{|y~|q{}y~x{}~|x{}~|y{|y~|nw~|ey~}e{}y~|f{}~}b{}~}c{|y~| v{|y}t~m{}y~sy~|o{|y~|f{|y~|ty~}o{|y~|t{|y~jy~|i{|y"
- "~|ty~}n{}y~t{}~}j{|y~d{|y~h{}y~{x~|e{|y~m{}y~ty~}u{|y~r{}y~t{}~}o{|y~|t{}y~n{}y~sy~|p{|y~|ty~}k{}y~g{|y~}b{}y~g"
- "{}~}t{}~}n{|y~|w{|y~o{|y~x{}~y|y~xy~}m{}w~}iy~|w{}y~f{}y~|g{|y~|d{}~}d{|y~}jy}y~}|t{|}X{~}w{|y~}v{~|r{|y~|v{|x~"
- "|m{|y~{|v~}|ku~|y~}n{|y~|t{}y~m{|y~}f{}y~t{}~}m{}y~w{}y~i{}y~{y~}k{}~}t{}~}qy~}ny~}s{|y~|y{|y~x{|y~my~|w{|y~|o{"
- "}y~x{|y~x{|y~|o{|y~sy~|p{|y~|t{}y~iy~|j{|y~s|}y~n{|y~}vy~}gx~|j{|y~|i{}y~o{|y~|t{|y~|w{}y~vy~}s{|y~|ry~}q{}y~|x"
- "{}y~x{|y~}s{|y~|{r|yy~}tx~}l|ly~|mk|x~|ly~|m{|x~o|x~|ly~|5{}y~w{~|j{}r~}ky~|uy~i{|x~}e{}~}i{}~}v{|y~V{|~y{|y~o{"
- "~|o{}x~|{y}k{}y~|y{}~}mk~}Z{|~w{}u~|v{~|@t|y~}t|n{}t~i{|}x~y}P{}~}t{}~}k{|}x~y{}~|iy~}Lt~|i{|}x~}h{|y~|y{}y~|rt"
- "~|yy~ux~}wt~|y{}~|{|~}y|}y~x{}v~}|y{|y~u{}y~}m{|y}i{|y~|vy~}m{|y~|vy~}m{|y~|vy~}m{|y~|vy~}m{|y~|vy~}ly~|vy~}py~"
- "}vo~t{|y~|c{|p~}n{|p~}n{|p~}n{|p~}j{|y~|e{|y~|e{|y~|e{|y~|m{}s~}u{}y~|s{|y~|xy~}xy~}r{|y~|oy~}t{|y~|oy~}t{|y~|o"
- "y~}t{|y~|oy~}t{|y~|oy~}n{|v~m{|y~|wy~|vy~}s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|jy~}i{|y~|u{}y~|o{}y~xx~|"
- "i{|y}t~k{|y}t~k{|y}t~k{|y}t~k{|y}t~k{|y}t~p{|y}t~s{|y~s{|y~|f{|y~|t{|y~o{|y~|t{|y~o{|y~|t{|y~o{|y~|t{|y~j{|y~d{"
- "|y~d{|y~d{|y~i{|y~|t{}~}n{}y~t{}~}o{|y~|t{}y~o{|y~|t{}y~o{|y~|t{}y~o{|y~|t{}y~o{|y~|t{}y~pk~}q{|y~|w{~}{}y~n{}~"
- "}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}my~|w{}y~l{}y~sy~|ny~|w{}y~;{}~|}p~{y~s{}~|}p~{y~s{}~|w{}x~vy~s{}~|w{|y~}vy"
- "~s{}~|vy~}vy~ky~|h{}~}w{}y~3y~}h{|y~x{|~|S{|l~r{}k~}qm~|p{}o~}o{|y~sy~|o{|y~sy~|ly~}yy~}j{|y~|y{|y~o{}i~ X{|y}"
- "y~u}K{}~}My~|xy~i{|}u~}i{|y~y{|y~|{|y~|t{}~}x{|x~w{|y~S{}y~a{|y~|f{~}jk~} x{}~}iy~}t{|y~|j{|y~|d{}y~|b{}y~|ny~|"
- "v{}y~c{|y~}nx~|u{}y~|ix~j{|y~|v{|y~}m{|t~y}y~|=x~}?{|x~}l{}y~m{~}wy~|v{|y~w{}~s{}y~u{}y~n{|y~|v{|}y~|p{|y~|e{|y"
- "~|q{}y~p{|y~|e{|y~|h{|y~|u{|u~}r{|y~|ry~}k{|y~|e{|y~|j{|y~|}x~g{|y~|i{|y~|y{|y~{}~}xy~}s{|y~|x{|y~|yy~}r{|y~|p{"
- "|y~}q{|s~}|o{|y~|p{|y~}r{|q~|ey|w~iy~}k{}y~r{|y~|n{|y~|x{}y~p{|y~|yy~|x{}~}y{}y~n{}v~ey~}f{}y~|e{}~}b{|~}c{|y~|"
- " w{}q~m{}y~sy~}o{|y~e{|y~s{}~}o{|n~jy~|i{|y~s{}~}n{}y~t{}~}j{|y~d{|y~h{}v~c{|y~m{}y~ty~|u{|y~r{}y~t{}~}o{|y~s{}"
- "y~n{}y~sy~}p{|y~s{}~}k{}y~f{}w~}|f{}y~g{}~}t{}~}m{}~}w{}~}o{|y~|yy~yy~xy~|lw~h{}y~wy~}g{}y~|i{}w~}c{}~}c{|w~}o{"
- "|s~}w|}~}X{~|vy~|vy}r{|y~tx~l{|y~w{|}x~|m{}y~x{}x~|n{|y~s{}y~l{|}v~j{}y~t{}~}m{|y~|xy~}j{|y~|{}y~k{}~}t{}~}qy~}"
- "vy~|vy~}s{|y~x{|y~x{|y~|ny~|w{|y~|o{}y~x{|y~x{|y~|o{|y~sy~}p{|y~s{}y~iy~|j{|y~s{}y~n{}y~u{}y~h{}y~|i{|y~|i{}y~|"
- "p{}y~s{|y~}w{}y~w{|y~}s{|y~|ry~}px~x{}y~x{}y~|s{|y~|p{|y~}u{}h~ly~|m{}h~ly~|mg~ly~|J{}~}i{}y~w{~|j{}r~}ky~ty~|i"
- "x~d{}~}i{}y~|vy~|W{|~y{|y~o{~|X{}y~xy~}^{|~}Z{|~w{}~}|}~}u{~|9{}~| v{}~}t{}~}hy~y{}~|iy~} s{|y~}y{}y~|st|y{}~|v"
- "{}~|~}wt|y{|~}sy~|wx|v{}~|vy}|~}m{|y~i{}y~u{}y~m{}y~u{}y~m{}y~u{}y~m{}y~u{}y~m{}y~u{}y~m{}y~u{}y~q{|y~|vy~}k{|y"
- "~|c{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|q{}y~|s{|y~|x{|y~|yy~}r{|y~|p{|y~}t{|y~|p{|y~}t{|y~|p{|"
- "y~}t{|y~|p{|y~}t{|y~|p{|y~}m{}x~|m{|y~|x{}~|v{|y~}s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|jy~}i{|y~|ux~n{}y"
- "~x{|x~}k{}q~l{}q~l{}q~l{}q~l{}q~l{}q~q{}f~s{|y~e{|n~o{|n~o{|n~o{|n~j{|y~d{|y~d{|y~d{|y~i{|y~s{}y~n{}y~t{}~}o{|y"
- "~s{}y~o{|y~s{}y~o{|y~s{}y~o{|y~s{}y~o{|y~s{}y~pk~}q{|y~wy~y{}y~n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}m{}y~wy~"
- "}l{}y~sy~}n{}y~wy~};{}~|}q~}{y~s{}~|}q~}{y~s{}~|x{|w~}wy~s{}~|x{|y~}uy~s{}~|vy~}vy~ky~g{|y~|xy~|4{}y~fy~|yy}R{|"
- "l~ri~po~|n{}p~n{|y~sy~|o{|y~sy~|m{|y~|y{}y~iy~}y{}~}o{}~} H{}r~}K{}~}N{|y~x{|y~f{|~}w~j{}~|y{}~}x{|y~ty~|w{|x~"
- "x{}~}S{}y~a{|y~|f{|ik~}T{}u~|Ly~|iy~}t{|y~|j{|y~|e{}y~|`y~}o{}~}u{}y~by~}nx~t{|y~|j{|y~}jy~}t{}y~k{}x~}|{}y~<v~"
- "}|hk|g{|}w~}l{}~}n{|~}wy~ty~wy~sy~}t|y~|o{|y~|t{}y~p{|y~}e{|y~|qx~p{|y~|e{|y~|h{|y~}py~}r{|y~|ry~}k{|y~|e{|y~|j"
- "{|y~|{}y~}h{|y~|i{|y~|xy~|y~|xy~}s{|y~|wy~}yy~}r{|y~}p{|y~|q{|y~w|k{|y~}p{|y~|r{|y~|w{}x~bx~|jy~}k{}y~r{|y~|my~"
- "}xy~}oy~}{|y~w{|y~yy~}o{|y~}{y~}fy~}g{|y~}d{}~}ay~c{|y~| x{}y~}|w{|y~m{}y~sy~}o{|y~e{}y~s{}~}o{|n~jy~|i{}y~s{}~"
- "}n{}y~t{}~}j{|y~d{|y~h{}w~}c{|y~m{}y~ty~|u{|y~r{}y~t{}~}o{}y~s{}y~n{}y~sy~}p{}y~s{}~}k{}y~e{|u~}h{}y~g{}~}t{}~}"
- "m{|y~wy~|ny~}{|~}y{}~|{|y~k{}y~}h{|y~|y{|y~|h{|y~}h{}w~|c{}~}c{|}x~}oy~}x|}r~|X{~|v{}~|v{~}r{}y~ty~}l{|y~t{}y~n"
- "{|y~|wx~|n{|y~s{}y~l{|u~j{}y~t{}~}ly~}y{|y~|j{}y~y{|y~|l{}~}t{}~}r{|y~}vy~|vy~}s{}y~x{|y~x{|y~|ny~|w{|y~|o{}y~x"
- "{|y~x{|y~|o{}y~sy~}p{|y~s{}y~iy~|j{|y~|t{}y~ny~}u{|y~|j{|y~}h{|y~|i{|y~}px~ry~}w{}y~w{}y~|s{|y~|ry~}p{|x~|{}y~y"
- "{}y~}r{|y~}p{|y~|u{|h~ly~|m{}h~ly~|m{}h~ly~|J{}~}i{}y~w{~|h{|y~|fy~|uy~mv}x~v}|Tx~|wy~U{~|yy~|q{|~or}ly~}xy~|^{"
- "|~}Y{~|x{}~}y{}~}w{|~8{}~| v{}~}t{}~}hy~y{}~| yr}h{}~}y{|y~|k{|~}v{|~y|~}ny~r{}~|p{|~}v{|~{|~}m{|y~iy~}t|y~|ny~"
- "}t|y~|ny~}t|y~|ny~}t|y~|ny~}t|y~|ny~}t|y~|r{}y~u|y~}k{|y~}c{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~"
- "|q{}y~r{|y~|wy~}yy~}r{|y~}p{|y~|t{|y~}p{|y~|t{|y~}p{|y~|t{|y~}p{|y~|t{|y~}p{|y~|n{|w~}m{|y~}y{}~}u{|y~|s{}y~r{|"
- "y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|jy~}i{|y~|w{|x~}n{}y~v{}x~|n{}y~}|w{|y~m{}y~}|w{|y~m{}y~}|w{|y~m{}y~}|w{|y~"
- "m{}y~}|w{|y~m{}y~}|w{|y~r{}y~}|w{|n~s{|y~e{|n~o{|n~o{|n~o{|n~j{|y~d{|y~d{|y~d{|y~i{|y~s{}y~n{}y~t{}~}o{}y~s{}y~"
- "o{}y~s{}y~o{}y~s{}y~o{}y~s{}y~o{}y~s{}y~pk|p{}y~x{}~|y{}y~n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}n{}~}t{}~}m{|y~|y{|y~|l"
- "{}y~sy~}n{|y~|y{|y~|;{|~}vy~|w{|~}s{|~}o{|~}s{|~}y{}y~}|y~}y{|~}s{|~}y{}y~}u{|~}s{|~}vy|v{|~}ky~fy~}y{}y~9k~}n{"
- "}y~y{~|R{}l~ri~p{|q~}lq~m{|y~sy~|o{|y~sy~|m{}y~x{|y~|j{}y~yy~}o{|y~ Ey~}E{|Qj~j{|~y{|y~}l{|~}xy~|wy~u{|y~|v{|x"
- "~{|y~R{}y~a{|y~K{}~|M{}u~|M{|y~hy~}t{}y~i{|y~|f{}y~|_{}y~o{}n~}ey~}n{}y~t{|y~|j{}y~|jy~}t{|y~|e{}y~;{|}v~}jk~}k"
- "{|}v~}j{}~}n{|~}wy~ty~wy~t{|o~}o{|y~|t{|y~|px~e{|y~|qy~}p{|y~|e{|y~|gx~py~}r{|y~|ry~}k{|y~|e{|y~|j{|y~|y{}y~}i{"
- "|y~|i{|y~|x{}w~wy~}s{|y~|w{|y~|{y~}qx~p{}y~|q{|y~|gx~p{}y~|r{|y~|v{|y~}c{|y~}jy~}k{}y~r{|y~|m{}y~y{}y~|o{}y~{|~"
- "}vy~{|y~|ox~y{|y~|gy~}h{|x~c{}~}a{}~|d{|y~| xy~|u{|y~m{}y~sy~}o{|y~e{|y~s{}~}o{|y~_y~|i{|y~s{}~}n{}y~t{}~}j{|y~"
- "d{|y~h{}y~|y~}d{|y~m{}y~ty~|u{|y~r{}y~t{}~}o{|y~s{}y~n{}y~sy~}p{|y~s{}~}k{}y~b{|}w~i{}y~g{}~}t{}~}ly~|y{}y~m{}~"
- "}{}~}y{|~}{}~}l{|w~|h{}~}y{}y~h{|y~}d{}y~|d{}~}d{|y~}|m{}t{|}x~}|V{~}w{|x~v{~|r{|y~ty~|l{|y~t{|y~|o{}y~v{}y~m{|"
- "y~s{}y~m{}y~}f{}y~t{}~}l{|y~y{}~}iy~|xy~}l{}~}t{}~}qy~}vy~}vy~}s{|y~x{|y~x{|y~|ny~|w{|y~|o{}y~x{|y~x{|y~|o{}y~s"
- "y~}p{|y~s{}y~iy~|j{|y~|ty~}o{|y~|ty~}k{|x~g{|y~|hx~q{|y~}r{}y~|x{}y~x{|x~r{|y~|ry~}o{}x~}x~}x~}px~p{}y~|t{}y~}]"
- "y~|^{|x~oy|yy~|y{|o{}y~|q{|x~oy|yy~|y{|M{}~}i{}y~w{~|h{|y~|f{}~}v{}~}n{|n~|S{}x~|{}~}Uy}y{}~}qy}p{|r~ky~}y{|y~}"
- "_{|~}Yy}x{}~}xy~|xy}8{}~| v{}~}t{}~}hy~y{}~| {{|r~iy~}y{|y~}jy~|w{|~|{|~}o{}~|s{|y~oy~v{|~|{|~}m{|y~j{|o~}o{|o~"
- "}o{|o~}o{|o~}o{|o~}o{|o~}s{|p~}jx~c{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|qx~r{|y~|w{|y~|{y~}qx~p"
- "{}y~|sx~p{}y~|sx~p{}y~|sx~p{}y~|sx~p{}y~|o{|x~|y~}my~}{}~}t{}y~|s{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|q{}y~r{|y~|jy~"
- "}i{|q~}m{}y~u{|x~|oy~|u{|y~my~|u{|y~my~|u{|y~my~|u{|y~my~|u{|y~my~|u{|y~ry~|u{|y~h{|y~e{|y~d{|y~d{|y~d{|y~_{|y~"
- "d{|y~d{|y~d{|y~i{|y~s{}y~n{}y~t{}~}o{|y~s{}y~o{|y~s{}y~o{|y~s{}y~o{|y~s{}y~o{|y~s{}y~U{|y~y{}~|x{}y~n{}~}t{}~}n"
- "{}~}t{}~}n{}~}t{}~}n{}~}t{}~}l{}~}y{}y~k{}y~sy~}m{}~}y{}y~:{|y~vy~|w{}~|s{|y~o{}~|s{|y~{|y~}y{|y~}{}~|s{|y~{|y~"
- "}t{}~|s{|y~o{}~|ky~f{}y~yy~}9k~}n{|y~y|~Q{|u~|y}u~r{}t~y}s~o{|s~}k{|s~|m{|y~sy~|ny~sy~ly~}wy~}j{|y~y|y~|ny~| F"
- "x~ uj~j{|~x{}y~ly~wy~|wy~u{|y~|u{|x~}~}R{|y~a{}y~K{}~| r{}~}h{}y~t{}y~i{|y~|g{}y~|^{}y~o{}n~}ey~}n{}y~t{|y~|jy~"
- "}iy~}t{|y~|ey~}8{|}w~}|mk~}n{|}v~}|hx}m{~}wy~|v{|y~x{|~}t{}n~|p{|y~|t{|y~}p{}y~|f{|y~|r{}y~|p{|y~|e{|y~|g{}y~|q"
- "y~}r{|y~|ry~}k{|y~|e{|y~|j{|y~|x{}y~}j{|y~|i{|y~|x{|x~}wy~}s{|y~|vy~}{y~}q{}y~|qx~p{|y~|g{}y~|qx~q{|y~|u{}y~|d{"
- "|y~}jy~}k{|y~|s{}y~|m{|y~|{y~}n{}y~{}~}v{}~y|y~|p{}y~|x{}y~gy~}hx~|c{}~}a{|~}d{|y~| y{|y~t{}y~m{}y~sy~|o{|y~|f{"
- "|y~|ty~}o{|y~|`y~|i{|y~|ty~}n{}y~t{}~}j{|y~d{|y~h{}y~{|x~e{|y~m{}y~ty~|u{|y~r{}y~t{}~}o{|y~|t{}y~n{}y~sy~|p{|y~"
- "|ty~}k{}y~_{|y~}j{}y~g{}~}ty~}l{}y~yy~}m{|y~{y~|y{|y~{y~}m{|y~y}y~h{|y~yy~|hx~by~}d{}~}d{}y~7{}~|y{|~}|~}x{}~q{"
- "|y~|v{|y~}l{|y~t{|y~|o{}~}v{}~}m{|y~|t{}y~my~|e{}y~t{}~}ky~|{y~|j{}y~w{}y~l{}~}ty~}qy~}vy~}vy~}s{|y~|y{|y~x{}y~"
- "my~|w{|y~|o{|y~x{|y~x{|y~|o{}y~sy~|p{|y~|t{}~}iy~|iy~}ty~|o{}y~s{}y~|lx~|g{|y~|h{|y~}rx~px~|y{}y~y{|x~|r{|y~|ry"
- "~}n{|r~}o{}y~|qx~r{}y~}^y~|_{|x~o{|y~|{y~|{}~}o{}y~|s{|x~o{|y~|{y~|{}~}Ny~}i{}y~w{~|h{|y~|f{|y~}|{|}y~|h{}y~L{|"
- "v~}T{|~xy~}|yx|x{~|U{}y~y{|y~}`{|~}Y{|~x{}~}x{|y~x{~|8{}~| v{}~}ty~}hy~y{}~| `{|y~}y{|y~|j{}~}v{~}y{|~}p{|y~ry~"
- "|p{}~|v{~}y{|~}mx~j{}n~|p{}n~|p{}n~|p{}n~|p{}n~|p{}n~s{}p~}j{}y~|d{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y"
- "~|k{|y~|r{|y~}r{|y~|vy~}{y~}q{}y~|qx~r{}y~|qx~r{}y~|qx~r{}y~|qx~r{}y~|qx~o{|x~y{|y~}n{}y~}~}sx~r{|y~|s{}y~|q{|y"
- "~|s{}y~|q{|y~|s{}y~|q{|y~|s{}y~|jy~}i{|s~}|l{}y~sy~}p{|y~t{}y~n{|y~t{}y~n{|y~t{}y~n{|y~t{}y~n{|y~t{}y~n{|y~t{}y"
- "~s{|y~t{}y~|i{|y~|f{|y~|e{|y~|e{|y~|e{|y~|`{|y~d{|y~d{|y~d{|y~i{|y~|t{}y~n{}y~t{}~}o{|y~|t{}y~o{|y~|t{}y~o{|y~|"
- "t{}y~o{|y~|t{}y~o{|y~|t{}y~ix|j{|y~|}~}w{}y~n{}~}ty~}n{}~}ty~}n{}~}ty~}n{}~}ty~}l{|y~yy~|k{}y~sy~|m{|y~yy~|9{}~"
- "}wy~|x{|y~q{}~}q{|y~q{}~}{~}w{|~y|y~q{}~}{~}t{|y~q{}~}q{|y~k{|y~f{|y~y|y~|9x|}y~}q|m{}~x}P{}w~}{}{v~|r{|t~y|}u~"
- "|n{}u~}i{|u~}l{|y~sy~|ny~|u{|y~ly~|w{}y~iy~y}y~m{|y~| G{|y~| ry~|xy~|f{|~x{}y~m{}~|wy~|wy~ty~}t{|w~Q{|y~|b{}y~"
- "K{}~| ry~|h{|y~|uy~}i{|y~|h{}y~|]x~ns|x~y|e{|y~}n{|y~|u{}y~|k{|y~|iy~}t{}y~e{}y~4{|}w~}|U{|}v~}|Ty~w{}~}v{}y~xy"
- "~sy~}ry~}p{|y~|t{|y~}p{|x~p{|r{|y~|s{|x~o{|y~|e{|y~|g{|x~qy~}r{|y~|ry~}k{|y~|e{|y~|j{|y~|w{}y~}k{|y~|i{|y~|wx~|"
- "wy~}s{|y~|v{|y~|y~}q{|x~r{}y~|p{|y~|g{|y~}r{}y~|q{|y~|u{|y~}d{|y~}jy~}k{|y~}sx~ky~}|y~|n{|y~|y~|v{}~y}y~p{|y~}w"
- "{|y~}hy~}i{}y~|b{}~}a{|y~d{|y~| y{|y~tx~m{}y~|u{|y~|ny~}ey~}u{|y~}ny~}`y~|hy~}u{|y~}n{}y~t{}~}j{|y~d{|y~h{}y~y{"
- "|x~f{|y~m{}y~ty~|u{|y~r{}y~t{}~}ny~}ty~}n{}y~|u{|y~|oy~}u{|y~}k{}y~^{}~}j{|y~g{}y~u{|y~}l{|y~y|y~|ly~|y~wy~|y~|"
- "mx~yy~}hy~y|y~hx~a{}y~d{}~}d{}~}6u~y{}y~}y~|py~|v{}y~}l{|y~t{|y~|o{}~}vy~|ly~}ty~}n{|y~|e{}y~t{}~}k{}~y}y~iy~}v"
- "y~|m{}y~ty~}qx~w{|x~w{|y~|ry~}y{|y~x{}~}my~|w{|y~|o{|y~x{|y~x{|y~n{}y~|u{|y~|oy~}ty~}iy~|i{}y~u{|y~ny~}s{|y~}m{"
- "}y~|f{|y~|g{}y~|t{}y~|p{|w~y}y~y}x~}q{|y~|ry~}ly}x~y}|n{|x~r{}y~|q{}y~}_y~|`{|x~mx~|y~|}y~|n{}y~|u{|x~mx~|y~|}y"
- "~|Ny~}i{|y~|x{~|h{|y~|fp~|i{}y~d{}~}d{|x~|S{~}x{}u~|y{}~S{}y~xy~}a{|~}X{~}y{|~|w{}~|{}~7y| u{}y~ty~}hy~y{}~| a{"
- "|y~}y{|y~|j{|y~v{}~x{|~}p{}~|s{}~|p{|y~v{}~x{|~}n{}y~|jy~}ry~}py~}ry~}py~}ry~}py~}ry~}py~}ry~}py~}ry~}ty~}ty~}j"
- "{|x~p{|p{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|rx~q{|y~|v{|y~|y~}q{|x~r{}y~|r{|x~r{}y~|r{|x~r{}y~"
- "|r{|x~r{}y~|r{|x~r{}y~|p{|x~w{|y~}o{|w~s{}y~|r{|y~}sx~p{|y~}sx~p{|y~}sx~p{|y~}sx~iy~}i{|y~w|h{}y~s{}~}p{|y~tx~n"
- "{|y~tx~n{|y~tx~n{|y~tx~n{|y~tx~n{|y~tx~s{|y~tx~}hy~}ey~}dy~}dy~}dy~}`{|y~d{|y~d{|y~d{|y~hy~}ty~}n{}y~t{}~}ny~}t"
- "y~}ny~}ty~}ny~}ty~}ny~}ty~}ny~}ty~}j{|x~iy~}~}vy~}n{}y~u{|y~}n{}y~u{|y~}n{}y~u{|y~}n{}y~u{|y~}ky~y|y~j{}y~|u{|y"
- "~|ly~y|y~7y~}xy~|y{|y~|py~}s{|y~|py~}s{|y~|py~}s{|y~|py~}s{|y~|k{|y~ey~y}y~6{|y~}b{|x~|O{}y~}y{}{|}y~|p{|w~}{|}"
- "{}w~}l{}v~g{}w~}k{|y~sy~|n{}y~uy~}m{|y~v{|y~|j{}w~}l{}y~| Gx~|u{}|Px|O{|y~x{|y~j{|w{|~x{}~}n{|y~v{}~|x{|y~t{}y"
- "~}t{}x~Py~|by~}K{}~|dx|Jx|f{|y~fx~v{}y~h{|y~|i{}y~|f{|s{}y~}f{}y~l{|t{|x~l{}y~ux~j{}y~h{}y~|v{|x~f{|y~}hx|dx|a{"
- "}v~}Xv~}|bx|m{}~|x{|y~}x{|x~{|y~|t{|y~|r{}y~p{|y~|t{}y~|o{}y~}s{|~|r{|y~|t{|x~|o{|y~|e{|y~|f{}y~}ry~}r{|y~|ry~}"
- "k{|y~|e{|y~|j{|y~|v{}y~}l{|y~|i{|y~|oy~}s{|y~|uv~}p{}y~}t{}y~}o{|y~|f{}y~}t{}y~}p{|y~|t{}y~|o{}r{}y~|jy~}j{}y~|"
- "u{|y~}k{}y~}y~ly~}y~u{|w~}px~u{|y~|iy~}j{}y~}a{}~}`y~|e{|y~| y{|y~|v{}x~m{}x~ux~m{}y~|f{}y~u{}y~}n{}y~|ay~|h{}y"
- "~u{}y~}n{}y~t{}~}j{|y~d{|y~h{}y~x{|x~g{|y~m{}y~ty~|u{|y~r{}y~t{}~}n{}y~|v{}y~|n{}x~ux~n{}y~u{}y~}k{}y~^y~}j{|y~"
- "g{|y~|v{}y~}ky~y}y~kw~}w{}w~m{}y~|y{|y~}i{}~}y~}i{}y~|a{}y~d{}~}d{}~}5{}y~}w{|y~}|o{}y~w{|w~l{|y~}u{}y~n{}~}w{}"
- "y~k{}y~|v{}y~|my~|e{}y~t{}~}k{|w~}j{}y~u{}~}m{}y~|v{}y~}q{}y~|x{}~}~|x{}y~q{}y~|{|y~y{|y~|my~|w{|y~|ny~}y{|y~x{"
- "}y~n{}x~ux~n{}y~|v{}y~|iy~|i{|y~}vy~}o{|y~|rx~n{|y~}e{|y~|fx~|v{}y~}n{|}q~|p{|y~|ry~}j{}y~j{}y~}t{}y~}o{}~}_y~|"
- "`{|y~ks~|l{}~|u{|y~ks~|My~}hx~x{~|h{|y~|gx~|}x~}|}y~|j{}y~d{}~}c{|x~S{|~}xy|}y|x{}~|R{}~|x{}~a{|}|X{|~}p{}~| /{"
- "}y~|v{}y~}hy~y{}~| a{|~|x{}~|i{}~|vr~|s{|~}s{}~|o{}~|vr~|q{}y~}j{|y~|r{}y~q{|y~|r{}y~q{|y~|r{}y~q{|y~|r{}y~q{|y"
- "~|r{}y~q{|y~|r{}y~u{|y~|ty~}i{}y~}s{|~|p{|y~|e{|y~|e{|y~|e{|y~|a{|y~|e{|y~|e{|y~|e{|y~|k{|y~|t{|x~}q{|y~|uv~}p{"
- "}y~}t{}y~}p{}y~}t{}y~}p{}y~}t{}y~}p{}y~}t{}y~}p{}y~}t{}y~}p{|x~u{|y~}o{}y~}t{}y~}p{}y~|u{|y~}o{}y~|u{|y~}o{}y~|"
- "u{|y~}o{}y~|u{|y~}iy~}i{|y~|e{}y~sy~}p{|y~|v{}x~n{|y~|v{}x~n{|y~|v{}x~n{|y~|v{}x~n{|y~|v{}x~n{|y~|v{}x~s{|y~|v{"
- "}w~|i{}y~|f{}y~|e{}y~|e{}y~|e{}y~|a{|y~d{|y~d{|y~d{|y~h{}y~|v{}y~|n{}y~t{}~}n{}y~|v{}y~|n{}y~|v{}y~|n{}y~|v{}y~"
- "|n{}y~|v{}y~|n{}y~|v{}y~|j{|x~i{}y~}v{}y~|n{|y~|v{}y~}n{|y~|v{}y~}n{|y~|v{}y~}n{|y~|v{}y~}k{}~}y~}j{}x~ux~k{}~}"
- "y~}7{|y~}|{y|{|}y~|o{|y~}|w{|}y~|o{|y~}|w{|}y~|o{|y~}|w{|}y~|o{|y~}|w{|}y~|j{|y~e{|w~|6y~}`x~I{|~hx|y{|}yw|jw~|"
- "f{}x~j{|y~sy~|mx~w|x~l{}~}uy~}j{}w~|k{}y~}| I{|x~}|{y|y~|Py~}O{|~}x{|~}j{}~|y{|~{|}y~|n{}~|v{|y~x{}~}sx~}|y{|}"
- "u~Q{}~}by~|K{}~|d{}y~Jy~}f{}~}f{|y~}|{|}y~}kw|y~w|m{}y~}s|my~}w|}w~e{}y~ly~}w|}x~}l{|x~|y{|x~|jy~}h{|x~}|{y|x~|"
- "m{~}w|}y~}g{}y~d{}y~_{|}y~}Xy~}|_y~}m{|~}w{|u~y}w~|sx~q{|y~|q{|y~t|}y~}m{}x~}v|}y~|r{|y~u|y}x~}n{|y~q|n{|y~|e{}"
- "x~}v|}x~}r{|y~|ry~}k{|y~|e{|y~|j{|y~|u{}y~}m{|y~q|r{|y~|oy~}s{|y~|u{|w~}o{}x~}|{y|}x~n{|y~|e{}x~}|{y|}x~o{|y~|t"
- "{|y~}oy~}x|{y|x~}iy~}j{|x~}w|}x~j{|w~}l{}x~}tw~|q{}y~|t{}y~iy~}k{|x~o|l{}~}`{}~}e{|y~| xx~|y{|}w~m{}w~|y{|x~|lx"
- "~}|yy|}|mx~|y{|}x~}m{}y~}|x{|}~}jy~|h{|x~|y{|}x~}n{}y~t{}~}j{|y~d{|y~h{}y~w{|x~h{|y~m{}y~ty~|u{|y~r{}y~t{}~}mx~"
- "|y{|}y~}m{}w~|y{|x~|mx~|y{|}x~}k{}y~g{}~}|x{|}y~|j{|y~}gx~|y{|}x~}k{}w~}k{}x~}w{|x~}n{|y~|w{}y~|j{|w~|j{}y~|`{}"
- "y~d{}~}d{}~} w{|y~}|{|y~}y~}m{|x~}|y{|}y~}n{|~}x{|y~|jx~|y{|}y~}l{}y~}|x{|y}m{}y~t{}~}jw~|jy~}u{|y~|n{}x~}|{|}w"
- "~y|s{|x~|{|y~|~}|{}y~}px~y|y~|}y~}ly~|vy~}n{}y~|{|y~y{}y~|n{}w~|y{|x~|mx~|y{|}y~}h{}y~|i{}y~}y{|x~nx~q|}y~|ox~r"
- "|m{|y~|iw|x~}x{}y~}w|o{|y}x~y}|n{|y~|ry~}j{}y~i{}x~}|{y|}x~m{|^y~|_{|iu~|j{|s{|iu~|Ly~}h{|y~}|{~|{|}mx|y~}t|o{|"
- "y~r{}~}j{}y~d{}~}b{|y~|S{|~}r{}~|Py|w{}:{|~}r{}~|=k|!{}x~}|{|}w~y|jy~y{}~| ay|w{}h{|~}uv|}~}|ry~s{}~|o{|~}uv|}~"
- "}|q{|y~}ix~q{|y~|rx~q{|y~|rx~q{|y~|rx~q{|y~|rx~q{|y~|r{}y~q{|y~|vx~sy~}r|q{}x~}v|}y~|p{|y~q|n{|y~q|n{|y~q|n{|y~"
- "q|j{|y~|e{|y~|e{|y~|e{|y~|k{|y~}u|}x~}p{|y~|u{|w~}o{}x~}|{y|}x~n{}x~}|{y|}x~n{}x~}|{y|}x~n{}x~}|{y|}x~n{}x~}|{y"
- "|}x~ox~s{|y~}pv~}|{y|}x~o{|x~}w|}x~n{|x~}w|}x~n{|x~}w|}x~n{|x~}w|}x~hy~}i{|y~|e{}y~{x|y{|}y~|ox~|y{|}w~mx~|y{|}"
- "w~mx~|y{|}w~mx~|y{|}w~mx~|y{|}w~mx~|y{|}w~rx~|y{|}y~|}y~}|x{|}~}qx~}|yy|}|m{}y~}|x{|}~}m{}y~}|x{|}~}m{}y~}|x{|}"
- "~}m{}y~}|x{|}~}j{|y~d{|y~d{|y~d{|y~gx~|y{|}y~}m{}y~t{}~}mx~|y{|}y~}lx~|y{|}y~}lx~|y{|}y~}lx~|y{|}y~}lx~|y{|}y~}"
- "i{|x~i{|x~|y{|}y~}lx~|y{|}x~}mx~|y{|}x~}mx~|y{|}x~}mx~|y{|}x~}k{|w~|j{}w~|y{|x~|k{|w~|6{|q~|m{|q~|m{|q~|m{|q~|m"
- "{|q~|i{|y~dw~5{}~_{}~}I{}~c{}~d{|y~|dy~|j{|y~sy~|m{|s~|ly~}u{}~}j{|w~i{}m~ S{|s~}Oy~}O{}~|x{}~|j{}q~}n{|~}t{}y"
- "~}x~q{}s~}{|y~}R{|y~c{|y~J{}~|dx~Jy~}fy~|e{}t~}k{}q~}no~|nq~}d{}y~lq~|j{|s~|j{|y~|g{|r~|ls~}f{}y~dx~\\y|X{}|]y~"
- "}ly~|w{|}y~}|{}~}|r{|y~}py~}q{|p~}k{|q~}q{|p~}|m{|o~|o{|y~|d{|p~}q{|y~|ry~}k{|y~|e{|y~|j{|y~|t{}y~}n{|o~r{|y~|o"
- "y~}s{|y~|t{}x~}n{}r~}m{|y~|d{}r~}n{|y~|s{}y~|pp~}hy~}i{|r~}hw~|l{}x~}tw~|r{|y~}s{|y~}jy~}k{}l~|m{}~}`{|y~e{|y~|"
- " x{|t~}|y~m{}y~|t~|j{}s~|m{|t~|}~}l{}r~}jy~|g{|t~|}~}n{}y~t{}~}j{|y~d{|y~h{}y~v{|x~i{|y~m{}y~ty~|u{|y~r{}y~t{}~"
- "}m{|s~}l{}y~|t~|l{|t~|}~}k{}y~g{}r~}h{}u~k{|t~|}~}k{|w~|k{|x~|w{|x~|ny~}u{}y~iw~io~h{}y~d{}~}d{}~} v{}t~{}x~}o{"
- "|q~}ly~}y|y~}i{|s~}j{}s~}m{}y~t{}~}j{}x~j{}y~sy~}n{}~}t~}x~}r{|u~|{t~o{|q~ky~|vw~}ot~}x~}m{}y~|t~|l{|s~}g{|v~j{"
- "}t~|o{|k~}p{|o~|n{|y~|is~y{|t~}l{}y~k{|y~|ry~}j{}y~h{}r~}Ny~|Kw~|Lw~|Ky~}g{|r~n{|o~}n{|p{|i{}y~d{}~}ay~|R{|y~}y"
- "|{y|}y~| a{|y~}y|{y|}y~|<k~}\"{}~}t~}x~}jy~y{}~| Gy~o{|~}r{}~|ty~|ny~o{|~}q{|y~}i{|y~}py~}s{|y~}py~}s{|y~}py~}s"
- "{|y~}py~}s{|y~}py~}s{|y~}py~}w{|y~|so~}q{|q~}o{|o~|o{|o~|o{|o~|o{|o~|k{|y~|e{|y~|e{|y~|e{|y~|k{|o~|o{|y~|t{}x~}"
- "n{}r~}l{}r~}l{}r~}l{}r~}l{}r~}n{|~q{|~p{}~y|r~}m{|r~}l{|r~}l{|r~}l{|r~}gy~}i{|y~|e{}y~{}t~}n{|t~}|y~m{|t~}|y~m{"
- "|t~}|y~m{|t~}|y~m{|t~}|y~m{|t~}|y~r{|s~|y{}r~|p{}s~|l{}r~}l{}r~}l{}r~}l{}r~}j{|y~d{|y~d{|y~d{|y~fs~}l{}y~t{}~}m"
- "{|s~}k{|s~}k{|s~}k{|s~}k{|s~}Rq~}k{|t~|}~}m{|t~|}~}m{|t~|}~}m{|t~|}~}jw~i{}y~|t~|iw~3{|}w~}|i{|}w~}|i{|}w~}|i{|"
- "}w~}|i{|}w~}|g{|~}d{}y~} r{|~|Iy~|dy~|d{|}cy|i{|y}sy}|k{}w~}k{|y~|u{|y~ix~}gy}p~ Q{|}x~}|Ny~}Oy~wy~h{|y}v~}|my"
- "~r{|x~}o{|}w~}|x{}y~}Ry~|d{}~}J{}~|dy~}Jy~}g{|y~c{|}x~}|j{}q~}no~|m{|}w~y}|c{}y~l{|y}w~y}f{}w~}hx~dy}x~y}|k{|}w"
- "~}|e{}y~dy~} ry~}l{|y~c{}y~|p{}y~q{|r~}|h{|}w~}|o{|s~y}|k{|o~|o{|y~|b{|}w~y}|o{|y~|ry~}k{|y~|e{|y~|j{|y~|s{}y~}"
- "o{|o~r{|y~|oy~}s{|y~|t{|x~}l{|}x~y}|l{|y~|b{|}v~}m{|y~|ry~}o{|y}w~y}|gy~}g{|}w~}|g{|x~k{|x~|t{}x~qx~q{}y~|ky~}k"
- "{}l~|m{}~}_y~|f{|y~| v{}x~}|{|y~m{}y~{|}x~}|h{|}x~y}|j{}x~}|{}~}k{|}w~y}|iy~|e{}x~}|{}~}n{}y~t{}~}j{|y~d{|y~h{}"
- "y~u{|x~j{|y~m{}y~ty~|u{|y~r{}y~t{}~}k{|}x~}|k{}y~{|}x~}|i{}x~}|{}~}k{}y~f{|y}w~}|fy}w~j{|}x~}y{}~}jx~}ix~ux~|o{"
- "}y~t{|y~}j{|y~}io~h{}y~d{}~}d{}~} u{|}x~}|y{}y~}o{|y~|}w~}|k{|v~}f{|}x~}|h{|}w~y}|m{}y~t{}~}j{|y~|jy~}s{}y~n{}~"
- "}{}x~}y{}~}|q{|}y~}|x{}x~}l{}v~}|jy~|v{|}y~}n{}s~|l{}y~{|}x~}|i{|}x~}|e{|}x~i{|}x~}m{}j~p{|o~|n{|y~|is~y{|t~}l{"
- "}y~k{|y~|ry~}j{}y~f{|}x~y}|My~|Jy~|Jy~|Jy~}f{|}u~}n{|o~}O{}y~d{}~}h{}|w{}y~O{|}v~}| ]{|}v~}|:k~}\"{}~}{}x~}y{}~"
- "}|jy~y{}~| H{}~|o{|~|s{|~}t{|t~|t{}~|o{|~|q{}y~h{}y~|p{}y~s{}y~|p{}y~s{}y~|p{}y~s{}y~|p{}y~s{}y~|p{}y~s{}y~|p{}"
- "y~w{}y~ro~}o{|}w~}|m{|o~|o{|o~|o{|o~|o{|o~|k{|y~|e{|y~|e{|y~|e{|y~|k{|s~y}|m{|y~|t{|x~}l{|}x~y}|i{|}x~y}|i{|}x~"
- "y}|i{|}x~y}|i{|}x~y}|U{|~}x{|}x~y}|j{|}w~}|i{|}w~}|i{|}w~}|i{|}w~}|fy~}i{|y~|e{}y~{|}w~}|k{}x~}|{|y~k{}x~}|{|y~"
- "k{}x~}|{|y~k{}x~}|{|y~k{}x~}|{|y~k{}x~}|{|y~p{}x~}|v{|}w~y}|n{|}x~y}|j{|}w~y}|j{|}w~y}|j{|}w~y}|j{|}w~y}|i{|y~d"
- "{|y~d{|y~d{|y~e{|}x~}|k{}y~t{}~}k{|}x~}|h{|}x~}|h{|}x~}|h{|}x~}|h{|}x~}|R{}~|{|}x~}|i{|}x~}y{}~}l{|}x~}y{}~}l{|"
- "}x~}y{}~}l{|}x~}y{}~}j{|y~}i{}y~{|}x~}|h{|y~} e{|~} V{| .{|~ p{}~}dy~|1{|y~2{}~} U{|y~ ^{}~} ){|y~| {"
- "}y~} 6{}~}_{}~}f{|y~| -y~}6{|y~ A{}y~Z{}~} j{|y~I{}y~d{}~}d{}~} \\{|y~a{|}y|*{}~}j{|y~|O{}~}F{|y~K{|}y~y|j{}"
- "y~ Ry~}c{|~| r{}~}h{}t~|K{| U{| '{}~}^y~y{}~|O{|~| wy|cy}|st|sy|ay~} ^{|~| 9{| Y{|~| 8y| Q{|y~h{}"
- "y~`{|y~ d{|~} c{|~ oy~e{}~}0{}~}2y~| U{}~} ]{}y~|r{|y} 7{}y~ y{}y~| 7{}~}_{|y~f{|y~| .{|y~|6{|y~ "
- "A{}y~Z{}~} j{}~}I{|y~d{}~}dy~} \\{|y~ g{}~}j{|y~|O{}~}F{|y~J{|y~h{}y~ Ry~}b{~| r{}~}h{|y}x~}| ){}~}^y~y{"
- "}~|N{}~ 'y~} ]y~ p{~} g{}~}h{}y~`{}~} h{|}|{}~} c{|~ o{}~}fy~/{}~ c{}~ [{}y~}|v{|}y~} "
- " 7x~ x{}y~| 8{}v~M{|v~| .{}y~5{}~} A{}y~Z{}~} k{|y~|I{|y~}e{}~}e{|y~| \\{|y~ g{}~}j{|y~|O{}~}F{|y~J{|y~h{}y"
- "~ Ry~}b{~| r{}~} i{}~}*{}~| (x~uy| e{}~| q{}~ h{|y~|h{}y~a{|y~| h{|y~|y~| c{|~ ny~"
- "g{}~} M{|}q~| 9y|}y~| 1{}w~}M{|v~| 6y}|x{|}y~|6{|y~} A{}y~Z{}~} l{|x~G{}w~}h{}~}h{}w~} [{|y~ g{}~}j{"
- "|y~|O{}~}F{|y~J{|y~h{}y~ Ry~}b{~| r{}~} i{}~}-x}y~ '{}x~w|y~| e{}~| qy~ i{|x~g{}y~b{|x~ f"
- "{}w~ e{|y}w~}| 8{|w~} ({|n~} m{}s~}7{|w~ @{}y~Z{}~} nv~|F{|y}~}h{}~}h{}~y}| Z{|y~ g{}~}j{"
- "|y~|O{}~}F{|y~J{|y~h{}y~ Rx| W{}~} i{}~}-{}y~}| &{}s~| i{|~y}y~ t{|~y}y~ kv~|g{}y~dv~| ex"
- "} s{|y~}| '{|n~} m{|y}w~}|6{|y~} ?{}y~Z{}~} nx~}|-{}~} B{|y~ g{}~}j{|y~|O{}~}F{|y~J{|y~"
- "h{}y~ q{}~} -{|}x~}| f{}y~}| t{|x~}| kx~}|f{}y~dx~}| "
- " :{}~} I{" };
- // Define a 52x64 font (large sans).
- static const char *const data_font_large[] = {
- " "
- " -{| "
- " [{|x}|Dw}|Pw}| @{}v~} C{|w}|Ew}|Pv}| xv|Ev|Pu| kv|Dw|P{|v} 6{|w}|E{|x}|P{"
- "|w}| pw}| "
- " G{|w~}F{}w~P{}v~}Q{}w~}|w{|x~X{|v~vv~|U{|r~| D{}w~F{}w~P{}u~S{|v~|wv~}V{}w~|G{|w~|Q{"
- "|u~|Sv~}w{}w~}\"{|}x~}|v{|x~W{}w~|F{}w~Q{|u~}Q{}x~}|v{|x~X{}w~}w{|v~ G{}w~F{}w~|Q{}u~Rv~|w{}w~}O{}w~ "
- " "
- " E{|w~|H{}w~P{}t~}Ss~}|y{}x~X{|v~vv~|V{|p~ Cw~}H{|w~|Q{|t~}T{|v~|wv~}U{}w~Gw~}Q{|s~|Tv~}w{}w~}#{|s~}|{|x~"
- "}V{}w~|H{}w~Ps~}St~}w{}y~}X{}w~}w{|v~ Fw~}H{|w~|Q{|t~}Sv~|w{}w~}P{|w~| "
- " D{|w~|J{|w~|Q{|x~}{w~|U"
- "{}l~}X{|v~vv~|Vw~}x{}x~} D{|w~|J{|w~|Q{|w~{}x~}U{|v~|wv~}T{}x~}Iw~}Pw~y|w~Tv~}w{}w~}#k~|U{}w~I{|w~|Q{}x~|{}x~|U"
- "{}r~|{|x~}X{}w~}w{|v~ Ew~|Iw~}Pw~|}x~}Tv~|w{}w~}Q{|w~| M{| "
- " q{}w~Jw~|Q{|x~}xw~Ux~}y{|}t~}W{|v~vv"
- "~|W{|x~|v{|x~| D{|w~|Kw~}Pw~x{}x~|V{|v~|wv~}S{}x~}K{}x~}P{}x~|y{|x~}Uv~}w{}w~}${|x~|y{|s~}S{}x~}K{|w~|Q{}x~}xw~"
- "Ux~}{|}r~W{}w~}w{|v~ E{|w~|K{}x~}Pw~|y{}x~|Uv~|w{}w~}Qw~| O{}v~} "
- " s{}x~}L{}x~|Pw~vw~W{|x~v{|}w~}"
- "V{|v~vv~|W{}y~}tx~} C{|w~L{}x~}P{}x~|w{}x~|W{|v~|wv~}R{}x~|M{}x~}P{}x~|w{|x~}Vv~}w{}w~}${|x~v{|}w~|Q{}x~}Lw~|Q{"
- "|x~}vw~W{|x~w{|t~|W{}w~}w{|v~ D{|w~L{|x~}P{}x~|w{}x~|Vv~|w{}w~}R{}x~} P{|r~| Y{}w~| "
- " A{}x~|N{}x~}P"
- "{}x~u{|x~}\"v|vv|V{}y~}t{}y~} B{}x~}N{|x~}P{}x~|u{}x~Vv|vv|P{}x~|O{|x~}P{}x~|u{|x~}Wv|uv| D{}x~|N{}x~|Q{|x~}tx~"
- "}X{|x~u{|}y~}|Vu|vv| C{|x~}N{|w~P{|x~|u{}x~Vv|vu|S{|x~} Op~| Zv~| "
- " ;v~ u{|v~ 6{|y}|N{|y}|P{|x}s{|x} I"
- "{}y~}t{}y~} Aw|Nw|Ow|sw| Qw|Nw|Pw|rx| 5{|x}N{|x}O{|y}|s{|y}| {{|y}| Dv|@v|Rv| C{}x~}x{|w~ Hu|@v|Rw| yv}@v}R"
- "{|w} lv|@v|Rv| 8v}@v}|S{|w} m{}w~| E{|y~x}| ;{|w~} "
- " vv~| J{}y~}t{}y~} e{}w~}B{|w~}Rv~| Dx~|v{|x~| H"
- "v~A{}w~|S{|w~} {{|w~}B{}w~|S{|v~| Ay|sx|Y{}w~|B{|w~}Rv~ 8{|w~}B{|w~}Rv~| o{|w~} ?y}~}| *x~ J{"
- "|y~| b{}x~|T{|x~} L{|q~} y{}q~| H{|w~} xw~} `{|w~| {{|}t~)w~}Cv~Lv~Tw~}Dv~ G"
- "{|x}w~}Tw~|U{|v~y}| 1{|y}v~y}| cv~y} p{|y}x~y}| {{v|vv| 3{}w~| I{|x~|v{|x~| "
- " %{| 5{|y}w~y}|U{}w~|Cv~R{}v~}Q{|}y~}|ux~|Y{|v~|wv~}W{|x~t{}y~} H{|w~}C{}w~|Ru~|S{}w~}w{|v~W{}w~|D{|w~}R"
- "t~S{|v~vv~|X{|v~}K{}w~}ux~X{}w~C{|w~}R{}v~}Q{|}y~}|ux~|Y{|v~vv~| J{|w~}D{|w~}R{}u~Rv~|w{}w~}N{|w~}Zw~}Hv~}w{}w~"
- "} N{|u~} ,{|y~} Ix|Tx|kw| Ru| 6{|y~|Yv|fx}|Zu| o{|w~Rw~|Hx| Xu| vt|Ns| =t| xt|Ot| [u| ds| kr|"
- " Qt| ut| ts| S{|q~} y{}q~| G{}w~| yw~} `{|w~|!{}q~)w~}Cv~Lv~Tw~}Dv~ I{|r~}Tw~|U{|r~} 5{|}o~| yr| "
- " ps~} t{|p~| kt| is| s{|y} r{|x}| rx}| bt| lu|S{|v~vv~|!{|y}w~y}| :{|l~|Qx| u{|y}w~}|Q{|x}w~y}|K{|w~| "
- " 9y|y}w~|O{|y}w~}|)y|x}dw~|hy|x}dw~|ly|x}y~y}|e{}x~| 6w~}x{}x~} us| lt|Nt|Nt|Nt|Nt| ut|p{}~| 9{|}o~|V{"
- "}w~D{}w~R{|t~|S{|u~}vx~|Y{|v~|wv~}W{}y~}t{|x~ G{|w~}E{|w~}R{}t~S{}w~}w{|v~V{}w~E{|w~}R{}t~}T{|v~vv~|W{|v~}s{|y}"
- "X{}u~}w{|x~Ww~}Dv~R{|t~|S{|u~}w{|x~X{|v~vv~| I{}w~|Ew~}R{|t~}Sv~|w{}w~}Nw~}Yw~}Hv~}w{}w~} O{|s~|cW} i{}y~|"
- "\"{|}L{|u~}|Z{|}v~}|p{}u~}V{|} /g| ({}r~}| v~}R{}x~}vw~}R{|x}|t{|x}|V{|y~|\\{|}t~|i{}x~|]{}q~}|O{}x~}Iw~|R{|"
- "w~Hx~ *{|w~V{|}s~}|Sy|y}v~}T{|}q~}|V{|y}p~}|L{|u~}\\{|g~}T{}q~y}|_{}c~}[{|}q~}|U{|}r~}| b{|}q~| w{}v~}X{}k~y"
- "}|R{|y}p~}|b{}m~x}y|W{}c~|`{}e~Y{|}o~}|a{}w~}hv~|Y{}w~}M{}w~}W{}w~}k{|u~}b{}w~}V{}t~|h{}t~|h{}u~}jv~^{|}p~}|Z{}"
- "m~y}|U{|}p~}|\\{}m~y}y|S{|}o~}y|bZ~g{|v~h{}w~}i{|v~|d{|v~|rv~|l{|v~}kv~|p{|v~|i{}v~g{}v~fv~}g\\~]{|q~}Uw~}I{}q~"
- "|P{|w}| w{}w~ yw~} `{|w~|\"o~)w~}Cv~Lv~Tw~}Dv~ J{|q~}Tw~|U{|q~} 7{}l~}\"y}p~y} sr~} v{}n~}R{}v~}V{"
- "}c~|_{}d~}^{|}p~}|R{|v~Y{}^~|iv~}r{|v~qv~}a{|}p~}| x{}x~} s{}w~ s{}w~| f{|}r~}|-{}w~|i{|v~({|q~}|W{|v~vv~|Ty|u"
- "}y|U{|}o~| ly|u}y|U{|l~|T{|}v~}| {|}p~|T{}p~}|N{|w~} yy|}m~} N{|r~|P{}q~|0{|y}t~|f{}x~}l{|y}t~|f{}x~}l{}p~}h{}"
- "x~}%{}v~}N{}v~}N{}v~}N{}v~}N{}v~}Q{|p~W{}\\~}b{|y}p~}|^{}c~|a{}c~|a{}c~|a{}c~|X{}w~}M{}w~}M{}w~}M{}w~}Z{|m~x}y|"
- "Z{}u~}jv~^{|}p~}|V{|}p~}|V{|}p~}|V{|}p~}|V{|}p~}|\"{|}q~y}t{}x~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}g{}"
- "v~fv~}c{}w~}J{|l~}Vw~}F{}w~|R{}x~|w~Ss~}x{|x~X{|v~|wv~}W{}y~}t{|x~ F{|w~|Fw~}R{|x~y}x~}T{}w~}w{|v~U{}x~}Fw~}R{|"
- "w~{w~|U{|v~vv~|V{}v~|x{|}v~Y{|s~}x{}x~W{|w~}F{}w~Qw~|w~Ss~}x{|x~X{|v~vv~| H{}w~F{}w~Qw~|}x~|Tv~|w{}w~}O{}w~Xw~}"
- "Hv~}w{}w~} P{|q~c{}Y~} ix~!y~|N{}r~}\\{}r~}s{|q~|Y{|y~} 5{|}e~} *{}m~|\"v~}R{}x~}vw~}Rw~|tw~|V{|y~|]{}q"
- "~}k{|w~^{|l~|Q{}x~}J{}w~P{}x~}Ix~ *{}x~}W{}n~|Zy|}p~}W{|}k~}Z{}i~|Nt~}\\{|g~}V{}l~|`{}c~}\\{}l~|X{}n~} e{|l~"
- "|Ty|y}u~y}|Rt~X{}g~}V{|}j~}d{}g~}|Z{}c~|`{}e~\\{|}i~}|d{}w~}hv~|Y{}w~}M{}w~}W{}w~}l{|u~}a{}w~}V{}t~}i{|s~|h{}t~"
- "|kv~`{|k~}|\\{}i~}|Z{|k~}|^{}i~}|W{|h~}dZ~g{|v~h{}w~}hv~}d{}v~q{}w~}l{}u~kv~|o{}v~j{|v~|fv~}h{}v~f\\~]{|v~u}U{}"
- "w~Iu}v~|Qt~| w{}x~} {{w~} `{|w~|#{}o~)w~}Cv~Lv~Tw~}Dv~ Ov| s~x}|Tw~|U{|x}s~| 9{}j~}%{}j~| uq~| x{}l"
- "~}St~V{}c~|_{}d~}`{|}k~|T{|v~Y{}^~|iv~}r{|v~qv~}c{|k~}| {}v~} t{}w~ t{}u~| i{|l~-v~i{}w~|Xw}|R{|l~X{|v~vv~|W{|"
- "}o~}|X{|m~| p{|}o~}|X{|l~|U{}r~}!{|n~}U{}n~|Ow~} {{|}j~} N{|r~|R{|n~}1{|r~|g{|w~k{|r~|g{|w~k{}n~iw~$t~Nt~Nt~Nt"
- "~Nt~P{|r~V[~}d{|}j~}`{}c~|a{}c~|a{}c~|a{}c~|X{}w~}M{}w~}M{}w~}M{}w~}Z{|g~}|]{}t~|kv~`{|k~}|Z{|k~}|Z{|k~}|Z{|k~}"
- "|Z{|k~}|&{|k~}w{|w~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}fv~}h{}v~b{}w~}K{}j~}W{|w~|H{|w~|R{|x~}{|x~}U{|"
- "x~}|w~}|{}x~X{|v~|wv~}W{|x~t{}y~} E{}w~G{}x~}Qw~|{}x~|U{}w~}w{|v~Tw~}H{}w~Q{}x~|{|x~}U{|v~vv~|U{}v~|}t~}Y{}x~|{"
- "}w~y|x~}V{|w~|H{|w~|R{}x~|{|x~}U{}x~}|w~}{|x~}X{|v~vv~| G{}x~}H{|w~|R{}x~|yw~Tv~|w{}w~}P{|w~|Xw~}Hv~}w{}w~} "
- "P{}w~y|w~|d{|Y~| j{|y~}\"{}x~Oo~}_{|o~}u{|o~}Zw~| 8{}b~} ,{|j~}#v~}R{}x~}vw~}Rw~sw~U{|y~|^{}o~}lw~|_{}k~|Q"
- "{}x~}Jw~|P{|w~|Jx~ *w~|Xk~|[m~}X{}h~}[{}h~}P{}t~}\\{|g~}X{|j~|`{}c~}^{|i~}[{|j~ gi~|X{|}l~}|V{}t~|Y{}e~|Y{}f"
- "~}f{}d~}\\{}c~|`{}e~]{}e~}|f{}w~}hv~|Y{}w~}M{}w~}W{}w~}m{|u~|`{}w~}V{}s~|j{}s~|h{}t~}kv~b{|g~}]{}g~}]{|g~}_{}g~"
- "}Y{}f~dZ~g{|v~h{}w~}h{}v~dv~}q{}w~}lt~|m{|v~mv~}kv~}e{|v~|j{|v~|f\\~]{|w~}O{|w~|D{|w~|Rr~| ww~} w~} `{|w~|${|v~"
- "}|#w~}Cv~Lv~Tw~}Dv~ Ov~ !{|v~}Nw~|O{|v~} :{|u~}|w{|}v~|'{}i~| r{|}v~} y{}v~}|x{|}v~}U{}t~|W{}c~|_{}d"
- "~}a{}g~|V{|v~Y{}^~|iv~}r{|v~qv~}e{|g~}\"{}t~} u{}w~ u{}s~| >y~}P{|k~-{|w~}k{|w~}Ww~|S{|k~X{|v~vv~|Y{|}k~}|Z{|y~"
- "}y|xy|}w~| s{|}k~}|Z{|l~|V{}p~}\"{|y~}|w{|}w~|V{|}|u{|v~P{}x~} {{}h~} N{|~y}y|}x~|S{|v~}|y{|}w~}2{|w~y}x~|g{}x"
- "~|k{|w~y}x~|g{}x~|kx}|w{|}w~}k{}x~}%{}t~|P{}t~|P{}t~|P{}t~|P{}t~|P{}t~}W{|[~}e{}f~}b{}c~|a{}c~|a{}c~|a{}c~|X{}w"
- "~}M{}w~}M{}w~}M{}w~}Z{|d~}|`{}t~}kv~b{|g~}]{|g~}]{|g~}]{|g~}]{|g~}){|g~|{|w~|h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f"
- "{|v~h{}w~}f{|v~|j{|v~|b{}w~}L{|u~}|w{|}v~|W{|w~|Iw~}Qw~x{}x~|V{}y~}x{}s~|X{|v~|wv~}Vx~}v{|x~| D{}x~}I{}w~Q{}x~|"
- "xw~U{}w~}w{|v~T{|w~|J{|w~Q{|x~}x{|x~|V{|v~vv~|T{}q~}|Wx~|x{}s~T{|w~I{|w~|R{|x~}x{}x~|Vx~}x{}s~|X{|v~vv~| Fw~}J{"
- "|w~|R{|x~}x{|x~}Uv~|w{}w~}Q{|w~|Ww~}Hv~}w{}w~} Pw~}y{|x~}cY~ i{}y~|#{|w~}Qm~|`m~}w{|m~|\\{}v~| ;{}`~} -"
- "{|r~x}t~}$v~}R{}x~}vw~}S{|w~t{|x~}U{|y~|_{|w~}w{}w~|n{}x~}_{|t~w}u~|Q{}x~}K{}w~N{}x~}Jx~ +{|w~Xs~y}s~|\\m~}X{}"
- "f~\\{}g~}R{|s~}\\{|g~}Y{|i~|`{}c~|_{|s~w}s~}]{|s~x}s~ hr~}r~|[{|f~}Xs~}Y{}d~|\\{|c~}g{}b~|^{}c~|`{}e~_{|a~|g{"
- "}w~}hv~|Y{}w~}M{}w~}W{}w~}n{|u~|_{}w~}V{}s~}jr~|h{}s~|lv~c{|p~}q~}^{}f~}_{|p~}q~}`{}e~[{}q~}p~dZ~g{|v~h{}w~}h{|"
- "v~|f{|v~p{|v~m{|t~}m{}w~}m{|v~|m{}v~c{}v~jv~}e\\~]{|w~}Nw~}D{|w~|Sp~| ww~|!w~} `{|w~|${}w~}!w~}Cv~Lv~Tw~}Dv~ "
- " Ov~ !{}w~}Mw~|N{|v~ :{}v~|s{|v~V{|t}|V{|t~s}w~| p{|v~ {{|v~|t{|v~|Vs~}W{}c~|_{}d~}c{|d~|W{|v~Y{}^~|iv~"
- "}r{|v~qv~}f{|p~}q~}${}r~} v{}w~ v{}q~| ?y~}Ps~x}u~,v~k{}w~|Ww~|Su~}v|}w~X{|v~vv~|Z{}v~}y|wy|}v~}[{|}q{}x~} t{}"
- "v~}y|wy|}v~}&{}w~|x{|w~}#y|r{}x~}Kw~|R{|w~ {{}p~}v|x~} H{}x~|S{}w~t{}w~|3x|x{}x~|h{|x~}j{|}|x{}x~|h{|x~}`{|w~l{"
- "|w~$s~}Ps~}Ps~}Ps~}Ps~}Pr~W{}[~}g{|c~}c{}c~|a{}c~|a{}c~|a{}c~|X{}w~}M{}w~}M{}w~}M{}w~}Z{|b~}a{}s~|lv~c{|p~}q~}_"
- "{|p~}q~}_{|p~}q~}_{|p~}q~}_{|p~}q~}+{|p~}q~}w~|g{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}e{}v~jv~}a{}w~}Lu~r{"
- "|v~V{|w~J{}x~}Q{}x~|w{}x~Vx~|w{}u~}Vv|vv|U{}x~}x|}w~ Bw~|K{|w~|R{|x~}w{|x~}Vu|vv|S{|w~K{|w~|Qx~}v{}x~Uv|vv|T{|}"
- "t~}|Tx~|w{|u~|S{}x~}Jw~}Qw~vw~Vx~|w{}u~}Vv|vv| Dw~|Kw~|Qw~v{}x~|Vv|vv|Pw~|Vw~}Hv|uv| G{|t}|P{|t}|P{|t}|P{|t}|P{"
- "|t}|Lw~|xw~c{|[~} iy~}\"u~|S{|l~a{}l~|x{}l~]{}t~ ={|^~} .{|u~}|u{|}w~}$v~}R{}x~}vw~}S{}x~}t{}x~}Xy|y}y~y}x"
- "|cw~}u{}w~o{|w~^u~}t{|}y~|Q{}x~}Kw~|N{|w~|T{}sx~s{} 4{}x~}Y{}v~}|v{}u~\\m~}X{}v~y}|wy|s~]{}x~}x|v{|}t~}Sr~}\\{"
- "|v~k|Z{|t~}|v{|y}y~|`h|u~^t~|u{|}u~|^u~}|v{|}v~} iv~y|v{|t~]{|o~y}p~|[{|r~|Z{}w~}q|}s~]{|s~}|t{|}u~}g{}w~}r|y"
- "}q~}_{}w~}h|_{}w~}j|`{|s~}|s{|}t~|g{}w~}hv~|Y{}w~}M{}w~}W{}w~}o{}u~|^{}w~}V{}r~k{|r~|h{}r~lv~d{|t~}|uy|s~_{}w~}"
- "s|y}t~}a{|t~}|uy|s~a{}w~}s|y}s~]{}u~}|ty|}v~dn|}v~}n|g{|v~h{}w~}gv~}f{}w~}ov~|n{|t~}mv~|l{}v~|o{|v~|bv~}l{}v~dc"
- "|u~}]{|w~}N{}w~D{|w~|T{}o~| x{|w~!w~} `{|w~|${}w~ w~} >w~}Dv~ Ov~ !{}w~|Mw~|M{}w~ :v~|q{}w~|Xp~}X{}v~|p{|"
- "}| o{}w~| v~|r{|v~W{|r~|X{}v~}i|^{}w~}h|d{|s~}y|xy|}s~}[{|y}u~y}y|]{}w~}h|v~|iv~}r{|v~qv~}g{|t~}|uy|s~&{}p"
- "~} w{}w~ w{}o~| @y~}Q{}v~}|u{|}y~,{|w~}m{|w~}Vw~|T{|v~|s{|}~({|w~}|o{|}w~|P{}x~| w{|w~}|o{|}w~|(x~}tw~ rw~K{}x"
- "~|Rw~ {{}o~}w{|x~} H{}x~|T{|w~r{}x~}-{}x~|hw~|d{}x~|hw~|_{}x~|mw~|%{|r~|R{|r~|R{|r~|R{|r~|R{|r~|R{}r~|Y{|v~|y{|"
- "v~}h|h{|s~}|t{|}u~}c{}w~}h|`{}w~}h|`{}w~}h|`{}w~}h|W{}w~}M{}w~}M{}w~}M{}w~}Z{|v~r|x}q~b{}r~lv~d{|t~}|uy|s~a{|t~"
- "}|uy|s~a{|t~}|uy|s~a{|t~}|uy|s~a{|t~}|uy|s~-{|t~}|u{|}q~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}dv~}l{}v~`"
- "{}w~}M{|v~p{}w~|V{}x~}L{}x~}Q{|x~|ux~}Wx~|v{|w~} {{}q~| Aw~|Lw~|Qw~u{}x~| y{|x~}Lw~|Q{}x~tx~}#{|}r~}Rx~u{|}y~}|"
- "Q{}x~}L{}x~}Q{}x~|v{|x~}Wx~|v{}w~} j{|w~L{}x~}Q{}x~|u{}x~ x{}x~}Uw~} b{|}p~}|V{|}p~}|V{|}p~}|V{|}p~}|V{|}p~}|"
- "P{|w~|xx|av~|fv~| j{|y~|#{}t~Sk~|c{|k~}y{|k~}_{|s~} ?{}t~}y| u{|u~|p{}y~}$v~}R{}x~}vw~}Sw~|tw~|[{|}m~}|h{"
- "|w~sw~|p{}x~|_{}v~|q{|}|Q{}x~}L{}w~Lw~}U{}y~|ux~u{|y~}U{|x}| `w~|Z{|v~}s{|v~}]w~y}y|{}w~}X{}x~|p{|u~|^y}|n{|u~"
- "|U{}x~y}w~}\\{|w~}K{|u~}o{}|Mv~|_{}v~}q{|u~_{}v~}r{|v~| jy~}|qu~|_{}t~}y|s{|}t~}\\{}w~}w~}Z{}w~}o{|u~}_{|t~|n"
- "{|}x~}g{}w~}n{|}t~}`{}w~}L{}w~}P{|t~}m{|}w~|g{}w~}hv~|Y{}w~}M{}w~}W{}w~}p{}u~|]{}w~}V{}w~}w~|l{}r~|h{}r~|mv~e{|"
- "u~}|p{|t~`{}w~}q{|}u~|c{|u~}|p{|t~b{}w~}p{}u~|_{|u~|n{|}y~W{|v~|Z{|v~h{}w~}g{|v~fv~|o{}w~}n{}x~}w~mv~|kv~}ov~}a"
- "{|v~|n{|v~|M{}v~}\\{|w~}N{|w~|E{|w~|U{}v~}{|u~| x{|x~}\"w~} `{|w~|$v~ w~} >w~}Dv~ Ov~ !v~Lw~|M{}w~| <{|w~"
- "}p{|w~}Xn~|Zv~ _{|v~ !{|w~}p{}w~}X{}w~}w~}W{}v~|M{}w~}R{|t~|p{|t~|_{|}l~}|`{}w~}hv~|iv~}r{|v~qv~}h{|u~}|p{|"
- "t~({}n~} x{}w~ x{}m~| Ay~}R{|v~}p{}+{}w~|nv~Uw~|T{}w~| x{|w~|k{|w~|Q{|x~| x{|w~|k{|w~|*{|x~rx~|R{|w}Fw~Kw~|S{}"
- "x~| {|n~}w{|x~} H{}x~|T{}x~}qw~|.{}x~|i{}x~}c{}x~|i{}x~}^{}x~|n{}x~}${}w~}w~}R{}w~}w~}R{}w~}w~}R{}w~}w~}R{}w~}w"
- "~}Rv~|w~}Y{}w~}x{|v~U{|t~|n{|}x~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~n{|}s~c{}r~|mv~e{|u~}|p{|"
- "t~c{|u~}|p{|t~c{|u~}|p{|t~c{|u~}|p{|t~c{|u~}|p{|t~/{|u~}|p{}t~}e{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}d{|v"
- "~|n{|v~|`{}w~}M{}w~}ow~}U{}x~|N{|w~Px~}t{|x~|Xx|sy| w{}s~| @{|w~M{}x~|Q{}x~|tw~ x{}x~}N{}x~|Q{|x~|t{|x~|&{}t~}v"
- "~} t{}x~|N{|x~}Q{|x~}t{}x~|Xx|sy| g{|x~}N{|x~}Q{|x~}sx~} {{|x~}Tw~} d{|j~|Z{|j~|Z{|j~|Z{|j~|Z{|j~|R{|w~Z{}w~}"
- "g{}w~} Ay|J{}y~#{|s~}Tk~}c{}j~|{}j~_q~| A{}u~} q{}v~|n{}~}$v~}R{}x~}vw~}Sw~t{|w~\\{|h~|i{}x~}s{}x~}q{|x~}^"
- "v~|C{}x~}Lw~}L{}w~V{|v~|wx~w{|v~|V{}w~ a{|w~Yv~}q{|v~|^{}y|u{}w~}Xy}|m{|u~M{|v~}V{|w~|}w~}\\{|w~}Ku~|?{|v~^u~o"
- "{}v~|a{|v~}p{}v~ j{~|nv~}`u~}|l{|}u~]v~{v~Z{}w~}mu~_u~}j{|y~}g{}w~}l{|}u~}a{}w~}L{}w~}Q{|u~}i{|}y~|g{}w~}hv~|"
- "Y{}w~}M{}w~}W{}w~}q{}u~|\\{}w~}V{}w~|w~}lw~|v~|h{}q~mv~f{|u~}m{|u~}a{}w~}o{}v~}d{|u~}m{|u~}c{}w~}o{|u~_{}v~|j{|"
- "W{|v~|Z{|v~h{}w~}fv~|h{}v~n{}w~}nw~|w~|o{|v~j{|v~}q{}v~_{}v~nv~}M{|u~[{|w~}Mw~}E{|w~|V{}v~}x{|u~| vw~} `{|w~|$"
- "w~} w~} >w~}Dv~ Ov~ !v~Lw~|M{}w~| <{}w~|ow~}Xm~|[v~ ^v~| \"v~|p{|v~Xv~{v~V{}v~|N{}w~}Ru~}l{}u~|b{|g~}"
- "|b{}w~}hv~|iv~}r{|v~qv~}i{|u~}m{|u~}*{}l~} y{}w~ y{}k~| By~}R{}v~ y{|w~}o{|w~}Uw~|T{}w~ x{|x~}g{}x~|R{|x~} y{|"
- "x~}g{}x~|+{}y~}r{}y~}R{}w~Fx~}M{|}w~ Mm~}w{|x~} H{}x~|Tw~p{}x~|.{}x~|j{|w~b{}x~|j{|w~]w~n{|w~#v~{v~Rv~{v~Rv~{v~"
- "Rv~{v~Rv~{v~S{|w~}{}w~|Zv~|x{|v~Uu~}j{|y~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~k{}t~d{}q~mv~f{|"
- "u~}m{|u~}e{|u~}m{|u~}e{|u~}m{|u~}e{|u~}m{|u~}e{|u~}m{|u~}1{|u~}m{|u~}e{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w"
- "~}c{}v~nv~}_{}w~}Mv~n{}w~Tw}N{|x}P{|x}r{|x} F{|}x~}| ={|x}|O{|x}|Px}|s{|x}| xw|Nw|Pw|rw|'{|v~}|y{|v~} tw}Nw}P{|"
- "x}rx}| 6w|Nw|Ox|rw| Nw~} e{}h~}\\{}h~}\\{}h~}\\{}h~}\\{}h~}S{|w~Z{|v~gv~| Ay~}L{|y~}${|q~}V{|j~ci~}|i~|a{}p~|"
- "Oy|Uw|jw|Vu|Wv|kw|b{}v~} p{|v~|l{|}$v~}R{}x~}vw~}T{|x~}t{|x~}]{|g~|i{}x~|s{|w~qw~|^v~B{}x~}M{|w~|L{|w~}V{|}"
- "w~}xx~x{}w~}|U{}w~ a{}w~Z{|v~o{}w~}U{}w~}X{|j{}v~|M{}v~Vw~}{}w~}\\{|w~}L{|v~|>v~}_{|v~|nv~}a{}v~nv~| \\{}w~}"
- "b{|u~|h{|}v~|`{|w~}{}w~|[{}w~}m{|v~|a{}v~}gy}g{}w~}j{}u~|b{}w~}L{}w~}Q{}v~}f{|~|g{}w~}hv~|Y{}w~}M{}w~}W{}w~}r{}"
- "u~|[{}w~}V{}w~y|w~m{|w~{v~|h{}w~}v~|nv~f{}v~}ju~|b{}w~}nu~d{}v~}ju~|d{}w~}n{}v~|`v~}D{|v~|Z{|v~h{}w~}f{}w~}hv~}"
- "n{|v~o{|w~{}x~}o{}w~}i{}v~|s{|v~|^v~}p{}v~M{|u~|[{|w~}M{}x~}E{|w~|W{}v~|v{|u~| ww~} `{|w~|$w~} w~} >w~}Dv~ "
- "Ov~ !v~Lw~|M{|w~| <{}w~|ow~}Xy~}w|}t~[v~| _{}w~} #{|w~}n{}w~|Z{|w~}{}w~|Vu~|O{}w~}S{}v~}j{}u~c{}d~|c{}w~"
- "}hv~|iv~}r{|v~qv~}i{}v~}ju~|,{}v~y}w~|v~} {{}w~ {{}v~y}w~|u~| Cy~}R{}w~}R{|ey|_{}w~|pv~Tw~|T{}w~ y{|x~}e{}x~|\\"
- "{|}p~} {{|x~}e{}x~|,{}y~}r{}y~}R{}w~G{}x~|Rq~| N{|m~}w{|x~} H{}x~|U{|w~p{|x~}.{}x~|j{}x~|b{}x~|j{}x~|_{|w~|n{}"
- "x~|${|w~}{}w~|T{|w~}{}w~|T{|w~}{}w~|T{|w~}{}w~|T{|w~}{}w~|T{}w~|{|w~}[{|v~w{|v~V{}v~}gy}c{}w~}M{}w~}M{}w~}M{}w~"
- "}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~j{|u~}e{}w~}v~|nv~f{}v~}ju~|f{}v~}ju~|f{}v~}ju~|f{}v~}ju~|f{}v~}ju~|c{}d{}|d{}v~}"
- "k{}u~|f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}bv~}p{}v~^{}m~y}|Yv~o{|}w~ Py~}|u{|v~} 2w~} f{"
- "}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~T{|w~Yv~|i{|v~ A{}x~}M{}y~|$o~|W{|j~ch~}i~}"
- "b{}n~T{|}t~y}|Zw~}kw~}X{}u~|X{}w~|m{}w~|d{|v~| ov~}j{|$v~}R{}x~}vw~}T{}x~}t{}x~}]u~}|{|y~|y{|y}x~|iw~|rw~r{"
- "}x~}]v~B{}x~}Mv~Jv~T{|}w~|{x~{|w~}|S{}w~ aw~}Z{}w~}o{|v~U{}w~}Ev~}M{|v~W{}w~y{}w~}\\{|w~}Lv~}>{|v~|_{|v~m{}w~}"
- "av~|n{|v~ 8{|y}6{|~|4{}v~c{|v~}d{|v~`{}w~|{|w~}[{}w~}lv~|b{|v~}e{|g{}w~}i{}u~b{}w~}L{}w~}R{|v~}dy|g{}w~}hv~|Y{}"
- "w~}M{}w~}W{}w~}s{}u~Y{}w~}V{}w~|{w~|nw~}{v~|h{}w~y|v~nv~g{|v~}i{|u~b{}w~}n{|v~|f{|v~}i{|u~d{}w~}n{|v~|a{|v~C{|v"
- "~|Z{|v~h{}w~}f{|v~|j{|v~|mv~|p{|w~{|x~}ov~|hv~}sv~}]{|v~|r{|v~|Mu~|Z{|w~}M{|w~E{|w~|X{}v~|t{|u~| xw~} `{|w~|$w"
- "~} w~} >w~}Dv~ Ov~ !w~}Lw~|M{|w~| <v~nw~}X{|s{}v~}\\{}v~| `{|v~ #{}w~|n{|w~}Z{}w~|{|w~}Uu~|P{}w~}T{|u"
- "~h{}v~}f{|r~y}v~}r~}d{}w~}hv~|iv~}r{|v~qv~}j{|v~}i{|u~-{}v~}{}w~{|v~} {}w~ {}v~}{}w~{|u~ Cy~}Rv~|S{}~}g{|y~|_v~"
- "q{}w~|Tw~|T{}w~| {{x~}t{|y}u~}|u{}x~^{}m~} {{x~}wq}y|s{}x~,{}y~}r{}y~}R{}w~H{|x~}Qs~} L{}m~}w{|x~} H{}x~|U{|x~"
- "}p{|x~}.{}x~|k{|x~}a{}x~|k{|w~cx}u~|n{|x~}#{}w~|{|w~}T{}w~|{|w~}T{}w~|{|w~}T{}w~|{|w~}T{}w~|{|w~}Tv~xv~[v~}w{|v"
- "~W{|v~}e{|c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~i{|u~|f{}w~y|v~nv~g{|v~}i{|u~g{|v~}i{|u~g{|v~}i{"
- "|u~g{|v~}i{|u~g{|v~}i{|u~d{}y~f{}y~|f{|v~}k{|s~f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}b{|v~|r{|v~|^{}i~}|"
- "\\v~q{}t~| F{}v~| C{~| mw~} gu~}p{}u~|au~}p{}u~|au~}p{}u~|au~}p{}u~|au~}p{}u~|V{|w~Y{}w~}i{}w~} B{"
- "}w~}Mx~${}n~W{|k~}d{|U~}c{|m~}W{|n~}[w~}kw~}Xt~}X{|w~}mv~cv~| o{|v~| mv~}R{}x~}vw~}Tw~|tw~|^{}v~|x{|y~|u{|~"
- "|iw~|rw~s{|w~\\v~B{}x~}N{|w~}J{}w~|S{|n~|Q{}w~ b{|w~|Zv~|nv~|V{}w~}E{}w~}M{|v~X{|w~|y{}w~}\\{|w~}M{|v~={}v~^{|"
- "v~m{}w~}b{|v~lv~| <{|}x~}6{|x~}|7{}w~}cv~|b{|w~}b{|v~xv~[{}w~}l{}w~}bu~|P{}w~}h{}v~}c{}w~}L{}w~}Ru~M{}w~}hv~|Y{"
- "}w~}M{}w~}W{}w~}t{}u~X{}w~}V{}w~|{}x~}o{|w~|{v~|h{}w~|{v~}ov~gu~|h{}v~|c{}w~}mv~}fu~|h{}v~|e{}w~}mv~}a{|v~C{|v~"
- "|Z{|v~h{}w~}ev~}j{}v~l{}w~}p{}x~}{|w~ov~|h{|v~}u{}v~[{}v~rv~}M{}v~}Y{|w~}Lw~|F{|w~|Y{}v~|qu~| Kt|Uw~}uu|Mt|Ru|u"
- "{|w~|Wt|Ow~}Mu|Tw~}uu| Jw~}Dv~Tu|mv|Vu|Pt|Ku|Qu|Bv|Us|Rv~ !w~}Lw~|M{|w~| iv|Sv~o{|w~}N{}v~\\{|t~}|Is|Mu| u{}"
- "w~| Zt| Lv~|n{|v~[{|v~xv~Tu~P{}w~}T{}v~|gu~g{|t~}|y{|v~x{}t~}e{}w~}hv~|iv~}r{|v~qv~}ju~|h{}v~|/{}v~}y{}w~y{|v"
- "~}!{}w~!{}v~}y{}w~y{|u~ F{|}y~}x|V{|v~S{}x~}i{|w~|`{}w~|rw~}Sw~|T{|v~|!{}y~}u{|n~}v{}y~}a{|k~} {}y~}vn~}t{}y~}"
- "-{}y~}r{}y~}R{}w~I{|w~Pt~}| L{}m~}w{|x~} H{}x~|U{|x~}p{|w~.{}x~|kw~|a{}x~|kw~|ct~}lw~|${|v~xv~U{|v~xv~U{|v~xv~U"
- "{|v~xv~U{|v~xv~U{|w~}x{}w~|]{|v~v{|v~Wu~|L{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~h{|v~}f{}w~|{v~}o"
- "v~gu~|h{}v~|hu~|h{}v~|hu~|h{}v~|hu~|h{}v~|hu~|h{}v~|f{}w~h{}w~|gu~|l{|r~|g{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~"
- "h{}w~}a{}v~rv~}]{}g~}]w~}s{|r~|Xt|Nt|Nt|Nt|Nt|Nt|Xt|lu|Ut|Pt|Nt|Nt|Nt| 0{}v~|Pu|Pt|Nt|Nt|Nt|Nt| ut|t{}y~} nw"
- "~}uu| t{}w~}|wv|v{}v~b{}w~}|m{}v~b{}w~}|m{}v~b{}w~}|m{}v~b{}w~}|m{}v~V{|w~Xv~iv~| C{|v~M{|y~}%{}m~}Wk~}d{|U~}d"
- "{|k~}Y{}k~|]w~}kw~}Y{|s~X{|v~n{|w~}d{}w~} n{}w~} lv~}R{}x~}vw~}U{|w~t{|w~]v~|w{|y~|`w~|rw~s{}x~|\\v~|C{}x~}"
- "N{}w~|J{|w~}Q{|r~|O{}w~ b{}w~Z{|v~m{}w~}V{}w~}E{}w~}M{|v~Xw~}x{}w~}\\{|w~}M{}w~}=v~}^{|v~m{}w~}b{|v~lv~} ?{|}u"
- "~}6{|u~}|:{}w~}d{}w~|`{|w~}c{}w~|x{}w~}\\{}w~}l{}w~}c{|v~}O{}w~}gu~c{}w~}L{}w~}S{|v~}M{}w~}hv~|Y{}w~}M{}w~}W{}w"
- "~}uu~}W{}w~}V{}w~|{|w~|p{}w~yv~|h{}w~|{|v~ov~h{|v~}fu~c{}w~}mv~}g{|v~}fu~e{}w~}mv~}a{|v~C{|v~|Z{|v~h{}w~}e{}v~j"
- "v~|l{}w~}pw~|yw~|q{|v~f{}v~|w{|v~|Zv~}t{}v~M{}v~}X{|w~}L{}x~}F{|w~|Z{}v~|o{}v~| P{|}q~}|Xw~}w{}s~}|S{|}q~}|X{}s"
- "~}|x{|w~|Z{|}r~}|W{}k~}W{}s~}|x{|w~|`w~}w{|s~}|Rv~Lv~Tw~}n{|v~}Xv~_w~}w{}s~}r{|s~}cw~}w{|s~}|V{|}r~}|Yw~}w{}s~}"
- "|V{}s~}|x{|w~|Zw~}w{}t~|Y{}o~}|Z{}i~]{|w~|m{}w~|c{|v~iv~i{}w~|pu~ow~}hv~}m{|v~|d{|v~iv~`d~Uw~}Lw~|M{|w~| l{|s~"
- "}|u{}x~}av~o{|w~}M{}w~|\\{}q~}|P{}o~}|\\w~}w{|s~}|^x~y}hv~W{}w~}X{|w~|m{}w~|d{}w~}h{}w~}]{|y}w{|}x~}|]_~|dv~t{}"
- "w~t{|w~}[{|q~}|U{|y}i~}f{|`~b{|v~lv~|\\{}w~|x{}w~}U{|u~Q{}w~}U{|v~}f{|v~|ht~|w{|v~v{}u~}f{}w~}hv~|iv~}r{|v~qv~}"
- "k{|v~}fu~/{|w~}x{}w~x{|w~}I{|T{}w~S{|i{|\\w~}x{}w~x{|w~|!v~}O{|}p~}|Y{|v~T{|v~}k{|v~}_v~s{}w~|Sw~|Su~|#{|x~u{}l"
- "~ux~|bv~}y|v{|x~} !{|x~ul~|ux~|.{|x~|t{|x~|R{}w~J{|w~|L{|}x~}&{|w~|m{}w~|a{}m~}w{|x~} H{}x~|U{|x~}p{|w~.{}x~|l{"
- "}x~}`{}x~|l{}x~}br~|o{}x~}Qv~|S{}w~|x{}w~}V{}w~|x{}w~}V{}w~|x{}w~}V{}w~|x{}w~}V{}w~|x{}w~}V{}w~|x{|w~}]{}w~}v{|"
- "v~X{|v~}K{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~gu~|g{}w~|{|v~ov~h{|v~}fu~i{|v~}fu~i{|v~}fu~i{|v~}"
- "fu~i{|v~}fu~g{|u~j{}v~}h{|v~}l{|w~}v~}g{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}`v~}t{}v~\\{}f~}^w~}t{}v~}y|Y"
- "{|}q~}|U{|}q~}|U{|}q~}|U{|}q~}|U{|}q~}|U{|}q~}|_{|}q~}|r{|}r~}[{|}q~}|W{|}r~}|T{|}r~}|T{|}r~}|T{|}r~}|Qv~Lv~Lv~"
- "Lv~O{|y}w~}u~|\\w~}w{|s~}|V{|}r~}|T{|}r~}|T{|}r~}|T{|}r~}|T{|}r~}|Q{}u~Q{|}r~}|x{}x~}b{|w~|m{}w~|a{|w~|m{}w~|a{"
- "|w~|m{}w~|a{|w~|m{}w~|c{|v~iv~aw~}w{}s~}|^{|v~iv~ W{}w~}u{}w~u{}w~}d{}w~}j{}w~}d{}w~}j{}w~}d{}w~}j{}w~}d{}w~}j{"
- "}w~}W{}w~X{}w~}k{|v~ C{|v~|M{}y~|&{|k~}X{}l~|cU~}di~|[{}i~|^w~}kw~}Y{}s~|Xv~|o{}w~|dw~} mv~| lv~}R{}x~}vw~}"
- "^{}Z~f{|w~}v{|y~|`w~|rw~t{|x~}[{}w~}C{}x~}Nv~Hv~O{}v~}M{}w~ bw~}Z{}w~}m{|v~V{}w~}E{}w~}M{|v~Y{}w~w{}w~}\\{|w~}"
- "Mv~|>{|v~]{|v~m{}w~}b{|w~}l{}w~}W{|v}M{}v~D{}r~}6{|r~}|>{|v~|e{}w~|^{|w~|dv~w{|v~\\{}w~}lv~|c{}v~N{}w~}g{}v~|d{"
- "}w~}L{}w~}S{}v~L{}w~}hv~|Y{}w~}M{}w~}W{}w~}vu~}V{}w~}V{}w~|yw~}pw~}yv~|h{}w~|y{}w~}pv~h{}v~e{}v~|d{}w~}mv~}g{}v"
- "~e{}v~|f{}w~}mv~}a{|v~C{|v~|Z{|v~h{}w~}dv~|l{|v~k{|v~q{|w~x{}x~}q{}w~}e{}v~wv~}Y{|v~|v{|v~|N{|v~}W{|w~}L{|w~F{|"
- "w~|[{}v~l{}v~ S{|}k~|Zw~}y{|o~}V{|k~|\\{|o~}y{|w~|\\{|m~}X{}k~}Y{|o~}y{|w~|`w~}y{|o~}Sv~Lv~Tw~}o{|v~}Wv~_w~}y{|"
- "o~|v{|o~|ew~}y{|o~}Y{|}n~}|[w~}y{|o~}Y{|o~}y{|w~|Zw~}y{|r~|[{}j~[{}i~]{|w~|m{}w~|b{}w~|k{|w~}i{|w~}q{|u~|q{|w~|"
- "h{|v~|o{|v~}b{}w~|k{|w~}`d~Uw~}Lw~|M{|w~| n{|o~}vw~|av~o{}w~|M{|v~[{|o~}|U{}k~}]w~}y{|o~}_u~|k{|w~}Wu~X{|w~|m{"
- "}w~|dv~|h{|v~_{}x~}x{}s~}__~|dv~t{}w~t{|w~}\\{}n~}Y{|}e~}f{|`~b{|w~}l{}w~|\\v~w{|v~T{|u~R{}w~}U{}v~dv~}i{}u~u{|"
- "v~u{|u~|g{}w~}hv~|iv~}r{|v~qv~|k{}v~e{}v~|c{~}I{|y~}w{}w~w{|y~}I{}~|U{}w~T{}~|k{}~|\\y~}w{}w~w{|y~| v~}P{}k~Z{|"
- "v~S{|v~}x{|}v~}|y{|v~}^{|w~}u{|w~}Rw~|S{|u~}${}y~|v{}v~}|wy|}y~u{|y~}c{|x~}r{|x~}Q{|q{| W{}y~|uw~vy|v~u{|y~}-w~"
- "|v{|w~Q{}w~K{|w~|I{|w~'{|w~|m{}w~|a{}m~}w{|x~} H{}x~|U{|x~}p{|x~}]{|q{|X{}x~|m{|w~_{}x~|m{|w~]{|}w~}q{|w~Pv~|Sv"
- "~w{|v~Vv~w{|v~Vv~w{|v~Vv~w{|v~Vv~w{|v~W{|v~vv~^{|v~|v{|v~X{}v~J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z"
- "{|v~g{|v~}g{}w~|y{}w~}pv~h{}v~e{}v~|j{}v~e{}v~|j{}v~e{}v~|j{}v~e{}v~|j{}v~e{}v~|g{|u~l{}v~}g{}v~kw~}{}v~g{|v~h{"
- "}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}`{|v~|v{|v~|\\{}w~}s|y}t~}_w~}u{|v~|Y{|}k~|Z{|}k~|Z{|}k~|Z{|}k~|Z{|}k~|Z{|"
- "}k~|d{|}k~|v{|m~}_{|k~|[{|m~}W{|m~}W{|m~}W{|m~}Rv~Lv~Lv~Lv~Q{|}l~\\w~}y{|o~}Y{|}n~}|X{|}n~}|X{|}n~}|X{|}n~}|X{|"
- "}n~}|S{}u~S{|}n~}{|x~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|b{}w~|k{|w~}aw~}y{|o~}^{}w~|k{|w~} X{|w~}"
- "t{}w~t{|w~}f{|w~}h{|w~}f{|w~}yy|p{|}y{|w~}f{|w~}ly|y{|w~}f{|w~}h{|w~}X{}x~}X{|v~kv~| Cv~|Lx~&{|i~|Y{|m~}bU~|e{}"
- "h~\\{|u~}|xy|}u~^w~}kw~}Yr~}X{}w~}ov~d{}w~ lv~| lv~}R{}x~}vw~}^{}Z~f{|w~|v{|y~|`w~|s{|w~tw~|[{|v~|D{}x~}Nw~"
- "}H{}w~|Q{|t~|N{}w~ c{|w~|Zv~|lv~|W{}w~}E{}w~}M{}w~}Z{|w~|w{}w~}\\{|w~}N{|v~={}w~}\\v~|nv~|b{}w~}l{}v~W{}v~M{}v"
- "~G{|}p~|6{|o~}@u~e{|w~|\\{}w~e{|w~}v{}w~|]{}w~}m{|v~|cv~}N{}w~}g{|v~}d{}w~}L{}w~}Sv~}L{}w~}hv~|Y{}w~}M{}w~}W{}w"
- "~}x{|u~}U{}w~}V{}w~|y{}w~q{|w~|yv~|h{}w~|y{|v~pv~hv~}e{|v~}d{}w~}mv~}gv~}e{|v~}f{}w~}mv~}a{|v~|D{|v~|Z{|v~h{}w~"
- "}d{}w~}l{}w~}jv~|r{|w~x{|x~}qv~|e{|v~}y{}v~W{}v~vv~}N{|u~V{|w~}Kw~|G{|w~|\\{}w~}j{}v~ T{}i~}[w~}{}m~}X{}j~|]{}m"
- "~}{|w~|]{}j~Y{}k~}Z{}m~}{|w~|`w~}{|l~Tv~Lv~Tw~}p{}v~}Vv~_w~}{|m~|x{|m~|fw~}{|m~}[{|j~|\\w~}{}m~}[{}m~}{|w~|Zw~}"
- "{|q~|\\{}i~[{}i~]{|w~|m{}w~|b{|w~}k{}w~|hw~}q{|u~}q{}w~|g{}v~ov~}a{|w~}k{}w~|`d~Uw~}Lw~|M{|w~| Gy|l{|Z{}m~}x{|w"
- "~`v~p{|v~Kv~Z{|m~|X{}j~}]w~}{|l~`t~|l{}w~|X{|u~}Y{|w~|m{}w~|e{}v~f{}w~}b{|v~}y{|q~}`_~|dv~t{}w~t{|w~}^{|k~}[{|c"
- "~}f{|`~b{}w~}l{}w~}]{|w~}vv~|T{|v~}S{}w~}Uv~}d{}v~j{|u~t{|v~t{|u~g{}w~}hv~|iv~}r{|v~r{|v~|kv~}e{|v~}dx~}I{|}v{}"
- "w~v{|}I{}x~|V{}w~U{}x~|m{}x~|\\{|v{}w~vy| {{v~}R{|i~Z{|v~R{|v~}|q~}|v~}\\v~u{}w~Qw~|R{|t~|'{|y~}v{}w~}p{|t{}y~|"
- "d{}x~|r{|x~}Ry}r{|~ X{|y~}tw~sw~|u{}y~|.{|w~}x|}w~|Q{}w~L{|w~|G{|x~}({|w~|m{}w~|a{}m~}w{|x~} H{}x~|U{|w~p{|x~}]"
- "{~|r{|}Y{}x~|mw~|_{}x~|m{}x~|[{|w~|r{}x~|Pv~|T{|w~}v{}w~|X{|w~}v{}w~|X{|w~}v{}w~|X{|w~}v{}w~|X{|w~}v{}w~|X{}w~}"
- "v{}w~}_{}w~}u{|v~Xv~}J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~fu~g{}w~|y{|v~pv~hv~}e{|v~}jv~}e{|v~}"
- "jv~}e{|v~}jv~}e{|v~}jv~}e{|v~}f{|u~n{}v~}fv~}l{}x~}y{|v~|h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}_{}v~vv~}["
- "{}w~}q{|}u~|`w~}uv~W{}i~}[{}i~}[{}i~}[{}i~}[{}i~}[{}i~}e{}i~}x{}k~}a{}j~|\\{}j~Y{}j~Y{}j~Y{}j~Sv~Lv~Lv~Lv~R{}j~"
- "}]w~}{|m~}[{|j~|Z{|j~|Z{|j~|Z{|j~|Z{|j~|T{}u~T{|f~`{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|b{|w~}k{}w~|a"
- "w~}{}m~}_{|w~}k{}w~| Xw~}s{}w~s{}w~fw~}f{}w~fw~}y{|y~|r{|y~}y{}w~fw~}l{|y~}y{}w~fw~}f{}w~X{}x~}Wv~|m{|v~ C{}w~}"
- "[{|}|o{|y~|&g~|Y{}n~|b{}V~e{|g~}]v~}r{|v~}_w~}kw~}Z{|r~}X{|v~p{|w~}dw~} pw|v~l| {{v~}R{}x~}vw~}^{}Z~f{|w~|v"
- "{|y~|`{}x~}s{|x~}u{}x~}Y{}v~|E{}x~}O{|w~}H{}w~|S{|}r~}|P{}w~ c{|w~Yv~|lv~|W{}w~}Ev~|N{|v~|Zw~}v{}w~}\\{|w~}|}v"
- "~y}|X{}w~}>{|v~|\\{}w~}o{|v~a{}w~}l{}v~W{}v~M{}v~J{|}p~}|2{|}p~}|D{}v~|e{}x~}p{|}w~}|vx|uw~|f{}w~|v{|w~}]{}w~}m"
- "{}v~c{|v~|N{}w~}fv~}d{}w~}L{}w~}T{|v~|L{}w~}hv~|Y{}w~}M{}w~}W{}w~}y{|u~}T{}w~}V{}w~|y{|w~|r{}x~}xv~|h{}w~|x{}w~"
- "}qv~i{|v~|dv~}d{}w~}mv~}h{|v~|dv~}f{}w~}n{|v~|`u~D{|v~|Z{|v~h{}w~}d{|v~m{|v~|j{}w~}r{}x~}x{|w~qv~|d{}v~y|v~|Vv~"
- "}x{}v~Mu~|V{|w~}K{}x~}G{|w~|]{}w~}h{|v~ U{}u~v}s~}\\w~}|v~w}t~}Zr~v}v~|^{}t~w}v~}|w~|^{}t~v}t~Zv}v~s}[{}t~w}v~}"
- "|w~|`w~}|u~x}t~}Uv~Lv~Tw~}q{}v~|Uv~_w~}|v~x}s~y{|v~x}s~fw~}|u~x}t~}]{|s~x}s~|]w~}|v~w}t~}]{|t~w}v~}|w~|Zw~}|t~}"
- "x~|]{}t~u}u~[{|x}v~q}]{|w~|m{}w~|av~kv~g{}w~q{}t~qv~e{}v~q{}v~_v~|m{|v~_d~Uw~}Lw~|M{|w~| J{|}v~}r{}v~}|_{}u~w}u"
- "~|y{}x~}`v~q{|v~}K{}w~|\\{}w~}p~}Z{}s~w}u~}]w~}|u~x}t~}as~m{|v~W{}t~Y{|w~|m{}w~|ev~|f{|v~c{|u~}yn~a_~|dv~t{}w~t"
- "{|w~}_{|t~w}t~}]{|b~}f{|`~b{}w~|l{}w~}]{}w~|v{|w~}S{|v~}T{}w~}Uv~|d{|v~|k{}v~|t{|v~s{}v~|h{}w~}hv~|i{}w~}r{|v~r"
- "{|v~|l{|v~|dv~}ev~}C{}w~C{}v~|W{}w~V{}v~n{|v~|W{}w~ sv~}S{|s~}y~x}v~Z{|v~Q{|e~}[{|w~}w{|w~}Qw~|R{}r~|){}y~|w{|w"
- "~}g{|y~}dw~q{}x~}S{}~}s{}y~ X{}y~|tw~s{}x~}u{|y~}-{}p~}P{}w~M{|w~|F{|x~}({|w~|m{}w~|a{}m~}w{|x~} H{}x~|Tw~p{}x~"
- "|]y~}s{|y~Z{}x~|n{|x~}^{}x~|n{|w~Y{|x~}s{|x~}Ov~|T{}w~|v{|w~}X{}w~|v{|w~}X{}w~|v{|w~}X{}w~|v{|w~}X{}w~|v{|w~}Xv"
- "~u{|v~_v~|u{|v~Y{|v~|J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{}v~g{}w~|x{}w~}qv~i{|v~|dv~}k{|v~|d"
- "v~}k{|v~|dv~}k{|v~|dv~}k{|v~|dv~}e{|u~p{}v~}f{|v~|m{}w~wv~}h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}^v~}x{}v"
- "~Z{}w~}o{}v~}`w~}v{|w~|W{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}\\{}u~v}s~}f{}u~v}s~}{s~w}t~}cr~v}"
- "v~|]{}t~v}t~[{}t~v}t~[{}t~v}t~[{}t~v}t~Tv~Lv~Lv~Lv~S{}h~|^w~}|u~x}t~}]{|s~x}s~|\\{|s~x}s~|\\{|s~x}s~|\\{|s~x}s~"
- "|\\{|s~x}s~|U{}u~U{|s~x}q~|`{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|av~|m{|v~`w~}|v~w}t~}_v~|m{|v~ X{|w~"
- "r{}w~rw~}h{|w~dw~}h{|w~y{|w~|t{|w~}yw~}h{|w~l{|w~}yw~}h{|w~dw~}Y{}x~}W{}w~}m{}w~} Xg|}v~s|e{|}x~}o{}y~&{}f~Y{|o"
- "~}a{|V~f{|e~}_{|w~}p{|v~_w~}kw~}Z{}w~}v~Wv~|q{}w~}e{|w~ pc~} {{v~}R{|x}|v{|x}|^{}Z~f{|w~|v{|y~|`{|w~s{}x~}v"
- "{|w~Wu~|F{|x}|O{}w~|H{|w~}U{|}w~|x~|w~}|R{}w~ c{}x~}Yv~|lv~|W{}w~}F{|v~N{|v~}Z{}w~u{}w~}\\{|k~}Z{}w~}x{|}u~y}|"
- "L{}v~Zv~|pv~}a{|v~l{}v~|X{}v~M{}v~M{|}p~}|,{|}p~}|H{}v~|e{|w~q{|q~}y{}x~|v{|x~}fv~tv~]{}w~}n{}v~|c{|v~|N{}w~}f{"
- "}v~d{}w~}L{}w~}T{}v~|L{}w~}hv~|Y{}w~}M{}w~}W{}w~}{|u~}S{}w~}V{}w~|xw~}rw~|xv~|h{}w~|x{|v~|rv~i{|v~|d{}v~d{}w~}n"
- "{|v~|h{|v~|d{}v~f{}w~}n{}v~|`{}v~}|F{|v~|Z{|v~h{}w~}cv~|n{}v~i{}w~}rw~|ww~|s{|v~b{}q~}U{|v~|{|v~|N{}v~|U{|w~}K{"
- "|w~G{|w~|^{}w~}f{|v~ V{}y~}|r{|u~|]r~|u{|u~}\\{}u~}s{|}y~|_{|u~|u{|}s~|_{}v~}|t{}v~}Vw~}T{|u~|u{|}s~|`r~|u{|u~|"
- "Vv~Lv~Tw~}ru~|Tv~_r~|v{|}v~}{w~|u{}v~}gr~|u{|u~|^u~}|v{|}u~]r~|u{|u~|_{|u~|u{|}s~|Zr~}|v{|\\v~}|r{|}y~Wv~S{|w~|"
- "m{}w~|a{}w~|m{|w~}g{}w~|rs~qw~}dv~}s{|v~|_{}w~}m{}w~|Nu~Uw~}Lw~|M{|w~| K{}r~u{|r~}a{|v~}|v{}v~yw~|`v~r{|u~|K{|w"
- "~|]{}w~|xy|}t~}[u~}|s{|}~}]r~|u{|u~|ay|v~|n{}w~|X{|s~|Z{|w~|m{}w~|f{|v~dv~|e{|u~}|{|v~y|}v~}bx}u~q}u~x}|dv~t{}w"
- "~t{|w~}_u~|u{|u~|_{|u~}|v{|}t~v}f{|q}u~p}b{}w~|l{|v~]v~tv~R{}v~}U{}w~}V{|v~|cv~}l{|v~}s{|v~s{|v~}h{}w~}hv~|i{}v"
- "~r{|v~r{|v~|l{|v~|d{}v~fu~|C{}w~C{|u~|X{}w~W{}v~}m{}v~|X{}w~ sv~}T{|u~}|yy~}x{|}y~Z{|v~P{|g~}Y{}w~|xv~Pw~|T{|v~"
- "}u~}*x~v{}w~ex~dw~qw~}U{|x~}t{}x~ Xx~sw~s{}x~}tx~,{|r~|O{}w~N{|w~|Dw~({|w~|m{}w~|a{|m~}w{|x~} H{}x~|T{}x~}qw~|]"
- "x~}t{|x~|\\{}x~|nw~]{}x~|nw~|Xw~sw~|Ov~|Tv~tv~Xv~tv~Xv~tv~Xv~tv~Xv~tv~Y{|w~}tv~|a{|v~t{|v~Y{|v~|J{}w~}M{}w~}M{}"
- "w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|x{|v~|rv~i{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{"
- "}v~d{|u~r{}v~}e{|v~|n{}w~v{}v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}^{|v~|{|v~|Z{}w~}nu~`w~}v{}w~V{}y~}|r"
- "{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|]{}y~}|r{|u~|g{}y~}|r{|o~}|u{|}v~}e{}u~}s{|}y~|^{}v~}|"
- "t{}v~}]{}v~}|t{}v~}]{}v~}|t{}v~}]{}v~}|t{}v~}Uv~Lv~Lv~Lv~T{}u~}|v{|}v~}^r~|u{|u~|^u~}|v{|}u~\\u~}|v{|}u~\\u~}|v"
- "{|}u~\\u~}|v{|}u~\\u~}|v{|}u~U{}u~Uu~}|u{}u~|_{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{}w~}m{}w~|`r~|u{"
- "|u~|`{}w~}m{}w~| Xw~|r{}w~r{|w~hw~|d{|w~hw~|yu~|v{|u~y{|w~hw~|m{|u~y{|w~hw~|d{|w~Y{}x~}Vv~mv~| XZ~}g{}t~oy~}'{}"
- "e~}Y{}p~_W~|fc~|`v~n{}w~|`w~}kw~}Zv~|}w~|X{}w~}qv~|e{}x~} q{|c~| {{v~} y{|x~}t{}x~}]{|w~}v{|y~|_w~|u{|w~|vw"
- "~|Wt~ p{}w~|H{|v~V{}w~}yx~y{}w~}S{}w~ cw~|Z{|v~k{}w~}W{}w~}Fv~}Qy|u~}Z{|w~|u{}w~}\\{|i~|\\v~|y{}p~}|Nv~}Z{|v~|"
- "s{|v~}`{|v~lu~|X{}v~M{}v~P{|}p~}|b{|Z~}b{|}p~}|L{}v~}d{}x~|r{|n~{}x~|uw~|h{}w~}t{}w~|^{}w~}q{|}u~}b{}v~M{}w~}f{"
- "}v~d{}w~}L{}w~}T{}v~K{}w~}hv~|Y{}w~}M{}w~}W{}w~}|u~}R{}w~}V{}w~|x{|w~s{}w~wv~|h{}w~|w{}w~}rv~i{}v~c{}v~d{}w~}n{"
- "}v~|h{}v~c{}v~f{}w~}o{|u~_{|t~}|H{|v~|Z{|v~h{}w~}c{}v~nv~}i{|v~s{|w~|w{}x~}s{}w~}b{|q~S{}v~|v~}N{}v~}T{|w~}K{|w"
- "~|H{|w~| s{}|m{}w~}]t~}q{}v~|^{}v~}ny|_u~q{}t~|`{|v~|q{|v~|Ww~}Tu~q{|t~|`t~}r{|v~}Vv~Lv~Tw~}t{|u~Rv~_t~}r{}v~}"
- "y~}r{}v~gt~}r{|v~}_{}v~|r{|v~}^s~q{}v~_{}v~|r{}t~|Zs~T{|w~}m{|Wv~S{|w~|m{}w~|a{|w~}mv~|g{|w~}s{|s~|s{|w~|d{|v~|"
- "u{|v~}]v~mv~N{}v~Tw~}Lw~|M{|w~| L{}p~w{|p~}bv~}s{}w~y|w~_v~wx|}t~}J{|w~}^{}w~r{}u~|]{|v~|Ot~}r{|v~}_{|v~nv~W{}s"
- "~}Z{|w~|m{}w~|f{}w~}d{}w~}eu~}x{|w~|x{}v~|`{|w~}q{|w~}`v~t{}w~t{|w~}`{}v~q{}v~_u~}r{|v~}V{|w~}Wv~|l{|v~^{}w~}t{"
- "}w~|R{}v~}V{}w~}V{|v~bv~}l{|v~|s{|v~r{}v~h{}w~}hv~|i{}v~r{|v~r{}v~k{}v~c{}v~gu~|B{}w~B{|u~|Y{}w~X{}v~}k{}v~|Y{}"
- "w~ sv~}Tu~|wy~}u{|Z{|v~O{|u~}|x{|}v~}_{|p~}y{|p~}Ww~|Tw~}y{|t~|,y~}vw~|e{}y~dw~|s{}w~}V{|w~}u{}w~ Xy~}sw~s{}x~}"
- "t{}y~*y}x~}|[m|}w~l|^{}w~C{|x~}({|w~|m{}w~|`m~}w{|x~} H{}x~|T{|w~|s{}x~}\\w~}u{|w~|]{}x~|o{}x~}]{}x~|o{}x~}Ww~t"
- "{}x~}Nv~|U{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~}t{}w~|Z{}w~|t{|w~}av~}t{|v~Y{}v~I{}w~}M{}w~}M{}w"
- "~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|w{}w~}rv~i{}v~c{}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~c{|"
- "u~t{}v~}d{}v~n{|w~|v{|v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}]{}v~|v~}Y{}w~}n{|v~|aw~}vv~V{}|m{}w~}]{}|m"
- "{}w~}]{}|m{}w~}]{}|m{}w~}]{}|m{}w~}]{}|m{}w~}g{}|m{}r~|q{|v~|g{}v~}ny|_{|v~|q{|v~|_{|v~|q{|v~|_{|v~|q{|v~|_{|v~"
- "|q{|v~|Vv~Lv~Lv~Lv~U{|v~}q{|v~|_t~}r{|v~}_{}v~|r{|v~}^{}v~|r{|v~}^{}v~|r{|v~}^{}v~|r{|v~}^{}v~|r{|v~}V{}u~V{}v~"
- "|r{|v~}_{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|`v~mv~_s~q{}v~_v~mv~ X{|w~q{}w~q{}x~|j{|w~b{}x~|j{|w~wu~"
- "|x{|u~|x{}x~|j{|w~m{|u~|x{}x~|j{|w~b{}x~|Z{}x~}V{}w~|o{|v~ WZ~}gx~}w~|q{}y~|({|c~}_v|{}r~u|d{}X~f{}b~|b{|w~}mw~"
- "}`w~}kw~}[{|v~{}w~}X{|w~}r{|v~d{}x~| q{}c~ yv~} y{}x~}t{}x~}\\v~}w{|y~|_{}w~|vw~}v{|x~}X{|r~ qv~Fv~X{}w~}|x"
- "x~x{|}w~}U{}w~ d{|w~Y{|v~k{}w~}W{}w~}G{}v~|Xm~}Y{}x~}t{}w~}\\{|h~}]v~y|l~}P{|v~|Y{|u~u|}v~}_{|v~|n{|u~|X{}v~M{"
- "}v~R{|o~}|`{|Z~}_{|}p~}|P{}v~}cw~r{|l~}x~|u{|x~|hv~|t{|v~^{}e~}a{}v~M{}w~}f{|v~|e{}d~|_{}g~|d{}v~K{}^~|Y{}w~}M{"
- "}w~}W{}p~|Q{}w~}V{}w~|ww~|tw~}wv~|h{}w~|vv~|sv~i{}v~c{|v~|e{}w~}o{|u~g{}v~c{|v~|g{}w~}p{|u~|^{}q~y}|M{|v~|Z{|v~"
- "h{}w~}c{|v~|p{|v~gv~|t{|w~v{|x~}sv~|a{|s~|Rq~}N{}v~}S{|w~}Jw~}H{|w~| bv~|^t~ov~}^v~}P{|v~|p{}u~|`v~|o{|v~Ww~}U"
- "{|v~o{}u~|`u~}p{|v~Vv~Lv~Tw~}u{|v~}Qv~_u~}pt~}pv~|hu~}p{|v~`{|v~|p{|v~|_t~ov~}a{|v~|p{}u~|Zt~S{}w~Gv~S{|w~|m{}w"
- "~|`v~|o{|v~ev~s{|x~y}x~}s{}w~|c{}v~uv~}\\{}w~|o{|w~}O{}v~|U{|w~}Lw~|M{|w~} M{|x~}x|}w~}xv~}x|}x~|d{}v~qw~y}x~}_"
- "v~x{}q~}I{|w~}_{|w~|q{|u~]{}w~|Nu~}p{|v~^{}w~|p{|w~}X{|q~Z{|w~|m{}w~|fv~|d{|v~f{|v~}w{}w~|wu~`{|w~}q{|w~}`v~t{}"
- "w~t{|w~}a{|v~ov~}a{|v~}p{}v~|W{|w~}Wv~}l|}v~^v~|t{|v~Q{}v~}W{}w~}V{|v~b{}w~}l{}v~r{|v~r{}v~|i{}w~}hv~|i{|v~|s{|"
- "v~r{}v~k{}v~xi~}y{|v~|iu~|A{}w~A{|u~|Z{}w~Y{}v~}i{}v~|Z{}w~ sv}|U{}v~|vy~}S{|v~O{|w~}s{|v~_{|o~|{o~}Ww~|U{}x~}v"
- "{}u~}.{|y~|w{|w~d{|y~|e{}w~t{}v~}W{|v~|v{}w~}cY|8{|y~|sw~sw~|t{|y~| `{|Z~}_{}x~}C{|w~}({|w~|m{}w~|`{|n~}w{|x~} "
- "H{}x~|Sv~|u{}w~|\\{}v~v{|v~|^{}x~|p{|w~\\{}x~|p{|w~W{|x~}u{|w~Mv}|Uv~|t{|v~Zv~|t{|v~Zv~|t{|v~Zv~|t{|v~Zv~|t{|v~"
- "Zv~rv~b{|v~s{|c~l{}v~I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}Z{|v~ev~}h{}w~|vv~|sv~i{}v~c{|v~|l{}v~c{|v"
- "~|l{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|c{|u~v{}v~}c{}v~o{|w~|u{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}\\q~"
- "}X{}w~}mv~}aw~}vv~Ev~|Mv~|Mv~|Mv~|Mv~|Mv~|Ws~|o{}w~}gv~}Ov~|o{|v~_v~|o{|v~_v~|o{|v~_v~|o{|v~Vv~Lv~Lv~Lv~Uv~}o{}"
- "w~}_u~}p{|v~`{|v~|p{|v~|`{|v~|p{|v~|`{|v~|p{|v~|`{|v~|p{|v~|`{|v~|p{|v~|Wt|W{|v~|q{}u~|`{|w~|m{}w~|a{|w~|m{}w~|"
- "a{|w~|m{}w~|a{|w~|m{}w~|`{}w~|o{|w~}_t~ov~}`{}w~|o{|w~} X{}x~}q{}w~q{|x~}j{}x~}b{|x~}j{}x~}vu~|yu~|w{|x~}j{}x~}"
- "mu~|w{|x~}j{}x~}b{|x~}Z{}x~}V{|v~o{}w~} WZ~}g{}|yw~}qx~'a~|c{|}t~}k~}|fY~}g{}`~b{|w~|m{}w~`w~}kw~}[{|w~}{|v~Wv~"
- "r{}w~}dw~| lv~| kv~| yw~|tw~|\\{}v~}|y{|y~|^v~}y|}v~uw~X{|p~ rv~Fv~Xw~|vx~v{|w~U{}w~ d{}x~}Y{|v~k{}w~}W{}w"
- "~}H{|v~}Wo~}|Y{|w~|t{}w~}\\{|v~x}|x}s~}^v~|j~}Q{}w~}V{}l~}]v~}n{}u~}X{}v~M{|v}U{|}p~}|]{|Z~}\\{}o~|S{}v~}c{|x~}"
- "rv~}|w{|}t~|tx~}i{|v~rv~|_{}h~}|_v~}M{}w~}f{|v~|e{}d~|_{}g~|dv~}K{}^~|Y{}w~}M{}w~}W{}q~|P{}w~}V{}w~|w{}w~u{|w~|"
- "wv~|h{}w~|v{}w~}sv~iv~}c{|v~|e{}w~}p{|u~|gv~}c{|v~|g{}w~}sy|}u~}\\{}m~}|Q{|v~|Z{|v~h{}w~}bv~}p{}w~}g{}w~}t{}x~}"
- "v{|w~sv~|`{}u~}Q{|r~|O{|u~R{|w~}J{}w~H{|w~| b{|w~}^u~|o{|v~_{}v~Ov~}nu~|a{}w~}m{}w~|Xw~}Uv~|nu~|`u~nv~|Wv~Lv~T"
- "w~}v{}v~}Pv~_u~o{}u~|p{}w~}hu~nv~|a{}w~}n{}w~}_u~|o{|v~a{}w~}nu~|Zu~|S{}w~Gv~S{|w~|m{}w~|`{}w~}o{}w~}e{}w~s{}x~"
- "}|w~sv~a{}v~w{}v~[{|w~}ov~|P{}v~|T{|w~}Lw~|M{|w~}:{|4x~|v{|w~}{}x~}u{}x~dv~}q{}s~|_v~x{}r~}S{|y}~y}|w{|w~}_w~}o"
- "{|v~}^{}w~Mu~nv~|_{|w~}pv~|X{}w~}v~|[{|w~|m{}w~|g{|v~bv~|g{}v~v{}w~v{|v~|a{|w~}q{|w~}`v~t{}w~t{|w~}a{}w~|o{|v~a"
- "{}v~nv~}W{|w~}W`~_{|v~rv~|Q{}v~|X{}w~}V{|v~b{}w~}lu~r{|v~r{|v~|i{}w~}hv~|hv~}s{|v~rv~}kv~}xi~}y{|v~|ju~|@{}w~@{"
- "|u~|[{}w~Z{}v~}g{}v~|[{}w~ Gv~}uy~}S{|v~Ow~}q{|w~|`{|n~}o~}Ww~|Uw~|t{}u~|0{|y~|w{|x~}d{|y~|e{|v~}w|t~}X{|v~|vv~"
- "}c{|Z~}8{|y~|sw~t{}w~s{|y~| `{|Z~}`{}x~}M{|~}|v{|}v~'{|w~|m{}w~|_{}o~}w{|x~}Vv}| s{}x~|S{|v~}|{y|}w~}Z{}v~|w{|v"
- "~}_{}x~|pw~|o{}w~m{}x~|p{}x~|vy|}w~y}|g{|w~|u{}x~|o{}w~3{|v~rv~|\\{|v~rv~|\\{|v~rv~|\\{|v~rv~|\\{|v~rv~|\\{}w~}"
- "r{}w~|c{}w~}s{|c~lv~}I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}_{}i~}nv~}h{}w~|v{}w~}sv~iv~}c{|v~|lv~}c{|"
- "v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|b{|u~x{}v~}bv~}p{|w~}t{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}\\{|r~|"
- "X{}w~}mv~}aw~}v{}w~}F{|w~}M{|w~}M{|w~}M{|w~}M{|w~}M{|w~}W{|u~}m{}w~h{}v~O{}w~}m{}w~|a{}w~}m{}w~|a{}w~}m{}w~|a{}"
- "w~}m{}w~|Wv~Lv~Lv~Lv~V{}v~n{|v~_u~nv~|a{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~},{}w~}q{}t~}`"
- "{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|`{|w~}ov~|_u~|o{|v~`{|w~}ov~| X{}x~|q{}w~q{|w~j{}x~|b{|w~j{}x~|u"
- "u~|u~|v{|w~j{}x~|nu~|v{|w~j{}x~|b{|w~Zw~}Uv~|q{|v~ VZ~}c{}w~r{|y~}({}`~d{}^~|h{|Z~g{|_~}c{}w~l{|w~`w~}kw~}[{}w~"
- "|yv~|X{}w~|sv~|dV~} 2v~| k{}w~| {{|w~t{|w~Zs~y}y~|^{|o~|v{}x~}rx|e{|v~y}u~n{|w~},{|v~Fv~|Y{|~}tx~t{}~|U{}w~ "
- " dw~|Y{|v~k{}w~}W{}w~}Hu~Vp~}|Y{|w~}s{}w~}\\{|~}|q{}t~|`{|q~}|xy|t~|Rv~|U{|}p~|[{}v~|ot~} V{|}p~}|Z{|Z~}Z{|}p~}"
- "|W{}v~|b{}x~|s{}w~|s{|u~|tw~i{}w~}r{}w~}_{}g~}|`v~}M{}w~}f{|v~|e{}d~|_{}g~|dv~}K{}^~|Y{}w~}M{}w~}W{}q~O{}w~}V{}"
- "w~|w{|w~|v{}w~vv~|h{}w~|uv~|tv~iv~}c{|v~|e{}w~}sy|s~fv~}c{|v~|g{}f~}Z{}k~}S{|v~|Z{|v~h{}w~}b{|v~pv~|g{}w~}tw~|u"
- "w~|u{|v~_{}u~O{}t~|O{|u~|R{|w~}J{|w~|I{|w~| aw~}^v~}m{}w~}`v~|P{|v~m{}v~|av~l{|w~}Xw~}V{|v~m{|v~|`v~}n{}w~|Wv~"
- "Lv~Tw~}w{}v~}Ov~_v~}o{|v~}o{|w~}hv~}n{}w~|av~|n{|v~|`u~mv~|bv~m{}v~|Zv~}R{}w~Gv~S{|w~|m{}w~|`{|v~ov~d{}w~|tw~|{"
- "w~|u{|w~}`v~}y{|v~|Z{}w~|q{|v~P{}v~|Sv~|Lw~|Lv~|W{|y}w~}|iy}5{|y~}sw~|x~}s{}y~|f{|v~|ps~^v~x{}q~}|W{|r~|y{|w~}`"
- "{}w~m{}v~^{}w~Mv~}n{}w~|^{}w~q{|v~Wv~y|w~}[{|w~|m{}w~|g{}v~b{}w~}h{|v~|v{}w~u{}w~}a{|w~}q{|w~}`v~t{}w~t{|w~}av~"
- "mv~|c{|v~|n{|v~W{|w~}W`~_{}w~}r{}w~}Q{|v~}X{}w~}V{|v~b{}w~}lv~}r{|v~r{|v~|i{}w~}hv~|h{}v~s{|v~s{|v~|kv~}xi~}y{|"
- "v~|ku~|?{}w~?{|u~|\\{}w~[{}v~}e{}v~|\\{}w~ H{}v~ty~}S{|v~P{|w~o{}w~_s|}r~s|Vw~|V{|w~r{|u~0{|y~v{}x~}d{|y~|d{}o~"
- "|x~}Y{}v~v{|v~|b{|Z~}8{|y~rw~u}v~|s{|y~| `{|Z~}a{}l~|X{|m~|'{|w~|m{}w~|^o~}w{|x~}W{|v~| xm~}W{|n~}X{|v~|vv~}e{}"
- "n~}v{}x~}o{|v~m{}x~|q{|w~w{|o~|t{|~}y|w{|}v~u{|x~}o{|v~3{}w~}r{}w~}\\{}w~}r{}w~}\\{}w~}r{}w~}\\{}w~}r{}w~}\\{}w"
- "~}r{}w~}\\v~|r{|w~}cv~|s{|c~lv~}I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}_{}i~}nv~}h{}w~|uv~|tv~iv~}c{|v"
- "~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|a{|u~|}v~}av~}pw~}s{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}["
- "{}t~|W{}w~}mv~}aw~}v{}v~|Fw~}Lw~}Lw~}Lw~}Lw~}Lw~}Vu~l{|w~|iv~|Ov~l{|w~}av~l{|w~}av~l{|w~}av~l{|w~}Wv~Lv~Lv~Lv~V"
- "v~|mv~|`v~}n{}w~|av~|n{|v~|av~|n{|v~|av~|n{|v~|av~|n{|v~|av~|n{|v~|-v~|r{|x~}v~`{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{"
- "}w~|a{|w~|m{}w~|_{}w~|q{|v~^u~mv~|`{}w~|q{|v~ Ww~p{}w~pw~jw~yd|yw~jw~t{|p~|tw~jw~nu~|tw~jw~pv~}qw~Zw~|U{}w~}q{}"
- "w~} F{}w~}W{|w~|s{}y~|){|_~}f{}\\~|h{}\\~|g{}^~c{}w~l{|w~|aw~}kw~}[v~x{}w~}X{|w~}t{|v~cV~} 2v~| k{}w~| {{|x~"
- "}t{|x~}Z{|o~}y|`{|}r~|v{|w~t{}u~}|hv~}y{}u~o{|w~|,{|v~F{}w~|X{|sx~s{|T{}w~ e{|w~X{|v~k{}w~}W{}w~}Iu~|Vm~|[{}w~"
- "r{}w~}L{}u~`{|r~|s{|u~S{}v~V{|}m~}|\\u~p{}t~} Y{|}p~}|VY|W{|}p~}|[{|v~|aw~rw~}q{|v~|t{}x~iv~q{|v~_{}e~}av~}M{}w"
- "~}f{|v~|e{}d~|_{}g~|dv~}m{}n~|h{}^~|Y{}w~}M{}w~}W{}q~}P{}w~}V{}w~|vw~}vw~}vv~|h{}w~|u{}v~tv~iv~}bv~|e{}e~|fv~}b"
- "v~|g{}g~}X{|}k~}U{|v~|Z{|v~h{}w~}av~|r{|v~f{|v~u{|w~|u{}x~}u{}w~}`{|t~|O{}v~}Nu~|Q{|w~}Iw~}I{|w~| a{}w~^v~|m{|"
- "w~}a{|v~O{|w~}lv~|b{|w~}kv~Xw~}V{|w~}lv~|`v~|n{|w~}Wv~Lv~Tw~}x{}v~|Nv~_v~|nv~|nv~hv~|n{|w~}b{|v~lv~|`v~}m{|w~}c"
- "{|w~}m{|v~|Zv~|R{}w~|Hv~S{|w~|m{}w~|_{}w~|q{|w~}d{|w~}u{|w~y{}x~|u{|w~|`{|v~y|v~}Y{|w~}q{}w~|Q{|v~}S{}v~Kw~|L{}"
- "w~}Y{|p~}|n{|y~}5{}y~r{|t~qy~}f{}v~ot~}^v~x{}o~}Y{}p~|{|w~|`w~}lv~|_{|w~}Nv~|n{|w~}^{|w~|r{}w~|X{}w~}yv~[{|w~|m"
- "{}w~|gv~}b{}v~h{|v~u{}w~u{|v~a{|w~}q{|w~}`v~t{}w~t{|w~}b{|w~}m{|w~}c{|v~lv~|X{|w~}W`~_v~|r{|v~Qu~W{}w~}V{|v~b{}"
- "w~}lv~}r{|v~qv~|i{}w~}hv~|h{|v~|t{|v~s{}v~jv~}xi~}xv~|lu~[|]{}w~\\\\|u~|]{}w~\\{}v~}c|u~|]{}w~ H{}w~}ty~}X{}g~|"
- "[{}x~}nw~Vs~|Nw~|V{}x~}pv~}1{}y~v{}x~}d{|y~}c{}r~}{|x~}Z{}w~}v{|v~|a{|Z~}8{}y~rn~}q{|y~} `{|Z~}a{}l~|X{|o~}|&{|"
- "w~|m{}w~|]{}q~}w{|x~}W{|v~| xm~}V{|}q~|V{|v~|v{}w~}fm~}vw~o{|u~rm~}vw~|w{}n~|u{|m~|uw~|p{|u~3v~q{|v~\\v~q{|v~\\"
- "v~q{|v~\\v~q{|v~\\v~q{|v~]{|v~pv~|e{}w~}r{|c~lv~}I{}d~|`{}d~|`{}d~|`{}d~|W{}w~}M{}w~}M{}w~}M{}w~}_{}i~}nv~}h{}w"
- "~|u{}v~tv~iv~}bv~|lv~}bv~|lv~}bv~|lv~}bv~|lv~}bv~|`{|p~}`v~}q{}x~}qv~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}"
- "w~}Z{}v~}V{}w~}mv~}aw~}uu~}G{}w~L{}w~L{}w~L{}w~L{}w~L{}w~V{}w~}kw~}j{|v~O{|w~}kv~b{|w~}kv~b{|w~}kv~b{|w~}kv~Wv~"
- "Lv~Lv~Lv~W{|v~l{}w~}`v~|n{|w~}b{|v~lv~|b{|v~lv~|b{|v~lv~|b{|v~lv~|b{|v~lv~|.{|v~r{|w~{}w~|a{|w~|m{}w~|a{|w~|m{}"
- "w~|a{|w~|m{}w~|a{|w~|m{}w~|_{|w~}q{}w~|^v~}m{|w~}`{|w~}q{}w~| Ww~yd~|{w~jw~yd~|{w~jw~s{|r~|sw~jw~ou~|sw~jw~pv~}"
- "qw~Zw~|U{|v~qv~| G{}w~}Uw~}sx~({}^~g{}Z~g]~}f{|_~|cw~}l{|w~|aw~}kw~}\\{|v~x{|v~Wv~t{}w~}cV~} 2v~| k{}w~| {{}"
- "x~}t{}x~}Y{|}m~}`{|}w~}|tw~|v{|q~}j{}v~w{}u~p{}w~|,{|w~}F{}w~|Ox~Z{|Z~} t{}x~}X{|v~k{}w~}W{}w~}J{}v~|Ut|}t~}]{"
- "|w~|r{}w~}K{}v~|a{|s~p{|v~}Tv~}W{}i~}]{}u~|t{|}s~} Z{|q~}| e{|}q~}\\v~}`x~}s{}w~ov~|t{}x~|k{|w~}p{}w~|`{}w~}p|}"
- "t~|cv~}M{}w~}f{|v~|e{}w~}i|^{}w~}l|cv~}m{}n~|h{}w~}h|v~|Y{}w~}M{}w~}W{}w~}u~}Q{}w~}V{}w~|v{}w~w{|w~uv~|h{}w~|tv"
- "~|uv~iv~}c{|v~|e{}f~|ev~}c{|v~|g{}i~}S{|}m~}V{|v~|Z{|v~h{}w~}a{}w~}rv~}ev~|v{|w~t{|w~uv~|`r~O{|v~|O{}v~}P{|w~}I"
- "{}w~I{|w~| a{}w~^v~|lv~a{}w~}O{}w~|lv~|b{|w~|k{}w~Xw~}V{}w~|lv~|`v~m{|w~}Wv~Lv~Tw~}yu~|Mv~_v~mv~mv~hv~m{|w~}b{"
- "}w~}l{}w~}`v~|m{|v~c{}w~|lv~|Zv~Q{}v~|Iv~S{|w~|m{}w~|_{|w~}q{}w~|cv~u{}x~}y{}x~}u{}w~^{}q~}Wv~qv~Q{|v~}Uy|}v~|K"
- "w~|L{|u~}|^{|k~}|s{|}x~}5y~}q{}v~|q{}y~f{}w~}o{}u~|^v~ty|}s~[{|u~y}v~y|w~|a{|w~}l{}w~}^{}w~|Ov~m{|w~}]w~}rv~Wv~"
- "|y{}w~}\\{|w~|m{}w~|gv~|b{|v~h{}w~}u{}w~tv~a{|w~}q{|w~}`v~t{}w~t{|w~}b{}w~|m{|v~c{}w~}l{}w~}X{|w~}W`~`{|w~}pv~|"
- "S{}v~|W{}w~}V{|v~bv~}lv~}r{|v~r{|v~|i{}w~}hv~|gu~t{|v~t{|v~}jv~}xh|y{|v~|mT~]{}w~]T~|^{}w~]{}U~|^{}w~ Hv~|ty~}X"
- "{}g~|[w~|nw~|W{}u~}Mw~|V{}w~ov~1{|y~v{}x~}d{|y~|ay}x~y}ww|[{}w~}v{|v~|`{|Z~}8{|y~ro~o{|y~| Q{}w~R{}l~|V{|y}v~y}"
- "|${|w~|m{}w~|\\{|}s~}w{|x~}W{|v~| xm~}T{|y}w~}|S{|v~|v{}w~}gm~}w{}x~}oy~y}x~rm~}w{}x~}v{}~}y|w{|v~u{|o~}t{}x~}o",
- "t~^v|V{|w~}p{}w~|^{|w~}p{}w~|^{|w~}p{}w~|^{|w~}p{}w~|^{|w~}p{}w~|^{}w~}p{}w~}ev~|r{|v~h|lv~}I{}w~}i|_{}w~}i|_{}"
- "w~}i|_{}w~}i|V{}w~}M{}w~}M{}w~}M{}w~}_v}u~r}nv~}h{}w~|tv~|uv~iv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|"
- "_{|r~}_v~}r{}w~q{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w~}mv~}aw~}u{|t~|I{}w~L{}w~L{}w~L{}w~"
- "L{}w~L{}w~V{}w~|kv~j{}w~}O{|w~|k{}w~b{|w~|k{}w~b{|w~|k{}w~b{|w~|k{}w~Wv~Lv~Lv~Lv~W{}w~}l{|w~}`v~m{|w~}b{}w~}l{}"
- "w~}b{}w~}l{}w~}b{}w~}l{}w~}b{}w~}l{}w~}b{}w~}l{}w~}eY|f{}w~}rw~y{|w~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|"
- "m{}w~|^v~qv~]v~|m{|v~_v~qv~ Vw~yd~|{}x~|kw~yd~|{}x~|kw~r{|t~|r{}x~|kw~pu~|r{}x~|kw~pv~}q{}x~|[w~|T{}w~|s{|v~ G{"
- "}v~T{}w~t{|y~}(]~|i{|Y~}h{|_~}d{|a~}bw~}kw~|aw~}kw~}\\{}w~}wv~|Xv~|u{}w~|cV~} 2v~| k{}w~| {{w~|tw~|W{|}m~}T{"
- "}x~}v{|o~}l{|v~|v{}u~q{}w~+{|w~}F{}w~|Ox~Z{|Z~}+m| ww~|X{|v~k{}w~}W{}w~}K{}v~}K{|}v~}^w~}q{}w~}Ju~a{|t~|o{}v~U{"
- "|v~|X{}u~}|wy|u~}]t~}y|{y|}q~} Z{|t~}| _{|}t~}\\v~`{|x~}s{}x~}o{|w~|t{}x~|kv~|p{|w~}`{}w~}n{|u~cv~}M{}w~}f{|v~|"
- "e{}w~}L{}w~}Tv~}m{}n~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}|u~}R{}w~}V{}w~|v{|w~|x{}x~}uv~|h{}w~|t{|v~uv~iv~}c{|v~|e{}h~"
- "}cv~}c{|v~|g{}h~}Qy|y}p~W{|v~|Z{|v~h{}w~}a{|v~s{|v~|e{}w~}v{}x~}t{|w~uv~|a{}r~}P{|v~|P{}v~}O{|w~}I{|w~|J{|w~| "
- "n{|y}l~^v~kv~a{}w~|Ov~|l{}w~|b{}w~|k{}w~|Yw~}Vv~|l{}w~|`v~m{|w~}Wv~Lv~Tw~}|u~Kv~_v~mv~mv~hv~m{|w~}b{}w~|l{|v~`v"
- "~kv~c{}w~|l{}w~|Zv~Pu~}|Kv~S{|w~|m{}w~|^v~qv~b{}w~u{}x~|y{|w~uv~]{}r~V{}w~|s{|w~}R{|v~}X{|q~}Jw~|K{|q~}c{}g~}w|"
- "}u~}5y~}pw~}p{}y~fv~|o{}u~]v~p{|t~\\v~}w{|w~}w~|a{}w~|l{|w~}]{}w~}y|Rv~m{|w~}]{}w~s{}w~}X{}w~}x{|v~\\{|w~|m{}w~"
- "|h{|v~|b{|v~|i{}w~|u{}w~tv~|b{|w~}q{|w~}`v~t{}w~t{|w~}bv~kv~c{}w~|l{|w~}X{|w~}Wv~jv~`v~|p{}w~}T{}v~|V{}w~}V{|v~"
- "|cv~|lv~}r{|v~r{|v~|i{}w~}hv~|g{}v~}u{|v~tu~|jv~}c{|v~|n{|T~]{}w~]T~}^{}w~]T~}^{}w~ I{|v~sy~}X{}g~|[w~m{}x~|Vu~"
- "|#{|w~|p{|w~|2{|y~|w{|x~}d{|y~|3v~}v{}v~|Aw~}8{|y~|sw~x{|w~}p{|y~| Q{}w~ p{|w~|m{}w~|Y{|}v~}w{|x~}W{|v~| jv~}"
- "v{}v~|W{|w~o{}y~{}x~r{}n~}x{|w~uy|rw~|ty|t}|s{|w~o{}y~|}x~^{}w~|Wv~|p{|w~}^v~|p{|w~}^v~|p{|w~}^v~|p{|w~}^v~|p{|"
- "w~}^v~|p{|v~f{|v~q{|v~Yv~}I{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~ev~}h{}w~|t{|v~uv~iv~}c{|v~|lv~}"
- "c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|^{|t~}^v~}s{}w~p{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w"
- "~}n{|v~|aw~}t{}t~}W{|y}l~Y{|y}l~Y{|y}l~Y{|y}l~Y{|y}l~Y{|y}l~c{|y}l~j{}w~j{}w~|O{}w~|k{}w~|c{}w~|k{}w~|c{}w~|k{}"
- "w~|c{}w~|k{}w~|Xv~Lv~Lv~Lv~W{}w~|l{|v~`v~m{|w~}b{}w~|l{|v~b{}w~|l{|v~b{}w~|l{|v~b{}w~|l{|v~b{}w~|l{|v~f{|Z~}f{}"
- "w~|s{}x~|y{|w~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|^{}w~|s{|w~}]v~kv~_{}w~|s{|w~} Vw~yd~|{}x~|kw~yd"
- "~|{}x~|kw~qt~|r{}x~|kw~qu~|q{}x~|kw~pv~}q{}x~|[w~|T{|w~}s{}w~} H{|v~|T{|w~|u{}y~({|]~}i{}X~g{|`~b{}b~aw~}kw~}aw"
- "~}kw~}\\v~|w{}w~}X{}w~}uv~bw~}Z| 5x|v~}p| v{}w~| {|w~t{|w~S{|}n~|Vw~uv~|y{|}w~}m{}w~}t{}u~rw~}+{|w~}F{}w~|Ox"
- "~Z{|Z~},{|m~ x{|w~|X{|v~k{}w~}W{}w~}L{}v~}H{}v~}`{}w~p{}w~}J{}v~`t~n{|v~|V{}v~X{}v~}q{}v~}^{|j~|v~| Z{|t~| ]{|}"
- "u~}]{|w~}`{|x~|sw~|o{|w~|t{}x~|l{|v~nv~`{}w~}lv~}dv~}M{}w~}f{|v~|e{}w~}L{}w~}Tv~}m{}n~|h{}w~}hv~|Y{}w~}M{}w~}W{"
- "}w~}{|t~S{}w~}V{}w~|u{}x~}y{|w~|uv~|h{}w~|sv~|vv~iv~}c{|v~|e{}k~}|av~}c{|v~|g{}w~}t|y}u~}M{|}s~}X{|v~|Z{|v~h{}w"
- "~}`v~}t{}v~d{}w~}vw~|sw~|w{|v~a{|v~}v~|Q{|v~|Q{|u~N{|w~}Hw~|J{|w~| p{}h~|_v~k{}w~|bv~|Ov~k{}w~|bv~j}v~|Yw~}Vv~"
- "k{}w~|`w~}m{|w~}Wv~Lv~Tq~}Jv~_w~}mv~mv~hw~}m{|w~}bv~|l{|v~`v~kv~|dv~k{}w~|Zv~P{}r~}y|Pv~S{|w~|m{}w~|^{}w~|s{|w~"
- "}b{|w~|vw~|xw~|w{|w~}\\s~|Uv~sv~|Ru~W{|s~}|Iw~|I{|}t~}d{|u~}w|}g~}5{|y~|p{|x~|p{}y~fv~|o{|v~}]v~n{}v~|^{}w~|ts~"
- "`v~|l{|v~\\{}p~}Xw~}m{|w~}]{|w~|tv~|Xv~|wv~|]{|w~|m{}w~|h{|v~|q{}x~}q{|v~|iv~|u{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{"
- "|w~}bv~kv~|dv~|l{|v~X{|w~}Wv~|l{|v~a{|v~nv~U{|v~}U{}w~}Uv~}d{|v~|l{}v~r{|v~r{|v~|i{}w~}hv~|fu~|v{|v~u{}v~}iv~}c"
- "{|v~|n{|T~]{}w~]T~}^{}w~]T~}^{}w~ rw|V{|w~}sy~}X{|w}u~q}Zw~m{}x~|V{}v~\"{|v~ow~|2{|y~|w{|w~d{|y~|4{}w~}v{|v~?w~"
- "}8{|y~|sw~vw~}q{|y~| Q{}w~ p{|w~|m{}w~|Ux~}w{|x~}W{|v~| i{}w~|v{|v~Ww~|p{|y~|{}x~`{}x~|j{|x~}bw~|p{|y~}{}x~^{"
- "}w~|X{|v~nv~_{|v~nv~_{|v~nv~_{|v~nv~_{|v~nv~_{|w~}nv~|g{}w~}q{|v~Yv~}I{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}"
- "M{}w~}Z{|v~ev~}h{}w~|sv~|vv~iv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|lv~}c{|v~|]{}u~|^v~}t{|w~|p{|v~|i{|v~h{}w~}"
- "f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w~}n{}v~|aw~}s{|s~|[{}h~|\\{}h~|\\{}h~|\\{}h~|\\{}h~|\\{}h~|f{}h~j}v~"
- "jv~|Ov~j}v~|cv~j}v~|cv~j}v~|cv~j}v~|Xv~Lv~Lv~Lv~Wv~|l{|v~`w~}m{|w~}bv~|l{|v~bv~|l{|v~bv~|l{|v~bv~|l{|v~bv~|l{|v"
- "~f{|Z~}fv~|t{}x~|wv~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|]v~sv~|]v~kv~|_v~sv~| Vw~yd~|{w~jw~yd~|{w~j"
- "w~rr~|sw~jw~ru~|pw~jw~pv~}qw~Zw~|Sv~sv~ H{|v~|Rw~}uy~}({|]~}i{}X~|g{}b~|a{}d~|aw~}kw~}aw~}kw~}]{|v~v{|v~X{|v~v{"
- "|w~}b{}x~} pf~ v{|w~ {{|w~t{|x~}P{|y~}r~W{}x~|v{}w~u{}w~mv~r{}u~t{|w~|+{|v~F{}w~|Ox~Z{|Z~},{|m~ x{}w~W{|v~k"
- "{}w~}W{}w~}M{}v~}F{}v~a{|w~|p{}w~}Iv~|au~}mv~}Vv~|Y{|v~}o{|v~|]{}m~|{v~| Z{|r~}| c{|}r~}]{|w~}`{|x~|sw~|nw~|t{}"
- "x~k{}w~}n{}w~}a{}w~}l{|v~|e{}v~M{}w~}f{}v~d{}w~}L{}w~}T{}v~mr|v~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}y{|t~T{}w~}V{}w~|u"
- "{|w~y{}w~tv~|h{}w~|s{|v~vv~i{}v~c{|v~|e{}w~}r|]{}v~c{|v~|g{}w~}q{}v~}K{|t~|Y{|v~|Z{|v~h{}w~}`{}v~tv~|d{|v~w{|w~"
- "|s{}x~}w{}w~}av~}{}v~Q{|v~|R{|u~M{|w~}H{}x~}J{|w~| r{|f~|_w~}k{}w~|bv~|Ov~k{}w~|b`~|Yw~}Vv~k{}w~|`w~}m{|w~}Wv~"
- "Lv~Tq~Iv~_w~}mv~mv~hw~}m{|w~}bv~jv~`v~k{}w~|dv~k{}w~|Zw~}O{}o~}|Sv~S{|w~|m{}w~|^{|w~}s{}w~|b{|w~}w{|w~w{}x~}w{|"
- "w~|\\{|u~}T{}w~|u{|w~}Ru~V{|s~}|Iw~|J{|}s~}d{|w~|s{|}k~|3y~}p{|x~}p{}y~fv~mv~|]v~m{}v~_{|w~}rt~`v~jv~Z{}r~}Xw~}"
- "m{|w~}\\w~}u{|w~}X{|w~}v{}w~}]{|w~|m{}w~|h{|v~|q{}x~}pv~|iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bv~k{}w~|dv~jv"
- "~X{|w~}W{}w~|l{|v~a{}w~}n{}w~}W{|u~T{}w~}U{}w~}d{}v~k{}v~|s{|v~r{}v~h{}w~}hv~|f{|u~|w{|v~v{}u~h{}v~c{|v~|n{|T~]"
- "{}w~]T~|^{}w~]{}U~}^{}w~ s{|w~V{|w~}sy~}S{|v~Pw~|nw~|V{|w~}!{}v~|q{}x~|1y~}vw~|e{}y~ci|]{}w~u{|w~|?w~}7y~}sw~v{"
- "|w~|r{}y~ P{}w~ p{|w~|m{}w~|Ux~}w{|x~}W{|v~| Fi|U{|w~|u{}w~X{}x~}p{|y~}y{}x~a{|w~i{|x~}c{}x~}p{|y~}y{}x~^{}w~|"
- "X{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~}n{}w~}`{}w~|n{}w~}h{|v~p{|v~Y{}v~I{}w~}M{}w~}M{}w~}M{}w~}"
- "D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|s{|v~vv~i{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|l{}v~c{|v~|^{}s~|_"
- "{}v~u{|w~|o{|v~|i{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{|v~|V{}w~}o{|u~`w~}q{}t~|^{|f~|^{|f~|^{|f~|^{|f~|"
- "^{|f~|^{|f~|h{|P~jv~|O`~|c`~|c`~|c`~|Xv~Lv~Lv~Lv~Wv~jv~`w~}m{|w~}bv~jv~bv~jv~bv~jv~bv~jv~bv~jv~f{|Z~}fv~|u{}x~}"
- "vv~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|]{}w~|u{|w~}\\v~k{}w~|_{}w~|u{|w~} Uw~yq}w~r}yw~jw~yd|yw~jw~"
- "sp~|tw~jw~su~|ow~jw~pv~}qw~Zw~|S{}w~}u{|w~} Hv~|Q{}w~|w{|y~|({|\\~iW~|f{}d~|_e~|`w~}kw~}aw~}kw~|]{}w~}uv~Wv~|w{"
- "}w~|b{}x~} q{|g~| v{|w~({}Z~X{|y~|{|}u~}Y{|w~uw~|tw~}o{|w~}q{}u~u{}w~*{|v~F{}w~|*m|}w~l|,{|m~ xw~}W{|v~k{}w"
- "~}W{}w~}N{}v~}Dv~|bw~}o{}w~}Iv~|au~|m{}w~}W{|v~X{}v~m{}v~\\{|p~}xv~| Y{}p~}| i{|}p~}|]{}w~}`{|x~|sw~mw~|t{}x~kv"
- "~}n|}v~a{}w~}kv~}e{}v~M{}w~}f{}v~d{}w~}L{}w~}T{}v~dv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}x{|t~U{}w~}V{}w~|tw~|{w~}tv~|"
- "h{}w~|rv~}wv~i{}v~c{}v~d{}w~}T{}v~c{}v~f{}w~}p{}v~|Ju~}Y{|v~|Z{|v~h{}w~}_v~|v{|v~bv~|x{|w~r{}w~wv~|b{}v~xv~}R{|"
- "v~|Ru~|M{|w~}H{|w~J{|w~| s{|q~t}v~|_w~}k{}w~|bv~Nv~k{}w~|b`~|Yw~}Vv~k{}w~|`w~}m{|w~}Wv~Lv~Tp~Jv~_w~}mv~mv~hw~}"
- "m{|w~}bv~jv~`w~}k{}w~|dv~k{}w~|Zw~}N{|m~|Uv~S{|w~|m{}w~|]v~t{|v~`v~w{}x~}w{|x~}w{}w~[{|u~|T{|w~}u{}w~|S{}v~|V{|"
- "x}t~}Jw~|K{|s~y}|d{|y~}n{|}p~}1y~}p{}w~p{}y~fv~mv~\\v~lv~|`{}w~|r{|v~}`v~jv~\\{|p~}Xw~}m{|w~}\\{}w~u{}w~|Xv~|v{"
- "|v~]{|w~|m{}w~|h{|v~p{}w~pv~}iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bw~}k{}w~|dv~jv~X{|w~}W{}w~|l{|w~}av~|n{|v"
- "~Wu~|T{}w~}U{}v~dv~}k{|v~}s{|v~s{|v~}h{}w~}hv~|e{}u~|x{|v~w{}u~|h{}v~c{}v~l{|u~}\\|]{}w~][|u~|]{}w~\\{}v~}c|u~}"
- "]{}w~ s{|w~V{|w~}sy~}S{|v~P{}x~}o{|w~`{|a~}+u~|rw~|1y~}v{}w~ex~d{|j~}]{}w~}v{|v~|@w~}7y~}sw~u{}w~rx~ P{}w~ p{|"
- "w~|m{}w~|Ux~}w{|x~} w{|j~}V{|v~|v{}w~}Xw~oy~}x{}x~aw~|i{|x~|cw~ox~x{}x~^{}w~|Xv~}n|}v~`v~}n|}v~`v~}n|}v~`v~}n|"
- "}v~`v~}n|}v~a{|b~h{}v~p|}v~Y{}v~I{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{|v~|h{}w~|rv~}wv~i{}v~c{"
- "}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~k{}v~c{}v~^{}q~|`{}v~v{|w~}n{}v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}Z{"
- "|v~|V{}w~}p{|u~|`w~}p{|t~}`{|q~t}v~|_{|q~t}v~|_{|q~t}v~|_{|q~t}v~|_{|q~t}v~|_{|q~t}v~|i{|q~t}`~|kv~N`~|c`~|c`~|"
- "c`~|Xv~Lv~Lv~Lv~Wv~jv~`w~}m{|w~}bv~jv~bv~jv~bv~jv~bv~jv~bv~jv~f{|Z~}fv~u{|x~}uv~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m"
- "{}w~|a{|w~|m{}w~|]{|w~}u{}w~|\\w~}k{}w~|_{|w~}u{}w~| U{}x~|q{}w~q{|w~j{}x~|b{|w~j{}x~|uu~|u~|v{|w~j{}x~|uu~|o{|"
- "w~j{}x~|qv}|r{|w~[{|w~|S{|v~uv~| TZ~}a{|w~}wx~'{|\\~iW~|ee~|^{|g~}_w~}kw~}aw~}kw~|]v~|u{}w~|X{}w~}wv~|b{|w~| "
- " r{}g~ u{|w~({}Z~X{|y~|w{}v~|Zw~|v{|w~s{|w~o{|w~}p{}u~vw~})v~Fv~| w{}w~ x{|m~ y{|w~|Vv~|lv~|W{}w~}O{}v~}C{}w~}"
- "c{|w~n|}w~}v|N{}w~}au~l{|v~Wv~}Xv~}m{|v~|[{|y}w~y}|x{|v~ V{|}p~}|XY|X{|}q~}|Z{}w~}`{|x~|sw~mw~|tw~l{|b~|b{}w~}k"
- "{}v~e{|v~|N{}w~}fv~}d{}w~}L{}w~}T{|v~|ev~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}w{|t~V{}w~}V{}w~|t{}w~|w~|tv~|h{}w~|r{|v~"
- "wv~i{|v~|d{}v~d{}w~}T{|v~|d{}v~f{}w~}o{}v~J{|u~Y{|v~|Z{|v~h{}w~}_{}w~}v{}w~}b{}w~}x{}x~}r{|w~wv~b{|v~|x{|v~|S{|"
- "v~|S{}v~|L{|w~}Gw~|K{|w~| t{|u~}|q{}w~|_v~k{}w~|bv~Nv~k{}w~|b`~|Yw~}Vv~k{}w~|`w~}m{|w~}Wv~Lv~Tw~}|u~Kv~_w~}mv~"
- "mv~hw~}m{|w~}bv~jv~`w~}k{}w~|dv~k{}w~|Zw~}L{|}o~}Vv~S{|w~|m{}w~|]{}w~}u{}w~}`{}w~|xw~|w{|w~wv~\\{|s~Sv~uv~S{}v~"
- "|O{}v~}Kw~|L{|v~}|_{|~|j{|y}x~y}|/x~q{|v~}qx~fv~m{}x~}\\v~l{}w~|`v~pv~}`v~jv~]n~}Xw~}m{|w~}\\{|w~|vv~X{|v~t{}w~"
- "|^{|w~|m{}w~|h{|v~p{}w~pv~|iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bw~}k{}w~|dv~jv~X{|w~}W{}w~}l{}w~}b{|v~lv~|Y"
- "{}v~|S{}w~}U{|v~}f{|v~|ju~|t{|v~s{}v~|h{}w~}hv~|dt~}y{|v~y{|t~|g{|v~|d{}v~k{|u~|?{}w~>u~|b{|v{}w~[{}v~|e{}v~}\\"
- "{}w~ s{|w~V{|w~}sy~}S{|v~P{|w~o{}x~}`{|a~}+{|u~}|u{|w~0{}y~v{|w~}g{|y~}d{|j~}\\{}v~|w{|v~}Aw~}7{}y~sw~tw~}t{|y~"
- "} P{}w~ p{|w~|m{}w~|Ux~}w{|x~} w{|j~}W{|v~|vv~}X{}x~|p{}y~|x{}x~b{}x~}hw~c{}x~}p{}y~|x{}x~^v~X{|b~|b{|b~|b{|b"
- "~|b{|b~|b{|b~|b{}b~}id~Y{|v~|J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~f{}v~g{}w~|r{|v~wv~i{|v~|d{}v"
- "~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~k{|v~|d{}v~_{}v~}u~|a{|v~|ww~}m{}v~h{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w~}f{|v~h{}w"
- "~}Z{|v~|V{}w~}sy|s~_w~}n{}u~|b{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|`{|u~}|q{}w~|j{|u"
- "~}|q{}a~|kv~N`~|c`~|c`~|c`~|Xv~Lv~Lv~Lv~Wv~jv~`w~}m{|w~}bv~jv~bv~jv~bv~jv~bv~jv~bv~jv~.v~v{|w~tv~a{|w~|m{}w~|a{"
- "|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|\\v~uv~[w~}k{}w~|^v~uv~ T{}x~}q{}w~q{|x~}j{}x~}b{|x~}j{}x~}vu~|{|u~|w{|x~}j{}"
- "x~}vu~|n{|x~}j{}x~}b{|x~}[{|w~Qv~|w{|v~ SZ~}`v~x{|y~}'{|]~}iW~|e{|g~}\\{}i~}^w~}kw~}aw~}l{|w~|^{|v~t{|w~}X{|v~x"
- "{|v~`w~} m{|v~ jw|({}Z~X{|y~|v{}w~}[{}x~}u{}x~}s{|w~o{}w~}o{}u~x{|w~|)v~Fv~ v{}w~ g{}w~Uv~|lv~|W{}w~}P{}v~"
- "}B{|v~c{|_~|O{}w~}a{}v~l{|v~X{|v~|Y{|v~|lv~|N{|v~ S{|}p~|[{|Z~}[{|}p~}|X{}w~}`{|x~|sw~|nw~|u{|x~}l{}b~}b{}w~}k{"
- "|v~e{|v~}N{}w~}g{|v~}d{}w~}L{}w~}T{|v~}ev~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}v{|t~W{}w~}V{}w~|t{|r~sv~|h{}w~|q{}w~}xv"
- "~i{|v~}dv~}d{}w~}T{|v~}dv~}f{}w~}nv~}J{}v~Y{|v~|Z{|v~|i{}w~}_{|v~vv~|b{}w~}xw~|qw~|y{|v~bv~}v{}v~S{|v~|T{}v~}K{"
- "|w~}G{}x~}K{|w~| tv~}n{}w~|_v~kv~|bv~|Ov~k{}w~|bv~Bw~}Vv~k{}w~|`w~}m{|w~}Wv~Lv~Tw~}{|u~|Mv~_w~}mv~mv~hw~}m{|w~"
- "}bv~|kv~`v~k{}w~|dv~k{}w~|Zw~}Iy|}q~Wv~S{|w~|m{}w~|]{|v~uv~_{|w~|xw~uw~|y{|w~}\\r~}T{|w~|w{}w~}T{}v~|M{|v~Kw~|L"
- "{}w~} O{}y~|rt~|s{|y~}fv~|nw~}\\v~l{|w~}`w~}p{}w~|`v~|kv~^u~}|Qw~}m{|w~}[w~}w{}w~}X{}w~|t{|w~}^{|w~|m{}w~|h{|v~"
- "pv~pv~|iv~t{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~}bv~k{}w~|dv~|l{|v~X{|w~}W{|w~}l{}w~|b{}w~}l{}w~}Z{|v~}R{}w~}T{}v"
- "~f{}v~i{|u~t{|v~t{|u~g{}w~}hv~|cr~}v~}s~}f{|v~}dv~}j{|u~|@{}w~?u~|b{}~|w{}w~vy~a{}v~|g{}v~}b{}~|w{}w~vy} {{}w~|"
- "W{|w~}sy~}S{|v~Ow~}q{|w~|`{|a~}){}u~}vw~}0{|y~}v{}w~}p{|t{}y~|d{|j~}[{|v~|vv~}Bw~}7{|y~}tw~t{|w~|u{}y~| P{}w~ "
- "p{|w~|m{}w~|Ux~}w{|x~} w{|j~}X{}v~v{|v~}X{|w~p{|y~|w{}x~bw~h{}x~|d{|w~p{|y~}w{}x~^v~X{}b~}b{}b~}b{}b~}b{}b~}b{"
- "}b~}b`~j{}d~Y{|v~}J{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~fv~}g{}w~|q{}w~}xv~i{|v~}dv~}k{|v~}dv~}k"
- "{|v~}dv~}k{|v~}dv~}k{|v~}dv~}`{}v~|{|u~|b{|v~}x{}x~}lv~}h{|v~|i{}w~}f{|v~|i{}w~}f{|v~|i{}w~}f{|v~|i{}w~}Z{|v~|V"
- "{}e~|_w~}m{|u~bv~}n{}w~|`v~}n{}w~|`v~}n{}w~|`v~}n{}w~|`v~}n{}w~|`v~}n{}w~|jv~}n{}w~Tv~|Ov~Lv~Lv~Lv~Av~Lv~Lv~Lv~"
- "Wv~|l{|v~`w~}m{|w~}bv~|kv~bv~|kv~bv~|kv~bv~|kv~bv~|kv~.v~vw~|u{|v~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}"
- "w~|\\{|w~|w{}w~}[v~k{}w~|^{|w~|w{}w~} T{|w~q{}w~q{}x~|j{|w~b{}x~|j{|w~wu~|x{|u~|x{}x~|j{|w~wu~|m{}x~|j{|w~b{}x~"
- "|[{|w~Q{|w~}w{}w~} SZ~}`{}w~|y{}y~|'{|n~y}~|n~}i{}k~x}k~c{|i~}Z{}j~]w~}kw~}a{}w~l{|w~|^{}w~}sv~Wv~|y{}w~}`{}w~|"
- " mv~| o{}Z~X{|y~|v{|w~}\\{|w~t{}x~|rw~|p{}w~}n{}u~yw~}(v~|Gv~ v{}w~ gw~}U{}w~}m{|v~V{}w~}Q{}v~}A{|v~c{|_~"
- "|O{}w~}a{}v~l{|v~X{}v~X{|v~k{}w~}N{}w~} Q{|}p~}|^{|Z~}^{|}p~}|U{}w~}`{|x~}sw~|o{|w~|u{}x~|l`~b{}w~}k{|v~|eu~N{}"
- "w~}g{}v~|d{}w~}L{}w~}Su~ev~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}u{|t~X{}w~}V{}w~|ss~}sv~|h{}w~|q{|v~|yv~hu~e{|v~|d{}w~}"
- "Su~e{|v~|f{}w~}n{}v~|K{|v~|Z{|v~|Yv~|i{}w~}^v~|x{}v~a{|v~y{|w~|q{}x~}y{}w~}c{}v~tv~}T{|v~|U{|v~}J{|w~}G{|w~K{|w"
- "~| u{|v~m{}w~|_v~kv~a{}w~|O{}w~|l{}w~|bv~|Cw~}V{}w~|l{}w~|`w~}m{|w~}Wv~Lv~Tw~}y{|u~|Nv~_w~}mv~mv~hw~}m{|w~}bv~"
- "|l{|v~`v~kv~cv~|l{}w~|Zw~}D{|}u~}Xv~S{|w~|m{}w~|\\{}w~|w{|w~}^w~}y{|w~u{}x~}y{}w~|]{}q~|Tv~wv~|U{|v~}K{}w~|Lw~|"
- "Lv~ N{|x~s{}x~{w~|tx~|fv~|o{|v~\\v~l{|w~}a{|w~|p{}w~_{}w~|l{|v~_{}v~|Ow~}m{|w~}[{}w~|xv~X{|v~rv~|_{|w~|m{}w~|h{"
- "|v~|qv~pv~|iv~|u{}w~t{}w~|b{|w~}q{|w~}`v~t{}w~t{|w~|bv~kv~c{}w~|l{|v~X{|w~}Vv~l{}w~|bv~|l{|v~[{|v~}Q{}w~}T{|v~}"
- "h{|v~|hu~}u{|v~u{|u~|g{}w~}hv~|b{}f~|du~e{|v~|i{|u~|A{}w~@u~|b{}x~|x{}w~ww~a{}v~|i{}v~}b{}x~|x{}w~w{}y~} {}w~|W"
- "{|v~sy~}S{|v~O{|w~}s{}w~}^q|}v~q|'{}t~|{|w~}.x~u{}v~}|wy|}y~tx~/{|v~|v{}w~}Cw~}6x~tw~s{}w~ux~ O{}w~ p{|w~|m{}w"
- "~|Ux~}w{|x~} B{}w~}v{|v~|Ww~|q{|y~}v{}x~c{}x~|i{}x~}cw~|q{|y~}v{}x~_{|v~X`~b`~b`~b`~b`~c{|`~|kc~Xu~J{}w~}M{}w~"
- "}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~g{|v~}g{}w~|q{|v~|yv~hu~e{|v~|ju~e{|v~|ju~e{|v~|ju~e{|v~|ju~e{|v~|a{}"
- "v~|x{|u~|bu~y{}w~l{|v~|gv~|i{}w~}ev~|i{}w~}ev~|i{}w~}ev~|i{}w~}Z{|v~|V{}f~|^w~}l{|v~|d{|v~m{}w~|a{|v~m{}w~|a{|v"
- "~m{}w~|a{|v~m{}w~|a{|v~m{}w~|a{|v~m{}w~|k{|v~m{}w~T{}w~|Ov~|Mv~|Mv~|Mv~|Bv~Lv~Lv~Lv~W{}w~|l{|v~`w~}m{|w~}bv~|l{"
- "|v~bv~|l{|v~bv~|l{|v~bv~|l{|v~bv~|l{|v~.v~|x{}x~|t{|v~a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|a{|w~|m{}w~|[v~wv~|[v"
- "~kv~\\v~wv~| Sw~|r{}w~r{|w~hw~|d{|w~hw~|yu~|v{|u~y{|w~hw~|yu~|m{|w~hw~|d{|w~Z{|w~Pv~wv~| SZ~}_w~}yx~%n~{|~{|o~|"
- "i{|l~}|}|l~}b{}j~Xk~|]w~}kw~}a{}w~l{}w~]v~|s{}w~|X{}w~}yv~|_w~} mv~} g{}x~}t{}x~}O{|y~|uw~}\\{}x~|t{}x~|rw"
- "~|p{|w~}m{}u~}w~|({}w~|H{|w~} v{}w~ h{|w~|U{}w~}m{|v~V{}w~}R{}v~}@{|v~c{|_~|Ov~|a{|v~l{}w~}Xv~}X{|v~k{}w~}Nv~|"
- " N{|}p~}|a{|Z~}a{|}p~}|R{|w}|_x~}s{}x~}o{}w~|v{|w~l{}`~|c{}w~}k{|v~|e{}v~|O{}w~}gu~c{}w~}L{}w~}S{}v~|fv~|h{}w~}"
- "hv~|Y{}w~}M{}w~}W{}w~}t{|t~Y{}w~}V{}w~|s{}t~rv~|h{}w~|p{}w~}yv~h{}v~|f{}v~c{}w~}S{}v~|f{}v~e{}w~}mv~}K{|v~|Z{|v"
- "~|Yv~|iv~|^{}w~}xv~}`v~|{|w~p{}w~yv~|d{|v~|t{|v~|U{|v~|V{|u~I{|w~}Fw~|L{|w~| u{}w~|mv~|_v~|m{|v~a{}w~}O{}w~|lv"
- "~|b{}w~|Cw~}V{}w~|lv~|`w~}m{|w~}Wv~Lv~Tw~}x{|u~|Ov~_w~}mv~mv~hw~}m{|w~}b{}w~|l{|w~}`v~kv~c{}w~|lv~|Zw~}B{|u~Xv~"
- "S{|w~|m{}w~|\\{|w~}w{}w~|^v~y{}x~}u{|x~}y{}w~]{|v~|}v~T{}w~|y{|w~}U{|v~}J{|w~}Lw~|M{|w~} Mx~}v{|w~|{|w~|v{}x~e{"
- "}w~|o{}v~\\v~l{|w~}a{|w~|pw~}_{}w~|l{|w~}_v~Mw~}m{|w~}[{|w~}y{|w~}X{}w~|r{}w~}_{|w~|m{}w~|h{|v~|qv~|r{|v~|i{}w~"
- "|u{}w~tv~|b{|w~}q{|w~}`v~t{}w~t{}w~|bv~kv~c{}w~|l{|w~}X{|w~}Vv~lv~b{}v~jv~|\\u~P{}w~}S{}v~|iu~g{|t~|w{|v~v{}u~}"
- "f{}w~}hv~|a{}h~|c{}v~|f{}v~g{|u~|B{}w~Au~|b{}v~|y{}w~xu~a{}v~|k{}v~}b{}v~|y{}w~x{}w~}!{}w~|Vv~sy~}S{|v~O{|u~}y|"
- "{y|u~}T{|w~}Lw}|P{|}p~}-{|y~}u{}l~u{}y~|.{|v~|v{}w~}Dw~}6{|y~}uw~rw~}w{}y~| O{}w~ p{|w~|m{}w~|Ux~}w{|x~} C{}w"
- "~}v{|v~|W{}x~}px~u{}x~d{|w~i{}x~}c{}x~}px~u{}x~_{}w~}Y{}`~|d{}`~|d{}`~|d{}`~|d{}`~|d{}w~}j|}w~}l{|c~X{}v~|K{}w~"
- "}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~gu~|g{}w~|p{}w~}yv~h{}v~|f{}v~i{}v~|f{}v~i{}v~|f{}v~i{}v~|f{}v~"
- "i{}v~|f{}v~a{}v~|v{|u~|c{}v~|}w~|l{}v~fv~|iv~|ev~|iv~|ev~|iv~|ev~|iv~|Z{|v~|V{}h~}\\w~}k{}w~|d{}w~|mv~|a{}w~|mv"
- "~|a{}w~|mv~|a{}w~|mv~|a{}w~|mv~|a{}w~|mv~|k{}w~|mv~|U{}w~}O{}w~|M{}w~|M{}w~|M{}w~|Bv~Lv~Lv~Lv~W{}w~}l{}w~}`w~}m"
- "{|w~}b{}w~|l{|w~}b{}w~|l{|w~}b{}w~|l{|w~}b{}w~|l{|w~}b{}w~|l{|w~}.{}w~|y{}x~|s{|w~}a{|w~|m{}w~|a{|w~|m{}w~|a{|w"
- "~|m{}w~|a{|w~|m{}w~|[{}w~|y{|w~}Zv~kv~\\{}w~|y{|w~} R{|w~r{}w~rw~}h{|w~dw~}h{|w~y{|w~|t{|w~}yw~}h{|w~y{|w~|lw~}"
- "h{|w~dw~}Z{|w~P{}w~|y{|w~} Rs}v~g}|_{}w~{|y~}%{|p~|{|~yp~}g{}m~{}~{}m~|a{}l~|X{|m~}\\w~}kw~}a{|w~|mv~]v~r{}w~}X"
- "{|v~{|v~^{}w~} n{}v~ gw~|tw~|O{|y~|uw~}]{|x~}sw~|rw~|p{|v~l{}r~}'{|w~}H{|w~} v{}w~ h{|w~T{|v~m{}w~}V{}w~}"
- "S{}v~}?{|v~c{|_~|Ov~|`v~|m{}w~}Y{|v~W{|v~k{}w~}O{|v~ J{|}p~}|d{|Z~}d{|}p~}|-w~s{|w~ov~|v{}x~|lv~|j{|v~c{}w~}k{}"
- "v~cv~}O{}w~}h{}v~|c{}w~}L{}w~}Rv~}fv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}rt~Z{}w~}V{}w~|ru~}rv~|h{}w~|p{|v~|{v~h{|v~}g"
- "{|v~}c{}w~}Rv~}g{|v~}e{}w~}m{|v~|L{|v~|Z{|v~|Y{}w~}j{|v~|^{|v~|{|v~_{}w~}{}x~}p{|w~yv~cv~}r{}v~U{|v~|W{|u~|I{|w"
- "~}F{}x~}L{|w~| u{}w~|n{|v~|_v~}m{}w~}a{|w~}O{|w~}m{|v~|b{}w~}Cw~}V{|w~}m{|v~|`w~}m{|w~}Wv~Lv~Tw~}vu~|Pv~_w~}mv"
- "~mv~hw~}m{|w~}b{|w~}l{}w~}`v~|m{|w~}c{|w~}lv~|Zw~}@v~|Yv~S{|w~}mv~|[v~wv~]{}w~|{w~|u{|w~yw~}]v~}y{}v~U{|w~}y{}w"
- "~|V{|v~}I{|w~}Lw~|M{|w~} M{|w~x}v~}x{}v~x}w~|e{}w~}ou~|]v~l{|w~|a{|w~p{}w~|_{|w~}l{}w~}`{|w~}Mw~}m{|w~}Zv~y{}w~"
- "|Y{|v~q{|v~_{|w~}m{}w~|gv~|r{|v~|r{|v~h{}w~}u{}w~tv~a{|w~}q{|w~}`v~t{}w~t{}w~|bv~|m{|w~}c{|w~}l{}w~}X{|w~}V{}w~"
- "|n{|w~}bv~}j{}v~]{}v~|P{}w~}Ru~j{}v~|f{|t~}|y{|v~x{|t~}e{}w~}hv~|`{|}l~}`v~}g{|v~}f{|u~|C{}w~Bu~|`u~|{}w~yu~|`{"
- "}v~|m{}v~}a{|u~|{}w~y{}v~}!{}w~|Vv~|ty~}S{|v~P{|g~}U{|w~}Lw~|N{|r~}+{}y~|u{|}o~}v{|y~}+v~}v{}v~Ew~}5{}y~|vw~r{|"
- "w~|y{|y~} N{}w~ p{|w~}m{}w~|Ux~}w{|x~} Dv~}v{}v~|W{|w~p{}y~|u{}x~dw~|j{}w~c{|w~p{}y~|u{}x~`{}v~|Yv~|j{|v~dv~|"
- "j{|v~dv~|j{|v~dv~|j{|v~dv~|j{|v~dv~|j{|v~l{}w~}n{|v~Wv~}K{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~h{"
- "|v~}f{}w~|p{|v~|{v~h{|v~}g{|v~}i{|v~}g{|v~}i{|v~}g{|v~}i{|v~}g{|v~}i{|v~}g{|v~}b{}v~|t{|u~|cq~|l{|v~}f{}w~}j{|v"
- "~|e{}w~}j{|v~|e{}w~}j{|v~|e{}w~}j{|v~|Z{|v~|V{}k~}|Zw~}k{}w~}d{}w~|n{|v~|a{}w~|n{|v~|a{}w~|n{|v~|a{}w~|n{|v~|a{"
- "}w~|n{|v~|a{}w~|n{|v~|k{}w~|n{|v~}U{|w~}O{}w~}M{}w~}M{}w~}M{}w~}Bv~Lv~Lv~Lv~W{|v~lv~|`w~}m{|w~}b{|w~}l{}w~}b{|w"
- "~}l{}w~}b{|w~}l{}w~}b{|w~}l{}w~}b{|w~}l{}w~}Xt|X{}w~}{}x~}r{}w~}a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|[{|w~}y"
- "{}w~|Zv~|m{|w~}\\{|w~}y{}w~| Qw~}s{}w~s{}w~fw~}f{}w~fw~}y{|y~|r{|y~}y{}w~fw~}y{|y~|l{}w~fw~}f{}w~Y{|w~P{|v~y{}w"
- "~| Kv~}J{|w~|}y~|${}r~}y{}~y{|q~f{|n~|{}~yn~}_m~|V{|o~}[w~}kw~}`w~}n{|w~}^{|w~}r{|v~Wv~{}w~}]v~| o{|v~| hw"
- "~t{|w~N{|y~|uw~}]w~|s{}x~|rw~|ov~|l{}s~&{|w~}H{}w~| v{}w~ h{}x~}Sv~|nv~|V{}w~}T{}v~}>{}w~}Q{}w~}J{}v~_{}w~}mv~"
- "}Y{}w~}Vv~|lv~|Ov~} G{|}p~}|0{|}o~}*{}x~rw~}q{}v~|w{}w~l{|v~hv~|d{}w~}ku~c{}v~}P{}w~}i{}u~b{}w~}L{}w~}R{}v~|gv~"
- "|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}qt~[{}w~}V{}w~|r{}v~|rv~|h{}w~|o{}w~}{v~g{}v~|hu~|c{}w~}R{}v~|hu~|e{}w~}lv~}L{}v~Y"
- "{|v~|Y{}v~j{}v~\\{}w~}{}w~}_{|v~{w~|ow~y|v~d{}v~pv~}V{|v~|Wu~|H{|w~}F{|w~L{|w~| u{}w~m{}v~|_u~mv~|a{|v~Nv~|n{}"
- "v~|b{|v~Cw~}Uv~|n{}v~|`w~}m{|w~}Wv~Lv~Tw~}uu~|Qv~_w~}mv~mv~hw~}m{|w~}b{|v~lv~|`v~}m{}w~}c{|v~m{|v~|Zw~}@{}w~|Yv"
- "~S{|w~}mv~|[{}w~|y{|w~}]{|w~}{w~sw~y|w~}^{}w~}wv~}U{}w~yv~Uv~}Gw~}Lw~|M{|w~| L{|q~}v{}q~|d{|w~}p{|u~|]v~l{}w~|a"
- "{|w~pv~^{|v~lv~|`{|w~|Mw~}m{|w~}Z{}w~y|v~X{}w~}pv~|`{|w~}mv~|gv~}r{|v~}r{}v~h{|v~u{}w~u{|w~}a{|w~}q{|w~}`{}w~|u"
- "{}w~tv~av~}m{}w~}c{|v~lv~|X{|w~}V{|w~}n{}w~|c{|v~i{|v~|_{}v~}O{}w~}R{|v~}l{}v~|d{|r~y}v~y}s~}d{}w~}hv~|]{|}s~y}"
- "|^{}v~|hu~|e{|v~}C{}w~C{}v~|^u~|}w~{}v~|^{}v~n{|v~}_{|u~|}w~{}v~} {}w~|V{}w~}ty~}S{|v~Q{}e~}V{|w~}Lw~|L{|t~*{|x"
- "~|t{|y}u~}|u{|x~|*{}w~|v{|v~Fw~}5{|x~|ww|qw|y{|x~| >{|w~}mv~|Ux~}w{|x~} Ev~}v{|v~U{}x~|q{|y~}t{}x~e{}x~}j{}w"
- "~b{}x~|q{|y~}t{}x~a{}v~}Y{|v~hv~|f{|v~hv~|f{|v~hv~|f{|v~hv~|f{|v~hv~|f{}v~hv~|n{|v~|n{|v~W{}v~}L{}w~}M{}w~}M{}w"
- "~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~i{|u~|f{}w~|o{}w~}{v~g{}v~|hu~|h{}v~|hu~|h{}v~|hu~|h{}v~|hu~|h{}v~|hu~|c{}"
- "v~|r{|u~|d{}s~|ku~|f{}v~j{}v~d{}v~j{}v~d{}v~j{}v~d{}v~j{}v~Y{|v~|V{}w~}r|Vw~}k{|w~|d{}w~m{}v~|a{}w~m{}v~|a{}w~m"
- "{}v~|a{}w~m{}v~|a{}w~m{}v~|a{}w~m{}v~|k{}w~m{}u~U{|v~O{|v~M{|v~M{|v~M{|v~Bv~Lv~Lv~Lv~Vv~|n{|v~_w~}m{|w~}b{|v~lv"
- "~|b{|v~lv~|b{|v~lv~|b{|v~lv~|b{|v~lv~|X{}u~X{|v~|x~}qv~|a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|a{|w~}mv~|Z{}w~yv~Yv~}m{}"
- "w~}[{}w~yv~ P{|w~}t{}w~t{|w~}f{|w~}h{|w~}f{|w~}yy|p{|}y{|w~}f{|w~}yy|l{|w~}f{|w~}h{|w~}Y{|w~Ov~y|v~ K{}w~}Hw~}y"
- "~}\"{}t~}x{}~x{|s~d{|p~}y{}~y{|p~}]o~|T{}p~Zw~}kw~}`{}w~|o{}w~|^{}w~|qv~|X{}w~|v~|]{|v~| o{}v~j{} {|x~}t{|"
- "x~}N{|y~|v{}w~}^{}x~}r{}x~}rw~|o{}v~k{}u~|%v~Hv~ u{}w~ hw~|S{}v~o{}v~U{}w~}U{}v~}>{|v~}Q{}w~}Ju~_{|v~n{|v~|Z{|"
- "v~|Vv~}m{|v~|P{}v~ C{}o~}4{|o~}|({|x~}s{}w~}s{}u~|x{}w~|l{}w~}h{}w~}d{}w~}l{|v~}bu~|g{|}g{}w~}j{}u~|b{}w~}L{}w~"
- "}R{|u~|hv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}pt~\\{}w~}V{}w~|r{|v}qv~|h{}w~|nv~|v~g{|u~|j{}v~}b{}w~}R{|u~i{}v~}d{}w~}"
- "l{|v~|M{}v~Y{|v~|Y{|v~|kv~}\\{|v~{v~|_{|v~|w~|o{}x~y}w~}e{|v~|p{|v~|W{|v~|X{}v~}G{|w~}F{|w~|M{|w~| u{}w~|nu~|_"
- "u~}o{}v~_v~}O{}w~}o{|u~|av~}Dw~}U{}w~}o{|u~|`w~}m{|w~}Wv~Lv~Tw~}t{}v~|Rv~_w~}mv~mv~hw~}m{|w~}av~|n{|v~_u~mv~|bv"
- "~|n{}v~|Zw~}@{}w~|Yv~Rv~n{}v~|[{|w~}y{}w~|\\w~}|x~}s{}x~y}w~|_{}v~v{|v~|V{|w~y}w~}Vu~Fw~}Lw~|M{|w~| K{|s~}t{}s~"
- "|bv~p{}u~}]v~|mv~`{|w~q{}w~}]v~}n{}v~_{|w~|Mw~}m{|w~}Yw~y}w~|Xv~o{|w~}`{|v~n{|v~|g{}v~r{}u~rv~}gv~|v{}w~uv~|a{|"
- "w~}q{|w~}`{|w~}u{}w~u{|v~au~mv~|bv~}n{}v~Vv~Uv~nv~b{}w~}hv~}`{|v~}N{}w~}Q{|v~}n{}v~}b{|c~}c{}w~}hv~|Z{|v~Z{|u~|"
- "j{}v~}c{|w~B{}w~B{}x~|\\u~}w~}v~|\\{}x~|m{}x~}]{|u~}w~}v~} {{v~|V{|v~|uy~}S{|v~R{}v~y|q~}|u~W{|w~}Lw~|J{}u~*{|x"
- "~|e{|x~|({}x~}u{|w~F{|x}|4{|x~|e{|x~| ={|v~n{|v~|Ux~}w{|x~} Ew~|u{|x~}U{|x~}p{}j~}iw~j{}w~b{|x~}p{}j~}f{}v~}"
- "X{}w~}h{}w~}f{}w~}h{}w~}f{}w~}h{}w~}f{}w~}h{}w~}f{}w~}h{}w~}fv~}h{}w~}n{}w~}m{|v~Vu~|g{|}c{}w~}M{}w~}M{}w~}M{}w"
- "~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~j{|u~}e{}w~|nv~|v~g{|u~|j{}v~}g{|u~|j{}v~}g{|u~|j{}v~}g{|u~|j{}v~}g{|u~|j{}v~}c{"
- "}v~|p{|u~|e{|t~}k{}v~}e{|v~|kv~}d{|v~|kv~}d{|v~|kv~}d{|v~|kv~}Y{|v~|V{}w~}Mw~}k{}w~|d{}w~|nu~|a{}w~|nu~|a{}w~|n"
- "u~|a{}w~|nu~|a{}w~|nu~|a{}w~|nu~|k{}w~|nt~|Uv~}Ov~}Mv~}Mv~}Mv~}Cv~Lv~Lv~Lv~V{}v~nv~}_w~}m{|w~}av~|n{|v~`v~|n{|v"
- "~`v~|n{|v~`v~|n{|v~`v~|n{|v~W{}u~Wr~q{|v~_v~n{}v~|`v~n{}v~|`v~n{}v~|`v~n{}v~|Z{|w~y}w~}Yu~mv~|[{|w~y}w~} O{}w~}"
- "u{}w~u{}w~}d{}w~}j{}w~}d{}w~}j{}w~}d{}w~}j{}w~}d{}w~}j{}w~}X{}w~O{|w~y}w~} L{}w~}G{}u~|!{|}x~}|w{}~v{}w~}b{|r~|"
- "x{}~w{}s~|\\{|q~}Rq~|Zw~}kw~}`{|v~p{|v~]v~p{}w~}X{|q~[{}v~} p{|v~}ly}$v}|\"{}x~}t{}x~}Yy}|s{|y~|w{|v~|_{|w~"
- "q{}x~}s{|w~n{|v~}l{|u~}%{}w~|Iw~} u{}w~L{}w~} tv}|P{|w~R{|v~|pv~}U{}w~}V{}v~}={}v~|Q{}w~}K{}v~|^v~|o{}v~Y{}v~U{"
- "}v~m{}v~P{|v~}U{|v}M{}w~}F{|}q~}6{|q~}|G{|w}|^w~ru~y|x{|}t~y|}v~|kv~|h{|v~d{}w~}m{|u~|b{|u~|i{|~}g{}w~}l{|}u~}a"
- "{}w~}L{}w~}Q{}u~|iv~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}ot~]{}w~}V{}w~|bv~|h{}w~|n{}q~f{}u~k{}u~a{}w~}Q{}u~k{|u~c{}w~}"
- "kv~}c{|}h{|v~}Y{|v~|X{}v~l{}v~|[v~}v~]v~}w~n{}r~|ev~}n{}v~W{|v~|Y{}v~}F{|w~}Ew~}M{|w~| u{}w~|o{}u~|_t~|q{|v~}_"
- "{|v~|P{|v~}pt~|a{|v~|Ew~}U{|v~|pt~|`w~}m{|w~}Wv~Lv~Tw~}s{}v~|Sv~_w~}mv~mv~hw~}m{|w~}a{}v~nv~}_u~}o{}v~a{}v~o{|u"
- "~|Zw~}@{}w~|Y{}w~|Sv~|p{|u~|Zv~{|v~[v~}x~}s{|r~_{|v~|u{}v~Uq~V{}v~|Fw~}Lw~|M{|w~| I{|y}~y}|r{|}x~}|`{}w~}qs~]u~"
- "n{|v~`{|w~r{|v~\\{|v~nv~}_{|w~}Mw~}m{|w~}Y{}r~X{}w~}nv~`{|v~|o{}v~|g{|v~|st~|t{|v~|g{}v~v{}w~v{|v~`{|w~}q{|w~}_"
- "v~|v{}w~uv~}au~}o{}v~a{|v~nv~}Vv~U{|w~}p{}w~}bv~|h{|v~`u~M{}w~}P{|u~|q{}v~}_{}g~}|b{}w~}hv~|Z{|v~Y{}u~k{}u~a{|y"
- "~A{}w~A{}~|Zl~|Z{}~|k{}~}[{|l~} yv~}Uv~}uy~}S{|v~S{}v~|x{|y}x~}|wu~X{|w~}Lw~|I{|v~}*{}x~|g{|x~}&{}y~}t{|x~ T{}x"
- "~|g{|x~} <{|v~|o{}v~|Ux~}w{|x~} Ex~|t{|y~}Tw~|p{}j~}j{}x~|k{}x~}aw~|p{}j~}g{}v~}Wv~|h{|v~fv~|h{|v~fv~|h{|v~f"
- "v~|h{|v~fv~|h{|v~g{|v~g{|v~|ov~|m{|v~V{|u~|i{|~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~k{}t~d{}w~"
- "|n{}q~f{}u~k{}u~e{}u~k{}u~e{}u~k{}u~e{}u~k{}u~e{}u~k{}u~c{}v~|n{|u~|e{}u~|l{}u~c{}v~l{}v~|c{}v~l{}v~|c{}v~l{}v~"
- "|c{}v~l{}v~|Y{|v~|V{}w~}Mw~}kv~c{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|a{}w~|o{}u~|k{}w~|o{"
- "}s~U{|v~|P{|v~|N{|v~|N{|v~|N{|v~|Dv~Lv~Lv~Lv~Uv~}p{}v~^w~}m{|w~}a{}v~nv~}`{}v~nv~}`{}v~nv~}`{}v~nv~}`{}v~nv~}W{"
- "}u~W{}t~|qv~}_v~|p{|u~|`v~|p{|u~|`v~|p{|u~|`v~|p{|u~|Yq~Xu~}o{}v~Yq~ M{}w~}|w{}x~}v{}v~b{}w~}|m{}v~b{}w~}|m{}v~"
- "b{}w~}|m{}v~b{}w~}|m{}v~W{}x~}Nq~| M{|v~F{|u~ py~|V{|}y~y}|vy~|w{|y}y~y}Y{|s~}Q{|s~|Yw~}kw~}_{}v~|s{}v~|^{|w~}p"
- "{|v~X{|r~}Z{}u~} q{}v~}o{|y~}$v~}\"w~|tw~|Y{}y~}|u{|y~|x{|u~^{}x~|q{|w~s{}x~}mu~}n{|s~}&{|w~|J{|w~| u{}w~L{"
- "}v~ u{|v~|P{}x~}Q{}v~|r{}v~T{}w~}W{}v~}O{}|k{}v~}P{}w~}]{}|l{}u~]{|v~|q{}v~|Yv~}U{|v~}o{}v~}Q{|v~}T{}v~M{}v~C{|"
- "}t~}6{|t~}|D{}w~}^{}x~|s{|m~y}q~|k{|v~fv~|e{}w~}n{|u~}`{}u~|l{|}y~}g{}w~}n{|}t~}`{}w~}L{}w~}P{}u~}jv~|h{}w~}hv~"
- "|Y{}w~}M{}w~}W{}w~}nt~^{}w~}V{}w~|bv~|h{}w~|mq~e{}u~|n{}u~|a{}w~}P{}u~|n{}u~|c{}w~}k{|v~|d{|y~}k{|u~|Y{|v~|X{|u"
- "~n{|u~Z{}r~}]{}s~}n{|r~e{}v~lv~}X{|v~|Z{|u~E{|w~}E{}w~M{|w~| u{|v~p{|t~|_s~|s{|u~]u~|P{}v~}s{|s~|`u~|k{|Ww~}T{"
- "}v~}s{|s~|`w~}m{|w~}Wv~Lv~Tw~}r{}v~}Tv~_w~}mv~mv~hw~}m{|w~}`v~}p{}v~|_t~|q{|v~|`v~}q{|t~|Zw~}Q{|kv~|Y{}w~}S{}v~"
- "pt~|Z{}w~y}w~}[{}s~|rs~}_v~}s{}w~}V{}s~}W{}v~|Ew~}Lw~|M{|w~| r{|v~|s{}s~}^u~}ov~|_w~|s{}w~|[v~}pu~]v~|Nw~}m{|w"
- "~}Y{|s~}Xv~m{}w~|a{|u~p{|u~|fv~}t{}x~}x~}t{}v~ev~}w{}w~w{|v~}`{|w~}q{|w~}_{}v~|w{}w~v{}v~`t~|q{|v~|`v~}p{}v~U{}"
- "w~|Uv~|r{|v~b{|v~fv~|bu~|M{}w~}O{|u~}t{|u~}\\{|k~}|`{}w~}hv~|Z{|v~X{}u~|n{}u~|`{|@{}w~@{|Xn~|X{|i{|Y{|n~} xv~}U"
- "{|v~}vy~}S{|v~T{|v~|jv~}Y{|w~}Lw~|H{|v~|*{}x~}i{}x~}${}~}s{|y~ S{}x~}i{}x~} ;{|u~p{|u~|Ux~}w{|x~} Ey~|s{|~}T"
- "{}x~}o{}j~}k{|w~k{}x~}a{}x~}o{}j~}h{}v~}W{|v~fv~|h{|v~fv~|h{|v~fv~|h{|v~fv~|h{|v~fv~|h{}w~}f{}w~}p{|v~l{|v~U{}u"
- "~|l{|}y~}c{}w~}M{}w~}M{}w~}M{}w~}D{}w~}M{}w~}M{}w~}M{}w~}Z{|v~n{|}s~c{}w~|mq~e{}u~|n{}u~|d{}u~|n{}u~|d{}u~|n{}u"
- "~|d{}u~|n{}u~|d{}u~|n{}u~|d{}v~|l{|u~|et~|n{}u~|c{|u~n{|u~b{|u~n{|u~b{|u~n{|u~b{|u~n{|u~X{|v~|V{}w~}Mw~}x{|p{}v"
- "~c{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|a{|v~p{|t~|k{|v~p{|q~j{|gu~|Pu~|k{|_u~|k{|_u~|k{|_u~|k{"
- "|Vv~Lv~Lv~Lv~U{|v~}r{}v~}^w~}m{|w~}`v~}p{}v~|_v~}p{}v~|_v~}p{}v~|_v~}p{}v~|_v~}p{}v~|W{}u~Vu~|q{}v~|_{}v~pt~|`{"
- "}v~pt~|`{}v~pt~|`{}v~pt~|Y{}s~}Xt~|q{|v~|Y{}s~} Lu~}p{}u~|au~}p{}u~|au~}p{}u~|au~}p{}u~|au~}p{}u~|W{}x~}N{}s~} "
- "M{|v~|Ev~} py~|Jy~|M{}t~O{|u~}Xw~}kw~}_{|t~}w|}u~}]{}w~}ov~|Xr~|Y{}t~}y| tt~|r{}x~}$v~}\"w~t{|w~X{}v~}y|y{|"
- "y~y|}t~|_{|x~}ow~}tw~|m{|t~|r{|}q~}&w~}J{}w~ t{}w~L{}v~ u{|v~|Pw~|Pu~|t{}v~|\\s|}w~}r|a{}v~}Nx~}|p{}t~O{}w~}]{}"
- "y~}|q{}t~|\\{}v~|s{}u~Y{|v~|T{}u~|r{}u~|_{~}|r{|}u~|T{}v~M{}v~@{|}w~}6{|w~}|A{}w~}^{|w~r{|o~}{}s~}iv~}f{}w~}e{}"
- "w~}q|y}s~|_{}t~|p{|}w~}g{}w~}r|y}q~}_{}w~}g|`{}w~}O{}t~}o{|}u~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}mt~_{}w~}h|i{}w~|bv~"
- "|h{}w~|m{}r~dt~}|r{|t~|`{}w~}Ot~}q{|t~}b{}w~}jv~}d{|w~}|p{|}u~}X{|v~|W{}u~|q{}u~|Z{|r~|]{|s~|mr~f{|v~|l{|v~|Y{|"
- "v~|[{|u~}b|^{|w~}E{|w~|N{|w~| tv~}r{}s~|_w~y}x~}|w{|}u~|]{|u~|p{|}|^t~y|x{|}w~}w~|`{|u~|n{|y~|Xw~}St~y|x{|}w~}"
- "w~|`w~}m{|w~}Wv~Lv~Tw~}q{}v~}Uv~_w~}mv~mv~hw~}m{|w~}`{}v~}r{}v~}^s~}s{|v~}_{}v~}s{|s~|Zw~}Qy~}|o{}w~}X{|v~}|U{|"
- "v~}s{|s~|Z{|q~Z{|s~qs~|`{}w~}qv~}Vs~|X{}v~|Dw~}Lw~|M{|w~| qu~|u{}w~|v~}_s~|s{|u~^{}w~t{}w~}Z{|v~}|t{|}v~|]{}v~"
- "|ny|^w~}m{|w~}Xs~|Y{}w~}m{|w~}a{|t~|s{}t~}f{}v~}v{|w~{w~|v{}v~}e{}v~}x{}w~x{}u~_{|w~}q{|w~}^u~}|y{}w~x{}u~}`s~}"
- "s{|v~}_{|v~}|t{|}v~|U{|v~}|W{|v~|t{|v~|bu~f|v~}c{}v~}h|_{}w~}Vs}t~}v{}t~s}`{|y}t~y}|]{}w~}hv~|Z{|v~Wt~}|r{|t~|#"
- "{}w~ vp~| {|p~} wv~}T{}v~}wy~}v{}~Z{|v~S{}x~|hx~}X{|w~}Lw~|G{}w~}){|w~|m{|w~|\"{|}q{} R{|w~|m{|w~| XY| ${|u~}r{"
- "|t~}Ux~}w{|x~} E{}qy|T{|w~c{}x~gw~|lw~}a{|w~c{}x~e{}v~}Vv~}f{}w~}hv~}f{}w~}hv~}f{}w~}hv~}f{}w~}hv~}f{}w~}hv~|f"
- "{|v~pv~}l{|v~}h|h{}t~|p{|}w~}c{}w~}g|a{}w~}g|a{}w~}g|a{}w~}g|X{}w~}M{}w~}M{}w~}M{}w~}Z{|v~r|x}q~b{}w~|m{}r~dt~}"
- "|r{|t~|bt~}|r{|t~|bt~}|r{|t~|bt~}|r{|t~|bt~}|r{|t~|d{|v~|j{|v~}f{}s~}|r{|t~|a{}u~|q{}u~|a{}u~|q{}u~|a{}u~|q{}u~"
- "|a{}u~|q{}u~|X{|v~|V{}w~}Mw~}xy~}y|wy|u~|bv~}r{}s~|`v~}r{}s~|`v~}r{}s~|`v~}r{}s~|`v~}r{}s~|`v~}r{}s~|jv~}r{}w~}"
- "|u~|o{|}y~g{|u~|p{|}|_{|u~|n{|y~|`{|u~|n{|y~|`{|u~|n{|y~|`{|u~|n{|y~|Wv~Lv~Lv~Lv~T{}u~}|x{|}u~}]w~}m{|w~}`{}v~}"
- "r{}v~}^{}v~}r{}v~}^{}v~}r{}v~}^{}v~}r{}v~}^{}v~}r{}v~}V{}u~V{|v~}r{}v~}^{|v~}s{|s~|`{|v~}s{|s~|`{|v~}s{|s~|`{|v"
- "~}s{|s~|Xs~|Xs~}s{|v~}Ws~| K{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~^{}u~}x|{x|}t~U{}x~}N{|s~| M"
- "{|w~|D{}w~| q{|y~}K{|y~}L{}v~|N{}v~Ww~}kw~}^{|j~}\\v~|o{}w~}X{}s~W{|^~} -s~}v|}v~}$v~}#{|w~t{|x~}X{}e~|^w~|o"
- "{|w~|v{}w~k{|s~}v|}t~y}v~}'{}w~Jw~} t{}w~L{}v~ u{|v~|Q{|w~O{|u~}w|}u~}\\{|e~|ab~`u~w}|x}r~|O{}w~}]{}v~w}|x}s~}Z"
- "t~}w|}t~X{}w~}S{|t~y}v|}t~}^v~y}y|y}s~|S{}v~M{}v~={|}~}6{|y~}|?{}w~}]w~}q{}r~|y{}u~}h{|v~|f{|v~e{}c~|]{}s~y}v|y"
- "}t~}g{}b~|^{}c~}`{}w~}N{}r~y}v|y}r~|h{}w~}hv~|Y{}w~}M{}w~}W{}w~}lt~`{}d~}i{}w~|bv~|h{}w~|lr~cr~}v|}s~}_{}w~}Ns~"
- "y}v|}s~}a{}w~}j{|v~|e{|s~}u|}r~W{|v~|Vs~}v|y}t~|Xs~}\\{|s~|m{}t~}fv~}j{}v~Y{|v~|[{}\\~}^{|w~}Dw~}N{|w~| t{}u~y"
- "|x{|}w~y}w~|_w~}{k~|[{|t~}|wy|}x~|^{|k~y|w~|_{|t~}|vy|y}w~|Xw~}S{|k~y|w~|`w~}m{|w~}Wv~Lv~Tw~}p{}v~}Vv~_w~}mv~mv"
- "~hw~}m{|w~}_{}u~}|x{|}u~}]w~y}w~y|yy|}u~|^{}u~}|x{|}w~}w~|Zw~}Qv~}y|v{|}u~|Wm~[{}u~}w|}v~}w~|Y{}s~}Yt~}q{}t~|a{"
- "}v~p{|v~|W{}t~W{}d~Uw~}Lw~|M{|w~| q{|u~}|y{|}v~{|s~br~}y|yy|}u~|^{|w~}v{}v~X{}u~}y|{y|}u~}\\{|t~}|vy|y}y~}^w~}"
- "m{|w~}X{}t~Xv~|lv~|b{|s~}v|}q~x}hu~}|{y|w~}{}w~}|{|}u~c{}u~}|}w~|}t~|_{|w~}q{|v~}_{|s~}v~}s~}_w~y}w~y|yy|}u~|^{"
- "}u~}y|{y|}u~}S{}r~}Z{}v~}|x{|}v~}b{|Z~c{}c~}_{}w~}Vk~v{}l~|^{|v~Y{}w~}hv~|Z{|v~Vr~}v|}s~|\"{}w~ ur~| y{|r~} vv~"
- "}St~}y|y~}{y|}x~`{}b~}a{}~|f{~}W{|w~}Lw~|G{|w~}({|v~}|s{|}v~| E{|v~}|s{|}v~| X{|Z~} ${|s~}y|{y|}q~}|}Xx~}w{|x~"
- "} l{}x~|c{}x~h{}x~}m{|w~|`{}x~|c{}x~f{|v~}V{|v~|f{|v~i{|v~|f{|v~i{|v~|f{|v~i{|v~|f{|v~i{|v~|f{|v~i{|v~dv~|r{|"
- "v~k{|b~g{}s~y}v|y}t~}c{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}w~}M{}w~}M{}w~}Z{|b~}a{}w~|lr~cr~}v|}s~}`r~}v|}s~}`r~}v|}"
- "s~}`r~}v|}s~}`r~}v|}s~}b{|x~|h{|x~}f{}o~}v|}s~}_s~}v|y}t~|_s~}v|y}t~|_s~}v|y}t~|_s~}v|y}t~|W{|v~|V{}w~}Mw~}xk~}"
- "a{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|`{}u~y|x{|}w~y}w~|j{}"
- "u~y|x{|}u~y{|t~}|vy|}v~f{|t~}|wy|}x~|^{|t~}|vy|y}w~|_{|t~}|vy|y}w~|_{|t~}|vy|y}w~|_{|t~}|vy|y}w~|Wv~Lv~Lv~Lv~S{"
- "}j~}\\w~}m{|w~}_{}u~}|x{|}u~}\\{}u~}|x{|}u~}\\{}u~}|x{|}u~}\\{}u~}|x{|}u~}\\{}u~}|x{|}u~}U{}u~V{}t~}|x{|}u~}\\{"
- "}u~}w|}v~}w~|_{}u~}w|}v~}w~|_{}u~}w|}v~}w~|_{}u~}w|}v~}w~|X{}t~Ww~y}w~y|yy|}u~|W{}t~ I{}h~}\\{}h~}\\{}h~}\\{}h~"
- "}\\{}h~}T{}x~}Ms~ K{|y~}C{|w~ p{}x~K{}x~Kw~|L{}x~|Ww~}kw~}]{|l~}\\{|v~n{|w~}X{|s~U{}`~} -{|h~|$v~}#{}x~}t{}x"
- "~}X{|}g~|^{}x~}m{}w~}y|}v~|j{|g~}y{}v~}({|w~|L{|w~| t{}w~L{}v~ u{|v~|Q{}x~}N{}k~}[{|e~|ab~`e~|N{}w~}]{}g~}Y{|i~"
- "|Xv~|R{|g~}]i~|R{}v~M{}v~;y|5{|<{}w~}]{|w~|p{|v}|w{|x}|e{}v~dv~}f{}e~}|[{}d~|g{}d~}\\{}c~}`{}w~}M{}c~}|g{}w~}hv"
- "~|Y{}w~}M{}w~}W{}w~}kt~a{}d~}i{}w~|bv~|h{}w~|l{|s~b{}f~|^{}w~}M{}f~|`{}w~}iv~}e{|c~V{|v~|Uf~}W{}t~|[s~l{}t~|g{}"
- "v~hv~}Z{|v~|[{}\\~}^{|w~}D{}w~N{|w~| sj~{}w~|_w~}{|m~|Y{}i~|]{|m~|{|w~|^{|f~|Xw~}R{|m~|{}w~|`w~}m{|w~}Wv~Lv~Tw"
- "~}o{}v~}Wv~_w~}mv~mv~hw~}m{|w~}^h~\\w~}{k~|\\k~y|w~|Zw~}Qg~}V{|n~Zk~{}w~|Y{|s~|Y{}u~}q{|t~a{|v~|o{}v~W{|u~|W{}d"
- "~Uw~}Lw~|M{|w~| p{|l~|ys~be~}\\{}v~x}u~V{}j~}Z{|h~}^w~}m{|w~}X{|u~|Y{|w~}k{}w~}b{|w~}m~|s~h{|m~xm~|b{}g~|^{|w~"
- "}pr~a{|f~}^w~}{k~|\\{}j~}R{|r~}Y{}l~}a{}Z~}d{}c~}_{}w~}Vk~v{}l~|^{|v~Y{}w~}hv~|Z{|v~U{}f~|!{}w~ tt~| w{|t~} uv~"
- "}R{}i~`{}b~}`{|?{|w~}Lw~|Fw~}&{}t~w}t~} A{}t~w}t~} V{|Z~} ${|w~}m~|s~Xx~}w{|x~} m{|x~}b{}x~hw~lk~k{|x~}b{}x~"
- "fv~}U{}v~dv~}j{}v~dv~}j{}v~dv~}j{}v~dv~}j{}v~dv~}j{}w~}d{}w~}r{}w~}k{|b~f{}d~|c{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}"
- "w~}M{}w~}M{}w~}Z{|d~}|`{}w~|l{|s~b{}f~|^{}f~|^{}f~|^{}f~|^{}f~|`{|~|f{|~}f{|w~|}f~|]f~}]f~}]f~}]f~}V{|v~|V{}w~}"
- "Mw~}xl~}_j~{}w~|_j~{}w~|_j~{}w~|_j~{}w~|_j~{}w~|_j~{}w~|ii~w{|f~e{}i~|]{|f~|^{|f~|^{|f~|^{|f~|Wv~Lv~Lv~Lv~R{}l~"
- "}[w~}m{|w~}^h~Zh~Zh~Zh~Zh~){|f~Zk~{}w~|^k~{}w~|^k~{}w~|^k~{}w~|X{|u~|Ww~}{k~|V{|u~| H{|j~|Z{|j~|Z{|j~|Z{|j~|Z{|"
- "j~|S{}x~}M{}u~} I{}Ax~} pw~|Lw~|L{|y~|Jy~|Vw~}kw~}[{}o~|[{}w~}mv~Wt~}T{|}b~} +{}l~}\"v~}#w~|tw~|U{|}l~}]{|w~"
- "ko~|h{|j~}|w{}u~({}w~L{}w~ s{}w~Lv~| u{|v~|Qw~}M{|m~}Z{|e~|ab~`g~}|M{}w~}]{}h~|W{|k~W{}v~P{|i~|\\k~}P{}v~Mv~| "
- "i{}w~}\\{}w~Jv~}d{}v~f{}g~}|X{|}h~}e{}g~}|Z{}c~}`{}w~}L{|}g~}|e{}w~}hv~|Y{}w~}M{}w~}W{}w~}jt~b{}d~}i{}w~|bv~|h{"
- "}w~|ks~a{|i~}\\{}w~}L{|i~}^{}w~}i{|v~|e{}f~}U{|v~|T{}i~|Ut~Z{}u~}l{|t~g{|v~|h{|v~|[{|v~|[{}\\~}^{|w~}D{|w~N{|w~"
- "| s{|l~|{}w~|_w~}x{}q~}|W{|j~|[{}p~|y{|w~|]{|g~|Xw~}P{}q~}|y{}w~|`w~}m{|w~}Wv~Lv~Tw~}n{|v~}Xv~_w~}mv~mv~hw~}m{"
- "|w~}]{}l~}[w~}{|m~|Zm~|{|w~|Zw~}Qh~|T{|o~Z{|m~|{}w~|Xs~X{}u~|pu~}av~}m{}w~}Wu~V{}d~Uw~}Lw~|M{|w~| o{|n~|w{}u~b"
- "f~}Z{}p~}T{}l~}X{|i~}^w~}m{|w~}Wu~Xv~|k{|v~b{|w~y|o~|{}t~g{|o~|x{|o~}`{}i~|]{|w~}p{}s~_{}j~}|]w~}{|m~|Z{}l~}P{|"
- "s~}X{}n~}`X~d{}c~}_{}w~}Vk~v{}l~|^{|v~Y{}w~}hv~|Z{|v~T{|i~} {{}w~ sv~| u{|v~} tv~}Q{}j~`{}b~}#{|w~}Lw~|G{|w~}${"
- "}m~} ={}m~} T{|Z~} ${|w~y|o~|{}t~Xx~}w{|x~} mw~|b{}x~i{}x~|lk~kw~|b{}x~g{|v~Tv~}d{}v~jv~}d{}v~jv~}d{}v~jv~}d"
- "{}v~jv~}d{}v~k{|v~|d{|v~rv~|k{|b~e{|}h~}a{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}w~}M{}w~}M{}w~}Z{|g~}|]{}w~|ks~a{|i~}["
- "{|i~}[{|i~}[{|i~}[{|i~}/{|w~|y{|i~}Z{}i~|[{}i~|[{}i~|[{}i~|U{|v~|V{}w~}Mw~}xm~|^{|l~|{}w~|_{|l~|{}w~|_{|l~|{}w~"
- "|_{|l~|{}w~|_{|l~|{}w~|_{|l~|{}w~|i{|l~}u{|g~d{|j~|\\{|g~|]{|g~|]{|g~|]{|g~|Wv~Lv~Lv~Lv~Q{|}p~}|Zw~}m{|w~}]{}l~"
- "}X{}l~}X{}l~}X{}l~}X{}l~}){|w~}l~}Y{|m~|{}w~|^{|m~|{}w~|^{|m~|{}w~|^{|m~|{}w~|Wu~Vw~}{|m~|Tu~ E{|}p~}|V{|}p~}|V"
- "{|}p~}|V{|}p~}|V{|}p~}|Qw~}Lu~| i{}y~| q{|w~}M{|w~}K{|}I{|}Uw~}kw~}Y{|y}w~y}|Yv~|m{}w~|X{}u~|Q{|}e~} *{|}p~"
- "}|!v~}#w~t{|w~Py|x}y~x}y|[w~|j{}r~|e{|n~}|t{}u~){|w~|N{|w~| s{}w~Lv~ t{|v~|R{|w~|L{|}p~|Y{|e~|ab~`y|}l~}|K{}w~}"
- "]{|}k~|S{}o~|Vv~}N{|m~}Z{}n~}|O{}v~Mv~ h{}w~}[v~L{|v~|d{|v~|g{}k~y}y|T{|}m~}|c{}m~x}y|W{}c~}`{}w~}J{|}k~}|c{}w"
- "~}hv~|Y{}w~}M{}w~}W{}w~}it~c{}d~}i{}w~|bv~|h{}w~|k{|t~_{|m~}|[{}w~}J{|l~|]{}w~}h{}w~}c{|}k~}|T{|v~R{|}m~|S{}v~}"
- "Z{|u~|kt~gv~}f{}v~[{|v~|[{}\\~}^{|w~}Cw~|O{|w~| q{}p~}x{}w~|_v}vy}w~y}|S{}m~}Xy}w~y}|w{|w}|[{|l~}|Vw~}N{|}w~y}"
- "|w{}w~|`v}lw}|Wv~Lv~Tv}m{|u}Yv}_w~}mv~mv~hw~}m{|w~}\\{|n~|Zw~}x{}q~}W{}q~}|y{|w~|Zw~}Q{|}l~}P{|y}s~X{}q~}x{}w~|"
- "X{}u~}X{|u~o{}v~|b{}w~}kv~}X{}w~}V{}d~Uv~Lw~|M{|w~| n{|}q~}u{|}w~bv~{}o~}|X{|r~|R{|}p~}|U{}l~}|^w~}m{|w~}W{}w~"
- "}Xw}|i{|w}b{|w~|{|q~|y{|t~f{|q~|v{|q~|^{|l~}[{|w~}os~]{|}o~}|[w~}x{}q~}W{|}p~}|M{|}v~}W{|p~|`{|X~|e{}c~}_{}w~}V"
- "k~v{}l~|^{|v~Y{}w~}hv~|Z{|v~R{|m~}| y{}w~ rx~| s{|x~} sv~}P{|}n~}|`{}b~}#{|w~}Lw~|Ty|pv~|\"y|}u~}y| 9y|}u~}y| "
- "R{|Z~} ${|w~|{|q~|y{|t~Xx~}w{|x~} y}| q{}x~}aw}j{|w~kk~l{}x~}aw}gv~}U{|v~|d{|v~|l{|v~|d{|v~|l{|v~|d{|v~|l{|v~|"
- "d{|v~|l{|v~|d{|v~|l{|v}bv}|t{}w~}j{|b~c{|}m~}|_{}c~}a{}c~}a{}c~}a{}c~}X{}w~}M{}w~}M{}w~}M{}w~}Z{|m~x}y|Z{}w~|k{"
- "|t~_{|m~}|X{|m~}|X{|m~}|X{|m~}|X{|m~}|.w~}v{|}n~}|X{|}m~|X{|}m~|X{|}m~|X{|}m~|S{|v~|V{}w~}Mv|wy|}u~y}|Z{}p~}x{}"
- "w~|]{}p~}x{}w~|]{}p~}x{}w~|]{}p~}x{}w~|]{}p~}x{}w~|]{}p~}x{}w~|g{}o~|r{|l~}|a{}m~}Y{|l~}|Y{|l~}|Y{|l~}|Y{|l~}|U"
- "v~Lv~Lv~Lv~O{|y}v~y}|Xw~}m{|w~}\\{|n~|V{|n~|V{|n~|V{|n~|V{|n~|(w~|{|n~|V{}q~}x{}w~|\\{}q~}x{}w~|\\{}q~}x{}w~|\\"
- "{}q~}x{}w~|W{}w~}Vw~}x{}q~}R{}w~} B{|t}|P{|t}|P{|t}|P{|t}|P{|t}|Nw~} 3{|~} ;f| '{|y}w~}y| 8{|y~|X{|x~}"
- "h{|}w~}|ay|y}w~y}| rw~}N{}w~ ?{|w~| D{}w~I{|y}w~y}|%b|\\{|x}u~y}|!y|y}u~y}y|O{|y}w~y}| {{y|}u~y}|Vy|y}v~}y| u{|"
- "w~| B{|v~| 1{|y}u~y}| o{|x}u~y}y| Fv~| 7y|y}v~y}| {{y|y}q~|#y|y}u~y}y| {{|y}v~y}y| a{|w~}C{}x~}O{|w~| oy}"
- "v~}|vv|!{|}t~y}|!{|y}t~y}|Sv|Av~\"v|Lv~ Rv|mv|mv|hv|lv|Z{|y}u~}|Xw~}v{|}w~y}|T{|}w~y}|w{|w~|Zv|Ny|y}u~y}| {{|y}"
- "w~}|uw|W{|u}|Wv}|o{|v}av|ju|Xv~| sv~Lw~|M{}w~| ly|}v~}|Uv~yy|}v~y}|S{|y}~y}|N{|y}v~y}|Qy|y}v~x}|[v|m{|w~}W{|w~"
- "|#{|w~|x{|}w~}|v{|}y~y}c{|y}x~y}ry}x~y}|Z{|y}s~}y|G{}w~}|Zy|v~}|Ww~}v{|}w~y}|T{|y}v~y}| x{|y}w~}| Ry|y}v~y}|"
- " Zy| rv~}M{|y}u~}|]`| Iw~|T{|y~}|u{|u~ 5{|w~|x{|}w~}|v{|}x~}Wx~}w{|x~} {}y~} r{|y}|Kw~|L{|y}|Hv~| E"
- "{|y}u~y}| qy|y}v~y}|Sy|y}v~y}|Sy|y}v~y}|Sy|y}v~y}|Sy|y}v~y}|+{|y~}r{|y}v~y}|R{|y}v~y}y|S{|y}v~y}y|S{|y}v~y"
- "}y|S{|y}v~y}y| oy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|Zy}v~}|vv|d{|}v~y}|n{|y}u~y}y|\\{|}t~y}|U{|y}"
- "t~y}|T{|y}t~y}|T{|y}t~y}|T{|y}t~y}|Rv|Lv|Lv|Lv|!v|lv|Z{|y}u~}|R{|y}u~}|R{|y}u~}|R{|y}u~}|R{|y}u~}|'{}x~|w{|y}u~"
- "}|S{|y}w~}|uw|Z{|y}w~}|uw|Z{|y}w~}|uw|Z{|y}w~}|uw|Vv~|Vw~}v{|}w~y}|Qv~| Mw~| K{|y~| e{|w~Nw~"
- "| ?{}w~ Cw~} .{}w~ @{|v~|d{}| Kv~| !u~| J{|w~}C{|w~O{|w~| 9w~} Iv~ bw~}9{|w~| X{|v~ rv"
- "~Lw~|M{}w~| <v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} )v~}Iy~} gw~|T{|u~y}s~| 5{|w~|Ax~}w{|x~} {"
- "{x~| 0{|v~ ?{}y~} R{|} 5x~| O{|y~} &{|v~Uw~}D{|v~ Lw~| K{|y~| d"
- "{}x~}P{}w~ >w~| D{|w~| .w~| ?{|v~}g{|x~| M{|v~ {|u~| K{|w~}Bw~|P{|w~| :{}w~} Iw~} bw~}9{"
- "|w~| X{}w~| r{}w~|Mw~|Mv~ ;v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} )v~}Iy~} gw~|T{|l~| 4{|w~"
- "|Ax~}w{|x~} {{}y~} /v~| ?x~| f{|x~ M{} %{}w~|Uw~}D{}w~| Lw~| K"
- "{|y~| d{|w~Pw~| ?{|w~ C{}w~ .{|w~ ={|u~}|l{|u~| N{}v~ {{|u~| L{|q~}H{}x~}V{}q~| :v~| Iw~}"
- " bw~}9{|w~| Xv~ q{}w~}Mw~|N{|v~ ;v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} )v~}Iy~} gw~|T{|}o~}| "
- " 3{|w~|Ax~}w{|x~} {{|x~| 0v~}m{} N{|x~ e{}y~} Rv~Tw~}Dv~ S{}x~x{|w~| "
- " K{|y~| c{}x~}R{}x~} >{|x~| Cw~} .{|x~| ;{}t~}|sy|}t~| N{|v~} y{|u~| M{|q~}H{|w~V"
- "{}q~| ;{}v~ I{|w~} bw~}9{|w~| Y{}w~} q{|v~}|Ow~|P{|}v~} ;v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} "
- " )v~}Iy~} gw~|Q{|y}v~y}| 1{|w~|Ax~}w{|x~} yx~| 0{}v~|p{|~} N{|x~| f{|x~ "
- " S{}w~}Tw~}E{}w~} S{}x~|y{|w~ J{|y~| bw~|Sw~| >{}y~} K{}y~} 9{|p~x}q~}| N{|u~"
- "| x{|u~ M{|q~} y{}q~| K{|}|p{|u~| I{}w~| bw~}9{|w~| Z{|v~ o{}q~}Tw~|U{|p~ :v~ S{|w~}W{|w~|#{|"
- "w~| j{}w~ s{}w~Uw~} )v~}Iy~} gw~| W{|w~|Aw|vx| y{|x~} 0{|u~|s{}x~} N{|x~| "
- " f{|x~| U{|v~Sw~}F{|v~ R{|x~}y{}w~ J{|y~| b{|x}|T{|x}| w{}g~}| Q"
- "x|y}u~} v{|u~ N{|p} yp}| K{|x~}y|wy|}u~} J{|}v~ aw~}9{|w~| \\{|}v~} nq~}Tw~|U{|q~| :v~ S{|w~}"
- "W{|w~|#{|w~| j{}w~ s{}w~Uw~} )v~}Iy~} gw~| W{|w~| :{|}w|}w~| /t~y}x|y}v~} U{|}|x{|w~| "
- " f{}x~| W{|}v~}Sw~}H{|}v~} Qq~| J{|y} *{|}l~}| O{}q"
- "~ tt| `{|i~} Lr~| aw~}9{|w~| `{}q~ l{}s~}Tw~|U{|s~}| 9v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~"
- "} )v~}Iy~} gw~| W{|w~| :{|q~ .{|i~} U{|q~ ly}w|}w~| [{}q~Rw~}"
- "L{}q~ P{}r~ M{|y}u~y}y| L{}r~| R{|j~} Ks~} `w~}9{|w~| "
- " `{}r~| jy|v}|Tw~|U{|u}| 6v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} )v~}Iy}| gw~| W{|w~| :{|r~| "
- " -{|k~}| U{|r~} l{}r~} Z{}r~|Rw~}L{}r~| O{}t~ "
- " k{}t~} -{|`}| `{|}m~}| Jt~} _w~}9{|w~| `{}s~| :w~| cv~ S{|w~}W{|w~|#{|w~| j{}w~ s{}"
- "w~Uw~} )v~} d{|w~| 9y}w~y} ){}o~}| S{|}u~}| k{}r~ Y{}s~|Qw~"
- "}L{}s~| M{}w~} j{}w~}| +{}`~} ]{|x}v~y}| Gw~y} ]w~}9{|w~"
- "| `{}v~}| 8w~| cv~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} g{|w~| 8{|}v~y}| Ly| "
- " g{|y}w~}| X{}v~}|Ow~}L{}v~}| Iy| "
- "l{}`~} Ww~| "
- " L{}`~} Ww}| "
- " r{" };
- // Define a 104x128 binary font (huge sans).
- static const char *const data_font_huge[] = {
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " "
- " FY AY "
- "'Z ;W @Y @Y 'Z Y @Y (Z :Y ?Y (Z 0Y ?Y (Z >X "
- " "
- " "
- " "
- " "
- " )X AX '\\ )XAV 7YDY -] BY BY '[ +YEY 2X AY (\\ -YDY 'XAU 3Y AY (\\ )XAV 8YD"
- "Y LY AY (\\ ,YEY #Y "
- " "
- " "
- " "
- " (X CX '^ +[CU 6ZEY .` C"
- "X CY '] -ZEZ 2X CY (^ .ZEZ )[CU 2Y CY (] *[CU 7ZEZ LY CY (] -ZEZ %Y "
- " "
- " "
- " "
- " "
- " 'Y EY '^ ,^FV 6ZEY /b CX DX '_ .ZEZ 2Y DX '_ /ZEZ +_FV 1X CX (_ ,^FV 7ZEZ "
- " KX CX (_ .ZEZ &Y "
- " "
- " "
- " "
- " %Y GY '` .aHV 6ZEY 1e DY FX"
- " 'a /ZEZ 1Y FX '` /ZEZ +aHV 0X EX '` .aHV 7ZEZ JX EX (a /ZEZ &X "
- " "
- " "
- " "
- " "
- " #X GX 'XNX 0dKW 6ZEY 1f DY HX &WMX 0ZEZ 0X GX 'XMW 0ZEZ ,dLX /X GX 'WMX 0dLX 7ZEZ"
- " IX GX 'WMX 0ZEZ 'X :T "
- " "
- " "
- " "
- " ;X IX 'XLX 1o 5ZEY 2ZLY "
- " CX IX &WKW 0ZEZ /X HX (XLX 1ZEZ ,o .Y HX (WKX 1o 6ZEZ IY IY (WKW 0ZEZ (X <Z "
- " "
- " "
- " "
- " "
- " =X KX 'XJX 3WKd 5ZEY 3XGX CX JX 'WIW 1ZEZ .X JX (XJX 2ZEZ -WKd -X "
- "IX (WIW 2WKd 6ZEZ HX IX (WIW 1ZEZ )X =^ "
- " "
- " "
- " "
- " >X MX &WH"
- "W 3VHa 4ZEY 3WDW CX LX 'WGW 2ZEZ -X LX 'WHW 2ZEZ -VHa +X KX (XHW 3VHa 5ZEZ GX KX (WGW 2ZEZ )X "
- " ?b "
- " "
- " "
- " "
- " ?W MW &WFW 4VF^ 3ZEY 4WBV BW MX 'WEW 3ZEZ ,W M"
- "X 'WFW 3ZEZ -VF^ )X MX 'WFW 4VF^ 4ZEZ FX MX 'WFW 3ZEZ *X ?d "
- " "
- " "
- " "
- " "
- " ?W X 'WDW 5UC[ 2ZEY 4VAV AW X &WDW 4ZEZ +W NW 'WDW 4ZEZ -UC[ 'W MW 'WDW 5UC[ 3ZEZ "
- "EW MW 'WDW 4ZEZ +X ?f "
- " "
- " "
- " "
- " @X \"X 'WBW 6UAW 0ZEY 4V@V B"
- "X !W &WBV 4ZEZ +X !W 'WBW 5ZEZ .VAW $W W 'WBW 6UAW 1ZEZ DW W 'WBV 4ZEZ +W >f "
- " "
- " "
- " "
- " "
- " ?X #W 'W@W U?V AX #W &W@V NX #W &V@W 9W \"W 'W@V .W "
- "\"W 'W@V !W >XHX "
- " 3Y "
- " "
- " "
- " 6W $W &V>V U?V @W $W &W>V "
- " NW $X 'V>V 8W $X (W>V /X $W 'W>V #W >XFX "
- " 5Z "
- " "
- " ,Z "
- " GZ "
- " #U?V NY 7Z ,X CVCW MY "
- " 7Z ,X $Z 7Z ,X >Z 6Y ,X 4Z 7Y +W 7Y @Z "
- " "
- " +Z "
- " "
- " HY \"U?V "
- " MY 8Y ,Y CVBV LY 9Z ,Y #Z 9Z ,Z >Z 8Y ,Y 3Y 8Z ,Y 9Y "
- " ?Z "
- " *Y "
- " "
- " IY !U?V "
- " LY :Y ,[ $R>U ,V@V MZ :Y +Z #Y 9Y +Z ?R"
- ">U 8Y 9Y +Z %S?U HY :Z ,[ ;Y ?[ "
- " "
- " )Y "
- " 8U "
- " 9Y V@U JY <Y"
- " +[ 'XAU ,V@V LY ;Y +\\ #Y ;Y +\\ CXAU 7Y ;Z ,\\ )XAV HY ;Y +[ <Z "
- " ?U ;T $W /W "
- " 8e !f LY Y LX "
- " L] :Y <Y NX 0X >Y @Y /X 0Y K` .X "
- " ^ =ZEY @Y "
- " NVAV <P -X +Y =Y +] )[CU 7YDY 4V@V KY ="
- "Y +] ,YDY 5Y =Y *] .YDY 5[ M[CU 6Y <Y ,] *[CV 7YDY Y =Y +] ,YEZ !Y =Y FYDY 8X "
- " EU :T %W .X "
- " 9e !f KY !Y LY \"a :Y "
- "<Y NX 0X >Y E^ /X 0_ %f 1] 'c "
- " @ZEZ AY MV"
- "CW <R 4a .Y >X *^ +]DU 7ZEZ 5U>U JY ?Y *^ -YEZ 4Y "
- " ?Y *^ .ZEZ 5[ ]DU 5Y >Y +^ ,]DU 6ZEZ Y ?Y +_ .ZEZ \"Y <Y FYEZ :[ FU "
- " 7Y -T 7W#W <Y 9X -W DU KY HZ \"\\ 4Z M[ \""
- "Y LZ +\\ 8] >Z G[ G\\ @e !f JX !Y "
- "LY %d :Y <Y NX 0X >Y Ha /X 0b *j L] D_ "
- " +g A[ LY 8Z -ZEZ \"Y 1o )V FX NZ FY "
- "%Y ,X NX*Z NW 3WEW H\\ #[ !Z \"[ \"[ \"[ G[7T 8g 0Y "
- "@Y +_ ,_FV 7ZEZ 5U>U IY @Y +` .YEZ 3X ?X *` /ZEZ 4[:P 8_FV 4X ?Y +` ._EU 6ZEZ NX @Y *_ .ZEZ #Y ;Y"
- " FYEZ ;] GU <b 1T :]'X @b >W ,X "
- " FV a \"d -g >d (d +b %b 4f Bg Ie \"e \"h "
- " Ge !f IX \"Y LY &e :Y <Y NX 0X >Y Jc /X 0c "
- " -n $g I` .j >a ;e HU .U +b Ac 2ZEZ 'b "
- " 5o -] Na (c KY .Y #_ 8Y!W'Y\"X.c$X 3XGX Mf -e +d "
- ",e ,e ,e \"e=V ;k 1Y BY +XNW .aGV 7ZEZ 5V@V HX AY +XNW .YEZ 3Y AY *WNW /ZEZ 4\\>T 9`GV 3"
- "X AY +XNW .`GV 6ZEZ NY AX *XNW /ZEZ $Y :Y FYEZ <_ IU (Q LZ 4Z2Z 1Q "
- " &g %Z +XCX MT <a)W Ah $X HX +X GV GX 3e )_ /j 4n L] ?y /i C~S =i 0g "
- " +g L\\ 8t (m Ks 2~R E} <o HZ(Z :Z \"Z 4Z,] LZ 2_'_(^-Z Ck :q 0k ?q *n J~d'Z(Z*Z LZ=Z.\\.Z7Z(Z([$Z'~^"
- " @e 3X Ff )\\ MY #Y LY (g :Y <Y NX 0X >Y Kd /X 0e 0p "
- " (m Lb 1m ,\\ 5~S E~R Ah 'Z :~]+[;Z;Z Ik LW DX DW /i ?Y(Y 4h 5ZEZ"
- " ,\\ ,h 7\\ -o .` $f -h NY No %_ %c @_\"X-_\"W0h&W .\\ $\\ \"\\ #\\ #\\ )g 5~a Lm D~S I~S "
- "H~R H~R 6Z !Z !Z \"Z :r 8^,Y Bk 2k 2k 2k 2k (kAX+Z(Z#Z(Z$Z(Z$Y'Y&[%[ MZ Im 1X CY *WMX /bHV 7ZEZ 5V@V G"
- "X CY *WLW /YEZ 2Y CY *WLW 0ZEZ 3[AW :bHV 3Y BX *WLW 0bHV 6ZEZ MY CX *XMX 0ZEZ $X 9Y FYEZ "
- " =a M~i 7U (Q N_ 9_8_ 3R )k 'Z +XCX +X@X 4T >e,X Cl &X IX *X GV "
- " GX 5i 0d 2p ;u !^ ?y 2o F~S @n 4j /l N\\ 8x .r Nx 7~R E} >t KZ(Z :Z \"Z 4Z-] KZ 2_'_(^-Z"
- " Ep =t 5o Au 1u N~d'Z(Z)Z MZ<Z/\\/Z5Z*['[&Z&~^ @e 3X Ff )] MY $Y LY )h :Y <Y NX 0X >Y "
- " Le /X 0e 1r +r c 3o -\\ 5~S E~R Dn *Z :~]+[;Z;Z Ko "
- " Y EX EY 2m @Y)Y 6l 7ZEZ 0e 2k >e 1o 0c 'j /i X !r (b 'g Eb\"W0c#X0i(W -"
- "\\ $] #\\ $] #\\ (f 6~b r F~S I~S H~R H~R 6Z !Z !Z \"Z :w =^,Y Ep 6p 7p 7o 7p ,oDY+Z(Z#Z(Z$Z(Z$Y'Y%Z%Z LZ Kp"
- " 1X DX *WKW /WMYJV 6ZEZ 5V@V GY EY *WKX 0YEZ 1Y EY *XKW 1ZEZ 2[EZ :WMZKV 1Y DX *WKX 1WLYKW 6ZEZ L"
- "Y EY *WKW 0ZEZ %X 8Y FYEZ >c M~h 7T (S !a <b:b 6S %| $o "
- ")Z +XCX +W?W 3T ?g.X Dp (X IX )X HV HY 6l 7i 5t <v #_ ?y 3p F~S Aq 8n 3p (Y $^ 9z 2v!{ :"
- "~R E} Az NZ(Z :Z \"Z 4Z.] JZ 2`)`(_.Z Gt ?w :s Cx 5x!~d'Z(Z)Z N[<Z/\\/Z5[,[%Z'[&~^ @e 2X Gf *_ MX $Y "
- "LY )h :Y <Y NX 0X >Y >X 8f /X 0f 3t -s c "
- " 4q /^ 6~S E~R Fr ,Z :~]+[;Z;Z Ms #[ FX F[ 4n @Y*Y 6m 7ZEZ 3k 5l Bk 4o 1f )k 0k #"
- "X #u (b (i Fb#X0c#W/k+X .^ %] $^ %] $^ (d 5~b\"v H~S I~S H~R H~R 6Z !Z !Z \"Z :{ A_-Y Gt :t ;t ;s ;t "
- " 0sGY*Z(Z#Z(Z$Z(Z$Y'Y$Z'[ LZ Ls 2X FX *WIW 1WJc 6ZEZ 4VBV EY FX *XJW 0YEZ 0X EX )WJW 1ZEZ 1[I^ <WJc 0"
- "X EX )WJW 2WJZNW 5ZEZ KX FY *WIW 1ZEZ &X 7Y FYEZ ?d M~h 8U )T #e ?d=e 8U "
- " *~Q &r *Z +XCX +W?W 3T @i/W Dq (X JX (X HV HX 7o <m 7x >x %_ ?y 5r F~S Ct :p"
- " 6s /e *^ 9| 6z#~ =~R E} B}!Z(Z :Z \"Z 4Z/\\ HZ 2`)`(_.Z Iw @y >w Ez 9z!~d'Z(Z)[ Z;Z0]/Z4Z,Z$[(Z%~^ "
- "@e 2X Gf +a MX %Y LY *i :Y <Y NX 0X >Y >Y 9f /X 0g 5v "
- " 0u d 6_K_ 0^ 6~S E~R Gu .Z :~]+[;Z;Z w &] GX G] 6U &o ?Y+Y 7X )n 7ZEZ "
- "6p 7m Eo 6o 2h *l 1l %X #v (b )k Gb$X/c$X/l,W -^ &_ %^ &_ %^ 'b 4~b$z J~S I~S H~R H~R 6Z !Z "
- "!Z \"Z :~ D_-Y Hw =v >w >w >w 4wIX)Z(Z#Z(Z$Z(Z$Y'Y$[)[ KZ Mt 1X HX )WHW 2VHb 6ZEZ 4WDW DX GX )WHW 1YE"
- "Z /X GX )WHW 2ZEZ 0[M` ;VHb /X GY *WHW 3VHb 5ZEZ JX GX )WHW 2ZEZ 'Y 7Y FYEZ ?e M~f "
- " 7U )U %g Bh@g :W .~T 't +Z +XCX ,X@X 3T Ak1X Er (X JX 'X IV HX 8q"
- " =m 7y ?y '` ?y 6s F~S Dv <r 8u 4m /_ 9~ :~%~Q ?~R E} D~Q\"Z(Z :Z \"Z 4Z0\\ GZ 2`*a(`/Z Jz Bz Az F{ "
- ";{!~d'Z(Z(Z Z;Z0^0Z3Z.[#[*Z$~^ @X %X :Y ,c MX &Y LY +^ .Y <Y NX 0X >Y >Y "
- " :] %X &] 5]C\\ 1v Nc 7\\D\\ 1_ 6~S E~R Iy 0Z :~]+[;Z;Z!y (_ H"
- "X H_ 7U 'p ?Y,Y 6X *o 7ZEZ 8t 9YH] Ht 9o 3i *XG[ 1VE[ &Y %x (b *[I[ Hb$W.c%X.VE[-X "
- " ._ &_ %_ '_ %_ '` 4~c%} L~S I~S H~R H~R 6Z !Z !Z \"Z :~Q F`.Y Jz @z Az Ay Az 7zKX(Z(Z#Z(Z$Z(Z$Y'Y#[*Z JZ Na"
- "J_ 2X IX )WGW 2VG` 5ZEZ 4XFX CX IX )WFW 2YEZ .X IX )WFW 3ZEZ /j 8VG` -X HX *WFW 4VG` 4ZEZ IX IX "
- ")WGW 2ZEZ 'X 6Y FYEZ ?XKX M~f 7T )W 'i DiAi ;X 1~V (w -Z "
- "+XCX ,X@X 3T AZI[2W Es (X KX &X IV HX 9s >m 7z @z )a ?y 7t F~R Dx >t 9v 8s 2` :~P <~Q&~S"
- " A~R E} E~T$Z(Z :Z \"Z 4Z2] FZ 2a+a(`/Z K| C{ C} H| =|!~d'Z(Z(Z!Z9Z1^1Z2[0[!Z+[$~^ @X $X ;Y -e MX 'Y "
- "LY +[ +Y <Y NX 0X >Y >Y :[ #X #Z 6\\?[ 2v F\\ "
- " 8Z@[ 2` 7~S E~R J{ 1Z :~]+[;Z;Z#} +` HX Ia 8U (q >Y-Y 6X +p 7ZEZ 9bMb ;U@Y JbMb :"
- "n 3ZIZ +T@Y 2R>Y 'X %y (XLV +ZEZ IXMW%X.YMW%W-R>Y.W -` '_ &` '_ &` '` 4~c'~R N~S I~S H~R H~R 6Z !Z "
- "!Z \"Z :~S Ha/Y K| B| C| D} D| 9|MX'Z(Z#Z(Z$Z(Z$Y'Y\"Z+[ JZ N]B\\ 2X JX *WEW 3UE_ 5ZEZ 3YJY AX JW )WE"
- "W 2YEZ -X KX (WFW 3ZEZ .f 5UE_ ,X JX )WFW 4VF_ 4ZEZ HX KX )WEW 3ZEZ (X 5Y FYEZ @YJW M~"
- "e 7U *X (j EkCk =Y 3~X )x -Z +XCX ,W?X 3T BYEY3X Ft (X KX %X JV "
- " IX 9u ?m 7{ A{ *a ?y 8u F~R Ez @v :v :w 4` :~Q >~S'~U C~R E} G~V$Z(Z :Z \"Z 4Z3] EZ 2a+a(a0Z M~P D"
- "| E~P I} ?}!~d'Z(Z'Z\"Z9Z1^1Z1Z0Z [,Z#~^ @X $X ;Y .g MW 'Y LY +Y )Y <Y NX 0X >Y "
- " >Y :Z \"X \"Z 7[=Z 3aE[ E[ 9Z>[ 3` 7~S E~R L~ 2Z :~]+[;Z;Z$"
- "~P -b IX Jc 9U )r >Y.Y 5X ,]DX 7ZEZ ;\\>\\ <R;X M]>\\ 0XDX ,R=Y MX (X %hEW (SG"
- "V ,YAY JSHW%W-SGW&X GX/W ,` (a '` (a '` (a 5~d(~S N~S I~S H~R H~R 6Z !Z !Z \"Z :~T Ia/Y L~P F~P F~P F~P F~P"
- " <~X&Z(Z#Z(Z$Z(Z$Y'Y\"[-[ IZ \\>Z 1X LX )VCW 4UD] 4ZEZ 2f ?X LX )WDW 3YEZ ,W KX )WDW 4ZEZ -b 2UD] *W"
- " KX )WDW 5UD] 3ZEZ GW LX (VCW 4ZEZ )X 4Y FYEZ @XIX M~d 7U *Y *l GmDl ?[ "
- " 6~Z *`C\\ -Z +XCX ,W?W 2T CYCY5X E]CZ (X LX $X JV IX 9]E^ @m 7aGb B^Ec ,b ?y "
- "9aF[ F~R E_C_ B_E^ ;]E_ ={ 7b ;~R @cBb'~V D~R E} HeBc$Z(Z :Z \"Z 4Z4] DZ 2b-b(a0Z NbCb E} GbCb J~ Aa"
- "B_!~d'Z(Z'Z#[9Z2_1Z0Z2[ N[.Z\"~^ @X $X ;Y /i MW (Y LY ,Y (Y <Y NX 0X >Y >Y "
- " :Y !X !Y 8[;Z 1\\ 0\\:U D[ ;Z<Z 4b 8~S E~R M~R 4Z :~]+[;Z;Z%bCb "
- "/d JX Ke :U )]BW =Y/Y 5X ,[?U 3Z8[ &W NZ7Z 2XBW EX LW )X %iEW KV -Y?Y @W&X"
- "!W&W EW0X -b )a (b )a 'a )a 5~d)dCb N~S I~S H~R H~R 6Z !Z !Z \"Z :~V Kb0Y MbCb HbCb HbCb HbCb HbCb >bCh%Z(Z"
- "#Z(Z$Z(Z$Y'Y![.Z HZ Z;Z 1X NX )WBV 5VBZ $e >W MX )WBW !X MX )WBW #` /UBZ (W MX )WBW 6UBZ "
- " 9X MW (WCW MX 3Y GXHW M~d 8U *[ +m HnFn A] 9~\\ +^=Y"
- " -Z +XCX -X@X 2U DXAX5W E\\=V (X LX #X .R@V?Q ,X :\\A\\ @m 7\\>_ CY<_ -c ?y :^=V F~Q E]>^ D]@] "
- " <Z@^ @~P 9b ;Z=d Aa;^'Z>j E~R E| Ha8^$Z(Z :Z \"Z 4Z5] CZ 2b-b(b1Z `<_ FZ@d I`=` K[@d C_:Z ~b&Z(Z'Z#Z8Z2`"
- "2Z0[4[ LZ/[\"~^ @X #X <Y 0\\N] NX )Y LY ,Y (Y ;X NX 0X >Y >Y ;Z "
- "!X !Y 8Z9Y 6d 4[5R CZ ;Y:Z 5b 8~R D~Q MbAb 8` =~]+[;Z;Z&`=` 1f KX Lg "
- " ;U *\\=T =Y0Y 4X ,Z;R 5Z3Y &W !Y3Y 3W@W EW LX *W %jEW KV -X=X @W'X W'X EX1W ,b "
- "*b (b )b )b )b 7ZH~R)a:] N~R H~R G~R H~R 6Z !Z !Z \"Z :Z>j Lb0Y N_<` J`<_ J`=` J`=` J`=` @`=e%Z(Z#Z(Z$Z(Z$Y'Y"
- " Z/[ HZ !Z9Y 0W X )WAW 6VAW \"d <W X (VAW X X (V@V &a .VAW &X NW (V@V 6UAW 6X X )WAW "
- " NW 2Y N\\ #[ \"\\ #\\ #[ MXHW L~b 7U +\\ ,n IoGp C_ ;~] ,]:X -Z "
- "+XCX -X@X 8c LX@X7X E[:T (X MX \"X /TAVAT .X :\\?\\ Am 7Y9] CT4] .c ?Y J]8S Z E\\;\\ E]=[ "
- " <W;\\ B~T ;b ;Z7_ C_5['Z7e GZ MZ '`3[$Z(Z :Z \"Z 4Z6] BZ 2b-b(b1Z!_8^ GZ;` K_9_ LZ:` D]5W 3Y 9Z(Z&Z$Z7Z3`3Z."
- "Z4Z JZ0Z \\ ?X #X <Y 1\\L] NX *Y LY ,Y (Y 8X >Y >Y ;Y X !Y "
- " 8Y8Y 6f 6Z2P BY <Z9Z 7c 7\\ Z (`;` >j BZ(Z+[;Z;Z'_9_ 3h LX Mi <"
- "U *[:R <Y2Z 4X -Z8P 6Y/X 'W #Y/Y 6W>V EW KW +W %kEW KV .X;W @W'W NW(X CW2X -c *c )b "
- "*c )c +c 7ZHZ 2_5[ NZ !Z Z !Z >Z !Z !Z \"Z :Z7d Mc1Y ^8_ K^8^ L_8^ L_9_ L^8_ B_9b$Z(Z#Z(Z$Z(Z$Y'Y [1[ GZ !Z"
- "8Y 0W !W (V?W I` :X !W (V?W X \"X (W@W *d EX !W (W@W 0X \"X (V?W !W 1Y #d ,"
- "e +d +d ,e #XHW LZ#Z 7U +] -o KqHp C_ <c 2]7V -Z +XCX -W?X <l#X?X7W E[7R "
- "(X MX \"Y 0VCVCV .X :[<[ B\\IZ 7V5] DQ0] 0XNZ ?Y K\\4Q !Z E\\9\\ F\\;[ =U8[ DdAc =d <Z5^ "
- "E^1Y'Z3b HZ MZ (_/Y$Z(Z :Z \"Z 4Z7] AZ 2c/c(c2Z!]4] HZ9^ L^5^ MZ8^ E\\0T 3Y 9Z(Z&Z%Z6Z3`3Z-Z6[ J[2Z \\ >X #X "
- " <Y 2\\J] NW *Y LY ,X 'Y 8X >Y >Y ;Y X X 9Z7X 6g 7Y"
- " #Z =Y8Z 7d 7[ Z )_7_ Bp EZ(Z+[;Z;Z(^5^ 5j MX Nk =U +[7P <Z3Y 3X -Y "
- " MX+W 'V $X+X 7V=W FW KW ,W $kEW KV .X;X AW(X NW(W BW2W ,d +c *d +c *d +c 7ZHZ 3^0X NZ !"
- "Z Z !Z >Z !Z !Z \"Z :Z3a Nc1Y!^5] L]4] N^5^ N^5^ N^5] C^5_#Z(Z#Z(Z$Z(Z$Y'Y N[2Z FZ \"Z7Y /W #W (W>V H^"
- " 8X #W (W>V NW \"W (W>W .h EW \"X )W>W 0W #X (V=V \"W 0Y &j 1i 0j 1j 1i &X <Z#Y "
- " 7U +_ /p KrJr Ea >` .\\5U -Z +XCX -W?W =r'X>W8X EZ ;X NY !X 1XDVDX 2X "
- " &X ;[;[ BWDZ 7T2\\ \"\\ 1XMZ ?Y L\\ 2Z E[7[ G\\9[ >S5[ F`7` ?YNY <Z3\\ F]-W'Z0` IZ MZ )^+W$"
- "Z(Z :Z \"Z 4Z8] @Z 2YNX/XNY(c2Z\"]2] IZ7] N]2] MZ6] G\\-R 3Y 9Z(Z&[&Z6Z4XNW3Z-[8[ HZ3[ !\\ =X #X <Y 3\\H] N"
- "W +Y LY ,X 'Y 8X >Y >Y ;Y X Y :Y6Y 7i 9Y \"Y "
- " >Y6Y 7YNY 6[ !Z *^3] Dt GZ(Z+[;Z;Z)]2] 6l NX m >U +Z !Y4Z 3X -Y NW(W (W "
- " &X)X 8V<V +X DW LW ,W $lEW KV .W9W AW(W MW)X CW2W +YNY ,YNZ +YNY ,ZNY +YNY +YNY 9ZGZ 4^.W NZ !Z"
- " Z !Z >Z !Z !Z \"Z :Z1` d2Y\"]2] N]2] ]2]!^2]!]2] E]2]\"Z(Z#Z(Z$Z(Z$Y'Y MZ3[ FZ \"Z6X .V $W 'V<V GZ "
- " 5W $W 'V<V NW $W 'V<V 2m EW #W (V<V /W $W (W=W #W 0Y (n 6o 5n 5n 6n (X ;Z%Z "
- " 7U ,a 0q LrJr Fc A_ ,\\2S -Z +XCX .X@X ?u(W=X:X DY :X NX Y 2ZFVFZ 2X "
- "'X :Z9[ CR?Z 7R/\\ \"[ 1XMZ ?Y L[ 2[ F[5Z G[7Z >R4[ G^1^ AZNY <Z2[ G]*U'Z.^ IZ MZ )](U$Z(Z :Z \"Z"
- " 4Z9] ?Z 2YNX0YNY(d3Z#]0] JZ6\\ N\\/\\ NZ5\\ G[ <Y 9Z(Z%Z&Z6Z4XNX4Z,Z8Z FZ4Z [ <X \"X =Y 4\\F] #Y "
- "LY -Y 'Y 8X >Y >Y ;Y X Y :Y6Y 7j :Y \"Y "
- " >Y6Z 9YMY 5[ \"Z *]1] Hy IZ(Z+[;Z;Z)\\/\\ 8n X !o ?U ,[ Y5Y 2X -Y W&W )W 'W%W 9V"
- "<V +X DW LW )mEW KV /X9X BW)X MW)W BW3X ,YMY ,YMY ,ZNZ -YMY +YNZ -YMY 9ZGZ 5]*U NZ !Z Z !Z >Z "
- "!Z !Z \"Z :Z/_!d2Y#]0]!]0]\"]0\\!\\/\\\"]0] F\\0]#Z(Z#Z(Z$Z(Z$Y'Y M[5[ EZ \"Y5X +P "
- " %_K[ CY *r 9q 8r 9r 9q *X ;Z%Z >Q JT ,b 0q MsKs Ge "
- "C^ *[0R -Z +XCX .X@X @v)X=X:W CY :X Y NX 1[HVH[ 1X 'X ;Z7Z 0Z 7P,[ ![ 3XLZ ?Y M["
- " 1Z EZ4[ I[5Z ?P1Z I^-] BYLY =Z1[ H\\(T'Z-^ JZ MZ *\\$S$Z(Z :Z \"Z 4Z:] >Z 2YMX1XMY(YNZ4Z$].\\ JZ5"
- "\\!\\-\\ Z4[ GZ ;Y 9Z(Z%Z'Z4Z5XNX5Z*Z:[ F[6Z [ ;X \"X =Y 5\\C[ #Y LY -Y 'Y 8X >Y "
- " >Y ;Y X Y :Y6Y 7k ;Y \"Z @Z5Y 9YLY 5[ #Z +\\.] J| KZ"
- "(Z+[;Z;Z*\\-\\ :p !X \"q @U ,Z NY6Y 1X -X W#V *W (W#W :U;V +X DW LW )mEW KV"
- " /X9X BW*X LW*X BW3W +YLY -YMY ,YLY -YMY ,YLY -YMZ ;ZFZ 5\\'S NZ !Z Z !Z >Z !Z !Z \"Z :Z-^\"e3Y#\\.]#].\\"
- "#\\-\\#\\-\\#\\-\\ H\\.]$Z(Z#Z(Z$Z(Z$Y'Y L[6Z DZ \"Y5Y /[G[ "
- " DY +u =u <u ;u =u ,X :Y&Z >S LU ,c 1q MtLt Hf E] )[.Q "
- " -Z +XCX .W?X Bx)X=X;X DZ :X X MY 0ZIVIZ /X 'X ;Z7[ 1Z AZ ![ 4XKZ ?Y MZ 0Z EZ3Z I[5Z "
- "Z J])\\ CYLY =Z1[ I\\%R'Z+] KZ MZ +\\\"R$Z(Z :Z \"Z 4Z;] =Z 2YMX1XMY(YNZ4Z$\\,\\ KZ4[\"\\+[ Z4\\ I[ ;Y 9Z(Z$Z"
- "(Z4Z5WLW5Z*[<[ DZ7[ !\\ ;X \"X =Y 6\\A[ $Y LY -Y 'Y 8X >Y >Y "
- " ;Y X Y :Y6Y 7l <Y !Y @Y4Z :YLY 4[ $Z ,\\,] M~Q MZ(Z+[;Z;Z+\\+\\ <r \"X"
- " #s AU ,Z MY7Y 1X -Y \"W!V :f (V!W ;U;V +X EX MW (mEW KV /W7W BW*W KW+X BW3X "
- " +YLY .YKY -YLY .YKY -YLY .ZLY ;ZFZ 6\\%R NZ !Z Z !Z >Z !Z !Z \"Z :Z,^#YNZ3Y$\\,\\#\\,\\$\\,\\%\\+\\%\\,\\ MP"
- " NP N\\-]$Z(Z#Z(Z$Z(Z$Y'Y KZ7[ Dq :Z4X /XC[ EY "
- " -x @x >x ?x @x -X :Z'Z ?U MU -e 2q MtLt Ig E[ 'Z,P -Z +XCX .W?W By)"
- "X<W;W CZ :X X MY .ZKVKZ -X (Y <Z5Z 1Z A[ !Z 4XKZ ?Y N[ 1Z DZ3Z IZ3Y NY K\\%[ EYKZ >Z0Z"
- " J\\#Q'Z*\\ KZ MZ +[ Q$Z(Z :Z \"Z 4Z<] <Z 2YMY3XLY(YMZ5Z%\\*\\ LZ4[\"[*\\!Z3[ IZ :Y 9Z(Z$Z)[4Z6XLW5Z)Z<Z BZ8Z"
- " !\\ :X !X >Y 7[>[ %Y LY -Y 'Y 8X >Y >Y ;Y X Y ;Y"
- "5Y 7UH_ <Z \"Z AY3Y ;YKZ 4[ %Z ,[*\\ N~S NZ(Z+[;Z;Z+[*\\ =\\NXM[ #X $\\MXN\\ "
- " BU ,Z *P DY8Y 0X -Y #W NV @k )V NV <V;V +X EW NY )nEW KV /W7W BW+X KW+W CY4X +YKZ /"
- "YKY .ZLZ /YKY .ZKY /YKY <ZEZ 7\\#Q NZ !Z Z !Z >Z !Z !Z \"Z :Z+]#YMZ4Y%\\*\\%\\*\\&\\*[%[)[%[*\\ R!R [-_%Z(Z#Z"
- "(Z$Z(Z$Y'Y K[9[ Ct =Y3X /U@[ \"Q EY .z B{ "
- "B{ Az B{ /X :Z'Y >V U -g 4r NvNu Ji *\\ 5X.X 6\\ 7Z1Z M[ '[ 8Z +XCX /X@X C`MTL_)W;"
- "W<X CY 9X !Y LX ,ZMVMZ +X (X ;Z5Z 1Z A[ !Z 5XJZ ?Y NZ 0Z DY2Z J[3Z )Q Q JZ M[!Z FYJY >Z0Z "
- "J[ 'Z)\\ LZ MZ ,\\ \"Z(Z :Z \"Z 4Z=] ;Z 2YLX3XLY(YMZ5Z%[([ LZ3[$\\)\\\"Z3[ IZ :Y 9Z(Z$Z)Z3Z6XLX6Z(Z>[ B[:Z !"
- "\\ 9X !X >Y 8[<[ &Y LY -Y 'Y 8X >Y >Y ;Y X Y ;Y5Y "
- "7RB] =\\ $Z BY2Y ;YJY 3[ &Z -[(\\!~U Z(Z+[;Z;Z,\\)\\ ?\\MXL[ $X %\\LXM\\ CU"
- " ,Y *Q\"R DY9Y 0X -Y #V=_?V Cm *V LV <U;V +X FX \"[ (nEW KV /W7W BW+W JW,X F[3W *YJY 0Z"
- "KZ /YJY /YKZ /YJY /YJY =ZEZ 7[!P NZ !Z Z !Z >Z !Z !Z \"Z :Z*]$YMZ4Y%[([%[(['\\)\\'\\)\\'\\)[!T#T\"\\-`&Z(Z#Z("
- "Z$Z(Z$Y'Y J[:Z Bw @Y6[ .Q<[ #S GY /`Da E`C"
- "` DaD` C`Da E`C` 0X 9Y(Z ?X !U .h 4r NvNu Kk .c 9X.X 7^ 7Y1Y M[ &Z 7Z +XCX /X@X C\\"
- "ITFY)W;W=X BY 9X !X KY +YNVNZ *X (X ;Z4Z 2Z @Z !Z 6YJZ ?Y Z /Z DY2Z JZ1Y ,T T MZ N[ NZ HZJ"
- "Y >Z0Z K[ &Z(\\ MZ MZ ,[ !Z(Z :Z \"Z 4Z>] :Z 2YLX3XLY(YLZ6Z&['\\ MZ3[$['[\"Z2Z IZ :Y 9Z(Z#Z*Z2Z7XLX7Z'[@[ @Z;"
- "[ ![ 8X !X >Y 9[:[ 'Y LY -Y 'Y 8X >Y >Y ;Y X Y ;Y"
- "5Y %\\ =] %Y BY2Z =ZJY 3\\ 'Z .\\'[#cLZLb!Z(Z+[;Z;Z,['[ @\\LXK[ %X &\\KXL\\ "
- " DU -Z +S$T EY:Y /X -Z %V?fBU Eo +VEg=V =V<V +X GX *b &nEW KV /W7W BW,X JW,W Nb2X +ZJY "
- "0YIY /YJY 0YIY /YJZ 1YIY =ZEZ 8\\ NZ !Z Z !Z >Z !Z !Z \"Z :Z)\\$YLZ5Y&\\'['['\\(['['['['['[#V%V#[-a&Z(Z#Z(Z$"
- "Z(Z$Y'Y IZ;Z Ay BY9^ G[ %U HY 0]<^ G^=^ F"
- "^<] E]<^ G^=^ 1X 9Z)Z @Z \"U .i 5r NvNu Lm 2h ;X.X 7^ 7Y1Y N[ &[ 7Z +XCX /W?X D[GTC"
- "V)W;W=W AZ :X \"Y KY *j (X (X <Z3Z 2Z @Z !Z 6XIZ ?Y Z 0Z DZ2Z JZ1Z 0W V Y NZ KZ IYIZ ?Z0Z "
- "K[ &Z(\\ MZ MZ -[ Z(Z :Z \"Z 4Z?\\ 8Z 2YKX5XKY(YLZ6Z&[&[ MZ3[%[&\\#Z2[ JZ :Y 9Z(Z#Z+Z1Z7WJW7Z&Z@Z >Z<Z ![ 7X"
- " X ?Y :[8[ \"\\ 3YBZ \\ ,ZAY 4\\ &Y \"Z 0YAZ \"X >Y .Y3Y 3Z '\\ MZ )Z ;Z 2^ +Y ;Y "
- "X Y 6Y /Y5Y $[ =` G^ !Z IZ M\\ #Y2Z =YIZ 3\\ (Z .[%[%aIZI`\"Z(Z+[;Z;Z-[%[ B\\KXJ["
- " &X '\\JXK\\ H\\ 1Z ,U&V EY;Y /X ,Z 'V@jDV Gp +UDj?V >V<V +X GW )` $nEW KV /W7W "
- "BW-X IW-X N`0W *YIZ 1YIY 0YHY 1YIY 0ZIY 1YIZ ?ZDZ 8[ MZ !Z Z !Z >Z !Z !Z \"Z :Z(\\%YLZ5Y&[&['[&[)\\&[)[%[)"
- "[&[$X'X%[-b&Z(Z#Z(Z$Z(Z$Y'Y I[=[ Az CY;` 5\\ $] $\\ \"\\ #\\ $] 8\\/[ 3\\ '\\ #\\ \"[ \"[ \"[ &Z &[ !["
- " #\\ #[ ![ G[@W IYBZ J]8] I\\7\\ H]8] I]8] I\\7\\ 2X 8Y*Z @Z \"U .k 5q N~o Mm 4l =X"
- ".X 7^ 7Z3Z NZ %Z 6Z +XCX /W?W D[FT@S)W;W>X AZ :X \"Y JX (f &X )X ;Z3Z 2Z @Z !Z 7"
- "XHZ ?Y !Z /Z CY1Y JZ1Z 2Y Y $Z Z HY JYHY ?Z/Y L[ %Z'\\ NZ MZ -[ Z(Z :Z \"Z 4Z@\\ 7Z 2YKX5XKY(YKZ7Z'["
- "$[ NZ2Z%[%[#Z2[ JZ :Y 9Z(Z#[,Z1Z8XJW7Z%ZB[ >[>Z !\\ 7X X ?Y ;[6[ (e 7YE` (e 3aEY 8c 2r 5`DX GYEa (X NX "
- "0X1Z 8Y FXD`9` YD` -c 9XD` /aEX :XD] 6g 7t BX0Y LY)Y+X6Z6X)Z/Z NX)Y I} 2Y X Y 9_>W KY5Y #[ =c h >XD` "
- "AT#X 5Y 6X0X LY'Y ?RCW ?~Y!X?X?X ;d 'r!~W KZ1Y =YHY 2\\ )Z /[$[%_GZG_#Z(Z+[;Z;Z-[%[ C\\JXI[ 'X (\\IXJ\\ "
- " (Y d 5Z -W(X FY<Y .X ,[ (UAmDV Iq ,VDl@U >V=W +X HX )^ ,Y1Y HnEW KV 0X7W BW-W HW.X M^/X )"
- "Y +YHY 2YHZ 1YHY 2ZHY 1YHY 2ZHY ?ZDZ 9[ LZ !Z Z !Z >Z !Z !Z \"Z :Z'[%YKZ6Y'\\%[)[$[*[%[)[%[)[%[%Y)Z&[.d'Z(Z#"
- "Z(Z$Z(Z$Y'Y H[>Z @{ DY=b ;f -f -f ,e -f -f Ae7c ;e /b )c *c *c 'Y NX NX X E[ >XD` -c )c *b *c )c '\\ &bDX L"
- "X0X GX0X GX0X GX0X KY)X KYE` ?Y*Y 8[4\\ K[3[ J\\4[ I[4\\ K[3[ 3X 8Z+Z AZ !U /m 6q N~o No 6o ?X.X 8_ "
- "6Y3Z Z $Z 6Z +XCX 0X@X DZET>Q)W;W>W ?Y :X \"X IY 'b $X )X ;Z2Y 2Z @Z !Z 8YHZ ?Y "
- "!Z 0[ CY1Y JZ1Z 5\\ \\ 'Z!Z FY LZHZ @Z/Y L[ %Z&[ NZ MZ .[ NZ(Z :Z \"Z 4ZA\\ 6Z 2YKX6YKY(YKZ7Z'[$[ NZ"
- "2Z&[#Z#Z2[ JZ :Y 9Z(Z\"Z,Z1Z8XJX8Z%[D[ <Z?[ \"\\ 6X X ?Y <[4[ -l :YGd ,k 9eGY :h 5r 8eGY GYGe +Y NX 0X3"
- "\\ 8Y FYGd=c!YGe 2h ;YGd 3eGX ;YG` 9m :t BY1Y LZ+Z+Y7[7Y*[1Z MY+Z J~ 2Y X Y <eAW KY5Y \"Z <f 'o CYFd D"
- "Y(Y 5Y 6Y1Y MY'Z CUE\\ B~Y!Y@X@Y =h 0z\"~W KY0Y >ZHY 1\\ *Z /[#['^EZE^$Z(Z+[;Z;Z.[#Z C[IXH[ (X ([HXI[ ("
- "Z $k 9Z .Y*Z FY=Y .X ,\\ *UAnCU J^CW -VCmAV ?W>V *X IX (a /Y1Y HnEW KV 0X7W BW.X HW.W La3X "
- "(Y ,ZHY 2YGY 2ZHZ 3YGY 1YHZ 3YGY @ZCZ 9[ LZ !Z Z !Z >Z !Z !Z \"Z :Z'\\&YJY6Y'[$[)[$[*[$[+[#[+[$[&[+\\([.e'Z("
- "Z#Z(Z$Z(Z$Y'Y GZ?Z ?| EY>c >l 4l 3l 2l 3l 4l Gl=h @k 5h /h /h /h )Y Y NX Y E[ ?XFd 1g .h /h /h /h )\\ )hHX "
- "LY0X HY0X GX0X GX0Y LZ+Y KYGd AY*Y 9[EXD[ M[1[ L[1[ K[1[ M[1[ 4X 8Z+Y A[ !T /n 6q N~o q 8q @X.X 8` 7"
- "Y3Y Z $Z 5Z +XCX 0X@X DYDT EW;W?X ?Y :X #Y IY %^ \"X )X <Z1Z 3Z @Z !Z 8XGZ ?Y !Z"
- " 0Z BY2Z JY0Z 8_ _ *Z!Y DX LYFY @Z/Y M[ $Z&[ NZ MZ .[ NZ(Z :Z \"Z 4ZB\\ 5Z 2YJX7XJY(YJZ8Z([#[ NZ2Z&["
- "#[$Z2[ JZ :Y 9Z(Z\"Z-Z/Z9XJX9Z#ZDZ :Z@Z \"\\ 5X NX @Y =[1Z 1q <YIh 0o =hHY <l 7r 9hIY GYHg ,Y NX 0X4\\ "
- "7Y FYIg@g#YHh 6l =YIh 7hHX ;YHa ;q <t BY1Y KY+Y*Y8\\8Y([3[ MY+Y I~ 2Y X Y =gCX KY6Z !Z <i -q CYHh F[*Y"
- " 5Z 7Y1Y NZ&Y EWG` D~Y!Y@X@Y >k 5}\"~W KY0Z ?YGZ 1[ *Z /Z\"[(]CZD^%Z(Z+[;Z;Z.[#[ CYHXGY 'X 'YGXHY 'Z &o"
- " ;Z /[,[ FZ?Y -X +\\ +UBoBU LZ>W -UBnAU >W@W *X JX 'c 1Y1Y HnEW KV /W7W BW.W GW/X Lc5W 'Y ,"
- "YFY 4ZGY 2YFY 3YGZ 3YFY 3YGZ AZCZ 9Z KZ !Z Z !Z >Z !Z !Z \"Z :Z&[&YJZ7Y'[#[*Z\"Z+[#[+[#[+[#[&[-\\'[/YM[(Z(Z#"
- "Z(Z$Z(Z$Y'Y G[A[ ?} FY?] :p 8q 8q 7q 8q 8p LqAl Do 9l 3l 3l 3l +Y Y NX Y #i @XHh 5k 2l 3l 3k 2l +\\ +lKX KY0"
- "X HY0X GX0X GX0Y KY,Z KYIh CZ,Z :ZCXC[ [/[ N[.Z MZ.[ [/[ 5X 7Y,Z AZ !U /o 7p M~n s :s AX.X 8` 7Z4Y Y"
- " #Z 5Z +XCX 0W?X EYCT EW;W@X >Z ;X #Y HX #Z X *X ;Z1Z 3Z @Z !Z 9XFZ ?Y \"Z /Z "
- "BY2Z KZ0[ <b a -[\"Y BX MYFY @Z0Z M[ $Z%[ Z MZ .Z MZ(Z :Z \"Z 4ZD] 4Z 2YJX7XJY(YJZ8Z([\"[ Z2Z&Z\"[$Z2"
- "[ JZ :Y 9Z(Z!Z.Z/Z9WHW9Z\"ZF[ :[BZ \"\\ 4X NX @Y >[/Z 4t =YJj 3q >kJY >o 8r ;kJY GYJk .Y NX 0X5\\ 6Y FY"
- "JiBi$YJk 8o ?YJj 9kJX ;YJc <r <t BY1Y KZ-Z)X8\\8Y'Z4[ LZ,Y I~ 2Y X Y ?jDX KY6Y Z ;k 1r CYIj G]-Z 5Z 7"
- "Y1Y NZ&Z HYHb E~Y!Y@X@Y @n 8~P\"~W KY0Z ?YFY 0[ +Z 0[!Z)]BZB]&Z(Z+[;Z;Z.Z\"[ LQ GWGXFW HQ /X /Q*Q @WFXGW &Z"
- " (q ;Z .[BVB[ DY@Z -X *] .UC^EXBU LX<W .VBWC[AU ?WAW )X KX %c 2Y1Y HnEW KV /W7W BW/X GW/W J"
- "c7X 'Y ,YFY 4YFZ 3YFY 4YEY 3YFY 4ZFY AYBZ :[ KZ !Z Z !Z >Z !Z !Z \"Z :Z&[&YIZ8Y([\"[+[\"[,[\"Z+Z!Z,[\"[%[/\\"
- "&Z/YL[(Z(Z#Z(Z$Z(Z$Y'Y F[BZ >Z@d GY@\\ :t ;t <u ;t ;t ;t tDn Gr <o 6o 6o 6o ,Y Y NX Y &l @XIj 8o 5o 6n 6o 5o"
- " -\\ ,nLW JY0X HY0X GX0X GX0Y KY,Y JYJj CY,Y :ZBXBZ!Z+Z Z,Z Z,Z!Z+Z 6X 7Z-Z BZ U 0q 7o M~n s ;u BX."
- "X 9a 6Y5Z!Y \"Z 5Z +XCX C~d&YCT EW;W@W =[ <X #Y HY $Z X *X ;Z1Z 3Z @Z !Z :YFZ ?"
- "Y \"Z 0Z AZ3Z KZ0[ 5Z \"[ ?e d 0Z\"Y AY YEZ AZ0Z MZ #Z%[ Z MZ /[ MZ(Z :Z \"Z 4ZE] 3Z 2YJY9XIY(YIZ9Z(Z![ "
- "Z2Z'[!Z$Z2[ JZ :Y 9Z(Z!Z/[/Z:XHW9Z\"[H[ 8ZC[ \"[ 3X NX @Y ?[-Z 5v ?YKm 6r ?mKY ?q 9r <mKY GYKm /Y NX 0X"
- "6[ 4Y FYKkEl%YKm ;r @YKl ;mKX ;YKd >t <t BY1Y JY-Y(Y9]9Y&Z5Z JY-Y H~ 2Y X Y @lFX JY6Y NY 9k 4s CYJl H"
- "^.Y 4[ 8Y1Y NY$Y J[Ie G~Y!Y@X@Y Ap ;~R\"~W KY0Z @YEZ 0[ ,Z 0Z [*\\AZA\\&Z(Z+[;Z;Z/[![ NS GUFXEU HS 0X 0S,S @U"
- "EXFU %Z )r ;Z -[G^G[ CZAY ,X )] /UC[>TAU NX;W )P9P =UAWAYAU >XDX )X LX HY 3Y1Y HnEW KV /W7W "
- "AP9P 9W0X FW0X ?Y8W &Y -YEZ 5YEY 4ZFZ 5YEY 4ZEY 5YEY BZBZ :[ KZ !Z Z !Z >Z !Z !Z \"Z :Z%['YIZ8Y([!Z+Z![,Z![-"
- "[![-[!Z$[1\\&[/XJZ(Z(Z#Z(Z$Z(Z$Y'Y EZCZ =Z;` HYA[ 8u <u =v <v =u <u!uGr Js =r 9r 9r 9r .Y Y NX Y (o AXJl :q "
- "7q 9r 9q 7q .\\ -y IY0X HY0X GX0X GX0Y KZ-Y JYKl DY-Z ;ZAXAZ\"Y)Y!Z*Z\"Z*Z\"Y)Y 6X 7Z-Y BZ NT 0s 8o"
- " L~m!u =w CX.X 9b 7Y5Y Y \"Z 5Z +XCX C~d&YCT EX<WAX <Z <X #X GY &^ \"X *X ;Z0Y 3Z"
- " @Z !Y 9XEZ ?Y \"Z 0Z AZ3Y JZ/Z 5Z \"[ Ag g 4[\"X ?X YDY AZ0Z MZ #Z%[ Z MZ /[ MZ(Z :Z \"Z 4ZF] 2Z 2YIX9"
- "XIY(YIZ9Z(Z Z Z2Z'[![%Z2[ JZ :Y 9Z(Z!Z/Z.Z:XHX:Z!ZHZ 6ZDZ \"\\ 3X NY AY @Z*Z 6w @YLo 9t @oLY At :r =oLY "
- "GYLo 0Y NX 0X7[ 3Y FYLmGn&YLo =t AYLo >oLX ;YLe ?u <t BY1Y JY-Y(Y9]9X%[7Z IZ.Y H~ 2Y X Y AnGX JY7Z N"
- "Z 9k 6t CYKn I^/Z 5\\ 8Y1Y Z$Z L\\Jg H~Y!Y@X@Y Br =~S\"~W LZ/Y @YDY /[ -Z 0Z NZ+\\@Z@\\'Z(Z*Z;Z;Z/[![ U GSEXDS"
- " HU 1X 1U.U @SDXES $Z +t ;Z ,[JbJ[ AYBY +X (^ 2UCZ9QAU NW:W *Q:Q >VAW?XAU ?ZHY (X MX EX 4Y1Y HnE"
- "W KV /W7W AQ:Q :W0W EW1X <X:X &Y -YDY 6ZEZ 5YDY 6ZEZ 5YDY 5YEZ CZBZ :Z JZ !Z Z !Z >Z !Z !Z \"Z :Z%['YHZ"
- "9Y(Z Z+Z Z-[![-[![-Z [$[3\\%[0XI[)Z(Z#Z(Z$Z(Z$Y'Y E[E[ =Z9^ HYBZ 6v =v >w =w >v =v\"vIt Lt >t ;t ;t ;t /Y Y N"
- "X Y *r BXKn <s :t ;t ;s :t /\\ /{ IY0X HY0X GX0X GX0Y JY.Z JYLo FZ.Y :Y@X?Y$Y'Y#YIP5PIY\"Y.PIY$Y'Y 7X 6Z/Z"
- " CZ NU 1u 8m K~m\"w ?^C] CX.X 9b 7Z6Y X \"Z 4Z +XCX C~d&XBT EX=XAW ;[ =X $Y GY ("
- "b $X +X :Y/Z 4Z @Z \"Z :XDZ ?Y \"Y 0[ @Y4Z JZ/Z 5Z \"[ Dj j 8[\"X =X\"ZDY AZ0Z N[ #Z$[!Z MZ /Z L"
- "Z(Z :Z \"Z 4ZG] 1Z 2YIX:YIY(YHZ:Z)[ [!Z2Z'Z [%Z2[ J[ ;Y 9Z(Z Z0Z-Z;XHX;Z NZJ[ 6[FZ \"\\ 2X MX AY AZ(Z 7x"
- " AYMq ;u AqMY Bv ;r >qMY GYMp 0Y NX 0X8[ 2Y FYMoIp'YMq ?v BYMp ?qMX ;YMf ?u <t BY1Y JZ/Z(Y:^:Y$[9[ HY/Z H~ 2Y "
- " X Y BpHX JY7Z MY ;o 9u CYLp J_0Y 4\\ 8Y1Y Y#Z M]Jh I~Y!Y@X@Y Ct ?~T\"~W LZ/Y AZDY .[ .Z 1[ NZ+[?Z?['Z"
- "(Z*Z;Z;Z/Z NZ!W GQDXCQ HW 2X 2W0W @QCXDQ #Z ,u ;Z +[MfM[ ?YCY +X '_ 4UDZ'U W:W +R;R >U@W?XAU >j (X "
- " NX CX 5Y1Y HnEW KV /W7W AR;R ;W1X EW1W :X<X %Y .ZDY 6YCY 5YDZ 7YCY 5YDZ 7ZDY DZAZ ;[ JZ !Z Z !Z >Z "
- "!Z !Z \"Z :Z$Z'YHZ9Y)[ [-[ [.[ Z-Z NZ-Z [#[5\\$Z0XH[)Z(Z#Z(Z$Z(Z$Y'Y D[FZ <Z7] IYBY 5w >w ?x >x ?w >w#wKv Nu ?v"
- " =v =v =v 0Y Y NX Y +s BXLp >u <v =v =u <v 0\\ 0{ HY0X HY0X GX0X GX0Y JZ/Y IYMp EY.Y ;Y?X?Y%Y%Y$YJR7RIY$"
- "Y.RJY%Y%Y 8X 6Z/Y CZ MU 2v 8m K~m#y @[>\\ DX.X :c 7Z7Z!Y \"Z 4Z +XCX C~d&XBT DW=XB"
- "X :[ >X $Y FY +f &X +X ;Z/Z 4Z AZ !Z ;YDZ ?YFP -Z?Q BZ ?Z5Z JZ/Z 5Z \"[ Gj Ii ;[\"X1Q,W\"YCZ BZ1"
- "Z MZ \"Z$[!Z MZ /Z LZ(Z :Z \"Z 4ZH] 0Z 2YHX;XHY(YHZ:Z)Z N[!Z2Z([ NZ%Z2Z I[ ;Y 9Z(Z Z1Z,Z;XGW;Z N[L[ 4[H[ #\\"
- " 1X MX AY BZ&Z 8^Ga AYN[H_ <cI\\ B`I[MY CaH_ <r ?`H[NY GYNr 1Y NX 0X9[ 1Y FYNqJp'YMq @aJa CYN[H_ A`I[MX "
- ";YNg @`E[ <t BY1Y IY/Y&X:^:Y#Z:[ GY/Y G~ 2Y X Y JW5V B`M_JX IY8Z LY =r ;cL_ CYM^Na J`1Y 5^ 9Y1Y!Z\"Z ^K"
- "j J~Y!Y@X@Y D_I` A~U\"~W LY.Y AYCZ .[ /Z 1Z MZ,\\?Z?\\(Z(Z*Z;Z<[/Z NZ\"Y ;X ;Y 3X 3Y2Y 3X EZ -hM[ ;Z *~Q >"
- "YDY *X )b 6UDY%U V9W ,S<S >U@W>W@T =h 'X X AW 5Y1Y HnEW KV /X9X AS<S <W1W DW2X 9W<W $Y .YCZ 7Y"
- "CY 6YBY 7YCY 6ZCY 7YCZ EZAZ ;[ JZ !Z Z !Z >Z !Z !Z \"Z :Z$Z'YGZ:Y)[ NZ-[ [.Z N[.Z NZ.[ NZ\"[7\\$[1XFZ)Z(Z#Z("
- "Z$Z(Z$Y'Y CZGZ ;Z6\\ IYCY 4^Ga ?^Ga @_Hb ?^Ga ?^Ga ?^Ga$^GaMaI`!bH\\ @aI` ?aI` ?aI` ?aI` 1Y Y NX Y ,u CXM^Nb"
- " @aKa >aJa ?aJa ?aKa =`Ja 1\\ 0`Ic GY0X HY0X GX0X GX0Y IY0Z IYN[H_ FZ0Z <Y>X>Y&X#X%YJT9TIY&Y.TJY&X#X 8X 5Y0"
- "Z CZ ;P4U 1w 9l J~m#z B[;[ EX.X :d 7Y7Y X )~Q #Z +XCX C~d&XBT DW=XCX 9\\ ?X $Y FY "
- "-j (X +X ;Z/Z 4Z AZ \"Z :XCZ ?YM_ 5ZE^ IZ >Y6Z IZ0[ 5Z \"[ Jj Ci ?\\\"X6\\2X#YBY BZ1Z MZ \"Z$[!Z "
- "MZ 0[ LZ(Z :Z \"Z 4ZI] /Z 2YHX;XHY(YGZ;Z)Z N[!Z3[([ NZ%Z2Z H[ <Y 9Z(Z NZ2Z,Z<XFW;Z MZLZ 2ZHZ #\\ 0X MX AY C"
- "Z$Z 9Y>^ BcB] >_?W C^CYNY C]A] 4Y /]Bc GYNYD^ 2Y NX 0X;\\ 0Y FYNXC\\KYD](YNYC] A]B^ DcB] C^CYNX ;YNZDQ A\\"
- ";V 5Y .Y1Y IY/Y&Y;_;Y\"Z;Z FZ0Y $[ 2Y X Y M];\\ F]E[JX IY9[ LY >ZKf =]=V CYNYC] K`2Z 5^ 9Y1Y!Z\"Z!^JZM^"
- " K~Y!Y@X@Y E]C^ CaHl\"~W LY.Z BYBY .\\ 0Z 1Z M[-[>Z>[(Z(Z*Z;Z<[0[ N[$[ <X <[ 4X 4[4[ 4X EZ ._KUHV ;Z )~ <Y"
- "EY *X *e 8UDY$T!W:X .U=T ?U?W>W@U =f &X !X @W 5Y1Y HnEW KV /X9X AT=T =W2X DW2W 8W=X $Y .YBY 8ZC"
- "Z 7YBY 8ZCZ 7YBY 8ZBY FZ@Z ;Z IZ !Z Z !Z >Z !Z !Z \"Z :Z$[(YGZ:Y)[ NZ-Z MZ.Z N[/[ N[/[ NZ![9\\#[2YFZ)Z(Z#Z(Z"
- "$Z(Z$Y'Y C[I[ ;Z5\\ JYCY 4X=^ @X=] @Y=] ?Y>^ @X=^ @X=^%X=l@\\\"_?W A]@\\ @]@\\ @^A\\ @^A\\ 1Y Y NX Y -w DXNY"
- "C] A^C^ ?^C^ A^B] @^C^ ?^C^ 2\\ 1^C_ FY0X HY0X GX0X GX0Y IY0Y HcB] FY0Y ;X=X=Y(Y#Y'YJV;VIX&X.VJY(Y#Y 9W 4Z1"
- "Z DZ =S4U 2y 9j I~l#{ BZ9Z EX.X :d 7Z8Y!Y *~R #Z +XCX C~d'YBT DX?XBW 7\\ @X $Y FY "
- "/ZNVNZ *X ,X :Z/Z 4Z AZ #Z :XBZ ?o 9ZGc MZ =Z8[ HY0\\ 6Z \"[ Li >j C\\\"X8aGVBW$ZBZ CZ2Z LZ \"Z#Z!"
- "Z MZ 0[ LZ(Z :Z \"Z 4ZJ] .Z 2YHX<YHY(YFY;Z)Z MZ!Z3[([ N[&Z3[ H] >Y 9Z(Z NZ2Z,Z<XFX<Z LZN[ 2[JZ \"[ /X LX B"
- "Y DZ\"Z :U7\\ Ca>\\ @^:T C\\?b D\\=\\ 5Y 0\\>a Ga?\\ 2Y NX 0X<\\ /Y Fa@\\MX@[(b@\\ B]?\\ Da?] D\\?a ;b 1Z6"
- "S 5Y .Y1Y IZ1Z&Y;_;X![=Z DY1Y #[ 2Y X Y `>` I\\B[KX IY:\\ LY ?ZDa ?\\7R Cb?\\ F[3Y 5_ 9Y1Y\"Z Y!]IYJ] L"
- "~Y!Y@X@Y F\\?\\ D^Ai\"~W LY.Z CZBZ .\\ 1Z 1Z LZ.[=Z>[(Z(Z*Z;Z<[0[ N[%\\ <X <\\ 5X 5\\4\\ 5X EZ /^IUFT ;Z ("
- "| ;YFY )X +h :TDY#U\"W:X /V?V ?U?W>XAU <c $X \"X ?X 6Y1Y HnEW KV .W9W @U>V ?W3X CW3X 8X>W #Y /Z"
- "BZ 9YAY 8ZBZ 9YAY 8ZBZ 9YAY FZ@Z ;Z IZ !Z Z !Z >Z !Z !Z \"Z :Z$[(YFZ;Y)Z MZ-Z MZ/[ MZ/[ N[/Z M[![;\\\"[3YE[*"
- "Z(Z#Z(Z$Z(Z$Y'Y B[JZ :Z4[ JYCX 3U8\\ @U8\\ AV8\\ @U7\\ AU7[ @U8\\%U8h=\\$]9T B\\=\\ B\\=\\ B\\=\\ B\\<[ 2Y Y "
- "NX Y .x Da?\\ C]?] A]?] B\\?] B]?] A]?] 3\\ 2]?] FY0X HY0X GX0X GX0Y IZ1Y Ha?] GY1Z <X<X<X(X!X'XJX=XJY(X.X"
- "JX(X!X 9W 4Z1Y >~d W5T 2{ 9i H~k$} DZ7Z FX.X :d 7Z9Z!X )~R #Z 0~d&XBT DX?XCX 6\\ "
- " =Y EY 0ZMVMZ +X ,X :Z/Z 4Z B[ %\\ :XBZ ?q ;YHg Z <Z:[ GZ1\\ 6Z \"[ i M~c Nj G\\!W9eIVBX%Y@Y CZ3[ M"
- "[ \"Z#Z!Z MZ 0Z KZ(Z :Z \"Z 4ZK] -Z 2YGX=XGY(YFZ<Z*[ MZ!Z3[(Z M[&Z3[ H^ ?Y 9Z(Z NZ3Z*Z=XFX=Z Kf 0[L[ #\\ /X "
- " LX BY JS4[ C`<\\ A\\5Q D[;` E[9Z 5Y 1\\<` G`<Z 2Y NX 0X=\\ .Y F_=[MV=[)`<[ D\\<\\ E`<[ E[;_ ;` 0Z3Q 5"
- "Y .Y1Y HY1Y%Y<`<Y [?[ DZ2Y $[ 1Y X Y !cBc J[?YLX HY<] JX @Y?_ @[ '`<[ EZ4Z 5` :Y1Y\"Z Z#\\GYI\\ EZ:Z IY@"
- "X@Y FZ;[ E]>\\ 0Z 6Y.Z CYAZ -\\ 2Z 1Z LZ.[=Z=[)Z(Z*Z;Z<Z/Z LZ&\\ ;X ;\\ 6X 6\\2\\ 6X EZ /\\GUCQ ;Z 'z 9YGY"
- " )X -ZN_ ;TDX\"U\"W;Y 0W@W ?T>W>X@T ;a #X #X =W 6Y1Y GmEW KV .X;X @W@W @W3W BW4X 6W?X #Y /Y@Y :"
- "ZAY 8Y@Y 9YAZ 9Y@Y 9YAZ GZ@Z ;Z IZ !Z Z !Z >Z !Z !Z \"Z :Z#Z(YFZ;Y)Z M[/[ MZ/[ MZ/Z LZ/Z M[ [=\\!Z3YD[*Z(Z#Z"
- "(Z$Z(Z$Y'Y AZKZ 9Z4[ JYDY 3R3[ AR3[ BS3Z @S4[ AS4[ AR3[&R3e:[&]6R C\\:[ D\\:[ D\\:[ D\\:[ 3Y Y NX Y /_B] E_<"
- "[ C[;[ B\\<\\ C\\<\\ C[;\\ C\\<\\ 3\\ 3\\<\\ FY0X HY0X GX0X GX0Y HY2Z H`<[ FY2Y ;X<X<X)X NX)YKZ?ZJX(X/ZKX)X"
- " NX ;X 3Y2Z >~d#Z6U 3} :h G~k%~P EY5Y FX.X ;ZNY 6Y9Z!X *~R \"Z 0~d&YCT CXAXBW 5] "
- " >Y EY 2ZKVKZ -X ,X :Z/Z 4Z BZ &] :XAZ ?s =YJk #[ ;[=[ FZ1\\ 6Z \"[ #j L~d Ki J\\!X:hKVAW%Y@Y CZ5\\ L"
- "[ \"Z#Z!Z MZ 0Z KZ(Z :Z \"Z 4ZL] ,Z 2YGX=XGY(YEZ=Z*[ M[\"Z4['Z LZ&Z4[ F` BY 9Z(Z MZ4Z*Z=XEW=Z Jd .ZLZ #\\ .X"
- " LX BY JQ1[ D_:[ B\\ ([9_ F[7Z 6Y 1[:_ G^9Z 3Y NX 0X>\\ -Y F^;b;Z)_:Z D[:\\ F_:[ G[9^ ;_ /Y EY .Y1Y "
- "HY2Z$Y=a=Y NZ@[ BY3Z %[ 0Y X Y \"eCd L[>YLX HY>^ IY AY=] @Z &_:Z DY4Y 5a :Y1Y\"Z Z$\\GYG\\ EY9Y IY@X@Y G"
- "Z9[ G\\;[ 0Y 5Y.Z DZ@Y ,\\ 3Z 1Z LZ.Z<Z=[)Z(Z*Z;Z<Z/Z LZ'\\ :X :\\ 7X 7\\0\\ 7X EZ 0\\FU -Z &x 8YHY (X -YK"
- "_ >UDX!T\"X<Y 1XAX ?T>W>X@U :] !X $X <W 6Y1Y GmEW KV .Y=X ?XAX AW4X BW4W 5W@X \"Y 0Z@Y :Y@Z 9Y@"
- "Y :Z@Y 9Y@Z ;Z@Y HZ?Z <[ IZ !Z Z !Z >Z !Z !Z \"Z :Z#Z(YEZ<Y*[ M[/[ M[0Z LZ/Z LZ/Z M[ N[?\\ Z3XBZ*Z(Z#Z(Z$Z(Z"
- "$Y'Y @ZM[ 9Z3[ KYDY 3P0Z AP0Z BQ0Z AQ0Z BP0Z AP0Z&P0b7Z'\\2P CZ7Z DZ7Z DZ7Z DZ7Z 3Y Y NX Y 0]<Z E^:Z D[9[ C["
- ":\\ E\\:[ D[9[ C[:\\ 4\\ 3[9[ GY0X HY0X GX0X GX0Y HZ3Y G_:[ GY2Y <X;X;X*X NX)XJ[A\\JX*X/[JX*X NX ;X 3Z3Z "
- " >~d&^7U 4~ 9f E~i%~R GY4Y FX.X ;ZNZ 7Y9Y!X )~R \"Z NW?W BYCT CYBXCX 6_ ?Y EZ 5ZI"
- "VIZ /X ,X :Z.Y 4Z C[ )_ :YAZ ?t >YKn %Z 9\\A\\ EZ1\\ 6Z \"[ &j I~d Hi N\\ W:jLVAW&Z@Z DZ8^ KZ !Z#[\"Z "
- " MZ 0Z KZ(Z :Z \"Z 4ZM] +Z 2YGY?XFY(YEZ=Z*Z L[\"Z4['Z LZ&Z4[ Fc EY 9Z(Z MZ5Z)Z>XDW=Z Ic .[NZ #\\ -X KX CY "
- " )Z D^8[ D\\ '[8^ FZ5Z 7Y 2[8^ G]8Z 3Y NX 0X?[ +Y F]9`9Y)^9Z E[8[ F^8Z GZ8^ ;^ .Y EY .Y1Y GY3Y#Y=WNX=Y M"
- "ZAZ AY3Y %[ /Y X Y #gEf N[<YMX HYBb IY BY;] BZ %^8Z DY5Y 5b ;Y1Y#Z NZ$[FYF[ EY9Y IY@X@Y HZ8[ H\\9[ 1Y 5Y"
- ".Z DZ@Z ,\\ 4Z 2[ LZ.Z<Z<Z)Z(Z*[<Z<Z/Z LZ(\\ 9X 9\\ 8X 8\\.\\ 8X EZ 1\\EU -Z %^E] EhIg 6X .YI_ ?UEX T!W="
- "Z 2YBY @U>W>W?U 7W <~d BX ;W 6Y1Y GmEW KV -X=X ?YBY BW4W AW5X 5W@W !Y 0Y?Z ;Y?Y :Z@Z ;Y?Y :Z?Y ;Y"
- "?Y HZ?Z <[ IZ !Z Z !Z >Z !Z !Z \"Z :Z#Z(YEZ<Y*[ LZ/[ M[0Z LZ/Z LZ0[ LZ M[A\\ NZ4XAZ*Z(Z#Z(Z$Z(Z$Y'Y @[NZ 8Z3"
- "[ KYDY AZ !Y Y Z !Z !Z 5`5Z([ %Z5Z FZ5Z FZ5Z FZ5Z 4Y Y NX Y 1\\:[ F]8Z F[7[ E[8[ E[8[ E[8[ E[8[ 4\\ 4[9\\"
- " GY0X HY0X GX0X GX0Y GY4Z G^8Z GZ4Z <X;X:W+X LX*WH[C\\IX*X0[HW+X LX <X 2Y4Z =~d(`7T 4~Q 9e E~i%~R GY3"
- "Y GX.X ;YMZ 7Z;Z!X *~R !Z X@X BZDT BXCYDX 6` ?Y DY 7[HVH[ 1X -X 9Z.Y 4Z D[ 7"
- "m 9X@Z ?v AZLp &Z 8^H_ DZ1\\ 6Z \"[ (i F~d Ei #\\ NW;lMV@W'Y>Y D~P JZ !Z#[\"~Q Dy Z K~] :Z \"Z 4ZN] *Z 2YFX?XF"
- "Y(YDZ>Z*Z L[\"Z5\\([ LZ&Z5\\ Eg JY 9Z(Z MZ5Z)Z>XDX>Z Ib ,f $\\ ,X KX CY (Y D]6Z D[ '[7^ GZ4Z 7Y 2Z6] "
- "G]7Z 4Y NX 0X@[ *Y F]8^8Z*]7Z FZ6[ G]6Z I[7] ;] -X DY .Y1Y GY3Y#Y=WNX=X L[CZ ?Y4Y &[ .X NX Y $iGh Z:XNX"
- " GYHg HY CY8\\ CY $]7Z DY6Y 4b ;Y1Y#Z MZ&[EYE[ FY9Y IY@X@Y HZ7[ I[7[ 2Y 5~V DY>Y +\\ 5Z 2Z KZ/[<Z<[*Z(Z)Z<Z<Z/"
- "ZIuIZ)\\ 8X 8\\ 9X 9\\,\\ 9X EZ 1[DU -Z $Z@[ EhJh 6X /YF_ ATDX U\"X?[ 3ZCZ @U>W>W?U K~d CX ;X "
- " 6Y1Y FlEW KV -Y?Y ?ZCZ CW5X AW5W 5XAX !Y 0Y>Y <Z?Z ;Y>Y <Z?Z ;Y>Y ;Y?Z JZ>~Q3[ I~Q G~Q F~Q G~Q 5Z !Z !Z "
- "\"Z :Z#Z(YDZ=Y*[ LZ/Z L[0Z L[0Z LZ0[ LZ L[C\\ N[5X@Z*Z(Z#Z(Z$Z(Z$Y'Y ?e 7Z3[ KYDY @Y Y !Z Y Y Y 4_4Y)[ %Z3"
- "Y GZ3Y FZ4Y FZ4Y 4Y Y NX Y 1[8Z F\\7Z F[7[ EZ6[ G[6[ G[6Z EZ6[ <Z9^ HY0X HY0X GX0X GX0Y GY4Y F]6Z GY4Y "
- " ;W:X:X,X LX+XG[E\\GW*W0[GX,X LX <X 2Z5Z =~d(`8U 4~R 9c D~h%~T HX2Y GX.X <ZLY 6Y;Z!X *~"
- "R !Z X@X BZDT BZGZCW 6b @Y DY 8ZFVFZ 2X -X 9Z.Y 4Z DZ 7l 8X?Z ?w BZMr ([ 7s C[3] 6Z \"[ +i C~d"
- " Cj '\\ NW;nNV@W(Z>Y D~ IZ !Z#[\"~Q Dy![ K~] :Z \"Z 4h )Z 2YFX@YFY(YDZ>Z*Z KZ\"Z5\\([ LZ&Z6\\ Ck Y 9Z(Z LZ6Z("
- "Z?XDX?Z G` *d #[ +X KX CY 'Y E]6[ F[ &Z5] GY2Y 7Y 3Z4\\ G\\6Z 4Y NX 0XA[ )Y F\\7]6Y*\\5Y G[5Z G\\5Z I"
- "Z5\\ ;] -X DY .Y1Y GZ5Z#Y>XMW>Y K[E[ ?Y5Y &[ .Y NX Y $XIZHZIY!Z:XNX GYHf GY DY6[ CY $\\5Y CX6Y 5c ;Y1Y#"
- "Z MZ&[EYDZ FY9Y IY@X@Y IZ5Z IZ5Z 2Y 5~V EZ>Y *[ 5Z 2Z KZ/[<Z<[*Z(Z)Z<Z=[0[IuIZ*\\ 7X 7\\ :X :\\*\\ :X L["
- "CU -Z %Z>Z EiKh 6X /XC^ BTDX U\"YA\\ 4ZCZ N~d &U>W?X>T K~d EY :W 5Y1Y EkEW KV ,YAY =ZCZ DW6X @W6"
- "X 5W@W 'Z>Y <Y=Y <Z>Z =Y=Y ;Y>Z =Z>Y JZ>~Q3Z H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z :Z#[)YDZ=Y*[ LZ/Z KZ0Z L[1[ LZ0[ L"
- "Z K[E\\ M[6Y@Z*Z(Z#Z(Z$Z(Z$Y'Y >d 7Z2Z KYDY @Y Y Y NY Y !Y 4^3Z*Z $Z3Z HZ3Z HZ3Z HZ2Y 5Y Y NX Y 2[6Z G"
- "\\6Y FZ5[ G[5Z GZ5[ GZ5[ G[5Z =[:_ HY0X HY0X GX0X GX0Y GZ5Y F\\5Z GY5Z <X:X:X,W JW+XF[G\\FX,X1[FX,W JW <X "
- "2Z5Y <~d'UNY9U 5~T H[LaM[!~g&~V JY1X GX.X <ZLZ 7Y;Y X Z 3Z W?X AZET A\\M\\CX 7d "
- " BZ DY 8XDVDX 2X -X 9Z.Y 4Z E[ 7j 7Y?Z ?x CZNt )Z 5p @Z3] 6Z \"[ .i @~d @i *\\ MW<^Ib@W(Y=Z E| GZ !Z"
- "\"Z\"~Q Dy![ K~] :Z \"Z 4f 'Z 2YEXAXEY(YCZ?Z*Z KZ\"Z6\\'[ LZ&Z8] An $Y 9Z(Z LZ7Z'Z?XDX?Z F_ *c #\\ +X JX DY "
- " 'Y E\\4Z FZ %Z4\\ HZ1Y 8Y 3Z4\\ G[4Y 4Y NX 0XC\\ (Y F[6]6Y*[4Y GZ4[ H\\4Z JY4\\ ;\\ ,X DY .Y1Y FY5Y!Y?"
- "XMX?Y JZF[ >Z6Y &[ .Y NX Y %WEYJYEX#Z8a GYHe FY DX4[ DY $\\5Y CY8Z 5d <Y1Y$Z LZ'[DYD[ GY9Y IY@X@Y IY4Z J"
- "[5[ 3Y 6~W EY=Z *[ 6Z 2Z KZ/Z;Z<[*Z(Z)Z<Z=Z/[IuI[,\\ 6X 6\\ ;X ;\\(\\ ;X LZBU -Z %Y<Z FjMi 6X 0X@] CTD"
- "W NU!ZE^ 5ZCZ M~d &T=W@X=T K~d FY :X 5Y1Y EkEW 3Z CV +ZEZ ;ZCZ EW6W ?W7XA]\"XAX 'Y=Z =Y=Y <Y<Y =Y="
- "Y <Z=Y =Y=Z KY=~Q3Z H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)YCZ>Y*Z KZ/Z KZ0Z L[1[ L[1[ LZ J[G\\ L[7Y?Z*Z(Z#Z(Z$Z(Z$"
- "Y'Y >c 6Z2Z KYDY ?Y X NX NY Y Y 4\\1Y+[ %Z1Y HY1Y HY1Y HY1Y 5Y Y NX Y 3[5Z G[5Z HZ3Z GZ4[ HZ4Z HZ3Z GZ"
- "4[ >Z9` IY0X HY0X GX0X GX0Y FY6Z F\\4Z GY6Y ;W9X9W-X JX,WD[I\\DW,W1[DW-X JX =X 1Y6Z <~d'RKY:U 5~U J"
- "~T$~g'~X KY1X GX.X <YKZ 7Z<Y W NZ 3Y NW?W @\\GT @jCW 7f CZ DY 7VCVCV 1X .X "
- "8Z.Y 4Z F[ 6h 5X>Z ?y DgF` *Z 2k >Z4^ 6Z \"[ 1j >~d =i -[ LW=\\C_?W)Y<Y Ez EZ !Z\"Z\"~Q Dy![ K~] :Z \"Z 4e &Z"
- " 2YEXAXEY(YCZ?Z*Z KZ\"Z8^'[ L['Z:_ @p 'Y 9Z(Z KZ8Z'Z@XBW?Z F^ (b $\\ *X JX DY &X E[2Y FZ &Z3\\ HY0Y 8Y"
- " 3Y2[ G[4Y 4Y NX 0XD\\ 'Y F[5[5Y*[4Y HZ2Z H[3Z KZ3[ ;[ ,Y DY .Y1Y FY5Y!Y?WLX?Y J[GZ <Y7Z '[ -Y NX Z 'WC"
- "YKXBV#Z8` FYHc +YCY EY4[ DY $[4Z CX8Y 5e <Y1Y$Z KZ([DYCZ GY9Y IY@X@Y IY3Z KZ3Z 3Y 6~W EY<Y )[ 7Z 2Z KZ/Z;Z;Z*Z("
- "Z)[=Z=Z/[IuI[-\\ 5X 5\\ <X <\\&\\ <X LZBU -Z &Y:Y FjNj 6X 0X?] EUEX NU!s 6ZCZ L~d &T=WAY=T K~d GX"
- " 9Y 5Y1Y DjEW 3Z CV *]M] 9ZCZ FW7X5X3W7WCc%XBX5Y JY<Y >Z=Z =Y<Y >Z=Z =Y<Y >Z=Z LZ=~Q3Z H~Q G~Q F~Q G~Q"
- " 5Z !Z !Z \"Z Ew5[)YCZ>Y*Z KZ/Z KZ0Z KZ1[ L[1Z KZ I[I\\ K[8Y>[+Z(Z#Z(Z$Z(Z$Y'Y =a 5Z2Z KYDY ?Y Y X MX Y Y"
- " 4\\1Y+Z $Y0Y IZ1Y IZ1Y IZ0X 5Y Y NX Y 3Z3Y GZ3Y HZ3Z HZ2Z IZ2Z IZ3Z GZ3Z >Z:a IY0X HY0X GX0X GX0Y FZ7Y E["
- "3Z GY6Y ;W9X9W-W HW,WC[K\\CW,W2[CW-W HW =X 1Z7Z <~d NX:U 5~V M~X%~e&~Y LX0Y HX.X =ZJY 6Y=Z W "
- " NZ 3Y X@X ?]IT ?hCW 7h2X ;Y CY 7TAVAT 1X .X 8Z.Y 4Z G\\ 6g 5X=Z ?X?a EeB^ +Z /f ;[5"
- "^ 4i ;~d :i 1[ LW<Z?]?W*Z<Z Fx CZ !Z\"Z\"~Q Dy![ K~] :Z \"Z 4e &Z 2YEXBYEY(YBZ@Z*Z KZ\"Z9^&[ L['[Ad >r *Y "
- "9Z(Z KZ8Z'Z@XBX@Y D\\ &` $\\ )X JX DY &X E[2Z HZ %Z3\\ IZ/X 8Y 4Z2[ GZ3Y 4Y NX 0XE\\ &Y FZ4[5Y*[4Z IZ"
- "2Z H[2Y KY2[ ;[ +X DY .Y1Y FZ7Z!Y?WLX?X H[IZ ;Y7Y '[ ,Y NX NY *Q NV@WLW?U#Z8` FYHd .^FY EX2[ DX $[3Y CX8Y"
- " 5YMY <Y1Y$Z KZ(ZCYCZ GY9Y IY@X@Y JY2Z L[3Z 3Y 6~W FZ<Z )[ 8Z 2Z KZ/Z;Z;Z*Z(Z)[=Z>[/[IuI[.\\ 4X 4\\ =X =\\$\\"
- " =X MZAU -Z &X8Y G~W 6X 0W<\\ FUEX MT iNW 8[D[ K~d &T=WE\\<T K~d HX NQ<Y 4Y1Y CiEW 3Z CV )k 7"
- "ZC[ HW7W5Y3W8XFh>Q<YAW5Z KZ<Z ?Y;Y >Z<Z ?Y;Y >Z<Z ?Z<Y LZ=~Q3Z H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)YBZ?Y*Z KZ/"
- "Z KZ0Z KZ1[ L[1Z KZ H[K\\ J[8X=[+Z(Z#Z(Z$Z(Z$Y'Y <` 5Z2Z KYDZ ?X Y Y NX NX NX 4[/Y,Z $Y/Y JY/Y JY/Y JY/Y "
- "6Y Y NX Y 3Z3Z HZ3Y IZ1Z IZ2Z IZ2Z JZ1Z IZ2Z ?Z:b IY0X HY0X GX0X GX0Y EY8Z E[2Y GZ8Z ;W9X9X.W HW-XB[M"
- "\\BW,W3[BX.W HW =X 0Y8Z ;~d NY;U 6~X!~[%~c&~Z LX0Y HX.X =ZJZ 7Y=Y N~l 4Z 3Y X@X ?`L"
- "T >eBX<U\"[M\\4Y ;Y CZ 7Q?V?Q 0X .X 8Y-Z 5Z H\\ 5j 9Y=Z ?T9_ Ec>] ,Z 1j <[7_ 7i 8~d 7i 5[ KW=Z="
- "\\?W*Y:Y F{ FZ !Z\"Z\"~Q Dy![1j&~] :Z \"Z 4e &Z 2YDXCXDY(YBZ@Z*Z KZ\"Z<a&Z K['} <s ,Y 9Z(Z KZ9Z%ZAXBXAZ E] &_ $"
- "\\ (X JY EY &Y F[2Z HZ %Y1[ IY.Y 9Y 4Z1Z GZ3Z 5Y NX 0XF\\ %Y FZ4Z3Y+Z2Y IZ1Z I[2Z LY1Z ;[ +X DY .Y1Y "
- "EY7Y NX@XKW@Y G[K[ :Y8Y ([ ,Z NX NY /[(R NU?XNW=U%Z7_ EYHg 3bHY FY1Z DX $Z2Y CY:Y 5ZMZ =Y1Y$Z KZ)[CYBY GY9Y"
- " IY@X@Y JY1Y LZ1Z 4Y 6~W FY;Z *[ 7Z 2Z KZ/Z;Z;Z*Z(Z(Z=Z>[/[IuI[/\\ 3X 3\\ >X >\\\"\\ >X MZAU -Z 'X6X 5c "
- "%X 1X;\\ GUEX MT NgMW 9[D[ J~d &T=m;T K~d In 4TA[ 4Y1Y BhEW 3Z DX )i 5[D[ IX9W5Z3W8WFj?TA[BX5Z KY"
- ";Z @Z;Z ?Y:Y @Z;Z ?Z;Y ?Y;Z NZ<~Q3Z H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)YAY?Y*Z KZ/Z KZ1[ KZ1[ L[1Z KZ G[M\\ IZ8"
- "X<[+Z(Z#Z(Z$Z(Z$Y'Y <_ 4Z2Z KYD[ @X NX Y NY X NX 3Z/Y-Z $Z/Y KZ/Y KZ/Y KZ/Y 6Y Y NX Y 4Z2Z HZ3Y IZ1Z I"
- "Z1Z JY1Z JZ1Z IZ1Z @Z;XNZ JY0X HY0X GX0X GX0Y EY8Y D[2Z GY8Y ;X9X8W.W HW-W@hAW-X4[@W.W:[:W =X 0Z9Z I"
- "[ 7Y<U 6~Y\"~^'~c'~\\ MX/X HX.X =YIZ 7Z>Y ~m 4Z 3Y W?X >g =cAW?]'[K\\5Y ;Y CZ %V M"
- "X /X 7Y-Z 5Z H[ 4l ;X<Z ?Q4^ Fb<] .[ 3o ?[7_ :i 5j 9[ JW=Y;[?W+Z:Y F~ IZ !Z\"Z\"~Q Dy![2l'~] :Z "
- "\"Z 4f 'Z 2YDXCXDY(YAZAZ*Z KZ\"~%Z K['| 9s .Y 9Z(Z JZ:Z%ZAXBXAZ E] %] #[ 'X IX EY &Y FZ0Y HY %Z1[ IY.Y"
- " 9Y 4Y0Z GZ2Y 5Y NX 0XG[ #Y FZ4Z3Y+Z2Y JZ0Z IZ0Y MZ1Z ;Z *Y EY .Y1Y EY8Z NYAXKXAY FZL[ 9Y9Y ([ +Y MX NZ 4b,"
- "S U=`=U%Z6^ EYHi 6dIY FY1Z DY %Z2Y BX:Y 5ZLY =Y1Y%[ KZ)ZBYBZ HY9Y IY@X@Y JY1Z MZ1Z 4Y 6~W GZ:Y +\\ 7Z 2Z KZ/Z"
- ";Z;Z*Z(Z([>Z>Z.[IuI[0\\ 2X 2\\ ?X ?\\ \\ ?X MY@U 8y ;X6X 4a $X 1X9[ HUEX MT MeLW :[D[ I~d &T=l:T "
- "K~d Io 5m 3Y1Y AgEW 3Z Nl 2g 3[D[%lDX5Z>mDXFk@mAW5[ LZ:Y @Y:Z ?Y:Y @Z:Y ?Y:Z AZ:Y NZ<~Q3Z H~Q G~Q F~Q G"
- "~Q 5Z !Z !Z \"Z Ew5[)YAZ@Y*Z KZ/Z KZ1[ KZ1[ L[1Z K[ Gh HZ9X;[+Z(Z#Z(Z$Z(Z$Y'Y ;] 3Z2Z KYC[ AX NX Y NY Y X"
- " 3Y.Y-Z $Y.Y KY.Y KY.Y KY.Y 6Y Y NX Y 4Z1Y HY2Y IZ1Z IY0Z KZ0Z KZ1Z IY0Z @Y;XMZ JY0X HY0X GX0X GX0Y DY9Y D"
- "Z0Y GY9Z ;W8X8W.W HW-W?f?W.W4[?W.W:[:W =X 0Z9Y HZ 5X<U 6~Z$~`'~a&~\\ NY/X HX.X =YHY 7Z?Z ~m "
- " 4Z 3Y W?W <i >_@XAa*[I\\6Y ;Y CZ %V MX /X 7Y-Z 5Z I[ 3n >X;Z ] G`9\\ .Z 4s @[9` "
- " =i /i ;Z IV=Y9Z>V+Z:Z G~P JZ !Z\"Z\"~Q Dy!Z1l'~] :Z \"Z 4g (Z 2YDYEXCY(YAZAZ*Z KZ\"}$Z K['z 5r /Y 9Z(Z JZ;Z"
- "$ZAW@WAZ F_ %\\ $[ &X IX EY &Y FZ0Y IZ %Y/Z IY.Y 9Y 4Y0Z GY1Y 5Y NX 0XH[ \"Y FY3Z3Y+Z2Y JZ0Z IZ0Y MY0"
- "Z ;Z *Z FY .Y1Y DY9Y MYAWJXAY F[MZ 8Z:Y )[ +Z MX N[ 7g1U U<^;U&Z6^ EYHj 9gJY FX/Y CY &Z2Y BY<Z 6ZKZ >Y1Y%Z"
- " J[*ZBYBZ HY9Y IY@X@Y KY0Z MY/Y 4Y 6~W GZ:Z ,[ 6Z 2Z KZ/Z;Z;Z*Z(Z([>Z?[.ZHuI[1\\ 1X 1\\ @X @\\ M\\ @X NZ"
- "@U 8y ;W4X 5` #X 1X8Z HUEX MT LbJW ;ZC[ H~d &T=j8U L~d Io 5l 2Y1Y @fEW 3Z Nl 0c 0[CZ&lDW5[>mEXE\\N^"
- "AlAX6\\ LZ:Z AY9Y @Z:Z AY9Y @Z:Z AY9Z!Z;~Q3Z H~Q G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)Y@ZAY*Z KZ/Z KZ1[ KZ1[ L[1Z K"
- "[ Ff GZ:X:[+Z(Z#Z(Z$Z(Z$Y'Y :\\ 3Z2Z KYC\\ BY X NX NY Y X 3Y-X-Y #Y-X KY-X KY-X KY-X 6Y Y NX Y 5Z0Y HY"
- "2Y IY/Y JZ0Z KZ0Z KY/Z KZ/Y AZ;WKY JY0X HY0X GX0X GX0Y DY:Z DZ0Y FY:Y :WK~KW.WK}KW-W>d>W.W5[>W.W:[:W =X /"
- "Y:Z IZ 4Y=T 6~[%~b'~_%~\\ NY/X HX.X >ZHY 6Y?Y N~m 4Z 3Y !X@X ;l @[>WBe,ZG\\7Y ;Y"
- " CZ %V ;~c LX 7Y-Z 5Z J\\ 2n @Y;Z N\\ G`8\\ /Z 5u A\\<b ?i *i ?Z IW=X8Z>V+Y8Y G~R LZ !Z\"Z\"~Q"
- " Dy![2l'~] :Z \"Z 4h )Z 2YCXEXCY(Y@ZBZ*Z KZ\"|#Z K['x 0q 1Y 9Z(Z IZ<Z$ZBX@XBY F` %[ $\\ &X IX EY &Y FZ"
- "0Z JZ %Y/Z JY,X 9Y 5Z0Z GY1Y 5Y NX 0XI[ !Y FY3Z3Y+Y1Y JZ/Y IZ0Y MY/Y ;Z *[ GY .Y1Y DY9Y MYBXIWBY Dg 7Y;Z *[ +"
- "[ MX M[ :l6W T:\\:U&Y5] DYHk :hKY GY/Z DZ 'Z2Y BY<Y 5ZKZ >Y1Y%Z IZ*YAYBZ HY9Y IY@X@Y KY/Y MY/Y 4Y 6~W GY9Z "
- "-[ 5Z 2[ LZ/Z;Z;Z*Z(Z'[?Z?[.[IuI[2~n BX B~n AX A~m AX NZ@U 8y <X4X 4_ #X 1X7Z IUEX MT J^HW <ZCZ F~d &T="
- "g5T -X ,o 5k 1Y1Y >dEW 3Z Nl ._ ,ZCZ'lEX6\\>mEWDVCZBkAX6] LY8Y BZ9Z AY8Y BZ9Z AY8Y BZ9Z!Z;~Q3Z H~Q "
- "G~Q F~Q G~Q 5Z !Z !Z \"Z Ew5[)Y@ZAY*Z KZ/Z KZ1[ KZ1[ L[1Z KZ Ee FZ;Y:[+Z(Z#Z(Z$Z(Z$Y'Y :[ 2Z2Z KYB\\ CY X NX"
- " NY Y Y 4Y-Y.Y #Y-X KY-X KY-Y LY-Y 7Y Y NX Y 5Z0Z IY2Y JZ/Z KZ/Y KY/Z KY/Z KZ/Y#~d$Z<WJY JY0X HY0X GX0X G"
- "X0Y DZ;Y CZ0Y FY:Y :WK~KW/WJ}JW.W=b=W.W6[=W/W9[9W >X /Z;Z JZ 2X>U 6~\\'~c&~^$~Z MY/X HX.X >YGZ 7Z@Y "
- "N~m 4Z 3Y !X@X :n 'WBg.ZE\\8X :Y CZ %V <~e NX 6Y-Y 4Z K\\ #a AX:Z M\\ H_6[ 0Z"
- " 6aI` A]?c ?f $f ?Z IW>Y7Y>V,Z8Z HZ8` MZ !Z\"Z\"Z MZ 1[2l'Z(Z :Z \"Z 4ZN] *Z 2YCXFYCY(Y@ZBZ*Z KZ\"{\"Z "
- "K['v +o 2Y 9Z(Z IZ<Z#YBX@XCZ Fa %Z %\\ %X HX FY 6i FZ0Z JZ %Y/Z JY,X 9Y 5Z/Y GY1Y 5Y NX 0XK\\ Y FY3Z"
- "3Y+Y1Y JY.Y IY/Z NY/Y ;Z *\\ HY .Y1Y DZ;Z LXBXIWBY Ce 6Y;Y )[ -\\ LX L\\ >q:X !U:[9U&Y5] DY?d =jLX FY/Z C[ "
- ")Y1Y AX=Z 6ZIY >Y1Y%Z IZ*YAYAY HY9Y IY@X@Y KY/Y NZ/Z 5Y 5Y-Y HZ8Y .[ 4Z 1Z LZ/Z;Z;Z*Z(Z'[?Z@[-[ L[3~o BX B~o BX"
- " B~o BX NZ@U 8y <X4X 4^ \"X 1X6Y IUEX MT GW *ZCZ E~d &T=g5T -X ,o 5i /Y1Y <bEW 3Z Nl *W 'ZCZ(l",
- "EW6]>mFXDS?YBi?W5] CY 4Z8Y BY7Y BZ8Z CY7Y AY8Z CZ8Y!Y:Z <Z HZ !Z Z !Z >Z !Z !Z \"Z Ew5[)Y?ZBY*Z KZ/Z KZ1[ KZ"
- "1[ L[1Z KZ Dc E[=Y9[+Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z2Z KYB^ &i 0i 1i /i 0i 0i Ej-Y/Z $Z-Y MZ-Y MZ-Y LY-Y 7Y Y NX Y 5Y/"
- "Z IY1X JZ/Z KZ/Z LY.Y LZ/Z KZ/Z$~d$Z=WIZ KY0X HY0X GX0X GX0Y CY<Z CY/Z GZ<Z :WK~KW/WJ}JW.W<`<W.W7[<W/W9[9W "
- ">X .Y;Y JZ 1Y?U 6~\\(~e'~]\"~X LX.X HX.X >YFY 7ZAZ N~m 4Z 3Y !W?X 9p +XCi0ZC\\9X "
- " :Y CZ %V <~e NX 6Z.Y 4Z L\\ M^ CY:Z L[ H^4Z 0Z 7^A^ C_Ce ?c Mc @Z HW>X6Y>V,Y7Z HZ5^ NZ !Z\""
- "Z\"Z MZ 1[2l'Z(Z :Z \"Z 4ZM] +Z 2YBXGXBY(Y?ZCZ*Z KZ\"z![ LZ&w 'k 3Y 9Z(Z IZ=Z\"ZCX@XCZ Gc &Z &\\ $X HX FY "
- " >q FY.Y JY $Y/Z JY,X 9Y 5Y.Y GY1Y 5Y NX 0XL\\ NY FY3Z3Y+Y1Y JY.Z JY/Z NY/Y ;Y (^ KY .Y1Y CY;Y KYCXIXCY "
- "Bc 4Y<Y *[ 2a KX La Du?Z !U9Z8T'Z5] DY9^ >\\IYMX FY/Z B\\ +Y1Y AY>Y 5ZIZ ?Y1Y%Z IZ*YAYAY HY9Y IY@X@Y KY/Y NZ"
- "/Z 5Y 5Y-Y HZ8Z 0\\ 4Z 1Z LZ/Z;Z;Z*Z(Z&[@Z@[-[ L[4~p BX B~o BX B~p CX NY?U 8y <W2W 3] \"X 1Y7Y IUEX MT "
- " JZCZ 8X &T=WIZ6T -X ,o 3e -Y1Y :`EW 3Z Nl (ZCZ)lFW5UNV>mFWCQ;XAe>X6UNW CY 4Y7Z DZ7Y BZ8Z CY7Z CZ7"
- "Y CY7Z#Z:Z <Z HZ !Z Z !Z >Z !Z !Z \"Z :Z#[)Y?ZBY*Z KZ/Z KZ0Z KZ1[ L[1Z KZ Ca D[>Y8[+Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z3[ "
- "KYA^ /q 9r 9q 7q 8q 9r Mq,Y/Z $Y,Y MY,Y MY,Y MZ-Y 7Y Y NX Y 5Y.Y IY1X JZ/Z KY.Z LY.Y LZ/Z KY.Z$~d$Y=XIZ KY0X"
- " HY0X GX0X GX0Y CY<Y BY/Z FY<Y 9WK~KW/WJ}JW.W;^;W.W8[;W/W9[9W >X .Y<Z K[ 1Y@U 6~](~f'~[ ~V KX.Y IX.X"
- " ?ZFY 6YAZ N~m 4Z 3Y !W?W 6p -WCk1ZB\\;Y :Y CZ %V <~e NX 6Z.Y 4Z M\\ J] EY9Z "
- " L[ H^4[ 2[ 8\\<\\ BbKi ?` Ha @Z HV=X5X>W-Y6Y HZ2\\ Z !Z\"Z\"Z MZ 1[2l'Z(Z :Z \"Z 4ZL] ,Z 2YBXGXBY(Y?Z"
- "CZ*Z KZ\"x N[ LZ&x #f 3Y 9Z(Z HZ>Z\"ZCW>WCZ Hd &Z &[ #X HX FY At FY.Y JY $Y/Z JY,Y :Y 5Y.Y GY1Y 5Y NX"
- " 0XM\\ MY FY3Y2Y+Y1Y JY.Z JY.Y Z/Y ;Y (b Y .Y1Y CY;Y KYCWHXCY Bb 3Y=Y *[ 6e JX Ke KzF^ !U9Y7T'Z4[ CY7] @[E"
- "XNX GZ.Y Ai 9Y1Y AY>Y 5YHZ ?Y1Y&[ IZ+ZAYAY HY9Y IY@X@Y KY/Y NZ.Y 5Y 5Y-Y IZ6Y 0[ 3Z 1Z LZ/Z;Z;Z*Z(Z&\\AZA[,[ L["
- "4~p BX B~o BX C~q CX NY?U 8y <W2W 3\\ )Y6Y JUEX NU KZCZ 7X &T=WGY7T -X J^ *Y1Y 7]EW 3Z "
- " 8ZCZ 4X6UMV GX-X=^;W6UMW CY 4Y6Y DZ7Z CY6Y DZ7Z CY6Y DZ7Z#Z:Z <Z HZ !Z Z !Z >Z !Z !Z \"Z :Z#[)Y>ZCY*Z K"
- "Z/Z KZ0Z L[1[ L[1Z KZ B_ C[>X7[+Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z3[ KY@_ 5u <u <t :t <u <u!t,Y/Y #Y,Y MY,Y MY,Y MY,Y 7Y Y "
- " NX Y 6Z.Y IX0X JY-Y KY.Z MZ.Y LZ.Y KY.Z$~d$Y>XHZ KY0X HY0X GX0X GX0Y BY=Y BY.Y FY=Z 9WK~KW/WJ}JW.W:\\:W.W"
- "9[:W/W9[9W >X .Z=Y JZ /X@U 6~^*~g&~Y N~V KX.Y IX.X ?ZFZ 7ZBY L~l 4Z 3Y \"X@X 3n /X"
- "CZIZ2Z@\\<Y :Y BY %V <~e Y 6Z.Y 4Z N\\ G\\ FX8Z K[ I]2Z 2Z 8\\9[ BsNZ ?] B^ @Y GV=W4X>W.Z6"
- "Z IZ1[ Z !Z#[\"Z MZ 1[2l'Z(Z :Z \"Z 4ZK] -Z 2YBXHYBY(Y>ZDZ*Z KZ\"v L[ LZ&z !c 4Y 9Z(Z HZ>Z\"ZDX>XDY Ge 'Z '[ "
- "\"X GX GY Dw FY.Y JY %Z/Z J~W :Y 5Y.Y GY1Y 5Y NX 0XN\\ LY FY3Y2Y+Y1Y JY.Z JY.Y Z/Y ;Y 'e $Y .Y1Y CZ=Z"
- " KYDXGWDY @a 3Z>Y +[ 5d IX Ic L~d !U8X7T'Z4[ CY5\\ AZCa GY-Y @h 9Y1Y @X?Z 6ZGY ?Y1Y&[9X9Z+ZAYAZ IY9Y IY@X@Y "
- "KY/Z Y-Y 5Y 5Y.Z IZ6Z 2[ 2Z 1Z M[/Z;Z<[*Z(Z%[AZB\\,[ LZ3~p BX B~o BX C~q CX NY?U 8y <W2W 2[ (Y7Y ITDW "
- "NU M[CZ 6X &T=WFY8T -X EY1Y 1WEW 3Z 7ZC[ 6W6ULV HX+W JX7ULW CY 5Z6Z EY5Y DZ6Z EY5Y DZ6Z E"
- "Z6Y$Z9Z <Z HZ !Z Z !Z >Z !Z !Z \"Z :Z#[)Y>ZCY*Z KZ/Z KZ0Z L[1[ L[1[ LZ A] B[?X6Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z3[ KY?"
- "_ 8w ?x ?w =w >w >w$~u/Y #~W M~W M~W M~W 7Y Y NX Y 6Z.Y IX0X JY-Y KY.Z MZ.Z MY-Y KY-Y$~d$Y?XFY KY0X HY0X GX0"
- "X GX0Y BY>Z BY.Y EY>Y 8WK~KW/WJ}JW.W;]:W.W:[9W/W9[9W >X -Y>Z KZ .YAU 6~^*~g%~W L~T JX.Y IX.X ?YEZ 7Z"
- "CZ L~k :y KY \"X@X 0m 1WCYEY3Y>\\=X 9Y BY %V <~e =l X 5Z.Y 4Z \\ E[ GY8Z JZ I]"
- "2Z 2Z 8[7[ BqMZ ?^ C^ @Y GV=W4X>V-Y5Z IZ0[!Z !Z#[\"Z MZ 1[2l'Z(Z :Z \"Z 4ZJ] .Z 2YAXIXAY(Y=YDZ*Z L[\"s"
- " I[ LZ&[Cc Na 5Y 9Z(Z HZ?Z YDX>XEZ Hg (Z (\\ \"X GX GY Fy FY.Y KZ %Z/Z J~W :Y 5Y.Y GY1Y 5Y NX 0e KY"
- " FY3Y2Y+Y1Y KZ.Z JY.Y Y.Y ;Y &h (Y .Y1Y BY=Y IXDXGWDY ?_ 1Y?Z ,[ 4b GX Ga L~c T6V6T'Z4[ CY4\\ CZ@_ GY-Y >f "
- "9Y1Y @Y@Y 5YFZ @Y1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y KX.Z Y-Y 5Y 5Y.Z IY5Z 3[ 1Z 1Z M[/[<Z<[*Z(Z%\\BZC\\+[ LZ3~p BX B~o "
- "BX C~q CX DX 4Z?U -Z (W2W 2Z 'Z7X ITDX U MZCZ 5X &U>WEY9T -X EY1Y 1WEW 3Z 6ZCZ 7X7"
- "UKV HW*W KX6ULW CY 5Y5Z FZ5Z EY4Y FZ5Z EZ5Y EY5Z%Z9Z <Z HZ !Z Z !Z >Z !Z !Z \"Z :Z#Z(Y=ZDY*[ LZ/Z KZ0Z L[0Z "
- "LZ0[ LZ A] B[@X5Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z4[ JY>` <y @y Ay ?y @y @y%~v/Y #~W M~W M~W M~W 7Y Y NX Y 6Z.Y IX0X JY"
- "-Y KY-Y MZ.Z MY-Y KY-Y$~d$Y?WEY KY0X HY0X GX0X GX0Y BZ?Y AY.Y EY>Y 8WK~KW/WJ}JW.W<_;W.W;[8W/W9[9W >X -Z?Z "
- " LZ -YBU 5~^*~h%~U J~R IX.Y IX.X @ZDY 6YCZ LW 'y JY \"W?X ,j 3WCYCY4Y=\\>X 9Y CZ"
- " %V <~e =l X 5Z.Y 4Z !\\ C[ IY7Z JZ I]2Z 3[ 9[5[ BoLZ ?a Ia @Y HW>X3W>V.Z4Y IZ/Z!Z !Z#[\"Z MZ 0"
- "Z Z'Z(Z :Z \"Z 4ZI] /Z 2YAXIXAY(Y=ZEZ*Z L[\"o DZ LZ&Z<^ M_ 5Y 9Z(Z GZ@Z ZEX>XEZ I[MZ (Z )\\ !X GX GY "
- "Gz FY.Y KZ %Y-Y J~W :Y 5Y.Y GY1Y 5Y NX 0c IY FY3Y2Y+Y1Y KZ.Z JY.Y Y.Y ;Y %j +Y .Y1Y BY=Y IYEXGXEY >] 0Y?Y ,[ "
- "3` EX E_ L\\Cx NT6V6T'Z4Z BY2Z CY>^ GY-Y ;c 9Y1Y @YAZ 6ZEY @Y1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y KX.Z Y-Y 5Y 5Y.Z JZ"
- "4Y 4\\ 1Z 1[ NZ.[<Z<Z)Z(Z$\\CZD]*Z LZ3~p BX B~o BX C~q CX DX 4Z?U -Z (W2W 2Z 'Z7X ITDX U MYBY 4X &U>"
- "WDX:U -X EY1Y 1WEW 3Z 5YBY 7W6UKV IX*W KW6UKW CY 6Z4Y FZ5Z FZ4Z GZ4Y EY4Z GZ4Y%Y8Z <[ IZ !Z "
- " Z !Z >Z !Z !Z \"Z :Z#Z(Y=ZDY*[ LZ/Z L[0Z L[0Z LZ0[ LZ B_ BZAY5Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z5\\ JY=` ?{ B{ Bz @z B{ "
- "B{'~x/Y #~W M~W M~W M~W 7Y Y NX Y 6Z.Y IX0X JY-Y LZ-Y MZ.Z MY-Y KY-Y$~d$Y@WDY KY0X HY0X GX0X GX0Y AY@Z AY.Y "
- "DY@Z 8WK~KW/WJ}JW.W=a<W.W<[7W/W9[9W >X ,Y?Y LZ +XBU 6~_+~i%~U I~P HX.Y IX.X @ZDZ 7YCY KX "
- " (y JY \"W?W (h 5XCXAX5Z<\\@Y 9Y CZ $T ;~e =l X 5Z/Z 4Z \"\\ AZ IX6Z JZ I\\1[ 4Z 8Z3Z AmKZ"
- " ?d d AZ HW>X3W>V.Z4Z JZ.Z\"[ \"Z#[\"Z MZ 0Z Z'Z(Z :Z \"Z 4ZH] 0Z 2YAYKX@Y(Y<ZFZ*[ M[\"Z /Z LZ&Z:\\ K"
- "^ 6Y 9Z(Z GZAZ NZEW<WEZ IZL[ )Z *\\ X FX HY H{ FY.Y KZ %Y-Y K~X :Y 5Y.Y GY1Y 5Y NX 0c IY FY3Y2Y+Y1Y"
- " KZ-Y JY.Y Y-X ;Y $l .Y .Y1Y AY?Y HYEWFXEX =\\ .Y@Y -[ 2b GX Ga LY=s LT6W7T'Z4Z BY2Z DY=^ GY-Z =d 9Y1Y ?XAY"
- " 5YDZ AY1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y KX.Z Y-Y 5Y 5Y.Z JZ4Z 5[ 0Z 0Z NZ-Z<Z<Z)Z(Z#\\DZD\\)Z LZ3~p BX B~o BX B~p CX"
- " DX 4Z?U -Z (W2W 2Z &[9X IUEX T s AXAY 4X &U>WCX;U -X EY1Y 1WEW 3Z Is 0YAX 8W6UJV IW)W"
- " LX7UJW CY 6Z4Z GY3Y FZ4Z GY3Y FZ4Z GY3Z'Z8Z <[ IZ !Z Z !Z >Z !Z !Z \"Z :Z#Z(Y<ZEY*[ M[/[ M[0Z LZ/Z LZ/Z LZ "
- "Ca CZBY4Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z5\\ JY<` A| C| C{ A{ C| C|(~y/Y #~W M~W M~W M~W 7Y Y NX Y 6Y-Z JX0X JY-Y LZ-Y"
- " MZ.Z MY-Y KY-Y$~d$YAWCY KY0X HY0X GX0X GX0Y AY@Y @Y.Y DY@Y 7WK~KW/XK}KX.W>c=W.W=[6W/X:[:X >X ,Y@Z M[ "
- "+YCT 5~`,~i$~S H~P HX.Y IX.X @YCZ 7ZDY KX )y HX #X@X (TNc 6WCX@X5Y:\\AX 8Y CZ :~e"
- " =l !X 4Z/Z 4Z #\\ @[ KY6Z IZ I[0Z 4Z 9Z2[ @jJZ ?f %g AZ HW>X3W>V.Y2Y JZ.Z\"[ \"Z#Z!Z MZ 0Z Z'Z(Z"
- " :Z \"Z 4ZG] 1Z 2Y@XKX@Y(Y<ZFZ*[ MZ!Z /Z LZ&Z8[ K] 6Y 9Z(Z FZBZ NZFX<XFY I[KZ )Z +\\ NX FX HY I| FY."
- "Y KZ %Y-Y K~X :Y 5Y.Y GY1Y 5Y NX 0d JY FY3Y2Y+Y1Y KZ-Y JY.Y Y-X ;Y #m 0Y .Y1Y AY?Y HYFXEWFY =\\ .YAY ,[ 2d I"
- "X Ic LW8n JU7W7T'Y2Y BY1Z EY<\\ FY-Z @g 9Y1Y ?YBY 6ZDZ AY1Y&Z8X8Z,Y@YAZ IY9Y IY@X@Y LY-Y Y-Y 5Y 5Y.Z JY3Z 6["
- " /Z 0Z [-[=Z=[)Z(Z#]EZE\\(Z LZ2~o BX B~n AX A~n BX DX 4Z?U -Z (X4X H~W <\\:W HUDX!T s AZCZ 5X %T>WBX<U"
- " -X EY1Y 1WEW \"s 1ZCZ 9X7UIV JX)W LW7UIW CY 6Y2Y HZ3Z GY2Y HZ3Z GY2Y HZ3Z'Z8Z <[ IZ !Z Z !Z"
- " >Z !Z !Z \"Z :Z#Z(Y<ZEY)Z M[/[ M[0[ MZ/Z LZ/Z M[ Dc DZCY3Z*Z(Z#Z(Z$Z(Z$Y'Y 9Z 2Z6\\ IY:` D} D} D| B| D} D})~z"
- "/Y #~W M~W M~W M~W 7Y Y NX Y 6Y-Z JX0X JY-Y LZ-Y MY-Z MY-Y LZ-Y$~d%ZBXCY KY0X HY0X GX0X GX0Y @YAY @Y.Y DYAZ "
- " 7W8X8W.W HW-W?e>W.W>[5W.W:[:W =W +ZAY LZ *YDU 5~`,~i#~Q F} GX.Y IX.X AZBY 7ZEZ KX "
- ")y HX 6~e 9TJ_ 7XCX?X6Y9\\BX 8Y CZ KX Nl !X 4Z/Z 4Z $\\ >Z LY5Z IZ I[0Z 5Z 8Z1Z >fHY =h "
- " +i @Z HW>X3W?W/Z2Z KZ.[#[ \"Z#Z!Z MZ 0Z Z'Z(Z :Z \"Z 4ZF] 2Z 2Y@XLY@Y(Y;ZGZ*[ MZ!Z /Z M[&Z7[ K\\ 6Y 9Z(Z FZ"
- "BZ MYFX<XGZ J[IZ *Z +[ MX FX HY Jb>Y FY.Y KZ %Y-Y K~X :Y 5Y.Y GY1Y 5Y NX 0e KY FY3Y2Y+Y1Y KZ-Y JY.Y"
- " Y-X ;Y !m 2Y .Y1Y AZAZ GYGXEXGY >] .ZBY -[ 1e JX Ke LU4k IU8Y8T'Y2X AY0Y EX:[ FY-Z Ah 9Y1Y >XCZ 6YBY AY1Y&"
- "Z8X8Z,Y@YAZ IY9Y IY@X@Y LY-Y Y-Y 5Y 5Z/Y JZ2Z 8[ .Z 0[!Z,[=Z=[)Z(Z\"]FZG]'Z M[1] 1X 1\\ @X @\\ L\\ AX DX 4"
- "Z?U -Z (X4X H~W ;\\;W GTDX\"U s A[D[ 6X %T>WBX<T ,X EY1Y 1WEW \"s 2[D[ 9W7UHV KX(W MX7UI"
- "W CY 7Z2Z IZ3Z HZ2Z IZ3Z HZ2Z IZ3Z(Z7Z ;Z IZ !Z Z !Z >Z !Z !Z \"Z :Z$[(Y;ZFY)Z M[/[ MZ/[ MZ/Z M[/Z M[ Ee EZC"
- "X3[*Z(Z#Z(Z$Z(Z$Y(Z 9Z 2Z8^ IY9` Fb=Y Eb=Y Eb=X Cb>Y Eb=Y Eb=Y*b=~V/Y #~W M~W M~W M~W 7Y Y NX Y 6Y-Z JX0X JY"
- "-Y LZ-Y MY-Z MY-Y LZ-Y CZCXBY KY0X HY0X GX0X GX0Y @YBZ @Y.Y CYBY 6W8X8W.W HW-W@g@X.W?[4W.W:[:W =W *YBZ "
- " MZ (XDU 5~`,~i\"~ D{ FX.Y IX.X AZBZ 7YEY IX +y GX 6~e 9TG] 8WBW>X6Y8\\DY 8Y CZ "
- " KX Nl !X 4Z/Z 4Z %\\ =Z LX4Z IZ I[0Z 5Z 9Z0Z <bFY ;i 1i =Z HW>X3W?W/~S KZ-Z\"Z \"Z#Z!Z MZ 0[!Z"
- "'Z(Z :Z \"Z 4ZE] 3Z 2Y?XMX?Y(Y;ZGZ)Z MZ!Z /[ N[&Z6[ K\\ 7Y 9Z(Z FZCZ LZGX<XGZ JZH[ +Z ,\\ MX FY IY K"
- "]8Y FY.Y KZ %Y-Y K~X :Y 5Y.Y GY1Y 5Y NX 0f LY FY3Y2Y+Y1Y KZ-Y JY.Y Y-X ;Y Mk 3Y .Y1Y @YAY FYGWDXGY >^ .YCZ ."
- "[ )_ KX L_ ES/e FU8Z9T'Z3X AY0Y FY:[ FY-Z Cj 9Y1Y >XCY 6ZBZ BY1Y&Z8X9[,Y@YAZ IY9Y IY@X@Y LY-Y Y-Y 5Y 5Z/Y J"
- "Z2Z 9\\ .Z /Z!Z,\\>Z>[(Z(Z!]GZH^'[ N[0\\ 1X 2\\ ?X ?[ M\\ @X DX 4Z?U -Z 'W4W G~W :]>X GTDY#U s @[D[ 7"
- "X %U?WAX>U ,X EY1Y 1WEW \"s 3ZC[ 9X7UHV KW(W MX7UHW CY 7~S J~S H~S I~S I~S I~S)} ;Z IZ !Z Z"
- " !Z >Z !Z !Z \"Z :Z$[(Y;ZFY)Z MZ-Z MZ/[ N[/[ N[/Z MZ Eg F[EX2[*Z(Z#Z(Z$Z(Z$Y(Z 9Z 2Z9^ HY7_ G]8Y F^8Y F^8X D]8"
- "Y E]8Y F^8Y+^8~V/Y #~W M~W M~W M~W 7Y Y NX Y 6Y-Z JX0X JY-Y LZ-Y MY-Z MY-Y LZ-Y BYDXAY KY0X HY0X GX0X GX0Y"
- " @ZCY ?Y.Y CYBY 5W9X8W.W HW-WAiAW,WA[3W.W9Y9W >X *ZCZ 6~d IYET 4~`,~i!| By EX.Y IX.X AYAZ 7ZFY IX "
- " Z 3X 6~e 9TF\\ 9WBX=W7Z7\\EX 7Y CZ KX Nl \"X 3Z/Z 4Z &\\ ;Z M~Z %Z I[0Z 6[ 9Z/"
- "Y 8ZCZ 8i 6~d 5i ;Z HW>X3W?W0~T KZ-Z\"Z \"Z$[!Z MZ 0[!Z'Z(Z :Z \"Z 4ZD] 4Z 2Y?XMX?Y(Y:ZHZ)Z N[!Z /[ NZ%Z6["
- " J[ 7Y 9Z(Y DZDZ LZGW:WGZ K[GZ +Z -\\ LX EX IY L\\6Y FY.Y KZ %Y-Y K~W 9Y 5Y.Y GY1Y 5Y NX 0XM\\ MY "
- "FY3Y2Y+Y1Y KZ.Z JY.Y Y-X ;Y Ji 4Y .Y1Y @YAY FYGWDXGX >` /YCY .[ $\\ LX M\\ AR+` CT9[:U'Z3X AY0Y FY9Z FY-Z "
- "D` .Y1Y >YEZ 6YAZ BY1Y&Z8X9[,ZAYAZ IY9Y IY@X@Y LY.Z Y-Y 5Y 5Z/Y KZ1Z 9[ -Z /Z\"[+[>Z>[(Z(Z ^IZJ_&[ NZ.\\ 2X 3"
- "\\ >X >[ \\ ?X DX 4Z?U -Z 'X6X G~W 9^@X GUDY$T Ns ?[CZ 8X %U?WAY?U ,X EY1Y 1WEW \"s 4"
- "ZCZ 7W7UGV LX)X MW7UGW CY 8~T J~T I~S J~T I~T K~T*~ ;Z IZ !Z Z !Z >Z !Z !Z \"Z :Z$[(Y:ZGY)[ NZ-Z N[.Z N[/[ N"
- "[/[ NZ Fi G[FX1Z)Z(Z#Z(Z$Z(Z$Z)Z 9Z 2Z<a HY5^ I[5Y F[5Y G\\5X E\\6Y F\\6Y F[5Y+[5~V/Y #~V L~V L~W M~W 7Y Y NX"
- " Y 6Y-Z JX0X JY-Y LZ-Y MZ.Z MY-Y KY-Y BYDW@Y KY0X HY0X GX0X GX0Y ?YDZ ?Y.Y BYDY 4W9X9X.W HW-XC\\L[BW,WB["
- "3X.W HW >X )YCY 5~d IYFU 4~`,~i!{ @x EX.Y IX.X AY@Y 7ZGZ IX Z 3X 6~e 9TD[ ;XBX=X8"
- "Z6\\GY 7Y CY JX Nl \"X 2Y/Z 4Z '\\ :Z M~Z %Z I[0Z 6Z 8Z/Z \"Z 5i 9~d 8i 8Z HW>X3W?W0~U LZ-Z\"[ "
- "#Z$[!Z MZ /Z!Z'Z(Z :Z \"Z 4ZC] 5Z 2Y?XNY?Y(Y:ZHZ)[ [!Z .Z NZ%Z5[ K[ 7Y 9Z(Y DZDY KZHX:XHY K[EZ ,Z .\\ KX EX"
- " IY LZ4Y FY.Y KZ %Z.Y KZ <Y 5Y.Y GY1Y 5Y NX 0XL\\ NY FY3Y2Y+Y1Y KZ.Z JY.Y Y.Y ;Y Ff 5Y .Y1Y @ZCZ FY"
- "HXCWHY ?b /YDY /[ ![ MX M[ @Q%W ?T9\\;U'Z3X AY0Z GX8Z FY-Z E\\ )Y1Y =XEY 6Z@Y BY1Y&Z9Y9[,ZAYAZ IY9Y IY@X@Y "
- "LY.Z Y-Y 5Y 4Y/Y KZ0Z ;[ ,Z /[#Z*\\?Z?\\(Z(Z N`LZL`$Z NZ-\\ 3X 4\\ JPCXCP J[\"\\ >X DX 4Z?U -Z 'X6X G~W "
- "8^BX FUDY%U Ns =ZCZ 9X $U@W@X?T +X EY1Y 1WEW \"s 5ZCZ 7W7UFV LW(W MX8UFW CY 8~U K~T J~U K~"
- "T J~U K~T*~ ;[ JZ !Z Z !Z >Z !Z !Z \"Z :Z$Z'Y9YGY)[ [-[ [.Z N[.Z NZ.[ NZ G\\L[ GZGX0Z)Z(Z#Z(Z$Z(Y#Z)Z 9Z 2~ "
- "GY4] J[4Y G[4Y G[4X EZ4Y FZ4Y G[4Y,[4X 1Y #Y Y Y Y 9Y Y NX Y 6Y-Z JX0X JY-Y LZ-Y MZ.Z MY-Y KY-Y BYEW?Y"
- " KY0X HY0X GX0X GX0Y ?YDY >Y.Y BYDY 4W9X9W-X JX,WD\\J[CW,WC[2W-X JX >X )YDZ 5~d HXFU 4~_+~i z @w DX.Y"
- " IX.X BZ@Y 6YGZ IY Y @~e 9TCZ ;WAX=X8Y4\\HX 6Y CY JX Mj !X 2Y/Y 3Z (\\ 9Z"
- " M~Z %Z I[0Z 6Z 8Z/Z \"Z 2i <~d ;i 5Z HW>X3W@W/~U LZ-[#[ #Z$Z Z MZ /Z!Z'Z(Z :Z \"Z 4ZB] 6Z 2Y>a>Y(Y9ZIZ)[ "
- "Z Z .Z [%Z4Z JZ 7Y 9Z)Z DZEZ JYHX:XIZ KZD[ -Z /\\ JX EX IY MZ3Y FY.Y JY %Z/Z JY <Y 5Y.Y GY1Y 5Y NX"
- " 0XK\\ Y FY3Y2Y+Y1Y KZ.Z JY.Y Y.Y ;Y Bc 6Y .Y1Y ?YCY DYIXCXIY @c /YEY /[ NZ MX N[ *U;^=U&Z4Y AY/Y HY7X"
- " EY-Y E[ 'Y1Y =YFY 6Z@Z CY1Y&Z9Y9Z+ZAYAZ IY9Y IY@X@Y LZ/Z Y-Y 5Y 4Y0Z KZ0Z <[ +Z .Z$[)\\@Z@\\'Z(Z M~Q#Z [,\\ 4"
- "X 5\\ JRDXDR J[$\\ KQCXDQ #Y 4Z?U -Z &X8X F~W 7_EY EUDY&U Ns <ZCZ :X $U@W?XAU +X EY1Y 1WEW "
- " \"s 6ZCZ 7X8UEV MX)X MW7UFW DZ 8~U L~V K~U L~V K~U K~U+~ :Z JZ !Z Z !Z >Z !Z !Z \"Z :Z%['Y9ZHY(Z [-[ Z"
- "-[ Z-Z [-Z [ H\\J[ HZHY1[)Z(Z#Z(Z$Z(Y#Z)Z 9Z 2} FY2\\ KZ3Y GZ3Y GY3Y FZ3Y GZ3Y GZ3Y,Z3X 1Y #Y Y Y Y 9Y Y "
- "NX Y 6Y-Z JX0X JY-Y KY.Z MZ.Z MY-Y KY-Y BYFX?Y KY0X HY0X GX0X GX0Y >YEY >Y.Y BYEZ 4X:X9W,W JW+WE\\H[EX,X"
- "E[1W,W JW =X )ZEY 4~d HYHU 2~^+~i Nx >u CX.Y IX.X BY?Z 7ZHY GX Z A~e 9TCZ <XAW<"
- "X8Z4\\JY 6Z DY JX 4X 1Z0Y 3Z )\\ 8Z M~Z %Z I[0Z 7Z 7Z/Z \"Y /i >~d >i 2Z GV>X3W@W0~V LZ-[\"Z "
- "#Z%[ Z MZ /[\"Z'Z(Z :Z \"Z 4ZA] 7Z 2Y>a>Y(Y9ZIZ(Z Z Z .[![%Z4[ KZ 7Y 9Z)Z CZFZ JZIX:XIZ L[CZ -Z /[ IX DX J"
- "Y MY2Y FY.Y JY %Z/Z JY <Y 5Y.Y GY1Y 5Y NX 0XJ\\ !Y FY3Y2Y+Y1Y JY.Z JY.Y Z/Y ;Y ?a 7Y .Y1Y ?YCY DYIWBX"
- "IY @d /YFY 0[ LY MX NZ )U<VNW=U&Z4Y AY/Y HY8Y EZ.Y F[ &Y1Y =YGZ 7Z>Y CY1Y&Z9Y9Z+ZAYAY HY9Y IY@X@Y LZ/Y N"
- "Y-Y 5Y 4Y0Z LZ.Y =[ *Z .[%Z(]AZA]'Z(Z L~\"[![+\\ 5X 6\\ JTEXET J[&\\ KSDXES $Y 3Y?U -Z &Y:Y F~W 5_GX DU"
- "CZ9QAU DZCZ ;X $VAW?YBU +X EY1Y 1WEW DZCZ 6W7UEV NX)X MX8UEW DY 8~V L~V L~W M~V K~V M~V"
- ",~P :Z JZ !Z Z !Z >Z !Z !Z \"Z :Z%['Y8ZIY(Z Z+Z Z-[![-[![-[![ I\\H[ I[JY0[(Y(Z#Z(Z$Z)Z#Z)Z 9Z 2| EY1\\ LY2Y "
- "HZ2Y HZ3Y FY2Y GY2Y GY2Y-Z2X 1Y #Y Y Y Y 9Y Y NX Y 6Z.Y IX0X JY-Y KY.Z MZ.Z MY-Y KY.Z BYGX?Z KY1Y HY0X"
- " GX0X GX0Y >YFZ >Y.Y AYFY 2W:X:X,W JW+XG\\F[FW+XF[1X,W JW =X (YEY 4~d GXHU 2kNRMk*tNq Mv <s BX.Y IY/X"
- " BY>Y 7ZIZ GY !Z A~e 9TBY <W@W;W8Z3\\KX 5Z DY JX 4X 1Z1Z 3Z *\\ 7Z M~Z %"
- "Z HZ0Z 7Z 7Y.Z #Z ,i A~d Aj 0Z GV=W4X@W0~W MZ-[\"[ $Z%[ Z MZ /[\"Z'Z(Z :Z \"Z 4Z@] 8Z 2Y>`=Y(Y8ZJZ([\"[ Z "
- ".[!Z$Z3Z KZ 7Y 9Z)Z CZGZ IZIW8WIZ M[AZ .Z 0\\ IX DX JY MY2Y FY.Y JY $Y/Z JY <Y 5Z/Y GY1Y 5Y NX 0XI"
- "\\ \"Y FY3Y2Y+Y1Y JY.Z JY.Y NY/Y ;Y ;] 7Y .Y1Y >YEY CYIWBXIX @f 0YGZ 0[ LZ NX NY 'U>WMW?V&Z4Y AY/Y HY8Y"
- " EZ.Y FZ %Y1Y <XGY 6Z>Y CY1Y&[:Z:Z+ZAYAY HY9Y IY@X@Y LZ/Y NZ.Y 5Y 4Y0Y KZ.Z ?\\ *Z -['['\\AZB]&Z(Z K|![!Z)\\ 6"
- "X 7\\ JVFXFV J[(\\ KUEXFU %Y 3Y?U -Z %Y<Y /Z M`KY BUC[=SAU CZCZ <X #UAW>XCU *X EY1Y 1WEW"
- " F[CZ 6X8UDV NW)X MX8UDW DY 8~W N~W L~W M~V L~W M~W-~P :[ KZ !Z Z !Z >Z !Z !Z \"Z :Z%['Y8ZIY([\"[+["
- "\"[,Z![-[!Z,[!Z I\\F[ J[KY/Z'Z)Z#Z)Z#Z)Z#Z)Z 9Z 2{ DY0[ MY1Y HY1Y HY2Y FY2Y HZ2Y HY1Y-Y2Y 1Z $Y Y Y Z :Y Y"
- " NX Y 6Z.Y IX0X JZ.Y KY.Z MZ.Y LZ.Y KY.Z BYHX>Z KY1Y HY1Y GX0X GX0Y =YGY =Y.Y AYFY 2X;X:W+X LX*WH\\D[HX"
- "*WG[0W+X LX =X (YFZ 4~d GYIU 2jLQLj*pNRNq Lt :q AX.Y IY0Y CZ>Y 6YIZ FX !Z A~e 9T"
- "BZ >W?W;W8Z2\\MY 4Y DY JX 4X 1Z1Z 3Z +\\ 6Z M~Z %Z HZ0Z 8[ 7Y.Z #Z )i D~d Ci -Z GV=W4XAW/~W M"
- "Z-[\"[ $Z&[ NZ MZ .Z\"Z'Z(Z :Z \"Z 4Z?] 9Z 2Y=_=Y(Y8ZJZ([\"[ Z -Z\"[$Z3[ L[ 8Y 9Z)Z BZHZ IZJX8XJY LZ@[ /Z 1\\"
- " HX DX JY NY1Y FZ0Z JY $Y/Z JY <Y 5Z0Z GY1Y 5Y NX 0XH\\ #Y FY3Y2Y+Y1Y JY.Y IY/Z NY/Y ;Y 9\\ 8Y .Y1"
- "Y >YEY BXJXAWJY A[N[ 1YGY 0[ JY NX NY 'V@WLX@U$Y5[ BY/Y HX7X DZ.Y FY $Y1Y <YIZ 6Y=Z DY1Y&[:Z:Z*YAYAY HY9"
- "Y IY@X@Y LZ/Y NZ/Z 5Y 3Y1Y KY-Z ?[ )Z -[([%]CZC]%Z(Z Jy M[#[(\\ 7X 8\\ JXGXGX J[*\\ KWFXGW &Y 3Y?U -Z %Z>Z "
- "/Z K_MZ BUC]BVBU A[D[ >X #VBW=XDU *X EY1Y 1WEW G[D[ 5W8UCV X*X LW8UCW EZ 8~W N~X M"
- "~W N~X M~W N~X.~Q :[ KZ !Z Z !Z >Z !Z !Z \"Z :Z&[&Y7ZJY([\"[+[\"[,[\"Z+[#[+Z\"[ J\\D[ JZKX/['Z*[#[*Z#Z)Z#Z)Z"
- " 9Z 2z CY/Z MY1Y HY2Z HY2Y GY1Y HY1Y HY1Y-Y2Z 2Z $Z !Z !Z !Z :Y Y NX Y 6Z.Y IX0X JZ/Z KY.Z LY.Y LZ/Z KY.Z "
- " BYHW=Z KY1Y GX1Y GX1Y GX0Y =YHZ =Y/Z @YHY 1X;X;X*W LW)XJ\\B[IX*XI[0X*W LW <X (ZGY 3~d GYJU 1iKQKi*pN"
- "RMo Jr 9q AX.Y HX0Y CZ>Z 7ZJY EY !Z 1X@X &TAY ?X?W;W8Z1\\NX 3Y DY JX 5Y 0"
- "Y1Z 3Z ,\\ 5Z M~Z %Z HZ0Z 8Z 6Y.Z #Z &i G~d Fi )X FV=X5XAW0~Y NZ-[!Z $Z&[ NZ MZ .[#Z'Z(Z :Z \"Z 4Z>] :Z 2"
- "Y=_=Y(Y7ZKZ'Z#[ NZ -[#[$Z2[ M[ 8Y 9Z)Z BZHZ HYJX8XKZ M[?Z /Z 2\\ GX CX KY NY1Y FZ0Z JZ %Y/Z JZ =Y 4"
- "Y0Z GY1Y 5Y NX 0XG\\ $Y FY3Y2Y+Y1Y JZ/Y IZ0Y MY/Y ;Y 8[ 8Y .Y1Y >ZGZ BYKXAXKY B[LZ 0YHY 1[ IY NX Z &VB"
- "XKXBV$Y5[ BY/Y HX8Y CY/Z GY #Y1Y <YIY 6Z<Y DY1Y%Z:Z:Z*YAYAY HY9Y IY@X@Y LZ/Y NZ/Z 5Y 3Y2Z LZ,Z A[ (Z ,[)[%^DZD^"
- "%Z(Z Iw L[#['\\ 8X 9\\ JZHXHZ J[,\\ KYGXHY 'Y 3Z@U -Z $[B[ .Z NW $j @UCpBU @[D[ ?X \"UBW=XEU )X "
- " EY1Y 1WEW H[D[ 5W8UBV W*X LX8UCW F[ 9~Y ~X N~Y ~X N~Y ~X.~Q 9[ LZ !Z Z !Z >Z !Z !Z \"Z :Z&[&"
- "Y7ZJY'[#Z)Z#[+[#[+[#[+[#[ K\\B[ K[MX.['Z*Z!Z*Z#Z)Z#Z)Z 9Z 2x AY.Z NY2Z HY2Z IY1Y GY1Y HY1Y HY2Z-X1Z 2Z $Z !Z !Z"
- " !Z :Y Y NX Y 5Y/Z IX0X JZ/Z KZ/Y KY.Y LZ/Z KZ/Y AYIW<Y IX1Y GX1Y GX1Y GY2Z =YHY <Z0Y ?YHY 0X<X;X*X N"
- "X)XJ[@[KX(XK[/X*X NX <X 'YHZ 3~d FXJU 0hKQKh(nMRMo Jq 7o @X.Y HX0X BY=Z 7ZKZ DY \"Z "
- " 1W?X &TAY ?W>W;W8Z0e 3Y EZ JX 5X /Z2Y 2Z -\\ 4Z M~Z %Z HZ0Z 8Z 6Z/Z $Z #j J~d Ii CW>X6Y"
- "BX0~Y NZ-[![ %Z'\\ NZ MZ -Z#Z'Z(Z :Z \"Z 4Z=] ;Z 2Y<]<Y(Y7ZKZ'[$[ NZ -[$[#Z1Z M[ 8Y 8Z*Z BZIZ GZKX8XKZ N[>[ 0"
- "Z 3\\ FX CX KY NY2Z FZ0Y IZ %Y/Z JZ =Y 4Y0Z GY1Y 5Y NX 0XF\\ %Y FY3Y2Y+Y1Y JZ/Y IZ0Y MY/Y ;Y 7Z 8Y"
- " .Y2Z =YGY AYKW@XKY BZJZ 1YIY 1[ HY NX Y %WEYIYFW#Y5[ BY/Y HX8Y CY/Z GY #Y1Y ;XIY 6Y;Z EY1Y%Z:Z:Z*ZBYBZ "
- "HY9Y IY@X@Y LZ/Y MY/Z 4Y 4Y2Y KZ,Z B[ 'Z +[+[#_FZF_$Z(Z Gt JZ$[%\\ 9X :\\ J\\IXI[ I\\/\\ K[HXI[ (Y 3Z@U -Z "
- "%^F^ /Z X \"f >VBnCU >[D[ @X \"VCW<XGV )X EY1Y 1WEW I[D[ 5X8UBV!W)X LW8UBW FZ 8~Y!~Z"
- " ~Y!~Z ~Y ~Y0~R 9[ LZ !Z Z !Z >Z !Z !Z \"Z :Z'[%Y6ZKY'[$[)[$[*[$[*[%[*[$[ K\\@[ Le.[&Z*Z!Z*Z\"Z*Z#Z*[ 9Z 2v "
- "?Y.Z NY2Z HX1Z IY1Y GY1Y HY2Z HX1Z.Y1Z 1Y #Y Y Y Y :Y Y NX Y 5Y/Z IX0X IY/Z KZ/Y KY/Z KY/Z KZ/Y 7\\ 7ZKW"
- ";Y IX1Y GX1Y GY2Y GY2Z <YIY <Z0Y ?YIZ 0X<X<X)X Y(XJY>YJX(XJY/X)X Y <X 'ZIZ 3~d FYKT /gJQJg(nMRLm Hp 6"
- "m ?X.Y HY1X CZ<Y 6YKZ DY \"Z 1W?W %TAY @X>W;W7Y/c 2Y EY IX 5X /Z3Z 2Z .\\"
- " 3Z M~Z &Z FY1Z 8[ 6Z/Z $Z i L~d Li @W>Y7YBW0Z*Y NZ-[![ %Z'[ MZ MZ -[$Z'Z(Z :Z \"Z 4Z<] <Z 2Y<]<Y(Y6ZL"
- "Z'[%[ MZ ,[%[#Z1[ N[ 8Y 8Z+[ AZJZ GZKW6WKZ NZ<[ 1Z 3[ EX CX KY NY2Z FZ0Y IZ %Z1[ IY =Y 4Z1Z GY1Y 5Y"
- " NX 0XE\\ &Y FY3Y2Y+Y1Y JZ0Z IZ0Y MZ1Z ;Y 6Y 8Y .Y2Z =YGY AYKW?WKX B[J[ 1YJY 2[ GY NX Y $ZL[H[JY#Y6\\ "
- "BY0Y GX8X BZ0Z GY #Y1Y ;YKZ 7Z:Y EY2Z%Z:Z:Z*ZBYBZ HY9Y IY@X@Y L[1Z MY/Y 3Y 4Z3Y LZ+Z C\\ 'Z +[,[!_GZG_#Z(Z Fq H"
- "[%[$\\ :X ;\\ H\\JXJ\\ H\\1\\ J\\IXJ\\ (Y 3Z@U -Z &x 0Z X c <UAmDV =[CZ AX !VDW<YHU (X E"
- "Y1Y 1WEW JZCZ 3W8UAV\"X*X LX9UAW G[ 9Z*Y!Z+Z Y*Y!Z+Z Y*Z\"Z+Z0Z3Z 8[ MZ !Z Z !Z >Z !Z !Z \"Z :Z(\\%"
- "Y6ZKY&[%[)\\&[)[%[)[%[)[%[ L\\>[ Ld.[&Z*Z!Z*Z\"Z+[\"Z+Z 8Z 2s <Y-Y NX1Z IY1Z IY2Z GY2Z HY2Z HX1Z.Y1Z 1Z $Y Y "
- "Z !Z ;Y Y NX Y 5Y/Z IX0X IY/Y JZ0Z KZ0Z KY/Y IY0Z 7\\ 6YLX<Z IX1Y GY2Y GY2Y GY2Z <YJZ <Z0Y >YJY .X=X=Y("
- "X!X'YJW<WJX&XJW/Y(X!X ;X &YIY #[ LYLU .fJQJf&lLRLm Gn 4k >X.Y HY2Y CZ<Z 7YKY BY #[ "
- " 3X@X %TAY @W=W;W7Z0b 1Y EY IX 5X /Z3Z 2Z /\\ 2Z )Z JZ FZ2Z 8Z 5Z/Z %Z Ki :j >W=X8ZC"
- "W/Z*Z Z-Z N[ &Z(\\ MZ MZ -\\%Z'Z(Z :Z \"Z 4Z;] =Z 2Y<]<Y(Y6ZLZ&[&[ MZ ,\\'[\"Z0Z NZ 7Y 8Z+Z @ZJY FZLX6XLY N[;"
- "Z 1Z 4\\ EX BX LY NY2Z F[2Z HZ %Y1[ IZ >Y 4Z2[ GY1Y 5Y NX 0XD\\ 'Y FY3Y2Y+Y1Y IY0Z IZ1Z MZ1Z ;Y 6Y"
- " 8Y .Y2Z =ZIZ @XLX?WLY C[H[ 2YKZ 3[ EX NX Y $hFh\"Z7\\ BY0Y GX9Y BZ1Z FX \"Y1Y ;YKY 6Y9Y EY2Z%Z;[:Z*ZBYB"
- "Y GY9Y IY@XAZ L[1Y LZ1Z 3Y 3Y3Y LZ*Z D[ &Z *[-[ aJZJa\"Z(Z Cl F\\'[\"\\ ;X <\\ F\\KXK\\ F\\3\\ H\\JXK\\ 'Y "
- "2ZAU -Z 'z 1Z X Na ;V@jDV :ZCZ BX UDW;XIU 'X EY2Z 1WEW KZCZ 3X9U@V\"W*X LX9VAW H[ "
- "8Z*Z\"Y)Y!Z*Z\"Z*Y!Z*Z\"Z*Z1Z3Z 8[ MZ !Z Z !Z >Z !Z !Z \"Z :Z(\\%Y5ZLY&[&['[&[([&[)\\'\\)[&[ L\\<[ Mc.[$Z,[!"
- "[,[\"Z+Z!Z+Z 8Z 2n 7Y-Y NX1Z IY2[ IY2Z GY2Z HY2Z IY2[.Y2\\ 2Z $Z !Z !Z !Z ;Y Y NX Y 5Z0Y HX0X IZ1Z IY0Z KZ0"
- "Y JZ1Z IZ1Z 7\\ 6YMX;Z IY3Z GY2Y GY2Y GY2Z ;YKY ;Z1Z >YJY .Y>X=X'Y#Y&XIU:UJY&YJU.X'Y#Y ;X &YJZ #Z JXLU"
- " -dIQId%kKRKk El 2j >X.Y HY2Y CY;Z 7ZMZ BZ #Z 3X@X %TAX @W<W;W7Z/a 0Y FY IX "
- " 6X -Z4Z 2Z 0\\ 2[ )Z JZ FZ2Z 8Z 5Z/Z %Z Hi @j :V=Y9ZDX/Z*Z Z-Z N\\ 'Z)\\ LZ MZ ,[%Z'Z(Z :Z \"Z"
- " 4Z:] >Z 2Y;[;Y(Y5ZMZ&\\([ LZ +['[\"Z0[ Z 7Y 8[,Z ?YKZ EYLX6XLY [:[ 2Z 5\\ DX BX LY NY3[ F[2Z HZ %Y1"
- "[ IZ >Y 3Y2[ GY1Y 5Y NX 0XC\\ (Y FY3Y2Y+Y1Y IZ2Z H[2Z LY1Z ;Y 6Z 9Y .Y2Z <YIY ?YMX?XMY CZFZ 2YKY 3[ DY X "
- "Y #gEf!Z7\\ BY0Y GX9Y BZ1Z FX \"Y1Y :XLZ 7Z9Z FY2Z%Z;\\<[)ZCYCZ GY9Y IZAXAZ L[1Y LZ1Z 3Y 3Y4Y KZ*Z E[ %Z )["
- "/[ MdNZNd!Z(Z Ag B['[!\\ <X =\\ D\\LXL\\ D[4\\ F\\KXL\\ &Z 3ZAU -Z (| 2Z X L^ 9V?fBU 8ZCZ CX V JV "
- " CY2Z 1WEW LZCZ 2W9V@V#X+X KW8U@W I[ 7Z*Z#Z)Z\"Z)Y#Z)Z\"Z)Y#Z)Z2Z2Z 7[ NZ !Z Z !Z >Z !Z"
- " !Z \"Z :Z)\\$Y5ZLY%[(\\'\\(['\\(['['['[(\\ M\\:[ Ma-[$Z,Z NZ,Z![,Z!Z,[ 8Z 2Z #Y-Y NX2[ IY2[ IY2Z GY3[ HX2[ IY2"
- "[.Y2\\ 2Z $Z !Z !Z Y ;Y Y NX Y 5Z1Z HX0X IZ1Z IZ1Z JZ2Z JZ1Z IZ1Z 7\\ 6c:Z IY3Z GY3Z GY3Z GY3[ ;YKY ;[2Z ="
- "YLY ,Y?X>Y&Y%Y%YIS8SJY$YJS.Y&Y%Y :X &ZKY #Z IYNU ,cISIb#jKRJi Cj 1i =X.Y GY4Y BY:Y 7ZMZ AZ "
- " $[,P )W?X %TBY AX<W;W7[/_ /Y FY IX 6X -Z5Z 1Z 1\\ 1Z (Z K[ EY2Z 9Z 4Z0[ &[ F"
- "j Ei 7W=Y;[EX/Z(Z!Z.[ M[!P'Z*] LZ MZ ,\\&Z'Z(Z :Z \"Z 4Z9] ?Z 2Y;[;Y(Y4YMZ%[)\\ LZ +\\)[!Z/Z Z 7Y 7Z-[ ?Z"
- "LZ EZMX6XMZ Z8[ 3Z 6\\ CX BX LY NY3[ F[2Y GZ %Z3\\ HZ ?Y 3Z4\\ GY1Y 5Y NX 0XB\\ )Y FY3Y2Y+Y1Y IZ2Z "
- "H[2Y KZ3[ ;Y 6Z 9Y .Z4[ <YIY ?YMW>XMY DZDZ 2YLY 3[ DY X Y \"eCd NY8^ CY0Y GX:Y @Z2Z FX \"Y1Y :YMY 6Y7Y "
- "FY2Z%[<\\<Z(ZCYCZ GY9Y HYAXAY K\\3Z KZ2Z 3Y 3Z5Y LZ)Z F[ $Z ([1[ K~U Z(Z ;[ <\\)[ N[ <X <Z B\\MXM\\ BZ3Z D\\L"
- "XM\\ %Z 3ZAU -Z )~ 3Z X J] 9V>a@V 7YBY CX NV LV BZ3Z 1WEW LYBY 2W8U?V#W+X KX9U"
- "?W J[ 7Z(Y#Z)Z#Z(Z$Z)Z\"Y(Z$Z(Y2Z2Z 7\\\"P NZ !Z Z !Z >Z !Z !Z \"Z :Z*\\#Y4ZMY%\\)[%[)\\&[)\\'\\)\\'\\)[ M\\8"
- "[ N`-[#Z,Z NZ,Z Z-[![-[ 8Z 2Z #Y-Y NX2[ IY2[ IY3[ GY3[ HY3[ HX2[.Y3^ 2Z $Z !Z !Z !Z <Y Y NX Y 4Z2Z HX0X HZ2"
- "Z IZ2Z IZ2Z IZ2Z IZ2Z 6\\ 5a:Z HY3Z GY3Z GY3Z GY3[ ;YLY :[2Y <YLY ,Y?X?Y$Y'Y#YIQ6QIY$YIQ.Y$Y'Y 9X %YLZ "
- "$Z HYNU +aHSH`!hJRIg Bi /g <X.Y GY4Y CZ:Y 6YMY @[ $Z-Q )W?W $TBY AW;W<X6Z.] .Y GY"
- " HX 6X -Z5Z 1Z 2\\ 0Z (Z L[ DZ4Z 8Z 4[1Z %Z Bj Ki 4W=Z=\\GY.Z(Z!Z.[ M\\#Q'Z+] KZ MZ +\\'Z"
- "'Z(Z :Z \"Z 4Z8] @Z 2Y:Y:Y(Y4ZNZ%\\*[ KZ *\\+\\!Z/[ \"[ 7Y 7Z-Z >ZMZ DZMW4WMZ![7Z 3Z 7\\ BX AX MY NY3"
- "[ F\\4Z FZ &Z3\\ HZ ?Y 3Z4\\ GY1Y 5Y NX 0X@[ *Y FY3Y2Y+Y1Y HZ3Z H\\4Z KZ3[ ;Y 5Y 9Y -Y4[ ;YKY >YNX=WNY D[D[ "
- "3YMY 3[ CY X Y !cAb MZ9^ CZ2Z GX:Y @Z3Z EX \"Y1Y :YMY 7Z7Y FZ4[$Z<\\<Z(ZCYCY FY9Y HYAXBZ K\\3Z KZ3Z 2Y 2"
- "Y6Z LZ(Z H\\ $Z (\\3[ I~R MZ(Z :Z ;\\+\\ MY ;X ;X @\\NXN\\ @X1X B\\MXN\\ $Z 2ZBU -Z *~Q 4Z X I] :W9U;V "
- " 5XAX CX MV NV AZ3Z 1WEW LXAX 2X8s+W,Y JW8t#\\ 7Z(Z%Z'Y#Z(Z$Y'Z$Z(Z$Z(Z4Z1Z 6[#Q NZ !"
- "Z Z !Z >Z !Z !Z \"Z :Z+]#Y4ZMY$[*\\%\\*[%\\+\\%\\+\\%\\+\\ N\\6[ N^-\\#[.[ N[.[ [.Z NZ-Z 7Z 2Z #Y-Y NY4\\ IY3"
- "\\ IY3[ GY3[ HY4\\ HX3\\.Y3^ 2Z $Z !Z !Z !Z <Y Y NX Y 4Z3Z GX0X HZ3Z GZ3Z IZ3[ IZ3Z GZ3Z 6\\ 5`9Z HY4[ GY4["
- " GZ5[ GZ5\\ :YMY :\\4Z ;XMZ +Y@X@Y#Z)Z\"Y(Y\"Y(Y#Z)Z 9X %ZMZ %Z F_ )^GSG^ NfIRHe @g -e ;X.Y GZ6Z CY9"
- "Z 7ZNY ?[ %[/R *X@X $TBY BX;X=X6[.] /Y GY HX 7X +Z7Z 0Z 3\\ 0[ (Z L[ DZ4"
- "Z 9[ 3Z2[ &Z >i i 2W<Z?]HZ.Y'Z!Z/\\ L\\&S'Z,] JZ MZ *\\(Z'Z(Z :Z \"Z 4Z7] AZ 2Y JY(Y3e$\\,\\ KZ )\\-"
- "\\ Z.Z \"[ 7Y 7[/[ =ZNZ DZNX4XNY![6[ 4Z 7[ AX AX MY NY4\\ F\\4Z F[ &Z5] H[ @Y 2Z6] GY1Y 5Y NX 0X?[ +"
- "Y FY3Y2Y+Y1Y HZ4Z G\\4Z JZ5\\ ;Y 6Y 8Y -Y5\\ ;YKY =XNX=WNY E[B[ 3YNY 4[ BY X Y N_=_ LZ:_ CZ2Y FX;Y >Z4"
- "Z EY #Y1Y 9XNZ 7Y6Z GZ4[$Z=]=['ZDYDZ FY9Y HZBXBZ K]5Z J[5[ 2Y 2Z7Y L[(Z H[ #Z '\\5[ F~ LZ(Z :Z :\\-\\ KW :X :"
- "V >r >V/V @s #Z 2[CU -Z +[MeL[ 5Z X G\\ :W!V 3W@W 7V!W AZ4[ 1WEW LW@W 1W7s,X-"
- "Y JX8t$\\ 7Z'Z%Z'Z$Z'Y%Z'Z$Z'Y%Z'Z4Z1Z 6\\&S NZ !Z Z !Z >Z !Z !Z \"Z :Z,]\"Y3ZNY$\\,\\#\\,\\$\\,\\$\\-\\$\\,"
- "\\ N\\4[ ]-\\![/Z LZ/[ N[/[ N[/[ 7Z 2Z #Y-Y NY4\\ HY5] IY4\\ GY4\\ HY4\\ HY4\\.Z5` 2Z $Z !Z !Z !Z =Y Y NX Y "
- "3Z4Z GX0X H[5[ GZ4Z GZ4Z H[5[ GZ4[ 6\\ 5_9[ HZ5[ GZ5[ FY5[ FY5\\ :YNZ :\\4Z ;YNY )YAXAZ\"Z+Z!Z*Y Y*Z\"Z+Z 8"
- "X $YMY %[ F^ '\\FSF\\ LcGRGc >f ,c :X.Y FZ7Y BY8Y 7e >[ %[1S -Y 'X@X ;Q:TCZ CX:X=X"
- "5[.] /Y HY HX NZ GZ 'X +[8Z 0Z 4\\ 0[ 'Z M\\ CZ6[ 9Z 2[3[ '[ 0Y Y ?f f BX DW=\\C_J[.Z&Z\"Z0\\ "
- "J\\(T'Z._ JZ MZ *])Z'Z(Z :Z \"Z 4Z6] BZ 2Y JY(Y3e#\\.\\ JZ )]/\\ NZ.[ NQ'[ 6Y 6[0[ =ZNZ CYNX4XNY!Z4[ 5Z 8[ @X"
- " AX MY NY5] F]6Z DZ &Z5] G[ AY 2[8^ GY1Y 5Y NX 0X>[ ,Y FY3Y2Y+Y1Y H[6[ G]6Z IZ5\\ ;Y 6Y 8Y -Z6\\ ;Z"
- "MZ =b=b EZ@Z 3d 5[ AY X Y L[:\\ IZ;` D[4Z FX<Z >Z5[ EY #Y1Y 9c 7Z5Y GZ5\\$[>^>['[EYE[ FY9Y HZBXCZ J]5Z "
- "IZ5Z 1Y 1Y8Z LZ&Z J[ \"Z &\\8] E| KZ(Z :Z :]/] JU 9X 9T <p <T-T >q \"Z 1ZCU -Z ,[JaI[ 6Z X F\\ :W#V 1"
- "V?V 7W#W @[5[ 1WEW LV?V 1X7s,W-Y JX7t%\\ 6Z&Z&Z'Z%Z&Z&Z'Z%Z&Z&Z&Y4Y0Z 5\\(T NZ !Z Z "
- "!Z >Z !Z !Z \"Z :Z.^!Y3e#\\.\\!\\.\\#].\\#]/]#\\.\\ N\\2[ ]/]![0[ L[0[ M[0[ N\\1[ 6Z 2Z #Y-Y NY5] HY5] IZ6] GY"
- "5] HY5] HY5]-Y5a 3[ %[ \"[ \"[ \"[ >Y Y NX Y 3Z5[ GX0X GZ5Z F[6[ G[6[ GZ5Z F[5Z 5\\ 4^9Z FY6\\ FY6\\ FY6\\ "
- "FY6] 9c 9]6Z :d )[CXBZ Z-Z NZ-[ [-Z Z-Z 7X $YNZ %Z D] $VCSDW G`FSG` ;d +c :X.Y F[9Z CZ8Y 6d =\\ "
- " '\\3T -Z (W?X ;S<TDZ BW8W=W4\\1` 0Y HY HX NZ GZ 'X *Z9Z /Z 5\\ 0\\ 'Z N\\ B[8[ 8Z"
- " 2\\5[ '[ /Z \"[ >d c @Z EW<_Ks-Z&Z\"Z1] J^,V'Z/_ IZ MZ )]*Z'Z(Z :Z \"Z 4Z5] CZ 2Y JY(Y2d#]0\\ IZ (]1] NZ-"
- "Z NS*\\ 6Y 6[1[ <e Bc4c\"[3Z 5Z 9\\ @X AX MY NZ6] F^8[ D[ &Z7^ G[ AY 1[:_ GY1Y 5Y NX 0X=[ -Y FY3Y2Y"
- "+Y1Y G[7Z F]7[ HZ7] ;Y 6Y 7Y .Z7] :YMY <a<a EZ>Z 4c 5[ @Y X Y HS3V FZ<a D\\5Z FX<Y =[7[ DZ $Y1Y 9c 7Y4"
- "Z H[6\\#Z?WNV>Z%ZEYF[ EY9Y GZCXD[ J^7Z H[7[ 1Y 1Z:Z KZ&Z K[ !Z %];] Bx IZ(Z :Z 9]1] HS 8X 8R :n :R+R <o !Z "
- "1[DU -Z -[F\\F[ 7Z X E\\ :W&W /U>U 6W%W ?[6\\ 1WEW LU>U 0W6s-X.X HW6t&\\ 5Z&Z'Z"
- "%Z&Z&Z'Z%Z&Z&Z&Z&Z6Z0Z 4],V NZ !Z Z !Z >Z !Z !Z \"Z :Z0`!Y2d\"\\0]!]0\\!]0\\!]1]!]1] \\0[ ]1] N[2\\ L\\2[ L\\"
- "2[ L[1[ 6Z 2Z #Y.Y MZ7^ HY6^ HY6] GZ6] HZ7^ HZ7^-Y6c 3[ %[ \"[ \"[ \"[ ?Y Y NX Y 3[7[ FX0X G[7[ E[7[ FZ7[ F"
- "[7[ E[7[ 5\\ 4]9[ FZ8] FZ8] FZ8] FZ7] 9c 9]7[ 9b '[DXD[ N[/Z LZ/[ M[0[ N[/Z 6X $d %Z C\\ ?S 2\\ETD"
- "\\ 9b )a 9X.Y E[<[ BY7Z 7c ;\\ '\\5U -Z (W?W :U>TE[ CX8X?X3\\3b 1Y IY GX NZ GZ ("
- "X )[;[ /Z 5[ %Q-\\ &Z BQ/] AZ9\\ 9Z 0[6\\ (\\ /Z \"[ ;a ` =Z EX<nNd,Z$Y\"Z2] H^.W'Z2a HZ MZ (^,Z'Z(Z :Z \""
- "Z 4Z4] DZ 2Y JY(Y2d\"]3^ IZ ']3] MZ-[ U-] 6Y 5\\4\\ ;d Bb2b#[2[ 6Z :\\ ?X @X NY MZ8^ F^8Z B[ '[9_ F[,"
- "P 7Y 1\\<` GY1Y 5Y NX 0X<[ .Y FY3Y2Y+Y1Y G[8[ F^9[ G[9^ ;Y *Q/Z 7Y -Z9^ :YMY <a;` F[>[ 4b 6[ ?Y X Y "
- "FZ=b E]7Z EX=Z <[9\\ D[ %Y1Y 8a 6Y3Y H\\8]#[@WNW@[%[FYG\\ EY9Y G[DXD[ J_9[ G[9[ /Y 1Z;Z LZ%Z L\\ !Z $]=\\ >t GZ"
- "(Z :Z 8]3] FQ 7X 7P 8l 8P)P :m Z 0[EU -Z .[?P?[ 8Z X D[ 9W(W -T<S 5X)X >\\8] 1WEW "
- " LS<T 0W5s-W.X HX6t'\\ 5Z$Y'Z%Z'[%Z(Z%Z&Z%Z(Z%Z6Z0Z 4^.W NZ !Z Z !Z >Z !Z !Z \"Z :Z2a Y2d\"^3] N]3^ ]3"
- "] N]3] N]3] \\.[!^3] M\\4\\ J\\4\\ K\\4\\ L\\4\\ 5Z 2Z #Y.Y MZ8_ HZ8_ HZ8^ FZ8^ HZ8_ HZ8_-Z8e-Q)\\ &\\-Q G\\-Q "
- "G\\-Q G\\-Q 5Y Y NX Y 2[9\\ FX0X F[9[ D\\9[ E[8[ E[9[ D\\9[ 4\\ 3[9[ EZ9^ FZ9^ FZ9^ F[9^ 9b 8^9[ 8b &[2["
- " L\\3\\ K[2[ K[2[ L\\3\\ 6X #c &Z B\\ ?S /UATAT 4a '_ 8X.Y E\\>\\ BY6Y 7c :] (\\7V "
- "-Z )X@X :W@TF[ BW7X?X3]6e 1X IY GX NZ GZ (X ([=[ .Z 6[ $S1^ &Z BS3^ @\\<\\ 8Z 0]9] FR6] .Z \"[ 8^ "
- " ^ ;Z DW;lMc+Z$Z#Z4_ G_2Y'Z5c GZ MZ '^/\\'Z(Z :Z \"Z 4Z3] EZ 2Y JY(Y1c!^6^ HZ '^6^ LZ,Z X1] 5Y 5]6\\ :c Ab2a"
- "\"Z0[ 7Z ;\\ >X @X NY MZ:` F_:[ B\\3P D[;` E\\1S 7Y 0\\>a GY1Y 5Y NX 0X;\\ 0Y FY3Y2Y+Y1Y F[:[ E_;\\ "
- "F[;_ ;Y *S1Y 6Z .[;_ :e ;`;` G[<[ 5a 6[ >Y X Y F[?YNY F_:[ DX?Z :[;\\ B[ &Y1Y 8a 7Z3Y H]:^#\\BXNWA[#["
- "GYH\\ DY9Y F\\FXF\\ I`;[ F\\;\\ /Z 2[=Z KZ$Z N\\ Z #^A] :n DZ(Z :Z 7]5] +X Mj (k NZ 0\\FUBP ;Z /[,[ "
- "9Z X CZ 8X+W *R;R 4X+X =]:^ 1WEW LR;R /X5s.W.X GW5t(\\ 4Z$Z(Z%Z'Z$Z(Z$Y'Z$Z(Z$Z"
- "8Z/Z 3_2Y NZ !Z Z !Z >Z !Z !Z \"Z :Z5c NY1c!^6^ L^6^ M^6^ M]5] M^6^ \\,[#a7^ K\\6] I\\6\\ J]6\\ J\\6] 5Z 2Z #"
- "Y/Z LZ:` H[:` H[:_ FZ:` GZ:` GZ:`-[:YN\\0S(\\4Q C\\0S F\\0S F\\0S F\\0S 5Y Y NX Y 1[:[ EX0X F\\;\\ C\\;[ C[:"
- "[ D\\;\\ C\\;\\ 4\\ 3[:\\ DZ;_ EZ;_ EZ;_ EZ;` 8a 8_;\\ 7a %\\6\\ J\\5\\ I\\6\\ I\\6\\ J\\5\\ 5X #c 'Z "
- "@[ @T JT _ %] 7X.Y D^D^ BZ6Y 6b 9_ *];X -Z )X@X :ZCTH] CX7YAX1^:h 2Y JY GX NZ"
- " GZ (X (\\?\\ .Z 7\\ $W7_ %Z BV8` ?\\>] 9[ /];] ET9] -Z \"[ 5[ [ 8Z DX;jLb*Z$Z#Z7a E`7\\'Z9f FZ MZ &`4^"
- "'Z(Z :Z \"Z 4Z2] FZ 2Y JY(Y1c _:_ GZ &_9^ KZ,[![6^ 4Y 4]9] 8b @a2a#[/Z 7Z ;[ =X @X NY M[<a Fa>\\ @]7R"
- " D\\=a E]4U 7Y /]Bc GY1Y 5Y NX 0X:\\ 1Y FY3Y2Y+Y1Y E\\>] E`=\\ E\\=` ;Y *U5[ 6[ /\\>a 9c :_:` GZ:Z 4` 6[ >Y "
- "X Y E[AYMZ G`<[ CX@Z 9\\=\\ A\\3Q EY1Y 7` 7Y2Z I^<_\"[BWMXC\\#]IYI\\ CY9Y F]GXG] Ia=\\ E\\=\\ .[ 2[?Z J"
- "Z$Z N[ NZ \"^C^ 7g @Z(Z :Z 7_9_ +X Lh &i MZ /]HUDR ;Z .Y*Y 8Z X BZ 8Y/X (Q:Q 2X/Y "
- " <^<` 2WEW LQ:Q .W MV(X/X GX NW\"\\ 3Z$Z)Z#Z(Z$Z)Z#Z(Z$Z)Z#Z8Z/Z 2`7\\ NZ !Z Z !Z >Z !Z !Z \"Z :"
- "Z9f MY0b _:_ J_:_ K_:_ L_9_ L_9^ N[*[$c:^ J^:^ H^:^ I^:] H]9] 4Z 2Z #YIP7[ L[<a G[<a G[=a F[<a G[<a G[<a,[=ZL\\"
- "4V'\\7S B\\4V E]5V E]5V E]5V 5Y Y NX Y 1\\=\\ DX0X E\\=\\ A\\=\\ C]>] C\\=\\ A\\=\\ 3\\ 2\\=\\ C[=` E[=` E[="
- "` E[=a 8a 8`=\\ 6` #]:] H]9] G]:] G]:] H]9] 4W !a 'Z ?Z ?U KT N] $] 7X.Y Cv AZ6Z 7a 7a "
- " -_?Z -Z )W?X :^GTK_ CX5XAX0_>k 3Y JX FX NZ GZ )Y ']C] ?} I~S IZ=b %Z BZ>a =]B^ 8Z ._?^ DX"
- "@_ ,Z \"[ 3Y X 5Z CW:gJ`)Z\"Z$~T Cb=_'~W E~S FZ %b:a'Z(Z :Z \"Z 4Z1] G~Q)Y JY(Y0b N`>` FZ %a?` JZ+Z!^<a 4Y "
- "3_>_ 8b @a2a$[.[ 8Z <~` AX ?X Y L\\@c Fb@] ?^<U C]Ac D^9X 7Y /aI[NY GY1Y 5Y NX 0X9\\ 2Y FY3Y2Y+Y1Y E]"
- "@] Db@\\ C]Ab ;Y *X9\\ 5] 1\\Ac 9c :_:_ GZ9[ 5` 7[ =Y X Y E]DZM[ Hb@] BXB[ 8]A^ @]8T EY1Y 7_ 7Z1Y I`@"
- "b#]EXLXE\\!]JYK^ CY9Y E_JXJ_ HcA] C]A] ,] 4[B\\ K~c!~T FZ 3oDo A[ :Z(Z :Z 6a?a *X Kf $g LZ .^JUGU H~U"
- " JW(W 7Z X AY 7Z3Y &P9P 1Y3Y <~d 3`@b 2WEW LP9P .X MV(W/X GX MW#\\ 3Z\"Z*Z#Z)Z\"Z*"
- "Z#Z)[#Z*Z#Z9Z.~T+b=_ N~T J~T I~S I~S 7Z !Z !Z \"Z :~V KY0b N`>` H`>` I`>` Ja?a Ja?` LY(Y$f?` H_>_ F_>_ G_>_ H_>"
- "_ 3Z 2Z #YIS;[ K\\?c G\\?c G\\?b E\\@c F\\@c G\\?c,\\?[L^9Y'^<V B_:Y E_:Y E_:Y D^:Y 5Y Y NX Y 0]@] DX0X D]A]"
- " @]@] A]@] A]A^ A]@] 2\\ 2]@] B]Ab E]Ab D\\Ab D\\Ac 7_ 7b@\\ 5` \"_@_ F_?_ E_@_ E_@_ F_?_ 3W !a 'Z ?Z "
- " ?U KT M\\ #[ 6X.Y Bu AY5Z 7a 6f 2aE] -Z )W?W 9~ BW4YCY/bFp 3X KY FX NZ GZ "
- ")X %^G^ >} I~S I~ $Z B| ;^F_ 7Z -aEa Dv +Z \"[ 0V U 2Z CX9dI^'Z\"Z$~S AfGd'~U C~S FZ $gGg&Z(Z :Z \"Z 4Z0] H"
- "~Q)Y JY(Y0b McGd EZ $dGc IZ+[\"cEd 3Y 3cGc 7a ?`1a$Z,[ 9Z =~a AX ?X Y L^DZNY FYNZF_ =`CY B^EZNY CaB] 7"
- "Y .qMY GY1Y 5Y NX 0X8\\ 3Y FY3Y2Y+Y1Y D_F_ CYNYE_ B^EZNX ;Y *]A^ 4k >^G[NY 8a 9_9^ H[8[ 5^ 6~P 2Y X Y "
- " D^H[La NfH` AYD[ 6^E_ ?`?X EY1Y 7_ 7Y0Y IcFk(]HZLZI^ `Nk BY9Z E~Q GYNZE^ B_E_ ,e ;]G] J~c!~T FZ 3oDo @Z :Z(Z :"
- "Z 5dGd )X Jd \"e KZ -`MUKY H~U IU&U 6Z X AY 5Z7Z LZ7Z ;~d 3cFk 8WEW "
- " BW LV)X0X FW LW$\\ 2Z\"Z+[#Z)Z\"Z*Z\"Z*Z\"Z*Z\"Z:Z.~T*fGd N~T J~T I~S I~S 7Z !Z !Z \"Z :~U JY/a MdGc FcGd GcGd"
- " HdGd HdGc JW&W$kGc FbFb DbFb FcFb FcGc 3Z 2Z #YIWB] I^DZNY F]D[NY F]D[NX E^DZNY F^DZNY F^E[NY+]D]J`@]&`BY AaA]"
- " DaA] DaA] DaA] 5Y Y NX Y /_F_ CX0X D_E_ ?_F_ ?_F_ @_E_ ?_F_ 7aF_ @^FZMX D^FZMX D_GZMX D_G[NY 7_ 7YNYE_ 4^"
- " dLd CdMd BdLd CdLd DeMd 2X !` %X =Y ?U LV MZ !Y 5X.Y As AZ4Y 6` 5~] )x -Z "
- "*X@X 9} BX3YFZ-{L] 4Y LY FX NZ GZ )X $t >} I~S I} #Z B{ :v 7[ ,{ Cu *Z \"[ -S S 0Z BW8aG[%[\"Z$~R"
- " ?~S'~T B~S FZ #~V%Z(Z :Z \"Z 4Z/] I~Q)Y JY(Y/a L~ DZ #~ HZ*Z\"~R 2Y 2} 5` ?`0_$[+Z 9Z =~a AX ?X Y KsN"
- "Y FYNr ;u AqMY B{ 7Y -oLY GY1Y 5Y NX 0X7\\ 4Y FY3Y2Y+Y1Y Cv BYNr ArMX ;Y *y 2j >qMY 8a 8^9^ I[6Z 5^ 6~P 2Y X "
- " Y CpK` N} ?YF[ 5w =x EY1Y 6] 7Z0Z J~Y(nJm M{ AY9\\ F~ FYMq @w *d ;r J~d!~T FZ 3oDo @Z :Z(Z :Z 4~ 'X "
- " Ib c JZ ,u H~U HS$S 5Z X AY 4\\>\\ I]>\\ :~d 3~Y 8WEW CW KV)W0X FX LW"
- "$[ 2[\"Z+Z!Z*Z\"Z+Z!Z*Z!Z,Z!Z:Z.~T)~S N~T J~T I~S I~S 7Z !Z !Z \"Z :~T IY/a L~ D~ E~ F~ E~ HU$U$~X D| B| D} D} "
- "2Z 2Z #YIr HrMY FsMY FsMX DsNY ErMY FsMY+uH|%v @| C| C| C| 5Y Y NX Y .v BX0X Cw =w >v >w =w 8{ ?qMX CqMX C"
- "qMX CqMY 6] 6YNr 3^ My Ay @y @z Ay 1X _ $V <X ?V LV LX NW 4X.Y @p ?Z4Z 7_ 2~[ "
- " (v ,Z *X@X 9| AW1[K[+yJ] 5Y LX EX NZ GZ )X #r =} I~S I| \"Z Bz 8t 6Z *y Bt )Z \"[ *P P -Z BX"
- "6[DX\"Z Z%~Q <~P&~R @~S FZ \"~T$Z(Z :Z \"Z 4Z.] J~Q)Y JY(Y/a K| CZ !{ GZ*[#~Q 1Y 1{ 4_ =_0_%[*[ :Z =~a AX >X !"
- "Y JqMY FYMp 9t ApLY Az 7Y ,mKY GY1Y 5Y NX 0X6\\ 5Y FY3Y2Y+Y1Y Bt AYMp ?pLX ;Y *x 1j =oLY 8a 8]8^ IZ4Z 6"
- "] 5~P 2Y X Y CoI_ N} ?[K] 3u ;w EY1Y 6] 7Y.Y JvM_'mJm Ly @Y9b K| EYLp ?u (c :p I~e\"~T FZ 3oDo @Z :Z(Z"
- " :Z 2{ &X H` Ma IZ +t H~U GQ\"Q 4Z X AY 2aLb FaKa 8~d 3YNlN_ 8WEW "
- "DX KV*W0o-W KW%[ 1Z Z,Z!Z+Z Z,Z!Z+Z Z,Z!Z;Z-~T'~P M~T J~T I~S I~S 7Z !Z !Z \"Z :~R GY.` K| B| C{ B{ B{ FS\"S$YM"
- "{ Bz @z B{ B{ 1Z 2Z #YIq GqLY EqLY EqLX CqMY ErMY EqLY*sF{$u ?{ B{ B{ B{ 5Y Y NX Y -t AX0X Bu ;u <t <u ;u "
- "8{ >pLX CpLX CpLX BoLY 6] 6YMp 1] Lv >w =v =v >w 0X _ #T ;X ?W MV LW LV 4X.Y ?n >Y3Z 7_ 1~Z "
- " 't +Z *W?X 8y @X1j)vG] 5X MY EX NZ GZ *X !p <} I~S Iz Z By 6r 5Z )w As (Z \"[ "
- " 5Z AX HZ Z%~ 9|$~P >~S FZ ~P\"Z(Z :Z \"Z 4Z-] K~Q)Y JY(Y.` Jy AZ x EZ)Z#~P 0Y /x 3_ =_0_%Z([ ;Z =~a AX "
- ">X !Y JpLY FYLn 7s @nKY @y 7Y +kJY GY1Y 5Y NX 0X5\\ 6Y FY3Y2Y+Y1Y Ar @YLn =nKX ;Y *w /i <mKY 7_ 7]8] IZ"
- "3[ 6\\ 5~P 2Y X Y BmH_ N{ <k 0r 9v EY1Y 6] 8Z.Z KYNkM_&kHk Jw ?Y8a Jy CYKn =s &b 9n H~e\"~T FZ 3oDo @Z"
- " :Z(Z :Z 1y %X G^ K_ HZ *s H~U *Z X AY 1t Bs 6~d 3YNkM_ 8WEW DW "
- "JV+X0o.X KW%Z 0Z Z-Z NZ,Z Z-[ Z,Z Z-[ Z<Z-~T&| K~T J~T I~S I~S 7Z !Z !Z \"Z :~P EY.` Iy @y @y @y @y DQ Q$YKy @x"
- " >x ?x @y 0Z 2Z #YIp EoKY DoKY DoKX BoLY DpLY DoKY)qCy#t =y @y @y @y 5Y Y NX Y ,r @X0X As 9s :r :s 9s 7z <"
- "nKX BnKX BnKX BnKY 6] 6YLn 0\\ Jt ;s :t ;t ;s .X N] !R 9V >W NX LU KU 3X.Y >l =Y2Y 7_ /~X "
- " %p )Z *W?W 4u @X/i(tE] 6Y NX DX NZ GZ *X m :} I~S Iy NZ Bw 2o 5Z 'u @r 'Z \"Z "
- " 4Z AY J[ Z%} 6x\"} <~S FZ N| Z(Z :Z \"Z 4Z,] L~Q)Y JY(Y.` Hv @Z Mu DZ)[$~ /Y .u 0^ =^/_&['Z ;Z =~a AX >X"
- " !Y InKY FYKl 5r ?lJY >w 7Y )hIY GY1Y 5Y NX 0X4\\ 7Y FY3Y2Y+Y1Y @p ?YKl ;lJX ;Y *v -h ;kJY 7_ 7]7\\ J[2"
- "[ 7\\ 5~P 2Y X Y AkE] Nz :i .p 7u EY1Y 5[ 7Y,Y KYMiL_%iGj Hu >Y8a Hv BYJl :p $a 7k H~f\"~T FZ 3oDo @Z "
- ":Z(Z :Z /u #X F\\ I] GZ )r H~U *Z X AY /p >o 4~d 3YMiK^ 8WEW EX "
- "JV+W/o/X JW&Z 0[ Z-Z NZ-[ [.Z NZ,Z NZ.Z NZ=Z,~T$x I~T J~T I~S I~S 7Z !Z !Z \"Z :| BY-_ Hv <v =v =u =v BXHu =v"
- " <v =u <u .Z 2Z #YIo CmJY CmJY CmJX BnKY CmJY CmJY(oAx!r <x ?x ?x ?x 5Y Y NX Y +p ?X0X ?p 7p 7p 7p 7p 6WNp"
- " 9lJX AlJX AlJX AlJY 5[ 5YKl /\\ Hp 8q 7p 7p 8q -X N] NP 9V ?Y X KS IS 2X.Y <h <Z2Y 6^ -~V "
- " $n (Z +X@X 1o =W-f$pB] 6X NX DX Z FZ *X Nk 9} I~S Iw LZ Bv 0m 4Z %q >p %Z \"Z "
- " 4Z @X JZ MZ&{ 3u z 9~S FZ Lx MZ(Z :Z \"Z 4Z+] M~Q)Y JY(Y-_ Fr >Z Lr BZ(Z!y -Y -s /] <^.]&[&[ <Z =~a AX "
- " =X \"Y GjIY FYJj 2p =iIY =u 6Y 'dGY GY1Y 5Y NX 0X3\\ 8Y FY3Y2Y+Y1Y >m >YJj 8iIX ;Y *u *f :iIY 7_ 6\\7"
- "\\ K[0Z 6Z 4~P 2Y X Y ?hC\\ NYMm 8f +m 3s EY1Y 5[ 8Z,Y KYLgJ^$gEh Fs =Y8a Fr @YIi 7m !` 6i G~g#~T FZ 3o"
- "Do @Z :Z(Z :Z .s \"X EZ G[ FZ 'p H~U *Z X AY ,k :k 2~d 3YLgJ^ 8WEW "
- " EW IV,X/o/W IW&Z 0Z MZ/[ NZ-Z MZ.Z N[.Z MZ.Z MZ>Z,~T\"t G~T J~T I~S I~S 7Z !Z !Z \"Z :y ?Y-_ Fr 8r 9r :s :r "
- " AXEr :r 8r :s :s -Z 2Z #YIn AkIY BkIY BkIX @jIY BkIY BkIY'l=t Mq :t ;t ;t ;t 3Y Y NX Y *m =X0X >m 3m 5n 5m"
- " 3m 6XLm 7iHX @iHX @jIX @jIY 5[ 5YJj -Z El 3k 2l 3l 4l *X N\\ 5U ?Y Y KR HQ 1X.Y 9b 9Y1Z 7"
- "] )~S \"j &Z +X@X -h ;X+c!l?\\ 6X Y DX Z FZ +X Kh 8} I~S Fr JZ As ,i 3[ $n ;m "
- "#Z \"Y 3Z ?X KZ MZ&x -p Mu 4~S FZ Js JZ(Z :Z \"Z 4Z*] N~Q)Y JY(Y-_ Dn <Z Jn @Z([ Nt +Y +o ,\\ ;].]&Z$"
- "[ =Z =~a AX =X \"Y FhHY FYHf .m ;gHY ;p 3Y %`EY GY1Y 5Y NX 0X2\\ 9Y FY3Y2Y+Y1Y =j <YHf 5gHX ;Y (q &d 9"
- "fGY 6] 5[6\\ KZ.Z 7Z 4~P 2Y X Y >gB[ NYLj 5d (j 0q EY1Y 5Z 7Y+Z LYKdG]\"dBd Bo ;Y7` Dn >YHg 4i L^ 4e "
- "E~g#~T FZ 3oDo @Z :Z(Z :Z ,n NX DX EY EZ %m G~U *Z X BZ )e 4e /~d 3YKeH] 8"
- "WEW FW HV,W.o0X IW'Z /Z MZ/Z LZ.Z MZ/[ MZ.Z MZ/[ MZ>Y+~T p E~T J~T I~S I~S 7Z !Z !Z \"Z :u ;Y,^ Dn 4"
- "n 5n 6o 6n @XBm 5n 4n 6o 6o +Z 2Z #YIl =gGY AhGY AhGX ?hHY @hHY @gGY%i:o Hm 7p 6o 6p 7p 1Y Y NX Y (i ;X0X "
- "<i 0j 1j 1j 1j 5XIi 3fGX >fGX >fGX >fGY 4Y 4YHf +Z Bg /g .g -g /g (X M[ 5T ?Z !Z JP 'X.Y 5"
- "[ 6Y0Y 7] &~P Ne $Z +W?X '] 6W)a Mh<\\ 7Y !X CX Y EZ +X Id 6} I~S Cm HZ =l 'e "
- "1Z i 6h !Z #Z 3Z ?Y M[ M['s &k Jo .~S FZ Gm GZ(Z :Z \"Z 4Z)] ~Q)Y JY(Y,^ Bi 9Z Gl AZ'Z Jm (Y (i )\\ "
- ";].]'[#Z =Z =~a AX =X \"Y DdFY FYFb *h 6cFY 8j 0Y \"YAY GY1Y 5Y NX 0X1\\ :Y FY3Y2Y+Y1Y ;f :YFb 1cFX ;Y"
- " $k ` 7cFY 6] 5[5Z KZ-[ 8Y 3~P 2Y X Y ;b=X NYJe 0` $e +l BY1Y 4Y 7Y*Y LYIaE[ b@a >k 9Y6_ Ah ;YFc 0e "
- "FZ 2a D~i$~T FZ 3oDo @Z :Z(Z :Z )i LX CV CW DZ #h D~U *Z X -R9Z #[ *[ *~d 3"
- "YIaE\\ 8WEW GX HV-W-o0W HW'Z 0Z L[0Z LZ/[ LZ0Z LZ/[ LZ0Z LZ?Z+~T Lj B~T J~T I~S I~S 7Z !Z !Z \"Z :o "
- "5Y,^ Ai /h 0i 0i 0i >W?i 1j 0j 1j 1i (Z 2Z #YGh 9cEY ?dEY ?dEX =dFY >dFY >cEY#d5j Ch 1j 1j 1j 1j -Y Y NX Y"
- " &e 9X0X :e ,f -f -e ,f 4XFe 0cEX <bEX <bEX <bEY 4Y 4YFb )Z ?` (a '` '` (a %X 'T "
- " L{ K_ 0T 4X&[ Ga AX \"Y :Y EX G_ Ie #e !_ c /a EY "
- " EY Hc ?e FZ +b Ni )d Nc (X =Y #Y A^ J^ %a /] N"
- "c ;Y NX *` 7YD^ ,]CX 1c ^ /Y DY X Y 8] 1YF] *\\ N` %c DY 4Y *YG\\A"
- "X J\\;] 9e A^ =` 7YC] *_ G[ >a NU CZ N` 9X -T<[ "
- " LYG]BX 5WEW %U HW NX MX GZ (d +b (b )b )a )b 9V;a "
- ")c *c *c *c =a 4_ &^ %^ $^ &_ &_ :_/c <b +c *c *c *c 3_ K_ &` '` '_ &` 1WB_ *] $] $^ %^ NZ "
- "4YD^ 'Y 6Q HQ GQ FQ HQ LX &S DQ )T 4W Q :Q"
- " 9Y #X :Y EX ?Q 8R ?R @Q @R MQ =Y DY @R -Q <Z "
- " #R >RM] !R <R X <X #Y ;Q <Q GR !Q @Q 2Y MX #R 0Y=Q Q=X 'Q "
- " @Q *Z DY X Y ;Y <P AQ CQ ;Y 4Y *YAQ8P @Q0Q -Y 8X 7Y 3Y=Q LQ "
- " JQ 4Z IU 3X -W@[ KYAQ8P 1WEW $U IV MW LW FZ "
- " V KQ GR HQ GQ GQ 0T2Q HR GR HR HQ ,Q %Q GP GQ FQ GQ "
- "GP *P NQ ,V MQ GR HR HR #Q =Q FQ HR HQ FQ *V:Q LQ GQ GQ GQ GY 3Y=Q !Y "
- " 9X MT +X #X :Y EX "
- " 5X BZ 7Y 7] 8X <X #Y "
- " HY MX 0Y 'X MY CY X Y ;Y 8Y 4Y *Y 1Y E"
- "X 3Y CZ IU 3X -\\I_ KY 8WEW $V"
- " %Z NU 0R #V "
- " )T <Z 3Y =Y 8X "
- " MT +X $X 9X DX 6Y AZ NR "
- " =Z 6\\ 8X <X #Y HY MX 0Y '"
- "X NZ CY X X :Y 8Y 4Y *Y 1Y EX 3Y "
- " CZ IU 3X -q JY 8WEW #V &Z NV "
- " 0V (R "
- " <Y 2Y =Y 8X MT *X "
- "%X 9X EY 6X @[!T >Z 5\\ "
- " 9X ;X $Y HY NY 0Y 'X NY BY X !Y "
- ":Y 8Y 4Y *Y 1Y EX 3Y CZ IU 3X -p "
- " IY 8WEW #V &Z MV "
- " 0U 'P ;Y 2Y >Z 8X "
- " MT *X &X 9X DX "
- " 5X ?\\%W ?Z 4\\ :X ;X $Y "
- " IZ NY 0Y 'X NY BZ !X !Y :Y 8Y 4Y *Y 1Y EX 3Y "
- " CZ IU 3X -o HY 8WEW \"V "
- " 'Z LU 0V "
- " CZ 2Y >Y 7X "
- " MT )X 'X 9X DX 5W <\\(X ?"
- "Z 3\\ ;Y <X $Y IY MY 0Y 'X "
- " Z AY !X !Y :Y 8Y 4Y *Y 1Y EX 3Y CZ I"
- "U 3X -n GY 8WEW \"V '[3Q <V "
- " 0V DY 1Y "
- "?Z 7X MT )X (X 8W "
- " CX 6X ;],[ AZ 1\\ <e"
- " GX 2f JZ MY 0Y 'X Y @Z \"X \"Z :Y 8"
- "Y 4Y *Y 1Y EX 3Y CZ IU 3X ,k "
- " EY 8WEW !V 'Z4R <V "
- " 0V EZ 1Y ?Y ARGX "
- " MT (X )X 8W DX 5W "
- " 9^1^ AZ 0\\ =e GX 2f KZ LY"
- " 0Y 'X !Z @[ #X #Z 9Y 8Y 4Y *Y 1Y EX 3Y "
- " CZ IU 3X )f CY 8WEW !V '[7T "
- " ;V 1V "
- " EY 0Y ?Y FWGW "
- " LT 'W *X 8W CX 5W 8`7` A[ "
- " /\\ >e GX 2f KZ LY 0Y 'X !Y >"
- "\\ %X &] 9Y 8Y 4Y *Y 1Y EX 3Y CZ IU 3"
- "X $^ @Y 8WEW !V '\\:V ;V "
- " 1W GZ 0Y @Z "
- " FWHX LT 'X +W 7W "
- " V 5b?c A[ -\\ ?e !f "
- " <P2\\ MY /Y 'X \"Z >f /X 0g 9Y 8Y 4Y *Y "
- " 1Y EX 3Y CZ IU 3X 5Y "
- " NV &\\=X ;V "
- "1W GY /Y AZ EWHX "
- " LT &W ,X 7V V 3~T "
- " A] ,\\ @e !f <R5\\ LY /Y "
- "'X #Z =f /X 0f 8Y 8Y 4Y *Y 1Y EX 3Y "
- " CZ IU 3X 5Y NW '^B[ <W "
- " 1W "
- " HZ /Y AZ DWIX LT &X -"
- "W 6U NV 1~P B_ *\\ "
- " Ae !f <U:] LZ /Y 'X #Z <e /X 0e 7Y "
- " 8Y 4Y *Y 1Y EX 3Y CZ IU 3X "
- " 5Y X &aJ_ <W "
- " 2X IZ .Y BZ CWJY "
- " LT %X /X "
- " 7| Hf )\\ Be !f <X?_ N[ "
- " .Y 'X %[ :d /X 0e 7Y 8Y 4Y *Y 1Y EX 3Y "
- " CZ IU 3X 5Y -PDX %v "
- " JQDX ?QEY "
- " J[ .Y D\\ CXLY "
- " KT 7x Fe "
- " -_Me %b .Y 'X /e 9c /X 0c "
- "5Y 8Y 4Y *Y 1Y EX 3Y CZ IU 3X "
- " 5Y -d $u Je "
- " ?d $d -Y Ne Ad "
- " KT "
- " 5s Cd ,v %b -"
- "Y 'X 0e 6a /X 0b 4Y 8Y 4Y *Y 1Y EX 3Y "
- " CZ IU 3X 5Y -d #t Jd "
- " >d "
- " %e -Y Nd @c "
- " (m @c "
- " +u $b -Y 'X 0d 2^ /X 0_ 1Y 8Y 4Y *Y "
- " 1Y EX 3Y CZ IT 2X 5Y "
- "-c !q Hd >c "
- " $d ,Y Nd ?b "
- " %g ="
- "b *t #a ,Y 'X 0d "
- " ,X /X 0Y +Y 8Y 4Y *Y 1Y EX 3Y CZ '"
- "X 5Y -c Nm Fc "
- " =c $c +Y Nc "
- " >a "
- " M\\ 8a \"~Y 1"
- "r !` +Y 'X 0c 1X 1Y 8Y 4Y *Y 1Y EX 3Y "
- " CZ &W 5Y -b Lj "
- " Db <b "
- " #b *Y Nb <_ "
- " (_ "
- " ~Y 1q _ *Y 'X 0b 0X 1Y "
- " 8Y 4Y *Y 1Y EX 3Y CZ "
- " 3Y -` He A` "
- " :` !a )Y Na :] "
- " "
- " '] M~Y .l M] (Y 'X "
- " 0` .X 1Y 8Y 4Y *Y 1Y EX 3Y "
- " KY *Z B^ 9Z "
- " 5Z M` (Y N` "
- " 8Z "
- " %X H~Y "
- " *d I[ &Y 'X 0^ ,X 1Y 8Y 4Y *Y 1Y EX 3Y"
- " KY "
- " "
- " H^ &Y N] 3V "
- " "
- " B~Y #X CU !X &X /Y (X 1"
- "Y 7X 4X )X 0Y EX 2X "
- " KY "
- " HZ \"X MY "
- " "
- " J~Y "
- " 9X "
- " "
- " "
- " "
- " 3~Y "
- " 9X "
- " "
- " "
- " "
- " 3~Y "
- " 9X "
- " "
- " "
- " '" };
- // Define a 40x38 'danger' color logo (used by cimg::dialog()).
- static const unsigned char logo40x38[4576] = {
- 177,200,200,200,3,123,123,0,36,200,200,200,1,123,123,0,2,255,255,0,1,189,189,189,1,0,0,0,34,200,200,200,
- 1,123,123,0,4,255,255,0,1,189,189,189,1,0,0,0,1,123,123,123,32,200,200,200,1,123,123,0,5,255,255,0,1,0,0,
- 0,2,123,123,123,30,200,200,200,1,123,123,0,6,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,29,200,200,200,
- 1,123,123,0,7,255,255,0,1,0,0,0,2,123,123,123,28,200,200,200,1,123,123,0,8,255,255,0,1,189,189,189,1,0,0,0,
- 2,123,123,123,27,200,200,200,1,123,123,0,9,255,255,0,1,0,0,0,2,123,123,123,26,200,200,200,1,123,123,0,10,255,
- 255,0,1,189,189,189,1,0,0,0,2,123,123,123,25,200,200,200,1,123,123,0,3,255,255,0,1,189,189,189,3,0,0,0,1,189,
- 189,189,3,255,255,0,1,0,0,0,2,123,123,123,24,200,200,200,1,123,123,0,4,255,255,0,5,0,0,0,3,255,255,0,1,189,
- 189,189,1,0,0,0,2,123,123,123,23,200,200,200,1,123,123,0,4,255,255,0,5,0,0,0,4,255,255,0,1,0,0,0,2,123,123,123,
- 22,200,200,200,1,123,123,0,5,255,255,0,5,0,0,0,4,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,21,200,200,200,
- 1,123,123,0,5,255,255,0,5,0,0,0,5,255,255,0,1,0,0,0,2,123,123,123,20,200,200,200,1,123,123,0,6,255,255,0,5,0,0,
- 0,5,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,19,200,200,200,1,123,123,0,6,255,255,0,1,123,123,0,3,0,0,0,1,
- 123,123,0,6,255,255,0,1,0,0,0,2,123,123,123,18,200,200,200,1,123,123,0,7,255,255,0,1,189,189,189,3,0,0,0,1,189,
- 189,189,6,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,17,200,200,200,1,123,123,0,8,255,255,0,3,0,0,0,8,255,255,
- 0,1,0,0,0,2,123,123,123,16,200,200,200,1,123,123,0,9,255,255,0,1,123,123,0,1,0,0,0,1,123,123,0,8,255,255,0,1,189,
- 189,189,1,0,0,0,2,123,123,123,15,200,200,200,1,123,123,0,9,255,255,0,1,189,189,189,1,0,0,0,1,189,189,189,9,255,
- 255,0,1,0,0,0,2,123,123,123,14,200,200,200,1,123,123,0,11,255,255,0,1,0,0,0,10,255,255,0,1,189,189,189,1,0,0,0,2,
- 123,123,123,13,200,200,200,1,123,123,0,23,255,255,0,1,0,0,0,2,123,123,123,12,200,200,200,1,123,123,0,11,255,255,0,
- 1,189,189,189,2,0,0,0,1,189,189,189,9,255,255,0,1,189,189,189,1,0,0,0,2,123,123,123,11,200,200,200,1,123,123,0,11,
- 255,255,0,4,0,0,0,10,255,255,0,1,0,0,0,2,123,123,123,10,200,200,200,1,123,123,0,12,255,255,0,4,0,0,0,10,255,255,0,
- 1,189,189,189,1,0,0,0,2,123,123,123,9,200,200,200,1,123,123,0,12,255,255,0,1,189,189,189,2,0,0,0,1,189,189,189,11,
- 255,255,0,1,0,0,0,2,123,123,123,9,200,200,200,1,123,123,0,27,255,255,0,1,0,0,0,3,123,123,123,8,200,200,200,1,123,
- 123,0,26,255,255,0,1,189,189,189,1,0,0,0,3,123,123,123,9,200,200,200,1,123,123,0,24,255,255,0,1,189,189,189,1,0,0,
- 0,4,123,123,123,10,200,200,200,1,123,123,0,24,0,0,0,5,123,123,123,12,200,200,200,27,123,123,123,14,200,200,200,25,
- 123,123,123,86,200,200,200,91,49,124,118,124,71,32,124,95,49,56,114,52,82,121,0 };
- //! Get/set default output stream for the \CImg library messages.
- /**
- \param file Desired output stream. Set to \c 0 to get the currently used output stream only.
- \return Currently used output stream.
- **/
- inline std::FILE* output(std::FILE *file) {
- cimg::mutex(1);
- static std::FILE *res = cimg::_stderr();
- if (file) res = file;
- cimg::mutex(1,0);
- return res;
- }
- // Return number of available CPU cores.
- inline unsigned int nb_cpus() {
- unsigned int res = 1;
- #if cimg_OS==2
- SYSTEM_INFO sysinfo;
- GetSystemInfo(&sysinfo);
- res = (unsigned int)sysinfo.dwNumberOfProcessors;
- #elif cimg_OS == 1
- res = (unsigned int)sysconf(_SC_NPROCESSORS_ONLN);
- #endif
- return res?res:1U;
- }
- // Lock/unlock mutex for CImg multi-thread programming.
- inline int mutex(const unsigned int n, const int lock_mode) {
- switch (lock_mode) {
- case 0 : cimg::Mutex_attr().unlock(n); return 0;
- case 1 : cimg::Mutex_attr().lock(n); return 0;
- default : return cimg::Mutex_attr().trylock(n);
- }
- }
- //! Display a warning message on the default output stream.
- /**
- \param format C-string containing the format of the message, as with <tt>std::printf()</tt>.
- \note If configuration macro \c cimg_strict_warnings is set, this function throws a
- \c CImgWarningException instead.
- \warning As the first argument is a format string, it is highly recommended to write
- \code
- cimg::warn("%s",warning_message);
- \endcode
- instead of
- \code
- cimg::warn(warning_message);
- \endcode
- if \c warning_message can be arbitrary, to prevent nasty memory access.
- **/
- inline void warn(const char *const format, ...) {
- if (cimg::exception_mode()>=1) {
- char *const message = new char[16384];
- std::va_list ap;
- va_start(ap,format);
- cimg_vsnprintf(message,16384,format,ap);
- va_end(ap);
- #ifdef cimg_strict_warnings
- throw CImgWarningException(message);
- #else
- std::fprintf(cimg::output(),"\n%s[CImg] *** Warning ***%s%s\n",cimg::t_red,cimg::t_normal,message);
- #endif
- delete[] message;
- }
- }
- // Execute an external system command.
- /**
- \param command C-string containing the command line to execute.
- \param module_name Module name.
- \return Status value of the executed command, whose meaning is OS-dependent.
- \note This function is similar to <tt>std::system()</tt>
- but it does not open an extra console windows
- on Windows-based systems.
- **/
- inline int system(const char *const command, const char *const module_name=0, const bool is_verbose=false) {
- cimg::unused(module_name);
- #ifdef cimg_no_system_calls
- cimg::unused(command,is_verbose);
- return -1;
- #else
- if (is_verbose) return std::system(command);
- #if cimg_OS==1
- const unsigned int l = (unsigned int)std::strlen(command);
- if (l) {
- char *const ncommand = new char[l + 24];
- std::memcpy(ncommand,command,l);
- std::strcpy(ncommand + l," >/dev/null 2>&1"); // Make command silent
- const int out_val = std::system(ncommand);
- delete[] ncommand;
- return out_val;
- } else return -1;
- #elif cimg_OS==2
- PROCESS_INFORMATION pi;
- STARTUPINFOA si;
- std::memset(&pi,0,sizeof(PROCESS_INFORMATION));
- std::memset(&si,0,sizeof(STARTUPINFO));
- GetStartupInfoA(&si);
- si.cb = sizeof(si);
- si.wShowWindow = SW_HIDE;
- si.dwFlags |= SW_HIDE | STARTF_USESHOWWINDOW;
- const BOOL res = CreateProcessA((LPCSTR)module_name,(LPSTR)command,0,0,FALSE,0,0,0,&si,&pi);
- if (res) {
- WaitForSingleObject(pi.hProcess,INFINITE);
- CloseHandle(pi.hThread);
- CloseHandle(pi.hProcess);
- return 0;
- } else {
- char* lpMsgBuf;
- // Get the error message.
- DWORD errorCode = GetLastError();
- FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
- 0,errorCode,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPSTR)&lpMsgBuf,0,0);
- cimg::warn("cimg::system() : Command '%s' (module name '%s) failed with error %lu: %s",
- module_name==0?"(null)":module_name,
- command==0?"(null)":command,
- errorCode,lpMsgBuf);
- return -1;
- }
- #else
- return std::system(command);
- #endif
- #endif
- }
- //! Return a reference to a temporary variable of type T.
- template<typename T>
- inline T& temporary(const T&) {
- static T temp;
- return temp;
- }
- //! Exchange values of variables \c a and \c b.
- template<typename T>
- inline void swap(T& a, T& b) { T t = a; a = b; b = t; }
- //! Exchange values of variables (\c a1,\c a2) and (\c b1,\c b2).
- template<typename T1, typename T2>
- inline void swap(T1& a1, T1& b1, T2& a2, T2& b2) {
- cimg::swap(a1,b1); cimg::swap(a2,b2);
- }
- //! Exchange values of variables (\c a1,\c a2,\c a3) and (\c b1,\c b2,\c b3).
- template<typename T1, typename T2, typename T3>
- inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3) {
- cimg::swap(a1,b1,a2,b2); cimg::swap(a3,b3);
- }
- //! Exchange values of variables (\c a1,\c a2,...,\c a4) and (\c b1,\c b2,...,\c b4).
- template<typename T1, typename T2, typename T3, typename T4>
- inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4) {
- cimg::swap(a1,b1,a2,b2,a3,b3); cimg::swap(a4,b4);
- }
- //! Exchange values of variables (\c a1,\c a2,...,\c a5) and (\c b1,\c b2,...,\c b5).
- template<typename T1, typename T2, typename T3, typename T4, typename T5>
- inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5) {
- cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4); cimg::swap(a5,b5);
- }
- //! Exchange values of variables (\c a1,\c a2,...,\c a6) and (\c b1,\c b2,...,\c b6).
- template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
- inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6) {
- cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5); cimg::swap(a6,b6);
- }
- //! Exchange values of variables (\c a1,\c a2,...,\c a7) and (\c b1,\c b2,...,\c b7).
- template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
- inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6,
- T7& a7, T7& b7) {
- cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6); cimg::swap(a7,b7);
- }
- //! Exchange values of variables (\c a1,\c a2,...,\c a8) and (\c b1,\c b2,...,\c b8).
- template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
- inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5, T6& a6, T6& b6,
- T7& a7, T7& b7, T8& a8, T8& b8) {
- cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6,a7,b7); cimg::swap(a8,b8);
- }
- //! Return the endianness of the current architecture.
- /**
- \return \c false for <i>Little Endian</i> or \c true for <i>Big Endian</i>.
- **/
- inline bool endianness() {
- const int x = 1;
- return ((unsigned char*)&x)[0]?false:true;
- }
- //! Reverse endianness of all elements in a memory buffer.
- /**
- \param[in,out] buffer Memory buffer whose endianness must be reversed.
- \param size Number of buffer elements to reverse.
- **/
- template<typename T>
- inline void invert_endianness(T* const buffer, const cimg_ulong size) {
- if (size) switch (sizeof(T)) {
- case 1 : break;
- case 2 : {
- for (unsigned short *ptr = (unsigned short*)buffer + size; ptr>(unsigned short*)buffer; ) {
- const unsigned short val = *(--ptr);
- *ptr = (unsigned short)((val>>8) | ((val<<8)));
- }
- } break;
- case 4 : {
- for (unsigned int *ptr = (unsigned int*)buffer + size; ptr>(unsigned int*)buffer; ) {
- const unsigned int val = *(--ptr);
- *ptr = (val>>24) | ((val>>8)&0xff00) | ((val<<8)&0xff0000) | (val<<24);
- }
- } break;
- case 8 : {
- const cimg_uint64
- m0 = (cimg_uint64)0xff, m1 = m0<<8, m2 = m0<<16, m3 = m0<<24,
- m4 = m0<<32, m5 = m0<<40, m6 = m0<<48, m7 = m0<<56;
- for (cimg_uint64 *ptr = (cimg_uint64*)buffer + size; ptr>(cimg_uint64*)buffer; ) {
- const cimg_uint64 val = *(--ptr);
- *ptr = (((val&m7)>>56) | ((val&m6)>>40) | ((val&m5)>>24) | ((val&m4)>>8) |
- ((val&m3)<<8) |((val&m2)<<24) | ((val&m1)<<40) | ((val&m0)<<56));
- }
- } break;
- default : {
- for (T* ptr = buffer + size; ptr>buffer; ) {
- unsigned char *pb = (unsigned char*)(--ptr), *pe = pb + sizeof(T);
- for (int i = 0; i<(int)sizeof(T)/2; ++i) swap(*(pb++),*(--pe));
- }
- }
- }
- }
- inline void invert_endianness(bool* const, const cimg_ulong) {}
- inline void invert_endianness(unsigned char* const, const cimg_ulong) {}
- inline void invert_endianness(char* const, const cimg_ulong) {}
- //! Reverse endianness of a single variable.
- /**
- \param[in,out] a Variable to reverse.
- \return Reference to reversed variable.
- **/
- template<typename T>
- inline T& invert_endianness(T& a) {
- invert_endianness(&a,1);
- return a;
- }
- // Conversion functions to get more precision when trying to store 'unsigned int' values as 'float'.
- inline unsigned int float2uint(const float value) {
- int tmp = 0;
- std::memcpy(&tmp,&value,sizeof(float));
- if (tmp>=0) return (unsigned int)value;
- unsigned int u;
- // use memcpy instead of assignment to avoid undesired optimizations by C++-compiler.
- std::memcpy(&u,&value,sizeof(float));
- return ((u)<<2)>>2; // set sign & exponent bit to 0
- }
- inline float uint2float(const unsigned int value) {
- if (value<(1U<<19)) return (float)value; // Consider 'uint32' safely stored as floats until 19bits (i.e 524287)
- float f;
- const unsigned int v = value | (3U<<(8*sizeof(unsigned int)-2)); // set sign & exponent bit to 1
- // use memcpy instead of simple assignment to avoid undesired optimizations by C++-compiler.
- std::memcpy(&f,&v,sizeof(float));
- return f;
- }
- //! Return the value of a system timer, with a millisecond precision.
- /**
- \note The timer does not necessarily starts from \c 0.
- **/
- inline cimg_uint64 time() {
- #if cimg_OS==1
- struct timeval st_time;
- gettimeofday(&st_time,0);
- return (cimg_uint64)st_time.tv_sec*1000 + (cimg_uint64)st_time.tv_usec/1000;
- #elif cimg_OS==2
- ULARGE_INTEGER ul;
- FILETIME ft;
- GetSystemTimeAsFileTime(&ft);
- ul.LowPart = ft.dwLowDateTime;
- ul.HighPart = ft.dwHighDateTime;
- return (cimg_uint64)ul.QuadPart/10000;
- #else
- return 0;
- #endif
- }
- // Implement a tic/toc mechanism to display elapsed time of algorithms.
- inline cimg_uint64 tictoc(const bool is_tic);
- //! Start tic/toc timer for time measurement between code instructions.
- /**
- \return Current value of the timer (same value as time()).
- **/
- inline cimg_uint64 tic() {
- return cimg::tictoc(true);
- }
- //! End tic/toc timer and displays elapsed time from last call to tic().
- /**
- \return Time elapsed (in ms) since last call to tic().
- **/
- inline cimg_uint64 toc() {
- return cimg::tictoc(false);
- }
- //! Sleep for a given numbers of milliseconds.
- /**
- \param milliseconds Number of milliseconds to wait for.
- \note This function frees the CPU resources during the sleeping time.
- It can be used to temporize your program properly, without wasting CPU time.
- **/
- inline void sleep(const unsigned int milliseconds) {
- #if cimg_OS==1
- struct timespec tv;
- tv.tv_sec = milliseconds/1000;
- tv.tv_nsec = (milliseconds%1000)*1000000;
- nanosleep(&tv,0);
- #elif cimg_OS==2
- Sleep(milliseconds);
- #else
- cimg::unused(milliseconds);
- #endif
- }
- inline unsigned int wait(const unsigned int milliseconds, cimg_uint64 *const p_timer) {
- if (!*p_timer) *p_timer = cimg::time();
- const cimg_uint64 current_time = cimg::time();
- if (current_time<*p_timer || current_time>=*p_timer + milliseconds) { *p_timer = current_time; return 0; }
- const unsigned int time_diff = (unsigned int)(*p_timer + milliseconds - current_time);
- *p_timer = current_time + time_diff;
- cimg::sleep(time_diff);
- return time_diff;
- }
- //! Wait for a given number of milliseconds since the last call to wait().
- /**
- \param milliseconds Number of milliseconds to wait for.
- \return Number of milliseconds elapsed since the last call to wait().
- \note Same as sleep() with a waiting time computed with regard to the last call
- of wait(). It may be used to temporize your program properly, without wasting CPU time.
- **/
- inline unsigned int wait(const unsigned int milliseconds) {
- cimg::mutex(3);
- static cimg_uint64 timer = cimg::time();
- cimg::mutex(3,0);
- return cimg::wait(milliseconds,&timer);
- }
- // Custom random number generator (allow re-entrance).
- inline cimg_uint64& rng() { // Used as a shared global number for rng
- static cimg_uint64 rng = 0xB16B00B5U;
- return rng;
- }
- inline unsigned int _rand(cimg_uint64 *const p_rng) {
- *p_rng = *p_rng*1103515245 + 12345U;
- return (unsigned int)*p_rng;
- }
- inline unsigned int _rand() {
- cimg::mutex(4);
- const unsigned int res = cimg::_rand(&cimg::rng());
- cimg::mutex(4,0);
- return res;
- }
- inline void srand(cimg_uint64 *const p_rng) {
- #if cimg_OS==1 || defined(__BORLANDC__)
- *p_rng = cimg::time() + (cimg_uint64)getpid();
- #elif cimg_OS==2
- *p_rng = cimg::time() + (cimg_uint64)_getpid();
- #endif
- }
- inline void srand() {
- cimg::mutex(4);
- cimg::srand(&cimg::rng());
- cimg::mutex(4,0);
- }
- inline void srand(const cimg_uint64 seed) {
- cimg::mutex(4);
- cimg::rng() = seed;
- cimg::mutex(4,0);
- }
- inline double rand(const double val_min, const double val_max, cimg_uint64 *const p_rng) {
- const double val = cimg::_rand(p_rng)/(double)~0U;
- return val_min + (val_max - val_min)*val;
- }
- inline double rand(const double val_min, const double val_max) {
- cimg::mutex(4);
- const double res = cimg::rand(val_min,val_max,&cimg::rng());
- cimg::mutex(4,0);
- return res;
- }
- inline double rand(const double val_max, cimg_uint64 *const p_rng) {
- const double val = cimg::_rand(p_rng)/(double)~0U;
- return val_max*val;
- }
- inline double rand(const double val_max=1) {
- cimg::mutex(4);
- const double res = cimg::rand(val_max,&cimg::rng());
- cimg::mutex(4,0);
- return res;
- }
- inline double grand(cimg_uint64 *const p_rng) {
- double x1, w;
- do {
- const double x2 = cimg::rand(-1,1,p_rng);
- x1 = cimg::rand(-1,1,p_rng);
- w = x1*x1 + x2*x2;
- } while (w<=0 || w>=1.);
- return x1*std::sqrt((-2*std::log(w))/w);
- }
- inline double grand() {
- cimg::mutex(4);
- const double res = cimg::grand(&cimg::rng());
- cimg::mutex(4,0);
- return res;
- }
- inline unsigned int prand(const double z, cimg_uint64 *const p_rng) {
- if (z<=1.e-10) return 0;
- if (z>100) return (unsigned int)((std::sqrt(z) * cimg::grand(p_rng)) + z);
- unsigned int k = 0;
- const double y = std::exp(-z);
- for (double s = 1.; s>=y; ++k) s*=cimg::rand(1,p_rng);
- return k - 1;
- }
- inline unsigned int prand(const double z) {
- cimg::mutex(4);
- const unsigned int res = cimg::prand(z,&cimg::rng());
- cimg::mutex(4,0);
- return res;
- }
- //! Cut (i.e. clamp) value in specified interval.
- template<typename T, typename t>
- inline T cut(const T& val, const t& val_min, const t& val_max) {
- return val<=val_min?(T)val_min:val>=val_max?(T)val_max:val;
- }
- //! Bitwise-rotate value on the left.
- template<typename T>
- inline T rol(const T& a, const unsigned int n=1) {
- return n?(T)((a<<n)|(a>>((sizeof(T)<<3) - n))):a;
- }
- inline float rol(const float a, const unsigned int n=1) {
- return (float)rol((int)a,n);
- }
- inline double rol(const double a, const unsigned int n=1) {
- return (double)rol((cimg_long)a,n);
- }
- inline double rol(const long double a, const unsigned int n=1) {
- return (double)rol((cimg_long)a,n);
- }
- #ifdef cimg_use_half
- inline half rol(const half a, const unsigned int n=1) {
- return (half)rol((int)a,n);
- }
- #endif
- //! Bitwise-rotate value on the right.
- template<typename T>
- inline T ror(const T& a, const unsigned int n=1) {
- return n?(T)((a>>n)|(a<<((sizeof(T)<<3) - n))):a;
- }
- inline float ror(const float a, const unsigned int n=1) {
- return (float)ror((int)a,n);
- }
- inline double ror(const double a, const unsigned int n=1) {
- return (double)ror((cimg_long)a,n);
- }
- inline double ror(const long double a, const unsigned int n=1) {
- return (double)ror((cimg_long)a,n);
- }
- #ifdef cimg_use_half
- inline half ror(const half a, const unsigned int n=1) {
- return (half)ror((int)a,n);
- }
- #endif
- //! Return absolute value of a value.
- template<typename T>
- inline T abs(const T& a) {
- return a>=0?a:-a;
- }
- inline bool abs(const bool a) {
- return a;
- }
- inline int abs(const unsigned char a) {
- return (int)a;
- }
- inline int abs(const unsigned short a) {
- return (int)a;
- }
- inline int abs(const unsigned int a) {
- return (int)a;
- }
- inline int abs(const int a) {
- return std::abs(a);
- }
- inline cimg_int64 abs(const cimg_uint64 a) {
- return (cimg_int64)a;
- }
- inline double abs(const double a) {
- return std::fabs(a);
- }
- inline float abs(const float a) {
- return (float)std::fabs((double)a);
- }
- //! Return hyperbolic arcosine of a value.
- inline double acosh(const double x) {
- #if cimg_use_cpp11==1 && !defined(_MSC_VER)
- return std::acosh(x);
- #else
- return std::log(x + std::sqrt(x*x - 1));
- #endif
- }
- //! Return hyperbolic arcsine of a value.
- inline double asinh(const double x) {
- #if cimg_use_cpp11==1 && !defined(_MSC_VER)
- return std::asinh(x);
- #else
- return std::log(x + std::sqrt(x*x + 1));
- #endif
- }
- //! Return hyperbolic arctangent of a value.
- inline double atanh(const double x) {
- #if cimg_use_cpp11==1 && !defined(_MSC_VER)
- return std::atanh(x);
- #else
- return 0.5*std::log((1. + x)/(1. - x));
- #endif
- }
- //! Return the sinc of a given value.
- inline double sinc(const double x) {
- return x?std::sin(x)/x:1;
- }
- //! Return base-2 logarithm of a value.
- inline double log2(const double x) {
- #if cimg_use_cpp11==1 && !defined(_MSC_VER)
- return std::log2(x);
- #else
- const double base2 = std::log(2.);
- return std::log(x)/base2;
- #endif
- }
- //! Return square of a value.
- template<typename T>
- inline T sqr(const T& val) {
- return val*val;
- }
- // Return inverse of error function.
- template<typename T>
- inline T erfinv(const T& val) {
- const T
- sgn = val<0?-1:1,
- x = (1 - val)*(1 + val),
- lnx = std::log(x),
- tt1 = (T)(2/(cimg::PI*0.147) + 0.5*lnx),
- tt2 = lnx/(T)0.147;
- return sgn*std::sqrt(-tt1 + std::sqrt(tt1*tt1 - tt2));
- }
- //! Return cubic root of a value.
- template<typename T>
- inline double cbrt(const T& x) {
- #if cimg_use_cpp11==1
- return std::cbrt(x);
- #else
- return x>=0?std::pow((double)x,1./3):-std::pow(-(double)x,1./3);
- #endif
- }
- template<typename T>
- inline T pow3(const T& val) {
- return val*val*val;
- }
- template<typename T>
- inline T pow4(const T& val) {
- return val*val*val*val;
- }
- //! Return the minimum between three values.
- template<typename t>
- inline t min(const t& a, const t& b, const t& c) {
- return std::min(std::min(a,b),c);
- }
- //! Return the minimum between four values.
- template<typename t>
- inline t min(const t& a, const t& b, const t& c, const t& d) {
- return std::min(std::min(a,b),std::min(c,d));
- }
- //! Return the minabs between two values.
- template<typename t>
- inline t minabs(const t& a, const t& b) {
- return cimg::abs(b)<cimg::abs(a)?b:a;
- }
- template<typename t>
- inline t minabs(const t& a, const t& b, const t& abs_b) {
- return abs_b<cimg::abs(a)?b:a;
- }
- //! Return the maximum between three values.
- template<typename t>
- inline t max(const t& a, const t& b, const t& c) {
- return std::max(std::max(a,b),c);
- }
- //! Return the maximum between four values.
- template<typename t>
- inline t max(const t& a, const t& b, const t& c, const t& d) {
- return std::max(std::max(a,b),std::max(c,d));
- }
- //! Return the maxabs between two values.
- template<typename t>
- inline t maxabs(const t& a, const t& b) {
- return cimg::abs(b)>cimg::abs(a)?b:a;
- }
- template<typename t>
- inline t maxabs(const t& a, const t& b, const t& abs_b) {
- return abs_b>cimg::abs(a)?b:a;
- }
- //! Return the sign of a value.
- template<typename T>
- inline T sign(const T& x) {
- return (T)(cimg::type<T>::is_nan(x)?0:x<0?-1:x>0);
- }
- //! Return the nearest power of 2 higher than given value.
- template<typename T>
- inline cimg_uint64 nearest_pow2(const T& x) {
- cimg_uint64 i = 1;
- while (x>i) i<<=1;
- return i;
- }
- //! Return the modulo of a value.
- /**
- \param x Input value.
- \param m Modulo value.
- \note This modulo function accepts negative and floating-points modulo numbers, as well as variables of any type.
- **/
- template<typename T>
- inline T mod(const T& x, const T& m) {
- if (!m) {
- if (cimg::type<T>::is_float()) return cimg::type<T>::nan();
- else throw CImgArgumentException("cimg::mod(): Specified modulo value is 0.");
- }
- const double dx = (double)x, dm = (double)m;
- if (!cimg::type<double>::is_finite(dm)) return x;
- if (cimg::type<double>::is_finite(dx)) return (T)(dx - dm * std::floor(dx / dm));
- return (T)0;
- }
- inline int mod(const bool x, const bool m) {
- if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0.");
- return x?1:0;
- }
- inline int mod(const unsigned char x, const unsigned char m) {
- if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0.");
- return x%m;
- }
- inline int mod(const char x, const char m) {
- if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0.");
- #if defined(CHAR_MAX) && CHAR_MAX==255
- return x%m;
- #else
- return x>=0?x%m:(x%m?m + x%m:0);
- #endif
- }
- inline int mod(const unsigned short x, const unsigned short m) {
- if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0.");
- return (int)(x%m);
- }
- inline int mod(const short x, const short m) {
- if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0.");
- return (int)(x>=0?x%m:(x%m?m + x%m:0));
- }
- inline int mod(const unsigned int x, const unsigned int m) {
- if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0.");
- return (int)(x%m);
- }
- inline int mod(const int x, const int m) {
- if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0.");
- return (int)(x>=0?x%m:(x%m?m + x%m:0));
- }
- inline cimg_int64 mod(const cimg_uint64 x, const cimg_uint64 m) {
- if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0.");
- return (cimg_int64)(x%m);
- }
- inline cimg_int64 mod(const cimg_int64 x, const cimg_int64 m) {
- if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0.");
- return (cimg_int64)(x>=0?x%m:(x%m?m + x%m:0));
- }
- //! Return the min-mod of two values.
- /**
- \note <i>minmod(\p a,\p b)</i> is defined to be:
- - <i>minmod(\p a,\p b) = min(\p a,\p b)</i>, if \p a and \p b have the same sign.
- - <i>minmod(\p a,\p b) = 0</i>, if \p a and \p b have different signs.
- **/
- template<typename T>
- inline T minmod(const T& a, const T& b) {
- return a*b<=0?0:(a>0?(a<b?a:b):(a<b?b:a));
- }
- template<typename T>
- inline T round(const T& x) {
- return (T)std::floor((_cimg_Tfloat)x + 0.5f);
- }
- template<typename T>
- inline int uiround(const T x) {
- return cimg::type<T>::is_float()?(int)(x + 0.5f):(int)x;
- }
- //! Return rounded value.
- /**
- \param x Value to be rounded.
- \param y Rounding precision.
- \param rounding_type Type of rounding operation (\c 0 = nearest, \c -1 = backward, \c 1 = forward).
- \return Rounded value, having the same type as input value \c x.
- **/
- template<typename T>
- inline T round(const T& x, const double y, const int rounding_type=0) {
- if (y<=0) return x;
- if (y==1) switch (rounding_type) {
- case 0 : return cimg::round(x);
- case 1 : return (T)std::ceil((_cimg_Tfloat)x);
- default : return (T)std::floor((_cimg_Tfloat)x);
- }
- const double sx = (double)x/y, floor = std::floor(sx), delta = sx - floor;
- return (T)(y*(rounding_type<0?floor:rounding_type>0?std::ceil(sx):delta<0.5?floor:std::ceil(sx)));
- }
- // Code to compute fast median from 2,3,5,7,9,13,25 and 49 values.
- // (contribution by RawTherapee: http://rawtherapee.com/).
- template<typename T>
- inline T median(T val0, T val1) {
- return (val0 + val1)/2;
- }
- template<typename T>
- inline T median(T val0, T val1, T val2) {
- return std::max(std::min(val0,val1),std::min(val2,std::max(val0,val1)));
- }
- template<typename T>
- inline T median(T val0, T val1, T val2, T val3, T val4) {
- T tmp = std::min(val0,val1);
- val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4);
- val3 = std::max(val0,tmp); val1 = std::min(val1,val4); tmp = std::min(val1,val2); val2 = std::max(val1,val2);
- val1 = tmp; tmp = std::min(val2,val3);
- return std::max(val1,tmp);
- }
- template<typename T>
- inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6) {
- T tmp = std::min(val0,val5);
- val5 = std::max(val0,val5); val0 = tmp; tmp = std::min(val0,val3); val3 = std::max(val0,val3); val0 = tmp;
- tmp = std::min(val1,val6); val6 = std::max(val1,val6); val1 = tmp; tmp = std::min(val2,val4);
- val4 = std::max(val2,val4); val2 = tmp; val1 = std::max(val0,val1); tmp = std::min(val3,val5);
- val5 = std::max(val3,val5); val3 = tmp; tmp = std::min(val2,val6); val6 = std::max(val2,val6);
- val3 = std::max(tmp,val3); val3 = std::min(val3,val6); tmp = std::min(val4,val5); val4 = std::max(val1,tmp);
- tmp = std::min(val1,tmp); val3 = std::max(tmp,val3);
- return std::min(val3,val4);
- }
- template<typename T>
- inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6, T val7, T val8) {
- T tmp = std::min(val1,val2);
- val2 = std::max(val1,val2); val1 = tmp; tmp = std::min(val4,val5);
- val5 = std::max(val4,val5); val4 = tmp; tmp = std::min(val7,val8);
- val8 = std::max(val7,val8); val7 = tmp; tmp = std::min(val0,val1);
- val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4);
- val4 = std::max(val3,val4); val3 = tmp; tmp = std::min(val6,val7);
- val7 = std::max(val6,val7); val6 = tmp; tmp = std::min(val1,val2);
- val2 = std::max(val1,val2); val1 = tmp; tmp = std::min(val4,val5);
- val5 = std::max(val4,val5); val4 = tmp; tmp = std::min(val7,val8);
- val8 = std::max(val7,val8); val3 = std::max(val0,val3); val5 = std::min(val5,val8);
- val7 = std::max(val4,tmp); tmp = std::min(val4,tmp); val6 = std::max(val3,val6);
- val4 = std::max(val1,tmp); val2 = std::min(val2,val5); val4 = std::min(val4,val7);
- tmp = std::min(val4,val2); val2 = std::max(val4,val2); val4 = std::max(val6,tmp);
- return std::min(val4,val2);
- }
- template<typename T>
- inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6, T val7, T val8, T val9, T val10, T val11,
- T val12) {
- T tmp = std::min(val1,val7);
- val7 = std::max(val1,val7); val1 = tmp; tmp = std::min(val9,val11); val11 = std::max(val9,val11); val9 = tmp;
- tmp = std::min(val3,val4); val4 = std::max(val3,val4); val3 = tmp; tmp = std::min(val5,val8);
- val8 = std::max(val5,val8); val5 = tmp; tmp = std::min(val0,val12); val12 = std::max(val0,val12);
- val0 = tmp; tmp = std::min(val2,val6); val6 = std::max(val2,val6); val2 = tmp; tmp = std::min(val0,val1);
- val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val2,val3); val3 = std::max(val2,val3); val2 = tmp;
- tmp = std::min(val4,val6); val6 = std::max(val4,val6); val4 = tmp; tmp = std::min(val8,val11);
- val11 = std::max(val8,val11); val8 = tmp; tmp = std::min(val7,val12); val12 = std::max(val7,val12); val7 = tmp;
- tmp = std::min(val5,val9); val9 = std::max(val5,val9); val5 = tmp; tmp = std::min(val0,val2);
- val2 = std::max(val0,val2); val0 = tmp; tmp = std::min(val3,val7); val7 = std::max(val3,val7); val3 = tmp;
- tmp = std::min(val10,val11); val11 = std::max(val10,val11); val10 = tmp; tmp = std::min(val1,val4);
- val4 = std::max(val1,val4); val1 = tmp; tmp = std::min(val6,val12); val12 = std::max(val6,val12); val6 = tmp;
- tmp = std::min(val7,val8); val8 = std::max(val7,val8); val7 = tmp; val11 = std::min(val11,val12);
- tmp = std::min(val4,val9); val9 = std::max(val4,val9); val4 = tmp; tmp = std::min(val6,val10);
- val10 = std::max(val6,val10); val6 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4); val3 = tmp;
- tmp = std::min(val5,val6); val6 = std::max(val5,val6); val5 = tmp; val8 = std::min(val8,val9);
- val10 = std::min(val10,val11); tmp = std::min(val1,val7); val7 = std::max(val1,val7); val1 = tmp;
- tmp = std::min(val2,val6); val6 = std::max(val2,val6); val2 = tmp; val3 = std::max(val1,val3);
- tmp = std::min(val4,val7); val7 = std::max(val4,val7); val4 = tmp; val8 = std::min(val8,val10);
- val5 = std::max(val0,val5); val5 = std::max(val2,val5); tmp = std::min(val6,val8); val8 = std::max(val6,val8);
- val5 = std::max(val3,val5); val7 = std::min(val7,val8); val6 = std::max(val4,tmp); tmp = std::min(val4,tmp);
- val5 = std::max(tmp,val5); val6 = std::min(val6,val7);
- return std::max(val5,val6);
- }
- template<typename T>
- inline T median(T val0, T val1, T val2, T val3, T val4,
- T val5, T val6, T val7, T val8, T val9,
- T val10, T val11, T val12, T val13, T val14,
- T val15, T val16, T val17, T val18, T val19,
- T val20, T val21, T val22, T val23, T val24) {
- T tmp = std::min(val0,val1);
- val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4);
- val3 = tmp; tmp = std::min(val2,val4); val4 = std::max(val2,val4); val2 = std::min(tmp,val3);
- val3 = std::max(tmp,val3); tmp = std::min(val6,val7); val7 = std::max(val6,val7); val6 = tmp;
- tmp = std::min(val5,val7); val7 = std::max(val5,val7); val5 = std::min(tmp,val6); val6 = std::max(tmp,val6);
- tmp = std::min(val9,val10); val10 = std::max(val9,val10); val9 = tmp; tmp = std::min(val8,val10);
- val10 = std::max(val8,val10); val8 = std::min(tmp,val9); val9 = std::max(tmp,val9);
- tmp = std::min(val12,val13); val13 = std::max(val12,val13); val12 = tmp; tmp = std::min(val11,val13);
- val13 = std::max(val11,val13); val11 = std::min(tmp,val12); val12 = std::max(tmp,val12);
- tmp = std::min(val15,val16); val16 = std::max(val15,val16); val15 = tmp; tmp = std::min(val14,val16);
- val16 = std::max(val14,val16); val14 = std::min(tmp,val15); val15 = std::max(tmp,val15);
- tmp = std::min(val18,val19); val19 = std::max(val18,val19); val18 = tmp; tmp = std::min(val17,val19);
- val19 = std::max(val17,val19); val17 = std::min(tmp,val18); val18 = std::max(tmp,val18);
- tmp = std::min(val21,val22); val22 = std::max(val21,val22); val21 = tmp; tmp = std::min(val20,val22);
- val22 = std::max(val20,val22); val20 = std::min(tmp,val21); val21 = std::max(tmp,val21);
- tmp = std::min(val23,val24); val24 = std::max(val23,val24); val23 = tmp; tmp = std::min(val2,val5);
- val5 = std::max(val2,val5); val2 = tmp; tmp = std::min(val3,val6); val6 = std::max(val3,val6); val3 = tmp;
- tmp = std::min(val0,val6); val6 = std::max(val0,val6); val0 = std::min(tmp,val3); val3 = std::max(tmp,val3);
- tmp = std::min(val4,val7); val7 = std::max(val4,val7); val4 = tmp; tmp = std::min(val1,val7);
- val7 = std::max(val1,val7); val1 = std::min(tmp,val4); val4 = std::max(tmp,val4); tmp = std::min(val11,val14);
- val14 = std::max(val11,val14); val11 = tmp; tmp = std::min(val8,val14); val14 = std::max(val8,val14);
- val8 = std::min(tmp,val11); val11 = std::max(tmp,val11); tmp = std::min(val12,val15);
- val15 = std::max(val12,val15); val12 = tmp; tmp = std::min(val9,val15); val15 = std::max(val9,val15);
- val9 = std::min(tmp,val12); val12 = std::max(tmp,val12); tmp = std::min(val13,val16);
- val16 = std::max(val13,val16); val13 = tmp; tmp = std::min(val10,val16); val16 = std::max(val10,val16);
- val10 = std::min(tmp,val13); val13 = std::max(tmp,val13); tmp = std::min(val20,val23);
- val23 = std::max(val20,val23); val20 = tmp; tmp = std::min(val17,val23); val23 = std::max(val17,val23);
- val17 = std::min(tmp,val20); val20 = std::max(tmp,val20); tmp = std::min(val21,val24);
- val24 = std::max(val21,val24); val21 = tmp; tmp = std::min(val18,val24); val24 = std::max(val18,val24);
- val18 = std::min(tmp,val21); val21 = std::max(tmp,val21); tmp = std::min(val19,val22);
- val22 = std::max(val19,val22); val19 = tmp; val17 = std::max(val8,val17); tmp = std::min(val9,val18);
- val18 = std::max(val9,val18); val9 = tmp; tmp = std::min(val0,val18); val18 = std::max(val0,val18);
- val9 = std::max(tmp,val9); tmp = std::min(val10,val19); val19 = std::max(val10,val19); val10 = tmp;
- tmp = std::min(val1,val19); val19 = std::max(val1,val19); val1 = std::min(tmp,val10);
- val10 = std::max(tmp,val10); tmp = std::min(val11,val20); val20 = std::max(val11,val20); val11 = tmp;
- tmp = std::min(val2,val20); val20 = std::max(val2,val20); val11 = std::max(tmp,val11);
- tmp = std::min(val12,val21); val21 = std::max(val12,val21); val12 = tmp; tmp = std::min(val3,val21);
- val21 = std::max(val3,val21); val3 = std::min(tmp,val12); val12 = std::max(tmp,val12);
- tmp = std::min(val13,val22); val22 = std::max(val13,val22); val4 = std::min(val4,val22);
- val13 = std::max(val4,tmp); tmp = std::min(val4,tmp); val4 = tmp; tmp = std::min(val14,val23);
- val23 = std::max(val14,val23); val14 = tmp; tmp = std::min(val5,val23); val23 = std::max(val5,val23);
- val5 = std::min(tmp,val14); val14 = std::max(tmp,val14); tmp = std::min(val15,val24);
- val24 = std::max(val15,val24); val15 = tmp; val6 = std::min(val6,val24); tmp = std::min(val6,val15);
- val15 = std::max(val6,val15); val6 = tmp; tmp = std::min(val7,val16); val7 = std::min(tmp,val19);
- tmp = std::min(val13,val21); val15 = std::min(val15,val23); tmp = std::min(val7,tmp);
- val7 = std::min(tmp,val15); val9 = std::max(val1,val9); val11 = std::max(val3,val11);
- val17 = std::max(val5,val17); val17 = std::max(val11,val17); val17 = std::max(val9,val17);
- tmp = std::min(val4,val10); val10 = std::max(val4,val10); val4 = tmp; tmp = std::min(val6,val12);
- val12 = std::max(val6,val12); val6 = tmp; tmp = std::min(val7,val14); val14 = std::max(val7,val14);
- val7 = tmp; tmp = std::min(val4,val6); val6 = std::max(val4,val6); val7 = std::max(tmp,val7);
- tmp = std::min(val12,val14); val14 = std::max(val12,val14); val12 = tmp; val10 = std::min(val10,val14);
- tmp = std::min(val6,val7); val7 = std::max(val6,val7); val6 = tmp; tmp = std::min(val10,val12);
- val12 = std::max(val10,val12); val10 = std::max(val6,tmp); tmp = std::min(val6,tmp);
- val17 = std::max(tmp,val17); tmp = std::min(val12,val17); val17 = std::max(val12,val17); val12 = tmp;
- val7 = std::min(val7,val17); tmp = std::min(val7,val10); val10 = std::max(val7,val10); val7 = tmp;
- tmp = std::min(val12,val18); val18 = std::max(val12,val18); val12 = std::max(val7,tmp);
- val10 = std::min(val10,val18); tmp = std::min(val12,val20); val20 = std::max(val12,val20); val12 = tmp;
- tmp = std::min(val10,val20);
- return std::max(tmp,val12);
- }
- template<typename T>
- inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6,
- T val7, T val8, T val9, T val10, T val11, T val12, T val13,
- T val14, T val15, T val16, T val17, T val18, T val19, T val20,
- T val21, T val22, T val23, T val24, T val25, T val26, T val27,
- T val28, T val29, T val30, T val31, T val32, T val33, T val34,
- T val35, T val36, T val37, T val38, T val39, T val40, T val41,
- T val42, T val43, T val44, T val45, T val46, T val47, T val48) {
- T tmp = std::min(val0,val32);
- val32 = std::max(val0,val32); val0 = tmp; tmp = std::min(val1,val33); val33 = std::max(val1,val33); val1 = tmp;
- tmp = std::min(val2,val34); val34 = std::max(val2,val34); val2 = tmp; tmp = std::min(val3,val35);
- val35 = std::max(val3,val35); val3 = tmp; tmp = std::min(val4,val36); val36 = std::max(val4,val36); val4 = tmp;
- tmp = std::min(val5,val37); val37 = std::max(val5,val37); val5 = tmp; tmp = std::min(val6,val38);
- val38 = std::max(val6,val38); val6 = tmp; tmp = std::min(val7,val39); val39 = std::max(val7,val39); val7 = tmp;
- tmp = std::min(val8,val40); val40 = std::max(val8,val40); val8 = tmp; tmp = std::min(val9,val41);
- val41 = std::max(val9,val41); val9 = tmp; tmp = std::min(val10,val42); val42 = std::max(val10,val42);
- val10 = tmp; tmp = std::min(val11,val43); val43 = std::max(val11,val43); val11 = tmp;
- tmp = std::min(val12,val44); val44 = std::max(val12,val44); val12 = tmp; tmp = std::min(val13,val45);
- val45 = std::max(val13,val45); val13 = tmp; tmp = std::min(val14,val46); val46 = std::max(val14,val46);
- val14 = tmp; tmp = std::min(val15,val47); val47 = std::max(val15,val47); val15 = tmp;
- tmp = std::min(val16,val48); val48 = std::max(val16,val48); val16 = tmp; tmp = std::min(val0,val16);
- val16 = std::max(val0,val16); val0 = tmp; tmp = std::min(val1,val17); val17 = std::max(val1,val17);
- val1 = tmp; tmp = std::min(val2,val18); val18 = std::max(val2,val18); val2 = tmp; tmp = std::min(val3,val19);
- val19 = std::max(val3,val19); val3 = tmp; tmp = std::min(val4,val20); val20 = std::max(val4,val20); val4 = tmp;
- tmp = std::min(val5,val21); val21 = std::max(val5,val21); val5 = tmp; tmp = std::min(val6,val22);
- val22 = std::max(val6,val22); val6 = tmp; tmp = std::min(val7,val23); val23 = std::max(val7,val23); val7 = tmp;
- tmp = std::min(val8,val24); val24 = std::max(val8,val24); val8 = tmp; tmp = std::min(val9,val25);
- val25 = std::max(val9,val25); val9 = tmp; tmp = std::min(val10,val26); val26 = std::max(val10,val26);
- val10 = tmp; tmp = std::min(val11,val27); val27 = std::max(val11,val27); val11 = tmp;
- tmp = std::min(val12,val28); val28 = std::max(val12,val28); val12 = tmp; tmp = std::min(val13,val29);
- val29 = std::max(val13,val29); val13 = tmp; tmp = std::min(val14,val30); val30 = std::max(val14,val30);
- val14 = tmp; tmp = std::min(val15,val31); val31 = std::max(val15,val31); val15 = tmp;
- tmp = std::min(val32,val48); val48 = std::max(val32,val48); val32 = tmp; tmp = std::min(val16,val32);
- val32 = std::max(val16,val32); val16 = tmp; tmp = std::min(val17,val33); val33 = std::max(val17,val33);
- val17 = tmp; tmp = std::min(val18,val34); val34 = std::max(val18,val34); val18 = tmp;
- tmp = std::min(val19,val35); val35 = std::max(val19,val35); val19 = tmp; tmp = std::min(val20,val36);
- val36 = std::max(val20,val36); val20 = tmp; tmp = std::min(val21,val37); val37 = std::max(val21,val37);
- val21 = tmp; tmp = std::min(val22,val38); val38 = std::max(val22,val38); val22 = tmp;
- tmp = std::min(val23,val39); val39 = std::max(val23,val39); val23 = tmp; tmp = std::min(val24,val40);
- val40 = std::max(val24,val40); val24 = tmp; tmp = std::min(val25,val41); val41 = std::max(val25,val41);
- val25 = tmp; tmp = std::min(val26,val42); val42 = std::max(val26,val42); val26 = tmp;
- tmp = std::min(val27,val43); val43 = std::max(val27,val43); val27 = tmp; tmp = std::min(val28,val44);
- val44 = std::max(val28,val44); val28 = tmp; tmp = std::min(val29,val45); val45 = std::max(val29,val45);
- val29 = tmp; tmp = std::min(val30,val46); val46 = std::max(val30,val46); val30 = tmp;
- tmp = std::min(val31,val47); val47 = std::max(val31,val47); val31 = tmp; tmp = std::min(val0,val8);
- val8 = std::max(val0,val8); val0 = tmp; tmp = std::min(val1,val9); val9 = std::max(val1,val9); val1 = tmp;
- tmp = std::min(val2,val10); val10 = std::max(val2,val10); val2 = tmp; tmp = std::min(val3,val11);
- val11 = std::max(val3,val11); val3 = tmp; tmp = std::min(val4,val12); val12 = std::max(val4,val12); val4 = tmp;
- tmp = std::min(val5,val13); val13 = std::max(val5,val13); val5 = tmp; tmp = std::min(val6,val14);
- val14 = std::max(val6,val14); val6 = tmp; tmp = std::min(val7,val15); val15 = std::max(val7,val15); val7 = tmp;
- tmp = std::min(val16,val24); val24 = std::max(val16,val24); val16 = tmp; tmp = std::min(val17,val25);
- val25 = std::max(val17,val25); val17 = tmp; tmp = std::min(val18,val26); val26 = std::max(val18,val26);
- val18 = tmp; tmp = std::min(val19,val27); val27 = std::max(val19,val27); val19 = tmp;
- tmp = std::min(val20,val28); val28 = std::max(val20,val28); val20 = tmp; tmp = std::min(val21,val29);
- val29 = std::max(val21,val29); val21 = tmp; tmp = std::min(val22,val30); val30 = std::max(val22,val30);
- val22 = tmp; tmp = std::min(val23,val31); val31 = std::max(val23,val31); val23 = tmp;
- tmp = std::min(val32,val40); val40 = std::max(val32,val40); val32 = tmp; tmp = std::min(val33,val41);
- val41 = std::max(val33,val41); val33 = tmp; tmp = std::min(val34,val42); val42 = std::max(val34,val42);
- val34 = tmp; tmp = std::min(val35,val43); val43 = std::max(val35,val43); val35 = tmp;
- tmp = std::min(val36,val44); val44 = std::max(val36,val44); val36 = tmp; tmp = std::min(val37,val45);
- val45 = std::max(val37,val45); val37 = tmp; tmp = std::min(val38,val46); val46 = std::max(val38,val46);
- val38 = tmp; tmp = std::min(val39,val47); val47 = std::max(val39,val47); val39 = tmp;
- tmp = std::min(val8,val32); val32 = std::max(val8,val32); val8 = tmp; tmp = std::min(val9,val33);
- val33 = std::max(val9,val33); val9 = tmp; tmp = std::min(val10,val34); val34 = std::max(val10,val34);
- val10 = tmp; tmp = std::min(val11,val35); val35 = std::max(val11,val35); val11 = tmp;
- tmp = std::min(val12,val36); val36 = std::max(val12,val36); val12 = tmp; tmp = std::min(val13,val37);
- val37 = std::max(val13,val37); val13 = tmp; tmp = std::min(val14,val38); val38 = std::max(val14,val38);
- val14 = tmp; tmp = std::min(val15,val39); val39 = std::max(val15,val39); val15 = tmp;
- tmp = std::min(val24,val48); val48 = std::max(val24,val48); val24 = tmp; tmp = std::min(val8,val16);
- val16 = std::max(val8,val16); val8 = tmp; tmp = std::min(val9,val17); val17 = std::max(val9,val17);
- val9 = tmp; tmp = std::min(val10,val18); val18 = std::max(val10,val18); val10 = tmp;
- tmp = std::min(val11,val19); val19 = std::max(val11,val19); val11 = tmp; tmp = std::min(val12,val20);
- val20 = std::max(val12,val20); val12 = tmp; tmp = std::min(val13,val21); val21 = std::max(val13,val21);
- val13 = tmp; tmp = std::min(val14,val22); val22 = std::max(val14,val22); val14 = tmp;
- tmp = std::min(val15,val23); val23 = std::max(val15,val23); val15 = tmp; tmp = std::min(val24,val32);
- val32 = std::max(val24,val32); val24 = tmp; tmp = std::min(val25,val33); val33 = std::max(val25,val33);
- val25 = tmp; tmp = std::min(val26,val34); val34 = std::max(val26,val34); val26 = tmp;
- tmp = std::min(val27,val35); val35 = std::max(val27,val35); val27 = tmp; tmp = std::min(val28,val36);
- val36 = std::max(val28,val36); val28 = tmp; tmp = std::min(val29,val37); val37 = std::max(val29,val37);
- val29 = tmp; tmp = std::min(val30,val38); val38 = std::max(val30,val38); val30 = tmp;
- tmp = std::min(val31,val39); val39 = std::max(val31,val39); val31 = tmp; tmp = std::min(val40,val48);
- val48 = std::max(val40,val48); val40 = tmp; tmp = std::min(val0,val4); val4 = std::max(val0,val4);
- val0 = tmp; tmp = std::min(val1,val5); val5 = std::max(val1,val5); val1 = tmp; tmp = std::min(val2,val6);
- val6 = std::max(val2,val6); val2 = tmp; tmp = std::min(val3,val7); val7 = std::max(val3,val7); val3 = tmp;
- tmp = std::min(val8,val12); val12 = std::max(val8,val12); val8 = tmp; tmp = std::min(val9,val13);
- val13 = std::max(val9,val13); val9 = tmp; tmp = std::min(val10,val14); val14 = std::max(val10,val14);
- val10 = tmp; tmp = std::min(val11,val15); val15 = std::max(val11,val15); val11 = tmp;
- tmp = std::min(val16,val20); val20 = std::max(val16,val20); val16 = tmp; tmp = std::min(val17,val21);
- val21 = std::max(val17,val21); val17 = tmp; tmp = std::min(val18,val22); val22 = std::max(val18,val22);
- val18 = tmp; tmp = std::min(val19,val23); val23 = std::max(val19,val23); val19 = tmp;
- tmp = std::min(val24,val28); val28 = std::max(val24,val28); val24 = tmp; tmp = std::min(val25,val29);
- val29 = std::max(val25,val29); val25 = tmp; tmp = std::min(val26,val30); val30 = std::max(val26,val30);
- val26 = tmp; tmp = std::min(val27,val31); val31 = std::max(val27,val31); val27 = tmp;
- tmp = std::min(val32,val36); val36 = std::max(val32,val36); val32 = tmp; tmp = std::min(val33,val37);
- val37 = std::max(val33,val37); val33 = tmp; tmp = std::min(val34,val38); val38 = std::max(val34,val38);
- val34 = tmp; tmp = std::min(val35,val39); val39 = std::max(val35,val39); val35 = tmp;
- tmp = std::min(val40,val44); val44 = std::max(val40,val44); val40 = tmp; tmp = std::min(val41,val45);
- val45 = std::max(val41,val45); val41 = tmp; tmp = std::min(val42,val46); val46 = std::max(val42,val46);
- val42 = tmp; tmp = std::min(val43,val47); val47 = std::max(val43,val47); val43 = tmp;
- tmp = std::min(val4,val32); val32 = std::max(val4,val32); val4 = tmp; tmp = std::min(val5,val33);
- val33 = std::max(val5,val33); val5 = tmp; tmp = std::min(val6,val34); val34 = std::max(val6,val34);
- val6 = tmp; tmp = std::min(val7,val35); val35 = std::max(val7,val35); val7 = tmp;
- tmp = std::min(val12,val40); val40 = std::max(val12,val40); val12 = tmp; tmp = std::min(val13,val41);
- val41 = std::max(val13,val41); val13 = tmp; tmp = std::min(val14,val42); val42 = std::max(val14,val42);
- val14 = tmp; tmp = std::min(val15,val43); val43 = std::max(val15,val43); val15 = tmp;
- tmp = std::min(val20,val48); val48 = std::max(val20,val48); val20 = tmp; tmp = std::min(val4,val16);
- val16 = std::max(val4,val16); val4 = tmp; tmp = std::min(val5,val17); val17 = std::max(val5,val17);
- val5 = tmp; tmp = std::min(val6,val18); val18 = std::max(val6,val18); val6 = tmp;
- tmp = std::min(val7,val19); val19 = std::max(val7,val19); val7 = tmp; tmp = std::min(val12,val24);
- val24 = std::max(val12,val24); val12 = tmp; tmp = std::min(val13,val25); val25 = std::max(val13,val25);
- val13 = tmp; tmp = std::min(val14,val26); val26 = std::max(val14,val26); val14 = tmp;
- tmp = std::min(val15,val27); val27 = std::max(val15,val27); val15 = tmp; tmp = std::min(val20,val32);
- val32 = std::max(val20,val32); val20 = tmp; tmp = std::min(val21,val33); val33 = std::max(val21,val33);
- val21 = tmp; tmp = std::min(val22,val34); val34 = std::max(val22,val34); val22 = tmp;
- tmp = std::min(val23,val35); val35 = std::max(val23,val35); val23 = tmp; tmp = std::min(val28,val40);
- val40 = std::max(val28,val40); val28 = tmp; tmp = std::min(val29,val41); val41 = std::max(val29,val41);
- val29 = tmp; tmp = std::min(val30,val42); val42 = std::max(val30,val42); val30 = tmp;
- tmp = std::min(val31,val43); val43 = std::max(val31,val43); val31 = tmp; tmp = std::min(val36,val48);
- val48 = std::max(val36,val48); val36 = tmp; tmp = std::min(val4,val8); val8 = std::max(val4,val8);
- val4 = tmp; tmp = std::min(val5,val9); val9 = std::max(val5,val9); val5 = tmp; tmp = std::min(val6,val10);
- val10 = std::max(val6,val10); val6 = tmp; tmp = std::min(val7,val11); val11 = std::max(val7,val11); val7 = tmp;
- tmp = std::min(val12,val16); val16 = std::max(val12,val16); val12 = tmp; tmp = std::min(val13,val17);
- val17 = std::max(val13,val17); val13 = tmp; tmp = std::min(val14,val18); val18 = std::max(val14,val18);
- val14 = tmp; tmp = std::min(val15,val19); val19 = std::max(val15,val19); val15 = tmp;
- tmp = std::min(val20,val24); val24 = std::max(val20,val24); val20 = tmp; tmp = std::min(val21,val25);
- val25 = std::max(val21,val25); val21 = tmp; tmp = std::min(val22,val26); val26 = std::max(val22,val26);
- val22 = tmp; tmp = std::min(val23,val27); val27 = std::max(val23,val27); val23 = tmp;
- tmp = std::min(val28,val32); val32 = std::max(val28,val32); val28 = tmp; tmp = std::min(val29,val33);
- val33 = std::max(val29,val33); val29 = tmp; tmp = std::min(val30,val34); val34 = std::max(val30,val34);
- val30 = tmp; tmp = std::min(val31,val35); val35 = std::max(val31,val35); val31 = tmp;
- tmp = std::min(val36,val40); val40 = std::max(val36,val40); val36 = tmp; tmp = std::min(val37,val41);
- val41 = std::max(val37,val41); val37 = tmp; tmp = std::min(val38,val42); val42 = std::max(val38,val42);
- val38 = tmp; tmp = std::min(val39,val43); val43 = std::max(val39,val43); val39 = tmp;
- tmp = std::min(val44,val48); val48 = std::max(val44,val48); val44 = tmp; tmp = std::min(val0,val2);
- val2 = std::max(val0,val2); val0 = tmp; tmp = std::min(val1,val3); val3 = std::max(val1,val3); val1 = tmp;
- tmp = std::min(val4,val6); val6 = std::max(val4,val6); val4 = tmp; tmp = std::min(val5,val7);
- val7 = std::max(val5,val7); val5 = tmp; tmp = std::min(val8,val10); val10 = std::max(val8,val10); val8 = tmp;
- tmp = std::min(val9,val11); val11 = std::max(val9,val11); val9 = tmp; tmp = std::min(val12,val14);
- val14 = std::max(val12,val14); val12 = tmp; tmp = std::min(val13,val15); val15 = std::max(val13,val15);
- val13 = tmp; tmp = std::min(val16,val18); val18 = std::max(val16,val18); val16 = tmp;
- tmp = std::min(val17,val19); val19 = std::max(val17,val19); val17 = tmp; tmp = std::min(val20,val22);
- val22 = std::max(val20,val22); val20 = tmp; tmp = std::min(val21,val23); val23 = std::max(val21,val23);
- val21 = tmp; tmp = std::min(val24,val26); val26 = std::max(val24,val26); val24 = tmp;
- tmp = std::min(val25,val27); val27 = std::max(val25,val27); val25 = tmp; tmp = std::min(val28,val30);
- val30 = std::max(val28,val30); val28 = tmp; tmp = std::min(val29,val31); val31 = std::max(val29,val31);
- val29 = tmp; tmp = std::min(val32,val34); val34 = std::max(val32,val34); val32 = tmp;
- tmp = std::min(val33,val35); val35 = std::max(val33,val35); val33 = tmp; tmp = std::min(val36,val38);
- val38 = std::max(val36,val38); val36 = tmp; tmp = std::min(val37,val39); val39 = std::max(val37,val39);
- val37 = tmp; tmp = std::min(val40,val42); val42 = std::max(val40,val42); val40 = tmp;
- tmp = std::min(val41,val43); val43 = std::max(val41,val43); val41 = tmp; tmp = std::min(val44,val46);
- val46 = std::max(val44,val46); val44 = tmp; tmp = std::min(val45,val47); val47 = std::max(val45,val47);
- val45 = tmp; tmp = std::min(val2,val32); val32 = std::max(val2,val32); val2 = tmp; tmp = std::min(val3,val33);
- val33 = std::max(val3,val33); val3 = tmp; tmp = std::min(val6,val36); val36 = std::max(val6,val36); val6 = tmp;
- tmp = std::min(val7,val37); val37 = std::max(val7,val37); val7 = tmp; tmp = std::min(val10,val40);
- val40 = std::max(val10,val40); val10 = tmp; tmp = std::min(val11,val41); val41 = std::max(val11,val41);
- val11 = tmp; tmp = std::min(val14,val44); val44 = std::max(val14,val44); val14 = tmp;
- tmp = std::min(val15,val45); val45 = std::max(val15,val45); val15 = tmp; tmp = std::min(val18,val48);
- val48 = std::max(val18,val48); val18 = tmp; tmp = std::min(val2,val16); val16 = std::max(val2,val16);
- val2 = tmp; tmp = std::min(val3,val17); val17 = std::max(val3,val17); val3 = tmp;
- tmp = std::min(val6,val20); val20 = std::max(val6,val20); val6 = tmp; tmp = std::min(val7,val21);
- val21 = std::max(val7,val21); val7 = tmp; tmp = std::min(val10,val24); val24 = std::max(val10,val24);
- val10 = tmp; tmp = std::min(val11,val25); val25 = std::max(val11,val25); val11 = tmp;
- tmp = std::min(val14,val28); val28 = std::max(val14,val28); val14 = tmp; tmp = std::min(val15,val29);
- val29 = std::max(val15,val29); val15 = tmp; tmp = std::min(val18,val32); val32 = std::max(val18,val32);
- val18 = tmp; tmp = std::min(val19,val33); val33 = std::max(val19,val33); val19 = tmp;
- tmp = std::min(val22,val36); val36 = std::max(val22,val36); val22 = tmp; tmp = std::min(val23,val37);
- val37 = std::max(val23,val37); val23 = tmp; tmp = std::min(val26,val40); val40 = std::max(val26,val40);
- val26 = tmp; tmp = std::min(val27,val41); val41 = std::max(val27,val41); val27 = tmp;
- tmp = std::min(val30,val44); val44 = std::max(val30,val44); val30 = tmp; tmp = std::min(val31,val45);
- val45 = std::max(val31,val45); val31 = tmp; tmp = std::min(val34,val48); val48 = std::max(val34,val48);
- val34 = tmp; tmp = std::min(val2,val8); val8 = std::max(val2,val8); val2 = tmp; tmp = std::min(val3,val9);
- val9 = std::max(val3,val9); val3 = tmp; tmp = std::min(val6,val12); val12 = std::max(val6,val12); val6 = tmp;
- tmp = std::min(val7,val13); val13 = std::max(val7,val13); val7 = tmp; tmp = std::min(val10,val16);
- val16 = std::max(val10,val16); val10 = tmp; tmp = std::min(val11,val17); val17 = std::max(val11,val17);
- val11 = tmp; tmp = std::min(val14,val20); val20 = std::max(val14,val20); val14 = tmp;
- tmp = std::min(val15,val21); val21 = std::max(val15,val21); val15 = tmp; tmp = std::min(val18,val24);
- val24 = std::max(val18,val24); val18 = tmp; tmp = std::min(val19,val25); val25 = std::max(val19,val25);
- val19 = tmp; tmp = std::min(val22,val28); val28 = std::max(val22,val28); val22 = tmp;
- tmp = std::min(val23,val29); val29 = std::max(val23,val29); val23 = tmp; tmp = std::min(val26,val32);
- val32 = std::max(val26,val32); val26 = tmp; tmp = std::min(val27,val33); val33 = std::max(val27,val33);
- val27 = tmp; tmp = std::min(val30,val36); val36 = std::max(val30,val36); val30 = tmp;
- tmp = std::min(val31,val37); val37 = std::max(val31,val37); val31 = tmp; tmp = std::min(val34,val40);
- val40 = std::max(val34,val40); val34 = tmp; tmp = std::min(val35,val41); val41 = std::max(val35,val41);
- val35 = tmp; tmp = std::min(val38,val44); val44 = std::max(val38,val44); val38 = tmp;
- tmp = std::min(val39,val45); val45 = std::max(val39,val45); val39 = tmp; tmp = std::min(val42,val48);
- val48 = std::max(val42,val48); val42 = tmp; tmp = std::min(val2,val4); val4 = std::max(val2,val4);
- val2 = tmp; tmp = std::min(val3,val5); val5 = std::max(val3,val5); val3 = tmp; tmp = std::min(val6,val8);
- val8 = std::max(val6,val8); val6 = tmp; tmp = std::min(val7,val9); val9 = std::max(val7,val9); val7 = tmp;
- tmp = std::min(val10,val12); val12 = std::max(val10,val12); val10 = tmp; tmp = std::min(val11,val13);
- val13 = std::max(val11,val13); val11 = tmp; tmp = std::min(val14,val16); val16 = std::max(val14,val16);
- val14 = tmp; tmp = std::min(val15,val17); val17 = std::max(val15,val17); val15 = tmp;
- tmp = std::min(val18,val20); val20 = std::max(val18,val20); val18 = tmp; tmp = std::min(val19,val21);
- val21 = std::max(val19,val21); val19 = tmp; tmp = std::min(val22,val24); val24 = std::max(val22,val24);
- val22 = tmp; tmp = std::min(val23,val25); val25 = std::max(val23,val25); val23 = tmp;
- tmp = std::min(val26,val28); val28 = std::max(val26,val28); val26 = tmp; tmp = std::min(val27,val29);
- val29 = std::max(val27,val29); val27 = tmp; tmp = std::min(val30,val32); val32 = std::max(val30,val32);
- val30 = tmp; tmp = std::min(val31,val33); val33 = std::max(val31,val33); val31 = tmp;
- tmp = std::min(val34,val36); val36 = std::max(val34,val36); val34 = tmp; tmp = std::min(val35,val37);
- val37 = std::max(val35,val37); val35 = tmp; tmp = std::min(val38,val40); val40 = std::max(val38,val40);
- val38 = tmp; tmp = std::min(val39,val41); val41 = std::max(val39,val41); val39 = tmp;
- tmp = std::min(val42,val44); val44 = std::max(val42,val44); val42 = tmp; tmp = std::min(val43,val45);
- val45 = std::max(val43,val45); val43 = tmp; tmp = std::min(val46,val48); val48 = std::max(val46,val48);
- val46 = tmp; val1 = std::max(val0,val1); val3 = std::max(val2,val3); val5 = std::max(val4,val5);
- val7 = std::max(val6,val7); val9 = std::max(val8,val9); val11 = std::max(val10,val11);
- val13 = std::max(val12,val13); val15 = std::max(val14,val15); val17 = std::max(val16,val17);
- val19 = std::max(val18,val19); val21 = std::max(val20,val21); val23 = std::max(val22,val23);
- val24 = std::min(val24,val25); val26 = std::min(val26,val27); val28 = std::min(val28,val29);
- val30 = std::min(val30,val31); val32 = std::min(val32,val33); val34 = std::min(val34,val35);
- val36 = std::min(val36,val37); val38 = std::min(val38,val39); val40 = std::min(val40,val41);
- val42 = std::min(val42,val43); val44 = std::min(val44,val45); val46 = std::min(val46,val47);
- val32 = std::max(val1,val32); val34 = std::max(val3,val34); val36 = std::max(val5,val36);
- val38 = std::max(val7,val38); val9 = std::min(val9,val40); val11 = std::min(val11,val42);
- val13 = std::min(val13,val44); val15 = std::min(val15,val46); val17 = std::min(val17,val48);
- val24 = std::max(val9,val24); val26 = std::max(val11,val26); val28 = std::max(val13,val28);
- val30 = std::max(val15,val30); val17 = std::min(val17,val32); val19 = std::min(val19,val34);
- val21 = std::min(val21,val36); val23 = std::min(val23,val38); val24 = std::max(val17,val24);
- val26 = std::max(val19,val26); val21 = std::min(val21,val28); val23 = std::min(val23,val30);
- val24 = std::max(val21,val24); val23 = std::min(val23,val26);
- return std::max(val23,val24);
- }
- //! Return sqrt(x^2 + y^2).
- template<typename T>
- inline T hypot(const T x, const T y) {
- #if cimg_use_cpp11==1 && !defined(_MSC_VER)
- return std::hypot(x,y);
- #else
- return std::sqrt(x*x + y*y);
- #endif
- }
- //! Return sqrt(x^2 + y^2 + z^2).
- template<typename T>
- inline T hypot(const T x, const T y, const T z) {
- return std::sqrt(x*x + y*y + z*z);
- }
- //! Return the factorial of n
- inline double factorial(const int n) {
- if (n<0) return cimg::type<double>::nan();
- if (n<2) return 1;
- double res = 2;
- for (int i = 3; i<=n; ++i) res*=i;
- return res;
- }
- //! Return the number of permutations of k objects in a set of n objects.
- inline double permutations(const int k, const int n, const bool with_order) {
- if (n<0 || k<0) return cimg::type<double>::nan();
- if (k>n) return 0;
- double res = 1;
- for (int i = n; i>=n - k + 1; --i) res*=i;
- return with_order?res:res/cimg::factorial(k);
- }
- inline double _fibonacci(int exp) {
- double
- base = (1 + std::sqrt(5.))/2,
- result = 1/std::sqrt(5.);
- while (exp) {
- if (exp&1) result*=base;
- exp>>=1;
- base*=base;
- }
- return result;
- }
- //! Calculate fibonacci number.
- // (Precise up to n = 78, less precise for n>78).
- inline double fibonacci(const int n) {
- if (n<0) return cimg::type<double>::nan();
- if (n<3) return 1;
- if (n<11) {
- cimg_uint64 fn1 = 1, fn2 = 1, fn = 0;
- for (int i = 3; i<=n; ++i) { fn = fn1 + fn2; fn2 = fn1; fn1 = fn; }
- return (double)fn;
- }
- if (n<75) // precise up to n = 74, faster than the integer calculation above for n>10
- return (double)((cimg_uint64)(_fibonacci(n) + 0.5));
- if (n<94) { // precise up to n = 78, less precise for n>78 up to n = 93, overflows for n>93
- cimg_uint64
- fn1 = ((cimg_uint64)303836)<<32 | 3861581201UL, // 1304969544928657ULL (avoid C++98 warning with ULL)
- fn2 = ((cimg_uint64)187781)<<32 | 2279239217UL, // 806515533049393ULL
- fn = 0;
- for (int i = 75; i<=n; ++i) { fn = fn1 + fn2; fn2 = fn1; fn1 = fn; }
- return (double)fn;
- }
- return _fibonacci(n); // Not precise, but better than the wrong overflowing calculation
- }
- //! Calculate greatest common divisor.
- inline long gcd(long a, long b) {
- while (a) { const long c = a; a = b%a; b = c; }
- return b;
- }
- //! Convert character to lower case.
- inline char lowercase(const char x) {
- return (char)((x<'A'||x>'Z')?x:x - 'A' + 'a');
- }
- inline double lowercase(const double x) {
- return (double)((x<'A'||x>'Z')?x:x - 'A' + 'a');
- }
- //! Convert C-string to lower case.
- inline void lowercase(char *const str) {
- if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = lowercase(*ptr);
- }
- //! Convert character to upper case.
- inline char uppercase(const char x) {
- return (char)((x<'a'||x>'z')?x:x - 'a' + 'A');
- }
- inline double uppercase(const double x) {
- return (double)((x<'a'||x>'z')?x:x - 'a' + 'A');
- }
- //! Convert C-string to upper case.
- inline void uppercase(char *const str) {
- if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = uppercase(*ptr);
- }
- //! Return \c true if input character is blank (space, tab, or non-printable character).
- inline bool is_blank(const char c) {
- return (unsigned char)c<=' ';
- }
- // Return \c true if specified argument is in set \c [a-zA-Z0-9_].
- inline bool is_varchar(const char c) {
- return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_';
- }
- //! Return \c true if argument \p str can be considered as a regular variable name.
- inline bool is_varname(const char *const str, const unsigned int length=~0U) {
- if (*str>='0' && *str<='9') return false;
- for (unsigned int l = 0; l<length && str[l]; ++l)
- if (!is_varchar(str[l])) return false;
- return true;
- }
- //! Read value in a C-string.
- /**
- \param str C-string containing the float value to read.
- \return Read value.
- \note Same as <tt>std::atof()</tt> extended to manage the retrieval of fractions from C-strings,
- as in <em>"1/2"</em>.
- **/
- inline double atof(const char *const str) {
- double x = 0, y = 1;
- return str && cimg_sscanf(str,"%lf/%lf",&x,&y)>0?x/y:0;
- }
- //! Compare the first \p length characters of two C-strings, ignoring the case.
- /**
- \param str1 C-string.
- \param str2 C-string.
- \param length Number of characters to compare.
- \return \c 0 if the two strings are equal, something else otherwise.
- \note This function has to be defined since it is not provided by all C++-compilers (not ANSI).
- **/
- inline int strncasecmp(const char *const str1, const char *const str2, const int length) {
- if (!length) return 0;
- if (!str1) return str2?-1:0;
- const char *nstr1 = str1, *nstr2 = str2;
- int k, diff = 0;
- for (k = 0; k<length && !(diff = lowercase(*nstr1) - lowercase(*nstr2)); ++k) { ++nstr1; ++nstr2; }
- return k!=length?diff:0;
- }
- //! Compare two C-strings, ignoring the case.
- /**
- \param str1 C-string.
- \param str2 C-string.
- \return \c 0 if the two strings are equal, something else otherwise.
- \note This function has to be defined since it is not provided by all C++-compilers (not ANSI).
- **/
- inline int strcasecmp(const char *const str1, const char *const str2) {
- if (!str1) return str2?-1:0;
- const int
- l1 = (int)std::strlen(str1),
- l2 = (int)std::strlen(str2);
- return cimg::strncasecmp(str1,str2,1 + (l1<l2?l1:l2));
- }
- //! Ellipsize a string.
- /**
- \param str C-string.
- \param l Max number of printed characters.
- \param is_ending Tell if the dots are placed at the end or at the center of the ellipsized string.
- **/
- inline char *strellipsize(char *const str, const unsigned int l=64,
- const bool is_ending=true) {
- if (!str) return str;
- const unsigned int nl = l<5?5:l, ls = (unsigned int)std::strlen(str);
- if (ls<=nl) return str;
- if (is_ending) std::strcpy(str + nl - 5,"(...)");
- else {
- const unsigned int ll = (nl - 5)/2 + 1 - (nl%2), lr = nl - ll - 5;
- std::strcpy(str + ll,"(...)");
- std::memmove(str + ll + 5,str + ls - lr,lr);
- }
- str[nl] = 0;
- return str;
- }
- //! Ellipsize a string.
- /**
- \param str C-string.
- \param res output C-string.
- \param l Max number of printed characters. String 'res' must be a size of at least 'l+1'.
- \param is_ending Tell if the dots are placed at the end or at the center of the ellipsized string.
- **/
- inline char *strellipsize(const char *const str, char *const res, const unsigned int l=64,
- const bool is_ending=true) {
- const unsigned int nl = l<5?5:l, ls = (unsigned int)std::strlen(str);
- if (ls<=nl) { std::strcpy(res,str); return res; }
- if (is_ending) {
- std::strncpy(res,str,nl - 5);
- std::strcpy(res + nl -5,"(...)");
- } else {
- const unsigned int ll = (nl - 5)/2 + 1 - (nl%2), lr = nl - ll - 5;
- std::strncpy(res,str,ll);
- std::strcpy(res + ll,"(...)");
- std::strncpy(res + ll + 5,str + ls - lr,lr);
- }
- res[nl] = 0;
- return res;
- }
- //! Remove delimiters on the start and/or end of a C-string.
- /**
- \param[in,out] str C-string to work with (modified at output).
- \param delimiter Delimiter character code to remove.
- \param is_symmetric Tells if the removal is done only if delimiters are symmetric
- (both at the beginning and the end of \c s).
- \param is_iterative Tells if the removal is done if several iterations are possible.
- \return \c true if delimiters have been removed, \c false otherwise.
- **/
- inline bool strpare(char *const str, const char delimiter,
- const bool is_symmetric, const bool is_iterative) {
- if (!str) return false;
- const int l = (int)std::strlen(str);
- int p, q;
- if (is_symmetric) for (p = 0, q = l - 1; p<q && str[p]==delimiter && str[q]==delimiter; ) {
- --q; ++p; if (!is_iterative) break;
- } else {
- for (p = 0; p<l && str[p]==delimiter; ) { ++p; if (!is_iterative) break; }
- for (q = l - 1; q>p && str[q]==delimiter; ) { --q; if (!is_iterative) break; }
- }
- const int n = q - p + 1;
- if (n!=l) { std::memmove(str,str + p,(unsigned int)n); str[n] = 0; return true; }
- return false;
- }
- //! Remove white spaces on the start and/or end of a C-string.
- inline bool strpare(char *const str, const bool is_symmetric, const bool is_iterative) {
- if (!str) return false;
- const int l = (int)std::strlen(str);
- int p, q;
- if (is_symmetric) for (p = 0, q = l - 1; p<q && is_blank(str[p]) && is_blank(str[q]); ) {
- --q; ++p; if (!is_iterative) break;
- } else {
- for (p = 0; p<l && is_blank(str[p]); ) { ++p; if (!is_iterative) break; }
- for (q = l - 1; q>p && is_blank(str[q]); ) { --q; if (!is_iterative) break; }
- }
- const int n = q - p + 1;
- if (n!=l) { std::memmove(str,str + p,(unsigned int)n); str[n] = 0; return true; }
- return false;
- }
- //! Replace reserved characters (for Windows filename) by another character.
- /**
- \param[in,out] str C-string to work with (modified at output).
- \param[in] c Replacement character.
- **/
- inline void strwindows_reserved(char *const str, const char c='_') {
- for (char *s = str; *s; ++s) {
- const char i = *s;
- if (i=='<' || i=='>' || i==':' || i=='\"' || i=='/' || i=='\\' || i=='|' || i=='?' || i=='*') *s = c;
- }
- }
- //! Replace escape sequences in C-strings by character values.
- /**
- \param[in,out] str C-string to work with (modified at output).
- **/
- inline void strunescape(char *const str) {
- #define cimg_strunescape(ci,co) case ci : *nd = co; ++ns; break;
- unsigned char val = 0;
- for (char *ns = str, *nd = str; *ns || (bool)(*nd = 0); ++nd) if (*ns=='\\') switch (*(++ns)) {
- cimg_strunescape('a','\a');
- cimg_strunescape('b','\b');
- cimg_strunescape('e',0x1B);
- cimg_strunescape('f','\f');
- cimg_strunescape('n','\n');
- cimg_strunescape('r','\r');
- cimg_strunescape('t','\t');
- cimg_strunescape('v','\v');
- cimg_strunescape('\\','\\');
- cimg_strunescape('\'','\'');
- cimg_strunescape('\"','\"');
- cimg_strunescape('\?','\?');
- case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' :
- val = (unsigned char)(*(ns++) - '0');
- if (*ns>='0' && *ns<='7') (val<<=3)|=*(ns++) - '0';
- if (*ns>='0' && *ns<='7') (val<<=3)|=*(ns++) - '0';
- *nd = (char)val;
- break;
- case 'x' : {
- char c = lowercase(*(++ns));
- if ((c>='0' && c<='9') || (c>='a' && c<='f')) {
- val = (unsigned char)(c<='9'?c - '0':c - 'a' + 10);
- c = lowercase(*(++ns));
- if ((c>='0' && c<='9') || (c>='a' && c<='f')) {
- (val<<=4)|=(c<='9'?c - '0':c - 'a' + 10);
- ++ns;
- }
- *nd = (char)val;
- } else *nd = c;
- } break;
- case 'u' : { // UTF-8 BMP
- char c1, c2, c3, c4;
- if ((((c1 = lowercase(ns[1]))>='0' && c1<='9') || (c1>='a' && c1<='f')) &&
- (((c2 = lowercase(ns[2]))>='0' && c2<='9') || (c2>='a' && c2<='f')) &&
- (((c3 = lowercase(ns[3]))>='0' && c3<='9') || (c3>='a' && c3<='f')) &&
- (((c4 = lowercase(ns[4]))>='0' && c4<='9') || (c4>='a' && c4<='f'))) {
- c1 = (c1<='9'?c1 - '0':c1 - 'a' + 10);
- c2 = (c2<='9'?c2 - '0':c2 - 'a' + 10);
- c3 = (c3<='9'?c3 - '0':c3 - 'a' + 10);
- c4 = (c4<='9'?c4 - '0':c4 - 'a' + 10);
- const unsigned int ival =
- ((unsigned int)c1<<12) | ((unsigned int)c2<<8) | ((unsigned int)c3<<4) | c4;
- if (ival<=0x007f) *nd = (char)ival;
- else if (ival<=0x07ff) {
- *(nd++) = (char)((ival>>6)|0xc0);
- *nd = (char)((ival&0x3f)|0x80);
- } else {
- *(nd++) = (char)((ival>>12)|0xe0);
- *(nd++) = (char)(((ival>>6)&0x3f)|0x80);
- *nd = (char)((ival&0x3f)|0x80);
- }
- ns+=5;
- } else *nd = *(ns++);
- } break;
- case 'U' : { // UTF-8 astral planes
- char c1, c2, c3, c4, c5, c6, c7, c8;
- if ((((c1 = lowercase(ns[1]))>='0' && c1<='9') || (c1>='a' && c1<='f')) &&
- (((c2 = lowercase(ns[2]))>='0' && c2<='9') || (c2>='a' && c2<='f')) &&
- (((c3 = lowercase(ns[3]))>='0' && c3<='9') || (c3>='a' && c3<='f')) &&
- (((c4 = lowercase(ns[4]))>='0' && c4<='9') || (c4>='a' && c4<='f')) &&
- (((c5 = lowercase(ns[5]))>='0' && c5<='9') || (c5>='a' && c5<='f')) &&
- (((c6 = lowercase(ns[6]))>='0' && c6<='9') || (c6>='a' && c6<='f')) &&
- (((c7 = lowercase(ns[7]))>='0' && c7<='9') || (c7>='a' && c7<='f')) &&
- (((c8 = lowercase(ns[8]))>='0' && c8<='9') || (c8>='a' && c8<='f'))) {
- c1 = (c1<='9'?c1 - '0':c1 - 'a' + 10);
- c2 = (c2<='9'?c2 - '0':c2 - 'a' + 10);
- c3 = (c3<='9'?c3 - '0':c3 - 'a' + 10);
- c4 = (c4<='9'?c4 - '0':c4 - 'a' + 10);
- c5 = (c5<='9'?c5 - '0':c5 - 'a' + 10);
- c6 = (c6<='9'?c6 - '0':c6 - 'a' + 10);
- c7 = (c7<='9'?c7 - '0':c7 - 'a' + 10);
- c8 = (c8<='9'?c8 - '0':c8 - 'a' + 10);
- const unsigned int ival =
- ((unsigned int)c1<<28) | ((unsigned int)c2<<24) | ((unsigned int)c3<<20) | ((unsigned int)c4<<16) |
- ((unsigned int)c5<<12) | ((unsigned int)c6<<8) | ((unsigned int)c7<<4) | (unsigned int)c8;
- if (ival<=0x007f) *nd = (char)ival;
- else if (ival<=0x07ff) {
- *(nd++) = (char)((ival>>6)|0xc0);
- *nd = (char)((ival&0x3f)|0x80);
- } else if (ival<=0xffff) {
- *(nd++) = (char)((ival>>12)|0xe0);
- *(nd++) = (char)(((ival>>6)&0x3f)|0x80);
- *nd = (char)((ival&0x3f)|0x80);
- } else {
- *(nd++) = (char)((ival>>18)|0xf0);
- *(nd++) = (char)(((ival>>12)&0x3f)|0x80);
- *(nd++) = (char)(((ival>>6)&0x3f)|0x80);
- *nd = (char)((ival&0x3f)|0x80);
- }
- ns+=9;
- } else *nd = *(ns++);
- } break;
- default : if (*ns) *nd = *(ns++);
- }
- else *nd = *(ns++);
- }
- // Return a temporary string describing the size of a memory buffer.
- inline const char *strbuffersize(const cimg_ulong size);
- // Return string that identifies the running OS.
- inline const char *stros() {
- #if defined(linux) || defined(__linux) || defined(__linux__)
- static const char *const str = "Linux";
- #elif defined(sun) || defined(__sun)
- static const char *const str = "Sun OS";
- #elif defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined (__DragonFly__)
- static const char *const str = "BSD";
- #elif defined(sgi) || defined(__sgi)
- static const char *const str = "Irix";
- #elif defined(__MACOSX__) || defined(__APPLE__)
- static const char *const str = "Mac OS";
- #elif defined(unix) || defined(__unix) || defined(__unix__)
- static const char *const str = "Generic Unix";
- #elif defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || \
- defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
- static const char *const str = "Windows";
- #else
- const char
- *const _str1 = std::getenv("OSTYPE"),
- *const _str2 = _str1?_str1:std::getenv("OS"),
- *const str = _str2?_str2:"Unknown OS";
- #endif
- return str;
- }
- //! Return the basename of a filename.
- inline const char* basename(const char *const s, const char separator=cimg_file_separator) {
- const char *p = 0, *np = s;
- while (np>=s && (p=np)) np = std::strchr(np,separator) + 1;
- return p;
- }
- // Return a random filename.
- inline const char* filenamerand() {
- cimg::mutex(6);
- static char randomid[9];
- for (unsigned int k = 0; k<8; ++k) {
- const int v = (int)cimg::rand(65535)%3;
- randomid[k] = (char)(v==0?('0' + ((int)cimg::rand(65535)%10)):
- (v==1?('a' + ((int)cimg::rand(65535)%26)):
- ('A' + ((int)cimg::rand(65535)%26))));
- }
- cimg::mutex(6,0);
- return randomid;
- }
- // Convert filename as a Windows-style filename (short path name).
- inline void winformat_string(char *const str) {
- if (str && *str) {
- #if cimg_OS==2
- char *const nstr = new char[MAX_PATH];
- if (GetShortPathNameA(str,nstr,MAX_PATH)) std::strcpy(str,nstr);
- delete[] nstr;
- #endif
- }
- }
- // Open a file (similar to std:: fopen(), but with wide character support on Windows).
- inline std::FILE *std_fopen(const char *const path, const char *const mode);
- //! Open a file.
- /**
- \param path Path of the filename to open.
- \param mode C-string describing the opening mode.
- \return Opened file.
- \note Same as <tt>std::fopen()</tt> but throw a \c CImgIOException when
- the specified file cannot be opened, instead of returning \c 0.
- **/
- inline std::FILE *fopen(const char *const path, const char *const mode) {
- if (!path)
- throw CImgArgumentException("cimg::fopen(): Specified file path is (null).");
- if (!mode)
- throw CImgArgumentException("cimg::fopen(): File '%s', specified mode is (null).",
- path);
- std::FILE *res = 0;
- if (*path=='-' && (!path[1] || path[1]=='.')) {
- res = (*mode=='r')?cimg::_stdin():cimg::_stdout();
- #if cimg_OS==2
- if (*mode && mode[1]=='b') { // Force stdin/stdout to be in binary mode
- #ifdef __BORLANDC__
- if (setmode(_fileno(res),0x8000)==-1) res = 0;
- #else
- if (_setmode(_fileno(res),0x8000)==-1) res = 0;
- #endif
- }
- #endif
- } else res = cimg::std_fopen(path,mode);
- if (!res) throw CImgIOException("cimg::fopen(): Failed to open file '%s' with mode '%s'.",
- path,mode);
- return res;
- }
- //! Close a file.
- /**
- \param file File to close.
- \return \c 0 if file has been closed properly, something else otherwise.
- \note Same as <tt>std::fclose()</tt> but display a warning message if
- the file has not been closed properly.
- **/
- inline int fclose(std::FILE *file) {
- if (!file) { warn("cimg::fclose(): Specified file is (null)."); return 0; }
- if (file==cimg::_stdin(false) || file==cimg::_stdout(false)) return 0;
- const int errn = std::fclose(file);
- if (errn!=0) warn("cimg::fclose(): Error code %d returned during file closing.",
- errn);
- return errn;
- }
- //! Version of 'fseek()' that supports >=64bits offsets everywhere (for Windows).
- inline int fseek(FILE *stream, cimg_long offset, int origin) {
- #if defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
- return _fseeki64(stream,(__int64)offset,origin);
- #else
- return std::fseek(stream,offset,origin);
- #endif
- }
- //! Version of 'ftell()' that supports >=64bits offsets everywhere (for Windows).
- inline cimg_long ftell(FILE *stream) {
- #if defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
- return (cimg_long)_ftelli64(stream);
- #else
- return (cimg_long)std::ftell(stream);
- #endif
- }
- // Get the file or directory attributes with support for UTF-8 paths (Windows only).
- #if cimg_OS==2
- inline DWORD win_getfileattributes(const char *const path);
- #endif
- //! Check if a path is a directory.
- /**
- \param path Specified path to test.
- **/
- inline bool is_directory(const char *const path) {
- if (!path || !*path) return false;
- #if cimg_OS==1
- struct stat st_buf;
- return (!stat(path,&st_buf) && S_ISDIR(st_buf.st_mode));
- #elif cimg_OS==2
- const DWORD res = win_getfileattributes(path);
- return res!=INVALID_FILE_ATTRIBUTES && (res&FILE_ATTRIBUTE_DIRECTORY);
- #else
- return false;
- #endif
- }
- //! Check if a path is a file.
- /**
- \param path Specified path to test.
- **/
- inline bool is_file(const char *const path) {
- if (!path || !*path) return false;
- #if cimg_OS==2
- const DWORD res = cimg::win_getfileattributes(path);
- return res!=INVALID_FILE_ATTRIBUTES && !(res&FILE_ATTRIBUTE_DIRECTORY);
- #else
- std::FILE *const file = cimg::std_fopen(path,"rb");
- if (!file) return false;
- cimg::fclose(file);
- return !is_directory(path);
- #endif
- }
- //! Get file size.
- /**
- \param filename Specified filename to get size from.
- \return File size or '-1' if file does not exist.
- **/
- inline cimg_int64 fsize(std::FILE *const file) {
- if (!file) return (cimg_int64)-1;
- const long pos = std::ftell(file);
- std::fseek(file,0,SEEK_END);
- const cimg_int64 siz = (cimg_int64)std::ftell(file);
- std::fseek(file,pos,SEEK_SET);
- return siz;
- }
- inline cimg_int64 fsize(const char *const filename) {
- std::FILE *const file = cimg::std_fopen(filename,"rb");
- const cimg_int64 siz = fsize(file);
- cimg::fclose(file);
- return siz;
- }
- //! Get last write time of a given file or directory (multiple-attributes version).
- /**
- \param path Specified path to get attributes from.
- \param[in,out] attr Type of requested time attributes.
- Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second }
- Replaced by read attributes after return (or -1 if an error occurred).
- \param nb_attr Number of attributes to read/write.
- \return Latest read attribute.
- **/
- template<typename T>
- inline int fdate(const char *const path, T *attr, const unsigned int nb_attr) {
- #define _cimg_fdate_err() for (unsigned int i = 0; i<nb_attr; ++i) attr[i] = (T)-1
- int res = -1;
- if (!path || !*path) { _cimg_fdate_err(); return -1; }
- cimg::mutex(6);
- #if cimg_OS==2
- HANDLE file = CreateFileA(path,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
- if (file!=INVALID_HANDLE_VALUE) {
- FILETIME _ft;
- SYSTEMTIME ft;
- if (GetFileTime(file,0,0,&_ft) && FileTimeToSystemTime(&_ft,&ft)) {
- for (unsigned int i = 0; i<nb_attr; ++i) {
- res = (int)(attr[i]==0?ft.wYear:attr[i]==1?ft.wMonth:attr[i]==2?ft.wDay:
- attr[i]==3?ft.wDayOfWeek:attr[i]==4?ft.wHour:attr[i]==5?ft.wMinute:
- attr[i]==6?ft.wSecond:-1);
- attr[i] = (T)res;
- }
- } else _cimg_fdate_err();
- CloseHandle(file);
- } else _cimg_fdate_err();
- #elif cimg_OS==1
- struct stat st_buf;
- if (!stat(path,&st_buf)) {
- const time_t _ft = st_buf.st_mtime;
- const struct tm& ft = *std::localtime(&_ft);
- for (unsigned int i = 0; i<nb_attr; ++i) {
- res = (int)(attr[i]==0?ft.tm_year + 1900:attr[i]==1?ft.tm_mon + 1:attr[i]==2?ft.tm_mday:
- attr[i]==3?ft.tm_wday:attr[i]==4?ft.tm_hour:attr[i]==5?ft.tm_min:
- attr[i]==6?ft.tm_sec:-1);
- attr[i] = (T)res;
- }
- } else _cimg_fdate_err();
- #endif
- cimg::mutex(6,0);
- return res;
- }
- //! Get last write time of a given file or directory (single-attribute version).
- /**
- \param path Specified path to get attributes from.
- \param attr Type of requested time attributes.
- Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second }
- \return Specified attribute or -1 if an error occurred.
- **/
- inline int fdate(const char *const path, unsigned int attr) {
- int out = (int)attr;
- return fdate(path,&out,1);
- }
- //! Get current local time (multiple-attributes version).
- /**
- \param[in,out] attr Type of requested time attributes.
- Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second |
- 7=millisecond }
- Replaced by read attributes after return (or -1 if an error occurred).
- \param nb_attr Number of attributes to read/write.
- \return Latest read attribute.
- **/
- template<typename T>
- inline int date(T *attr, const unsigned int nb_attr) {
- int res = -1;
- cimg::mutex(6);
- #if cimg_OS==2
- SYSTEMTIME st;
- GetLocalTime(&st);
- for (unsigned int i = 0; i<nb_attr; ++i) {
- res = (int)(attr[i]==0?st.wYear:
- attr[i]==1?st.wMonth:
- attr[i]==2?st.wDay:
- attr[i]==3?st.wDayOfWeek:
- attr[i]==4?st.wHour:
- attr[i]==5?st.wMinute:
- attr[i]==6?st.wSecond:
- attr[i]==7?st.wMilliseconds:-1);
- attr[i] = (T)res;
- }
- #else
- struct timeval _st;
- gettimeofday(&_st,0);
- struct tm *st = std::localtime(&_st.tv_sec);
- for (unsigned int i = 0; i<nb_attr; ++i) {
- res = (int)(attr[i]==0?st->tm_year + 1900:
- attr[i]==1?st->tm_mon + 1:
- attr[i]==2?st->tm_mday:
- attr[i]==3?st->tm_wday:
- attr[i]==4?st->tm_hour:
- attr[i]==5?st->tm_min:
- attr[i]==6?st->tm_sec:
- attr[i]==7?_st.tv_usec/1000:-1);
- attr[i] = (T)res;
- }
- #endif
- cimg::mutex(6,0);
- return res;
- }
- //! Get current local time (single-attribute version).
- /**
- \param attr Type of requested time attribute.
- Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second |
- 7=millisecond }
- \return Specified attribute or -1 if an error occurred.
- **/
- inline int date(unsigned int attr) {
- int out = (int)attr;
- return date(&out,1);
- }
- // Get/set path to the \c curl binary.
- inline const char *curl_path(const char *const user_path=0, const bool reinit_path=false);
- // Get/set path to the \c dcraw binary.
- inline const char *dcraw_path(const char *const user_path=0, const bool reinit_path=false);
- // Get/set path to the FFMPEG's \c ffmpeg binary.
- inline const char *ffmpeg_path(const char *const user_path=0, const bool reinit_path=false);
- // Get/set path to the GraphicsMagick's \c gm binary.
- inline const char* graphicsmagick_path(const char *const user_path=0, const bool reinit_path=false);
- // Get/set path to the \c gunzip binary.
- inline const char *gunzip_path(const char *const user_path=0, const bool reinit_path=false);
- // Get/set path to the \c gzip binary.
- inline const char *gzip_path(const char *const user_path=0, const bool reinit_path=false);
- // Get/set path to the ImageMagick's \c convert binary.
- inline const char* imagemagick_path(const char *const user_path=0, const bool reinit_path=false);
- // Get/set path to the Medcon's \c medcon binary.
- inline const char* medcon_path(const char *const user_path=0, const bool reinit_path=false);
- // Get/set path to store temporary files.
- inline const char* temporary_path(const char *const user_path=0, const bool reinit_path=false);
- // Get/set path to the \c wget binary.
- inline const char *wget_path(const char *const user_path=0, const bool reinit_path=false);
- #if cimg_OS==2
- // Get/set path to the \c powershell binary.
- inline const char *powershell_path(const char *const user_path=0, const bool reinit_path=false);
- #endif
- //! Split filename into two C-strings \c body and \c extension.
- /**
- filename and body must not overlap!
- **/
- inline const char *split_filename(const char *const filename, char *const body=0) {
- if (!filename) { if (body) *body = 0; return ""; }
- const char * p = std::strrchr(filename,'.');
- if (!p || std::strchr(p,'/') || std::strchr(p,'\\')) { // No extension.
- if (body) std::strcpy(body,filename);
- return filename + std::strlen(filename);
- }
- const unsigned int l = (unsigned int)(p - filename);
- if (body) { if (l) std::memcpy(body,filename,l); body[l] = 0; }
- return p + 1;
- }
- // Generate a numbered version of a filename.
- inline char* number_filename(const char *const filename, const int number,
- const unsigned int digits, char *const str);
- //! Read data from file.
- /**
- \param[out] ptr Pointer to memory buffer that will contain the binary data read from file.
- \param nmemb Number of elements to read.
- \param stream File to read data from.
- \return Number of read elements.
- \note Same as <tt>std::fread()</tt> but may display warning message if all elements could not be read.
- **/
- template<typename T>
- inline size_t fread(T *const ptr, const size_t nmemb, std::FILE *stream) {
- if (!ptr || !stream)
- throw CImgArgumentException("cimg::fread(): Invalid reading request of %u %s%s from file %p to buffer %p.",
- nmemb,cimg::type<T>::string(),nmemb>1?"s":"",stream,ptr);
- if (!nmemb) return 0;
- const size_t wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T);
- size_t to_read = nmemb, al_read = 0, l_to_read = 0, l_al_read = 0;
- do {
- l_to_read = (to_read*sizeof(T))<wlimitT?to_read:wlimit;
- l_al_read = std::fread((void*)(ptr + al_read),sizeof(T),l_to_read,stream);
- al_read+=l_al_read;
- to_read-=l_al_read;
- } while (l_to_read==l_al_read && to_read>0);
- if (to_read>0)
- warn("cimg::fread(): Only %lu/%lu elements could be read from file.",
- (unsigned long)al_read,(unsigned long)nmemb);
- return al_read;
- }
- //! Write data to file.
- /**
- \param ptr Pointer to memory buffer containing the binary data to write on file.
- \param nmemb Number of elements to write.
- \param[out] stream File to write data on.
- \return Number of written elements.
- \note Similar to <tt>std::fwrite</tt> but may display warning messages if all elements could not be written.
- **/
- template<typename T>
- inline size_t fwrite(const T *ptr, const size_t nmemb, std::FILE *stream) {
- if (!ptr || !stream)
- throw CImgArgumentException("cimg::fwrite(): Invalid writing request of %u %s%s from buffer %p to file %p.",
- nmemb,cimg::type<T>::string(),nmemb>1?"s":"",ptr,stream);
- if (!nmemb) return 0;
- const size_t wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T);
- size_t to_write = nmemb, al_write = 0, l_to_write = 0, l_al_write = 0;
- do {
- l_to_write = (to_write*sizeof(T))<wlimitT?to_write:wlimit;
- l_al_write = std::fwrite((void*)(ptr + al_write),sizeof(T),l_to_write,stream);
- al_write+=l_al_write;
- to_write-=l_al_write;
- } while (l_to_write==l_al_write && to_write>0);
- if (to_write>0)
- warn("cimg::fwrite(): Only %lu/%lu elements could be written in file.",
- (unsigned long)al_write,(unsigned long)nmemb);
- return al_write;
- }
- //! Create an empty file.
- /**
- \param file Input file (can be \c 0 if \c filename is set).
- \param filename Filename, as a C-string (can be \c 0 if \c file is set).
- **/
- inline void fempty(std::FILE *const file, const char *const filename) {
- if (!file && !filename)
- throw CImgArgumentException("cimg::fempty(): Specified filename is (null).");
- std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
- if (!file) cimg::fclose(nfile);
- }
- // Try to guess format from an image file.
- inline const char *ftype(std::FILE *const file, const char *const filename);
- // Get or set load from network mode (can be { 0=disabled | 1=enabled }).
- inline bool& network_mode(const bool value, const bool is_set) {
- static bool mode = true;
- if (is_set) { cimg::mutex(0); mode = value; cimg::mutex(0,0); }
- return mode;
- }
- inline bool& network_mode() {
- return network_mode(false,false);
- }
- // Load file from network as a local temporary file.
- inline char *load_network(const char *const url, char *const filename_local,
- const unsigned int timeout=0, const bool try_fallback=false,
- const char *const referer=0, const char *const user_agent=0);
- //! Return options specified on the command line.
- inline const char* option(const char *const name, const int argc, const char *const *const argv,
- const char *const _default, const char *const usage, const bool reset_static) {
- static bool first = true, visu = false;
- if (reset_static) { first = true; return 0; }
- const char *res = 0;
- if (first) {
- first = false;
- visu = cimg::option("-h",argc,argv,(char*)0,(char*)0,false)!=0;
- visu |= cimg::option("-help",argc,argv,(char*)0,(char*)0,false)!=0;
- visu |= cimg::option("--help",argc,argv,(char*)0,(char*)0,false)!=0;
- }
- if (!name && visu) {
- if (usage) {
- std::fprintf(cimg::output(),"\n %s%s%s",cimg::t_red,cimg::basename(argv[0]),cimg::t_normal);
- std::fprintf(cimg::output(),": %s",usage);
- std::fprintf(cimg::output()," (%s, %s)\n\n",cimg_date,cimg_time);
- }
- if (_default) std::fprintf(cimg::output(),"%s\n",_default);
- }
- if (name) {
- if (argc>0) {
- int k = 0;
- while (k<argc && std::strcmp(argv[k],name)) ++k;
- res = (k++==argc?_default:(k==argc?argv[--k]:argv[k]));
- } else res = _default;
- if (visu && usage) std::fprintf(cimg::output()," %s%-16s%s %-24s %s%s%s\n",
- cimg::t_bold,name,cimg::t_normal,res?res:"0",
- cimg::t_green,usage,cimg::t_normal);
- }
- return res;
- }
- inline const char* option(const char *const name, const int argc, const char *const *const argv,
- const char *const _default, const char *const usage=0) {
- return option(name,argc,argv,_default,usage,false);
- }
- inline bool option(const char *const name, const int argc, const char *const *const argv,
- const bool _default, const char *const usage=0) {
- const char *const s = cimg::option(name,argc,argv,(char*)0);
- const bool res = s?(cimg::strcasecmp(s,"false") && cimg::strcasecmp(s,"off") && cimg::strcasecmp(s,"0")):_default;
- cimg::option(name,0,0,res?"true":"false",usage);
- return res;
- }
- inline int option(const char *const name, const int argc, const char *const *const argv,
- const int _default, const char *const usage=0) {
- const char *const s = cimg::option(name,argc,argv,(char*)0);
- const int res = s?std::atoi(s):_default;
- char *const tmp = new char[256];
- cimg_snprintf(tmp,256,"%d",res);
- cimg::option(name,0,0,tmp,usage);
- delete[] tmp;
- return res;
- }
- inline char option(const char *const name, const int argc, const char *const *const argv,
- const char _default, const char *const usage=0) {
- const char *const s = cimg::option(name,argc,argv,(char*)0);
- const char res = s?*s:_default;
- char tmp[8];
- *tmp = res; tmp[1] = 0;
- cimg::option(name,0,0,tmp,usage);
- return res;
- }
- inline float option(const char *const name, const int argc, const char *const *const argv,
- const float _default, const char *const usage=0) {
- const char *const s = cimg::option(name,argc,argv,(char*)0);
- const float res = s?(float)cimg::atof(s):_default;
- char *const tmp = new char[256];
- cimg_snprintf(tmp,256,"%g",res);
- cimg::option(name,0,0,tmp,usage);
- delete[] tmp;
- return res;
- }
- inline double option(const char *const name, const int argc, const char *const *const argv,
- const double _default, const char *const usage=0) {
- const char *const s = cimg::option(name,argc,argv,(char*)0);
- const double res = s?cimg::atof(s):_default;
- char *const tmp = new char[256];
- cimg_snprintf(tmp,256,"%g",res);
- cimg::option(name,0,0,tmp,usage);
- delete[] tmp;
- return res;
- }
- //! Print information about \CImg environment variables.
- /**
- \note Output is done on the default output stream.
- **/
- inline void info() {
- std::fprintf(cimg::output(),"\n %s%sCImg Library %u.%u.%u%s, compiled %s ( %s ) with the following flags:\n\n",
- cimg::t_red,cimg::t_bold,cimg_version/100,(cimg_version/10)%10,cimg_version%10,
- cimg::t_normal,cimg_date,cimg_time);
- std::fprintf(cimg::output()," > Operating System: %s%-13s%s %s('cimg_OS'=%d)%s\n",
- cimg::t_bold,
- cimg_OS==1?"Unix":(cimg_OS==2?"Windows":"Unknown"),
- cimg::t_normal,cimg::t_green,
- cimg_OS,
- cimg::t_normal);
- std::fprintf(cimg::output()," > CPU endianness: %s%s Endian%s\n",
- cimg::t_bold,
- cimg::endianness()?"Big":"Little",
- cimg::t_normal);
- std::fprintf(cimg::output()," > Verbosity mode: %s%-13s%s %s('cimg_verbosity'=%d)%s\n",
- cimg::t_bold,
- cimg_verbosity==0?"Quiet":
- cimg_verbosity==1?"Console":
- cimg_verbosity==2?"Dialog":
- cimg_verbosity==3?"Console+Warnings":"Dialog+Warnings",
- cimg::t_normal,cimg::t_green,
- cimg_verbosity,
- cimg::t_normal);
- std::fprintf(cimg::output()," > Stricts warnings: %s%-13s%s %s('cimg_strict_warnings' %s)%s\n",
- cimg::t_bold,
- #ifdef cimg_strict_warnings
- "Yes",cimg::t_normal,cimg::t_green,"defined",
- #else
- "No",cimg::t_normal,cimg::t_green,"undefined",
- #endif
- cimg::t_normal);
- std::fprintf(cimg::output()," > Support for C++11: %s%-13s%s %s('cimg_use_cpp11'=%d)%s\n",
- cimg::t_bold,
- cimg_use_cpp11?"Yes":"No",
- cimg::t_normal,cimg::t_green,
- (int)cimg_use_cpp11,
- cimg::t_normal);
- std::fprintf(cimg::output()," > Using VT100 messages: %s%-13s%s %s('cimg_use_vt100' %s)%s\n",
- cimg::t_bold,
- #ifdef cimg_use_vt100
- "Yes",cimg::t_normal,cimg::t_green,"defined",
- #else
- "No",cimg::t_normal,cimg::t_green,"undefined",
- #endif
- cimg::t_normal);
- std::fprintf(cimg::output()," > Display type: %s%-13s%s %s('cimg_display'=%d)%s\n",
- cimg::t_bold,
- cimg_display==0?"No display":cimg_display==1?"X11":cimg_display==2?"Windows GDI":"Unknown",
- cimg::t_normal,cimg::t_green,
- (int)cimg_display,
- cimg::t_normal);
- #if cimg_display==1
- std::fprintf(cimg::output()," > Using XShm for X11: %s%-13s%s %s('cimg_use_xshm' %s)%s\n",
- cimg::t_bold,
- #ifdef cimg_use_xshm
- "Yes",cimg::t_normal,cimg::t_green,"defined",
- #else
- "No",cimg::t_normal,cimg::t_green,"undefined",
- #endif
- cimg::t_normal);
- std::fprintf(cimg::output()," > Using XRand for X11: %s%-13s%s %s('cimg_use_xrandr' %s)%s\n",
- cimg::t_bold,
- #ifdef cimg_use_xrandr
- "Yes",cimg::t_normal,cimg::t_green,"defined",
- #else
- "No",cimg::t_normal,cimg::t_green,"undefined",
- #endif
- cimg::t_normal);
- #endif
- std::fprintf(cimg::output()," > Using OpenMP: %s%-13s%s %s('cimg_use_openmp' %s)%s\n",
- cimg::t_bold,
- #if cimg_use_openmp!=0
- "Yes",cimg::t_normal,cimg::t_green,"defined",
- #else
- "No",cimg::t_normal,cimg::t_green,"undefined",
- #endif
- cimg::t_normal);
- std::fprintf(cimg::output()," > Using PNG library: %s%-13s%s %s('cimg_use_png' %s)%s\n",
- cimg::t_bold,
- #ifdef cimg_use_png
- "Yes",cimg::t_normal,cimg::t_green,"defined",
- #else
- "No",cimg::t_normal,cimg::t_green,"undefined",
- #endif
- cimg::t_normal);
- std::fprintf(cimg::output()," > Using JPEG library: %s%-13s%s %s('cimg_use_jpeg' %s)%s\n",
- cimg::t_bold,
- #ifdef cimg_use_jpeg
- "Yes",cimg::t_normal,cimg::t_green,"defined",
- #else
- "No",cimg::t_normal,cimg::t_green,"undefined",
- #endif
- cimg::t_normal);
- std::fprintf(cimg::output()," > Using TIFF library: %s%-13s%s %s('cimg_use_tiff' %s)%s\n",
- cimg::t_bold,
- #ifdef cimg_use_tiff
- "Yes",cimg::t_normal,cimg::t_green,"defined",
- #else
- "No",cimg::t_normal,cimg::t_green,"undefined",
- #endif
- cimg::t_normal);
- std::fprintf(cimg::output()," > Using Magick++ library: %s%-13s%s %s('cimg_use_magick' %s)%s\n",
- cimg::t_bold,
- #ifdef cimg_use_magick
- "Yes",cimg::t_normal,cimg::t_green,"defined",
- #else
- "No",cimg::t_normal,cimg::t_green,"undefined",
- #endif
- cimg::t_normal);
- std::fprintf(cimg::output()," > Using FFTW3 library: %s%-13s%s %s('cimg_use_fftw3' %s)%s\n",
- cimg::t_bold,
- #ifdef cimg_use_fftw3
- "Yes",cimg::t_normal,cimg::t_green,"defined",
- #else
- "No",cimg::t_normal,cimg::t_green,"undefined",
- #endif
- cimg::t_normal);
- std::fprintf(cimg::output()," > Using LAPACK library: %s%-13s%s %s('cimg_use_lapack' %s)%s\n",
- cimg::t_bold,
- #ifdef cimg_use_lapack
- "Yes",cimg::t_normal,cimg::t_green,"defined",
- #else
- "No",cimg::t_normal,cimg::t_green,"undefined",
- #endif
- cimg::t_normal);
- char *const tmp = new char[1024];
- cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::curl_path());
- std::fprintf(cimg::output()," > Path of 'curl': %s%-13s%s\n",
- cimg::t_bold,
- tmp,
- cimg::t_normal);
- cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::dcraw_path());
- std::fprintf(cimg::output()," > Path of 'dcraw': %s%-13s%s\n",
- cimg::t_bold,
- tmp,
- cimg::t_normal);
- cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::ffmpeg_path());
- std::fprintf(cimg::output()," > Path of 'ffmpeg': %s%-13s%s\n",
- cimg::t_bold,
- tmp,
- cimg::t_normal);
- cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::graphicsmagick_path());
- std::fprintf(cimg::output()," > Path of 'graphicsmagick': %s%-13s%s\n",
- cimg::t_bold,
- tmp,
- cimg::t_normal);
- cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::gunzip_path());
- std::fprintf(cimg::output()," > Path of 'gunzip': %s%-13s%s\n",
- cimg::t_bold,
- tmp,
- cimg::t_normal);
- cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::gzip_path());
- std::fprintf(cimg::output()," > Path of 'gzip': %s%-13s%s\n",
- cimg::t_bold,
- tmp,
- cimg::t_normal);
- cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::imagemagick_path());
- std::fprintf(cimg::output()," > Path of 'imagemagick': %s%-13s%s\n",
- cimg::t_bold,
- tmp,
- cimg::t_normal);
- cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::medcon_path());
- std::fprintf(cimg::output()," > Path of 'medcon': %s%-13s%s\n",
- cimg::t_bold,
- tmp,
- cimg::t_normal);
- cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::temporary_path());
- std::fprintf(cimg::output()," > Temporary path: %s%-13s%s\n",
- cimg::t_bold,
- tmp,
- cimg::t_normal);
- cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::wget_path());
- std::fprintf(cimg::output()," > Path of 'wget': %s%-13s%s\n",
- cimg::t_bold,
- tmp,
- cimg::t_normal);
- #if cimg_OS==2
- cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::powershell_path());
- std::fprintf(cimg::output()," > Path of 'powershell_path': %s%-13s%s\n",
- cimg::t_bold,
- tmp,
- cimg::t_normal);
- #endif
- std::fprintf(cimg::output(),"\n");
- delete[] tmp;
- }
- // Declare LAPACK function signatures if LAPACK support is enabled.
- #ifdef cimg_use_lapack
- template<typename T>
- inline void getrf(int &N, T *lapA, int *IPIV, int &INFO) {
- dgetrf_(&N,&N,lapA,&N,IPIV,&INFO);
- }
- inline void getrf(int &N, float *lapA, int *IPIV, int &INFO) {
- sgetrf_(&N,&N,lapA,&N,IPIV,&INFO);
- }
- template<typename T>
- inline void getri(int &N, T *lapA, int *IPIV, T* WORK, int &LWORK, int &INFO) {
- dgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);
- }
- inline void getri(int &N, float *lapA, int *IPIV, float* WORK, int &LWORK, int &INFO) {
- sgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);
- }
- template<typename T>
- inline void gesvd(char &JOB, int &M, int &N, T *lapA, int &MN,
- T *lapS, T *lapU, T *lapV, T *WORK, int &LWORK, int &INFO) {
- dgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO);
- }
- inline void gesvd(char &JOB, int &M, int &N, float *lapA, int &MN,
- float *lapS, float *lapU, float *lapV, float *WORK, int &LWORK, int &INFO) {
- sgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO);
- }
- template<typename T>
- inline void getrs(char &TRANS, int &N, T *lapA, int *IPIV, T *lapB, int &INFO) {
- int one = 1;
- dgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO);
- }
- inline void getrs(char &TRANS, int &N, float *lapA, int *IPIV, float *lapB, int &INFO) {
- int one = 1;
- sgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO);
- }
- template<typename T>
- inline void syev(char &JOB, char &UPLO, int &N, T *lapA, T *lapW, T *WORK, int &LWORK, int &INFO) {
- dsyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO);
- }
- inline void syev(char &JOB, char &UPLO, int &N, float *lapA, float *lapW, float *WORK, int &LWORK, int &INFO) {
- ssyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO);
- }
- template<typename T>
- inline void sgels(char & TRANS, int &M, int &N, int &NRHS, T* lapA, int &LDA,
- T* lapB, int &LDB, T* WORK, int &LWORK, int &INFO) {
- dgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO);
- }
- inline void sgels(char & TRANS, int &M, int &N, int &NRHS, float* lapA, int &LDA,
- float* lapB, int &LDB, float* WORK, int &LWORK, int &INFO) {
- sgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO);
- }
- #endif
- } // namespace cimg { ...
- /*------------------------------------------------
- #
- #
- # Definition of mathematical operators and
- # external functions.
- #
- #
- -------------------------------------------------*/
- #define _cimg_create_operator(typ) \
- template<typename T> \
- inline CImg<typename cimg::superset<T,typ>::type> operator+(const typ val, const CImg<T>& img) { \
- return img + val; \
- } \
- template<typename T> \
- inline CImg<typename cimg::superset<T,typ>::type> operator-(const typ val, const CImg<T>& img) { \
- typedef typename cimg::superset<T,typ>::type Tt; \
- return CImg<Tt>(img._width,img._height,img._depth,img._spectrum,val)-=img; \
- } \
- template<typename T> \
- inline CImg<typename cimg::superset<T,typ>::type> operator*(const typ val, const CImg<T>& img) { \
- return img*val; \
- } \
- template<typename T> \
- inline CImg<typename cimg::superset<T,typ>::type> operator/(const typ val, const CImg<T>& img) { \
- return val*img.get_invert(); \
- } \
- template<typename T> \
- inline CImg<typename cimg::superset<T,typ>::type> operator&(const typ val, const CImg<T>& img) { \
- return img & val; \
- } \
- template<typename T> \
- inline CImg<typename cimg::superset<T,typ>::type> operator|(const typ val, const CImg<T>& img) { \
- return img | val; \
- } \
- template<typename T> \
- inline CImg<typename cimg::superset<T,typ>::type> operator^(const typ val, const CImg<T>& img) { \
- return img ^ val; \
- } \
- template<typename T> \
- inline bool operator==(const typ val, const CImg<T>& img) { \
- return img == val; \
- } \
- template<typename T> \
- inline bool operator!=(const typ val, const CImg<T>& img) { \
- return img != val; \
- }
- _cimg_create_operator(bool)
- _cimg_create_operator(unsigned char)
- _cimg_create_operator(char)
- _cimg_create_operator(signed char)
- _cimg_create_operator(unsigned short)
- _cimg_create_operator(short)
- _cimg_create_operator(unsigned int)
- _cimg_create_operator(int)
- _cimg_create_operator(cimg_uint64)
- _cimg_create_operator(cimg_int64)
- _cimg_create_operator(float)
- _cimg_create_operator(double)
- _cimg_create_operator(long double)
- template<typename T>
- inline CImg<_cimg_Tfloat> operator+(const char *const expression, const CImg<T>& img) {
- return img + expression;
- }
- template<typename T>
- inline CImg<_cimg_Tfloat> operator-(const char *const expression, const CImg<T>& img) {
- return CImg<_cimg_Tfloat>(img,false).fill(expression,true)-=img;
- }
- template<typename T>
- inline CImg<_cimg_Tfloat> operator*(const char *const expression, const CImg<T>& img) {
- return img*expression;
- }
- template<typename T>
- inline CImg<_cimg_Tfloat> operator/(const char *const expression, const CImg<T>& img) {
- return expression*img.get_invert();
- }
- template<typename T>
- inline CImg<T> operator&(const char *const expression, const CImg<T>& img) {
- return img & expression;
- }
- template<typename T>
- inline CImg<T> operator|(const char *const expression, const CImg<T>& img) {
- return img | expression;
- }
- template<typename T>
- inline CImg<T> operator^(const char *const expression, const CImg<T>& img) {
- return img ^ expression;
- }
- template<typename T>
- inline bool operator==(const char *const expression, const CImg<T>& img) {
- return img==expression;
- }
- template<typename T>
- inline bool operator!=(const char *const expression, const CImg<T>& img) {
- return img!=expression;
- }
- template<typename T>
- inline CImg<T> transpose(const CImg<T>& instance) {
- return instance.get_transpose();
- }
- template<typename T>
- inline CImg<_cimg_Tfloat> invert(const CImg<T>& instance, const bool use_LU=false, const float lambda=0) {
- return instance.get_invert(use_LU,lambda);
- }
- #define _cimg_create_pointwise_function(name) \
- template<typename T> \
- inline CImg<_cimg_Tfloat> name(const CImg<T>& instance) { \
- return instance.get_##name(); \
- }
- _cimg_create_pointwise_function(sqr)
- _cimg_create_pointwise_function(sqrt)
- _cimg_create_pointwise_function(erf)
- _cimg_create_pointwise_function(exp)
- _cimg_create_pointwise_function(log)
- _cimg_create_pointwise_function(log2)
- _cimg_create_pointwise_function(log10)
- _cimg_create_pointwise_function(abs)
- _cimg_create_pointwise_function(sign)
- _cimg_create_pointwise_function(cos)
- _cimg_create_pointwise_function(sin)
- _cimg_create_pointwise_function(sinc)
- _cimg_create_pointwise_function(tan)
- _cimg_create_pointwise_function(acos)
- _cimg_create_pointwise_function(asin)
- _cimg_create_pointwise_function(atan)
- _cimg_create_pointwise_function(cosh)
- _cimg_create_pointwise_function(sinh)
- _cimg_create_pointwise_function(tanh)
- _cimg_create_pointwise_function(acosh)
- _cimg_create_pointwise_function(asinh)
- _cimg_create_pointwise_function(atanh)
- /*-----------------------------------
- #
- # Define the CImgDisplay structure
- #
- ----------------------------------*/
- //! Allow the creation of windows, display images on them and manage user events (keyboard, mouse and windows events).
- /**
- CImgDisplay methods rely on a low-level graphic library to perform: it can be either \b X-Window
- (X11, for Unix-based systems) or \b GDI32 (for Windows-based systems).
- If both libraries are missing, CImgDisplay will not be able to display images on screen, and will enter
- a minimal mode where warning messages will be outputted each time the program is trying to call one of the
- CImgDisplay method.
- The configuration variable \c cimg_display tells about the graphic library used.
- It is set automatically by \CImg when one of these graphic libraries has been detected.
- But, you can override its value if necessary. Valid choices are:
- - 0: Disable display capabilities.
- - 1: Use \b X-Window (X11) library.
- - 2: Use \b GDI32 library.
- Remember to link your program against \b X11 or \b GDI32 libraries if you use CImgDisplay.
- **/
- struct CImgDisplay {
- cimg_uint64 _timer, _fps_frames, _fps_timer;
- unsigned int _width, _height, _normalization;
- float _fps_fps, _min, _max;
- bool _is_fullscreen;
- char *_title;
- unsigned int _window_width, _window_height, _button, *_keys, *_released_keys;
- int _window_x, _window_y, _mouse_x, _mouse_y, _wheel;
- bool _is_closed, _is_resized, _is_moved, _is_event,
- _is_keyESC, _is_keyF1, _is_keyF2, _is_keyF3, _is_keyF4, _is_keyF5, _is_keyF6, _is_keyF7,
- _is_keyF8, _is_keyF9, _is_keyF10, _is_keyF11, _is_keyF12, _is_keyPAUSE, _is_key1, _is_key2,
- _is_key3, _is_key4, _is_key5, _is_key6, _is_key7, _is_key8, _is_key9, _is_key0,
- _is_keyBACKSPACE, _is_keyINSERT, _is_keyHOME, _is_keyPAGEUP, _is_keyTAB, _is_keyQ, _is_keyW, _is_keyE,
- _is_keyR, _is_keyT, _is_keyY, _is_keyU, _is_keyI, _is_keyO, _is_keyP, _is_keyDELETE,
- _is_keyEND, _is_keyPAGEDOWN, _is_keyCAPSLOCK, _is_keyA, _is_keyS, _is_keyD, _is_keyF, _is_keyG,
- _is_keyH, _is_keyJ, _is_keyK, _is_keyL, _is_keyENTER, _is_keySHIFTLEFT, _is_keyZ, _is_keyX,
- _is_keyC, _is_keyV, _is_keyB, _is_keyN, _is_keyM, _is_keySHIFTRIGHT, _is_keyARROWUP, _is_keyCTRLLEFT,
- _is_keyAPPLEFT, _is_keyALT, _is_keySPACE, _is_keyALTGR, _is_keyAPPRIGHT, _is_keyMENU, _is_keyCTRLRIGHT,
- _is_keyARROWLEFT, _is_keyARROWDOWN, _is_keyARROWRIGHT, _is_keyPAD0, _is_keyPAD1, _is_keyPAD2, _is_keyPAD3,
- _is_keyPAD4, _is_keyPAD5, _is_keyPAD6, _is_keyPAD7, _is_keyPAD8, _is_keyPAD9, _is_keyPADADD, _is_keyPADSUB,
- _is_keyPADMUL, _is_keyPADDIV;
- //@}
- //---------------------------
- //
- //! \name Plugins
- //@{
- //---------------------------
- #ifdef cimgdisplay_plugin
- #include cimgdisplay_plugin
- #endif
- #ifdef cimgdisplay_plugin1
- #include cimgdisplay_plugin1
- #endif
- #ifdef cimgdisplay_plugin2
- #include cimgdisplay_plugin2
- #endif
- #ifdef cimgdisplay_plugin3
- #include cimgdisplay_plugin3
- #endif
- #ifdef cimgdisplay_plugin4
- #include cimgdisplay_plugin4
- #endif
- #ifdef cimgdisplay_plugin5
- #include cimgdisplay_plugin5
- #endif
- #ifdef cimgdisplay_plugin6
- #include cimgdisplay_plugin6
- #endif
- #ifdef cimgdisplay_plugin7
- #include cimgdisplay_plugin7
- #endif
- #ifdef cimgdisplay_plugin8
- #include cimgdisplay_plugin8
- #endif
- //@}
- //--------------------------------------------------------
- //
- //! \name Constructors / Destructor / Instance Management
- //@{
- //--------------------------------------------------------
- //! Destructor.
- /**
- \note If the associated window is visible on the screen, it is closed by the call to the destructor.
- **/
- ~CImgDisplay() {
- assign();
- delete[] _keys;
- delete[] _released_keys;
- }
- //! Construct an empty display.
- /**
- \note Constructing an empty CImgDisplay instance does not make a window appearing on the screen, until
- display of valid data is performed.
- \par Example
- \code
- CImgDisplay disp; // Does actually nothing
- ...
- disp.display(img); // Construct new window and display image in it
- \endcode
- **/
- CImgDisplay():
- _width(0),_height(0),_normalization(0),
- _min(0),_max(0),
- _is_fullscreen(false),
- _title(0),
- _window_width(0),_window_height(0),_button(0),
- _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
- _window_x(cimg::type<int>::min()),_window_y(cimg::type<int>::min()),
- _mouse_x(-1),_mouse_y(-1),_wheel(0),
- _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
- assign();
- }
- //! Construct a display with specified dimensions.
- /** \param width Window width.
- \param height Window height.
- \param title Window title.
- \param normalization Normalization type
- (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()).
- \param is_fullscreen Tells if fullscreen mode is enabled.
- \param is_closed Tells if associated window is initially visible or not.
- \note A black background is initially displayed on the associated window.
- **/
- CImgDisplay(const unsigned int width, const unsigned int height,
- const char *const title=0, const unsigned int normalization=3,
- const bool is_fullscreen=false, const bool is_closed=false):
- _width(0),_height(0),_normalization(0),
- _min(0),_max(0),
- _is_fullscreen(false),
- _title(0),
- _window_width(0),_window_height(0),_button(0),
- _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
- _window_x(cimg::type<int>::min()),_window_y(cimg::type<int>::min()),
- _mouse_x(-1),_mouse_y(-1),_wheel(0),
- _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
- assign(width,height,title,normalization,is_fullscreen,is_closed);
- }
- //! Construct a display from an image.
- /** \param img Image used as a model to create the window.
- \param title Window title.
- \param normalization Normalization type
- (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()).
- \param is_fullscreen Tells if fullscreen mode is enabled.
- \param is_closed Tells if associated window is initially visible or not.
- \note The pixels of the input image are initially displayed on the associated window.
- **/
- template<typename T>
- explicit CImgDisplay(const CImg<T>& img,
- const char *const title=0, const unsigned int normalization=3,
- const bool is_fullscreen=false, const bool is_closed=false):
- _width(0),_height(0),_normalization(0),
- _min(0),_max(0),
- _is_fullscreen(false),
- _title(0),
- _window_width(0),_window_height(0),_button(0),
- _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
- _window_x(cimg::type<int>::min()),_window_y(cimg::type<int>::min()),
- _mouse_x(-1),_mouse_y(-1),_wheel(0),
- _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
- assign(img,title,normalization,is_fullscreen,is_closed);
- }
- //! Construct a display from an image list.
- /** \param list The images list to display.
- \param title Window title.
- \param normalization Normalization type
- (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()).
- \param is_fullscreen Tells if fullscreen mode is enabled.
- \param is_closed Tells if associated window is initially visible or not.
- \note All images of the list, appended along the X-axis, are initially displayed on the associated window.
- **/
- template<typename T>
- explicit CImgDisplay(const CImgList<T>& list,
- const char *const title=0, const unsigned int normalization=3,
- const bool is_fullscreen=false, const bool is_closed=false):
- _width(0),_height(0),_normalization(0),
- _min(0),_max(0),
- _is_fullscreen(false),
- _title(0),
- _window_width(0),_window_height(0),_button(0),
- _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
- _window_x(cimg::type<int>::min()),_window_y(cimg::type<int>::min()),
- _mouse_x(-1),_mouse_y(-1),_wheel(0),
- _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
- assign(list,title,normalization,is_fullscreen,is_closed);
- }
- //! Construct a display as a copy of an existing one.
- /**
- \param disp Display instance to copy.
- \note The pixel buffer of the input window is initially displayed on the associated window.
- **/
- CImgDisplay(const CImgDisplay& disp):
- _width(0),_height(0),_normalization(0),
- _min(0),_max(0),
- _is_fullscreen(false),
- _title(0),
- _window_width(0),_window_height(0),_button(0),
- _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
- _window_x(cimg::type<int>::min()),_window_y(cimg::type<int>::min()),
- _mouse_x(-1),_mouse_y(-1),_wheel(0),
- _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
- assign(disp);
- }
- //! Take a screenshot.
- /**
- \param[out] img Output screenshot. Can be empty on input
- **/
- template<typename T>
- static void screenshot(CImg<T>& img) {
- return screenshot(0,0,cimg::type<int>::max(),cimg::type<int>::max(),img);
- }
- #if cimg_display==0
- static void _no_display_exception() {
- throw CImgDisplayException("CImgDisplay(): No display available.");
- }
- //! Destructor - Empty constructor \inplace.
- /**
- \note Replace the current instance by an empty display.
- **/
- CImgDisplay& assign() {
- return flush();
- }
- //! Construct a display with specified dimensions \inplace.
- /**
- **/
- CImgDisplay& assign(const unsigned int width, const unsigned int height,
- const char *const title=0, const unsigned int normalization=3,
- const bool is_fullscreen=false, const bool is_closed=false) {
- cimg::unused(width,height,title,normalization,is_fullscreen,is_closed);
- _no_display_exception();
- return assign();
- }
- //! Construct a display from an image \inplace.
- /**
- **/
- template<typename T>
- CImgDisplay& assign(const CImg<T>& img,
- const char *const title=0, const unsigned int normalization=3,
- const bool is_fullscreen=false, const bool is_closed=false) {
- _no_display_exception();
- return assign(img._width,img._height,title,normalization,is_fullscreen,is_closed);
- }
- //! Construct a display from an image list \inplace.
- /**
- **/
- template<typename T>
- CImgDisplay& assign(const CImgList<T>& list,
- const char *const title=0, const unsigned int normalization=3,
- const bool is_fullscreen=false, const bool is_closed=false) {
- _no_display_exception();
- return assign(list._width,list._width,title,normalization,is_fullscreen,is_closed);
- }
- //! Construct a display as a copy of another one \inplace.
- /**
- **/
- CImgDisplay& assign(const CImgDisplay &disp) {
- _no_display_exception();
- return assign(disp._width,disp._height);
- }
- #endif
- //! Return a reference to an empty display.
- /**
- \note Can be useful for writing function prototypes where one of the argument (of type CImgDisplay&)
- must have a default value.
- \par Example
- \code
- void foo(CImgDisplay& disp=CImgDisplay::empty());
- \endcode
- **/
- static CImgDisplay& empty() {
- static CImgDisplay _empty;
- return _empty.assign();
- }
- //! Return a reference to an empty display \const.
- static const CImgDisplay& const_empty() {
- static const CImgDisplay _empty;
- return _empty;
- }
- #define cimg_fitscreen(dx,dy,dz) CImgDisplay::_fitscreen(dx,dy,dz,-25,-85,false), \
- CImgDisplay::_fitscreen(dx,dy,dz,-25,-85,true)
- static unsigned int _fitscreen(const unsigned int dx, const unsigned int dy, const unsigned int dz,
- const int dmin, const int dmax, const bool return_y) {
- const int
- u = CImgDisplay::screen_width(),
- v = CImgDisplay::screen_height();
- const float
- mw = dmin<0?cimg::round(u*-dmin/100.f):(float)dmin,
- mh = dmin<0?cimg::round(v*-dmin/100.f):(float)dmin,
- Mw = dmax<0?cimg::round(u*-dmax/100.f):(float)dmax,
- Mh = dmax<0?cimg::round(v*-dmax/100.f):(float)dmax;
- float
- w = (float)std::max(1U,dx),
- h = (float)std::max(1U,dy);
- if (dz>1) { w+=dz; h+=dz; }
- if (w<mw) { h = h*mw/w; w = mw; }
- if (h<mh) { w = w*mh/h; h = mh; }
- if (w>Mw) { h = h*Mw/w; w = Mw; }
- if (h>Mh) { w = w*Mh/h; h = Mh; }
- if (w<mw) w = mw;
- if (h<mh) h = mh;
- return std::max(1U,(unsigned int)cimg::round(return_y?h:w));
- }
- //@}
- //------------------------------------------
- //
- //! \name Overloaded Operators
- //@{
- //------------------------------------------
- //! Display image on associated window.
- /**
- \note <tt>disp = img</tt> is equivalent to <tt>disp.display(img)</tt>.
- **/
- template<typename t>
- CImgDisplay& operator=(const CImg<t>& img) {
- return display(img);
- }
- //! Display list of images on associated window.
- /**
- \note <tt>disp = list</tt> is equivalent to <tt>disp.display(list)</tt>.
- **/
- template<typename t>
- CImgDisplay& operator=(const CImgList<t>& list) {
- return display(list);
- }
- //! Construct a display as a copy of another one \inplace.
- /**
- \note Equivalent to assign(const CImgDisplay&).
- **/
- CImgDisplay& operator=(const CImgDisplay& disp) {
- return assign(disp);
- }
- //! Return \c false if display is empty, \c true otherwise.
- /**
- \note <tt>if (disp) { ... }</tt> is equivalent to <tt>if (!disp.is_empty()) { ... }</tt>.
- **/
- operator bool() const {
- return !is_empty();
- }
- //@}
- //------------------------------------------
- //
- //! \name Instance Checking
- //@{
- //------------------------------------------
- //! Return \c true if display is empty, \c false otherwise.
- /**
- **/
- bool is_empty() const {
- return !(_width && _height);
- }
- //! Return \c true if display is closed (i.e. not visible on the screen), \c false otherwise.
- /**
- \note
- - When a user physically closes the associated window, the display is set to closed.
- - A closed display is not destroyed. Its associated window can be show again on the screen using show().
- **/
- bool is_closed() const {
- return _is_closed;
- }
- //! Return \c true if display is visible (i.e. not closed by the user), \c false otherwise.
- bool is_visible() const {
- return !is_closed();
- }
- //! Return \c true if associated window has been resized on the screen, \c false otherwise.
- /**
- **/
- bool is_resized() const {
- return _is_resized;
- }
- //! Return \c true if associated window has been moved on the screen, \c false otherwise.
- /**
- **/
- bool is_moved() const {
- return _is_moved;
- }
- //! Return \c true if any event has occurred on the associated window, \c false otherwise.
- /**
- **/
- bool is_event() const {
- return _is_event;
- }
- //! Return \c true if current display is in fullscreen mode, \c false otherwise.
- /**
- **/
- bool is_fullscreen() const {
- return _is_fullscreen;
- }
- //! Return \c true if any key is being pressed on the associated window, \c false otherwise.
- /**
- \note The methods below do the same only for specific keys.
- **/
- bool is_key() const {
- return _is_keyESC || _is_keyF1 || _is_keyF2 || _is_keyF3 ||
- _is_keyF4 || _is_keyF5 || _is_keyF6 || _is_keyF7 ||
- _is_keyF8 || _is_keyF9 || _is_keyF10 || _is_keyF11 ||
- _is_keyF12 || _is_keyPAUSE || _is_key1 || _is_key2 ||
- _is_key3 || _is_key4 || _is_key5 || _is_key6 ||
- _is_key7 || _is_key8 || _is_key9 || _is_key0 ||
- _is_keyBACKSPACE || _is_keyINSERT || _is_keyHOME ||
- _is_keyPAGEUP || _is_keyTAB || _is_keyQ || _is_keyW ||
- _is_keyE || _is_keyR || _is_keyT || _is_keyY ||
- _is_keyU || _is_keyI || _is_keyO || _is_keyP ||
- _is_keyDELETE || _is_keyEND || _is_keyPAGEDOWN ||
- _is_keyCAPSLOCK || _is_keyA || _is_keyS || _is_keyD ||
- _is_keyF || _is_keyG || _is_keyH || _is_keyJ ||
- _is_keyK || _is_keyL || _is_keyENTER ||
- _is_keySHIFTLEFT || _is_keyZ || _is_keyX || _is_keyC ||
- _is_keyV || _is_keyB || _is_keyN || _is_keyM ||
- _is_keySHIFTRIGHT || _is_keyARROWUP || _is_keyCTRLLEFT ||
- _is_keyAPPLEFT || _is_keyALT || _is_keySPACE || _is_keyALTGR ||
- _is_keyAPPRIGHT || _is_keyMENU || _is_keyCTRLRIGHT ||
- _is_keyARROWLEFT || _is_keyARROWDOWN || _is_keyARROWRIGHT ||
- _is_keyPAD0 || _is_keyPAD1 || _is_keyPAD2 ||
- _is_keyPAD3 || _is_keyPAD4 || _is_keyPAD5 ||
- _is_keyPAD6 || _is_keyPAD7 || _is_keyPAD8 ||
- _is_keyPAD9 || _is_keyPADADD || _is_keyPADSUB ||
- _is_keyPADMUL || _is_keyPADDIV;
- }
- //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise.
- /**
- \param keycode Keycode to test.
- \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
- your code stay portable (see cimg::keyESC).
- \par Example
- \code
- CImgDisplay disp(400,400);
- while (!disp.is_closed()) {
- if (disp.key(cimg::keyTAB)) { ... } // Equivalent to 'if (disp.is_keyTAB())'
- disp.wait();
- }
- \endcode
- **/
- bool is_key(const unsigned int keycode) const {
- #define _cimg_iskey_test(k) if (keycode==cimg::key##k) return _is_key##k;
- _cimg_iskey_test(ESC); _cimg_iskey_test(F1); _cimg_iskey_test(F2); _cimg_iskey_test(F3);
- _cimg_iskey_test(F4); _cimg_iskey_test(F5); _cimg_iskey_test(F6); _cimg_iskey_test(F7);
- _cimg_iskey_test(F8); _cimg_iskey_test(F9); _cimg_iskey_test(F10); _cimg_iskey_test(F11);
- _cimg_iskey_test(F12); _cimg_iskey_test(PAUSE); _cimg_iskey_test(1); _cimg_iskey_test(2);
- _cimg_iskey_test(3); _cimg_iskey_test(4); _cimg_iskey_test(5); _cimg_iskey_test(6);
- _cimg_iskey_test(7); _cimg_iskey_test(8); _cimg_iskey_test(9); _cimg_iskey_test(0);
- _cimg_iskey_test(BACKSPACE); _cimg_iskey_test(INSERT); _cimg_iskey_test(HOME);
- _cimg_iskey_test(PAGEUP); _cimg_iskey_test(TAB); _cimg_iskey_test(Q); _cimg_iskey_test(W);
- _cimg_iskey_test(E); _cimg_iskey_test(R); _cimg_iskey_test(T); _cimg_iskey_test(Y);
- _cimg_iskey_test(U); _cimg_iskey_test(I); _cimg_iskey_test(O); _cimg_iskey_test(P);
- _cimg_iskey_test(DELETE); _cimg_iskey_test(END); _cimg_iskey_test(PAGEDOWN);
- _cimg_iskey_test(CAPSLOCK); _cimg_iskey_test(A); _cimg_iskey_test(S); _cimg_iskey_test(D);
- _cimg_iskey_test(F); _cimg_iskey_test(G); _cimg_iskey_test(H); _cimg_iskey_test(J);
- _cimg_iskey_test(K); _cimg_iskey_test(L); _cimg_iskey_test(ENTER);
- _cimg_iskey_test(SHIFTLEFT); _cimg_iskey_test(Z); _cimg_iskey_test(X); _cimg_iskey_test(C);
- _cimg_iskey_test(V); _cimg_iskey_test(B); _cimg_iskey_test(N); _cimg_iskey_test(M);
- _cimg_iskey_test(SHIFTRIGHT); _cimg_iskey_test(ARROWUP); _cimg_iskey_test(CTRLLEFT);
- _cimg_iskey_test(APPLEFT); _cimg_iskey_test(ALT); _cimg_iskey_test(SPACE); _cimg_iskey_test(ALTGR);
- _cimg_iskey_test(APPRIGHT); _cimg_iskey_test(MENU); _cimg_iskey_test(CTRLRIGHT);
- _cimg_iskey_test(ARROWLEFT); _cimg_iskey_test(ARROWDOWN); _cimg_iskey_test(ARROWRIGHT);
- _cimg_iskey_test(PAD0); _cimg_iskey_test(PAD1); _cimg_iskey_test(PAD2);
- _cimg_iskey_test(PAD3); _cimg_iskey_test(PAD4); _cimg_iskey_test(PAD5);
- _cimg_iskey_test(PAD6); _cimg_iskey_test(PAD7); _cimg_iskey_test(PAD8);
- _cimg_iskey_test(PAD9); _cimg_iskey_test(PADADD); _cimg_iskey_test(PADSUB);
- _cimg_iskey_test(PADMUL); _cimg_iskey_test(PADDIV);
- return false;
- }
- //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise.
- /**
- \param keycode C-string containing the keycode label of the key to test.
- \note Use it when the key you want to test can be dynamically set by the user.
- \par Example
- \code
- CImgDisplay disp(400,400);
- const char *const keycode = "TAB";
- while (!disp.is_closed()) {
- if (disp.is_key(keycode)) { ... } // Equivalent to 'if (disp.is_keyTAB())'
- disp.wait();
- }
- \endcode
- **/
- bool& is_key(const char *const keycode) {
- static bool f = false;
- f = false;
- #define _cimg_iskey_test2(k) if (!cimg::strcasecmp(keycode,#k)) return _is_key##k;
- _cimg_iskey_test2(ESC); _cimg_iskey_test2(F1); _cimg_iskey_test2(F2); _cimg_iskey_test2(F3);
- _cimg_iskey_test2(F4); _cimg_iskey_test2(F5); _cimg_iskey_test2(F6); _cimg_iskey_test2(F7);
- _cimg_iskey_test2(F8); _cimg_iskey_test2(F9); _cimg_iskey_test2(F10); _cimg_iskey_test2(F11);
- _cimg_iskey_test2(F12); _cimg_iskey_test2(PAUSE); _cimg_iskey_test2(1); _cimg_iskey_test2(2);
- _cimg_iskey_test2(3); _cimg_iskey_test2(4); _cimg_iskey_test2(5); _cimg_iskey_test2(6);
- _cimg_iskey_test2(7); _cimg_iskey_test2(8); _cimg_iskey_test2(9); _cimg_iskey_test2(0);
- _cimg_iskey_test2(BACKSPACE); _cimg_iskey_test2(INSERT); _cimg_iskey_test2(HOME);
- _cimg_iskey_test2(PAGEUP); _cimg_iskey_test2(TAB); _cimg_iskey_test2(Q); _cimg_iskey_test2(W);
- _cimg_iskey_test2(E); _cimg_iskey_test2(R); _cimg_iskey_test2(T); _cimg_iskey_test2(Y);
- _cimg_iskey_test2(U); _cimg_iskey_test2(I); _cimg_iskey_test2(O); _cimg_iskey_test2(P);
- _cimg_iskey_test2(DELETE); _cimg_iskey_test2(END); _cimg_iskey_test2(PAGEDOWN);
- _cimg_iskey_test2(CAPSLOCK); _cimg_iskey_test2(A); _cimg_iskey_test2(S); _cimg_iskey_test2(D);
- _cimg_iskey_test2(F); _cimg_iskey_test2(G); _cimg_iskey_test2(H); _cimg_iskey_test2(J);
- _cimg_iskey_test2(K); _cimg_iskey_test2(L); _cimg_iskey_test2(ENTER);
- _cimg_iskey_test2(SHIFTLEFT); _cimg_iskey_test2(Z); _cimg_iskey_test2(X); _cimg_iskey_test2(C);
- _cimg_iskey_test2(V); _cimg_iskey_test2(B); _cimg_iskey_test2(N); _cimg_iskey_test2(M);
- _cimg_iskey_test2(SHIFTRIGHT); _cimg_iskey_test2(ARROWUP); _cimg_iskey_test2(CTRLLEFT);
- _cimg_iskey_test2(APPLEFT); _cimg_iskey_test2(ALT); _cimg_iskey_test2(SPACE); _cimg_iskey_test2(ALTGR);
- _cimg_iskey_test2(APPRIGHT); _cimg_iskey_test2(MENU); _cimg_iskey_test2(CTRLRIGHT);
- _cimg_iskey_test2(ARROWLEFT); _cimg_iskey_test2(ARROWDOWN); _cimg_iskey_test2(ARROWRIGHT);
- _cimg_iskey_test2(PAD0); _cimg_iskey_test2(PAD1); _cimg_iskey_test2(PAD2);
- _cimg_iskey_test2(PAD3); _cimg_iskey_test2(PAD4); _cimg_iskey_test2(PAD5);
- _cimg_iskey_test2(PAD6); _cimg_iskey_test2(PAD7); _cimg_iskey_test2(PAD8);
- _cimg_iskey_test2(PAD9); _cimg_iskey_test2(PADADD); _cimg_iskey_test2(PADSUB);
- _cimg_iskey_test2(PADMUL); _cimg_iskey_test2(PADDIV);
- return f;
- }
- //! Return \c true if specified key sequence has been typed on the associated window, \c false otherwise.
- /**
- \param keycodes_sequence Buffer of keycodes to test.
- \param length Number of keys in the \c keycodes_sequence buffer.
- \param remove_sequence Tells if the key sequence must be removed from the key history, if found.
- \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
- your code stay portable (see cimg::keyESC).
- \par Example
- \code
- CImgDisplay disp(400,400);
- const unsigned int key_seq[] = { cimg::keyCTRLLEFT, cimg::keyD };
- while (!disp.is_closed()) {
- if (disp.is_key_sequence(key_seq,2)) { ... } // Test for the 'CTRL+D' keyboard event
- disp.wait();
- }
- \endcode
- **/
- bool is_key_sequence(const unsigned int *const keycodes_sequence, const unsigned int length,
- const bool remove_sequence=false) {
- if (keycodes_sequence && length) {
- const unsigned int
- *const ps_end = keycodes_sequence + length - 1,
- *const pk_end = (unsigned int*)_keys + 1 + 128 - length,
- k = *ps_end;
- for (unsigned int *pk = (unsigned int*)_keys; pk<pk_end; ) {
- if (*(pk++)==k) {
- bool res = true;
- const unsigned int *ps = ps_end, *pk2 = pk;
- for (unsigned int i = 1; i<length; ++i) res = (*(--ps)==*(pk2++));
- if (res) {
- if (remove_sequence) std::memset((void*)(pk - 1),0,sizeof(unsigned int)*length);
- return true;
- }
- }
- }
- }
- return false;
- }
- #define _cimg_iskey_def(k) \
- bool is_key##k() const { \
- return _is_key##k; \
- }
- //! Return \c true if the \c ESC key is being pressed on the associated window, \c false otherwise.
- /**
- \note Similar methods exist for all keys managed by \CImg (see cimg::keyESC).
- **/
- _cimg_iskey_def(ESC); _cimg_iskey_def(F1); _cimg_iskey_def(F2); _cimg_iskey_def(F3);
- _cimg_iskey_def(F4); _cimg_iskey_def(F5); _cimg_iskey_def(F6); _cimg_iskey_def(F7);
- _cimg_iskey_def(F8); _cimg_iskey_def(F9); _cimg_iskey_def(F10); _cimg_iskey_def(F11);
- _cimg_iskey_def(F12); _cimg_iskey_def(PAUSE); _cimg_iskey_def(1); _cimg_iskey_def(2);
- _cimg_iskey_def(3); _cimg_iskey_def(4); _cimg_iskey_def(5); _cimg_iskey_def(6);
- _cimg_iskey_def(7); _cimg_iskey_def(8); _cimg_iskey_def(9); _cimg_iskey_def(0);
- _cimg_iskey_def(BACKSPACE); _cimg_iskey_def(INSERT); _cimg_iskey_def(HOME);
- _cimg_iskey_def(PAGEUP); _cimg_iskey_def(TAB); _cimg_iskey_def(Q); _cimg_iskey_def(W);
- _cimg_iskey_def(E); _cimg_iskey_def(R); _cimg_iskey_def(T); _cimg_iskey_def(Y);
- _cimg_iskey_def(U); _cimg_iskey_def(I); _cimg_iskey_def(O); _cimg_iskey_def(P);
- _cimg_iskey_def(DELETE); _cimg_iskey_def(END); _cimg_iskey_def(PAGEDOWN);
- _cimg_iskey_def(CAPSLOCK); _cimg_iskey_def(A); _cimg_iskey_def(S); _cimg_iskey_def(D);
- _cimg_iskey_def(F); _cimg_iskey_def(G); _cimg_iskey_def(H); _cimg_iskey_def(J);
- _cimg_iskey_def(K); _cimg_iskey_def(L); _cimg_iskey_def(ENTER);
- _cimg_iskey_def(SHIFTLEFT); _cimg_iskey_def(Z); _cimg_iskey_def(X); _cimg_iskey_def(C);
- _cimg_iskey_def(V); _cimg_iskey_def(B); _cimg_iskey_def(N); _cimg_iskey_def(M);
- _cimg_iskey_def(SHIFTRIGHT); _cimg_iskey_def(ARROWUP); _cimg_iskey_def(CTRLLEFT);
- _cimg_iskey_def(APPLEFT); _cimg_iskey_def(ALT); _cimg_iskey_def(SPACE); _cimg_iskey_def(ALTGR);
- _cimg_iskey_def(APPRIGHT); _cimg_iskey_def(MENU); _cimg_iskey_def(CTRLRIGHT);
- _cimg_iskey_def(ARROWLEFT); _cimg_iskey_def(ARROWDOWN); _cimg_iskey_def(ARROWRIGHT);
- _cimg_iskey_def(PAD0); _cimg_iskey_def(PAD1); _cimg_iskey_def(PAD2);
- _cimg_iskey_def(PAD3); _cimg_iskey_def(PAD4); _cimg_iskey_def(PAD5);
- _cimg_iskey_def(PAD6); _cimg_iskey_def(PAD7); _cimg_iskey_def(PAD8);
- _cimg_iskey_def(PAD9); _cimg_iskey_def(PADADD); _cimg_iskey_def(PADSUB);
- _cimg_iskey_def(PADMUL); _cimg_iskey_def(PADDIV);
- //@}
- //------------------------------------------
- //
- //! \name Instance Characteristics
- //@{
- //------------------------------------------
- #if cimg_display==0
- //! Return width of the screen (current resolution along the X-axis).
- /**
- **/
- static int screen_width() {
- _no_display_exception();
- return 0;
- }
- //! Return height of the screen (current resolution along the Y-axis).
- /**
- **/
- static int screen_height() {
- _no_display_exception();
- return 0;
- }
- #endif
- //! Return display width.
- /**
- \note The width of the display (i.e. the width of the pixel data buffer associated to the CImgDisplay instance)
- may be different from the actual width of the associated window.
- **/
- int width() const {
- return (int)_width;
- }
- //! Return display height.
- /**
- \note The height of the display (i.e. the height of the pixel data buffer associated to the CImgDisplay instance)
- may be different from the actual height of the associated window.
- **/
- int height() const {
- return (int)_height;
- }
- //! Return normalization type of the display.
- /**
- The normalization type tells about how the values of an input image are normalized by the CImgDisplay to be
- correctly displayed. The range of values for pixels displayed on screen is <tt>[0,255]</tt>.
- If the range of values of the data to display is different, a normalization may be required for displaying
- the data in a correct way. The normalization type can be one of:
- - \c 0: Value normalization is disabled. It is then assumed that all input data to be displayed by the
- CImgDisplay instance have values in range <tt>[0,255]</tt>.
- - \c 1: Value normalization is always performed (this is the default behavior).
- Before displaying an input image, its values will be (virtually) stretched
- in range <tt>[0,255]</tt>, so that the contrast of the displayed pixels will be maximum.
- Use this mode for images whose minimum and maximum values are not prescribed to known values
- (e.g. float-valued images).
- Note that when normalized versions of images are computed for display purposes, the actual values of these
- images are not modified.
- - \c 2: Value normalization is performed once (on the first image display), then the same normalization
- coefficients are kept for next displayed frames.
- - \c 3: Value normalization depends on the pixel type of the data to display. For integer pixel types,
- the normalization is done regarding the minimum/maximum values of the type (no normalization occurs then
- for <tt>unsigned char</tt>).
- For float-valued pixel types, the normalization is done regarding the minimum/maximum value of the image
- data instead.
- **/
- unsigned int normalization() const {
- return _normalization;
- }
- //! Return title of the associated window as a C-string.
- /**
- \note Window title may be not visible, depending on the used window manager or if the current display is
- in fullscreen mode.
- **/
- const char *title() const {
- return _title?_title:"";
- }
- //! Return width of the associated window.
- /**
- \note The width of the display (i.e. the width of the pixel data buffer associated to the CImgDisplay instance)
- may be different from the actual width of the associated window.
- **/
- int window_width() const {
- return (int)_window_width;
- }
- //! Return height of the associated window.
- /**
- \note The height of the display (i.e. the height of the pixel data buffer associated to the CImgDisplay instance)
- may be different from the actual height of the associated window.
- **/
- int window_height() const {
- return (int)_window_height;
- }
- //! Return X-coordinate of the associated window.
- /**
- \note The returned coordinate corresponds to the location of the upper-left corner of the associated window.
- **/
- int window_x() const {
- return _window_x;
- }
- //! Return Y-coordinate of the associated window.
- /**
- \note The returned coordinate corresponds to the location of the upper-left corner of the associated window.
- **/
- int window_y() const {
- return _window_y;
- }
- //! Return X-coordinate of the mouse pointer.
- /**
- \note
- - If the mouse pointer is outside window area, \c -1 is returned.
- - Otherwise, the returned value is in the range [0,width()-1].
- **/
- int mouse_x() const {
- return _mouse_x;
- }
- //! Return Y-coordinate of the mouse pointer.
- /**
- \note
- - If the mouse pointer is outside window area, \c -1 is returned.
- - Otherwise, the returned value is in the range [0,height()-1].
- **/
- int mouse_y() const {
- return _mouse_y;
- }
- //! Return current state of the mouse buttons.
- /**
- \note Three mouse buttons can be managed. If one button is pressed, its corresponding bit in the returned
- value is set:
- - bit \c 0 (value \c 0x1): State of the left mouse button.
- - bit \c 1 (value \c 0x2): State of the right mouse button.
- - bit \c 2 (value \c 0x4): State of the middle mouse button.
- Several bits can be activated if more than one button are pressed at the same time.
- \par Example
- \code
- CImgDisplay disp(400,400);
- while (!disp.is_closed()) {
- if (disp.button()&1) { // Left button clicked
- ...
- }
- if (disp.button()&2) { // Right button clicked
- ...
- }
- if (disp.button()&4) { // Middle button clicked
- ...
- }
- disp.wait();
- }
- \endcode
- **/
- unsigned int button() const {
- return _button;
- }
- //! Return current state of the mouse wheel.
- /**
- \note
- - The returned value can be positive or negative depending on whether the mouse wheel has been scrolled
- forward or backward.
- - Scrolling the wheel forward add \c 1 to the wheel value.
- - Scrolling the wheel backward subtract \c 1 to the wheel value.
- - The returned value cumulates the number of forward of backward scrolls since the creation of the display,
- or since the last reset of the wheel value (using set_wheel()). It is strongly recommended to quickly reset
- the wheel counter when an action has been performed regarding the current wheel value.
- Otherwise, the returned wheel value may be for instance \c 0 despite the fact that many scrolls have been done
- (as many in forward as in backward directions).
- \par Example
- \code
- CImgDisplay disp(400,400);
- while (!disp.is_closed()) {
- if (disp.wheel()) {
- int counter = disp.wheel(); // Read the state of the mouse wheel
- ... // Do what you want with 'counter'
- disp.set_wheel(); // Reset the wheel value to 0
- }
- disp.wait();
- }
- \endcode
- **/
- int wheel() const {
- return _wheel;
- }
- //! Return one entry from the pressed keys history.
- /**
- \param pos Index to read from the pressed keys history (index \c 0 corresponds to latest entry).
- \return Keycode of a pressed key or \c 0 for a released key.
- \note
- - Each CImgDisplay stores a history of the pressed keys in a buffer of size \c 128. When a new key is pressed,
- its keycode is stored in the pressed keys history. When a key is released, \c 0 is put instead.
- This means that up to the 64 last pressed keys may be read from the pressed keys history.
- When a new value is stored, the pressed keys history is shifted so that the latest entry is always
- stored at position \c 0.
- - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
- your code stay portable (see cimg::keyESC).
- **/
- unsigned int& key(const unsigned int pos=0) const {
- static unsigned int key0;
- return pos<128?_keys[pos]:(key0 = 0);
- }
- //! Return one entry from the released keys history.
- /**
- \param pos Index to read from the released keys history (index \c 0 corresponds to latest entry).
- \return Keycode of a released key or \c 0 for a pressed key.
- \note
- - Each CImgDisplay stores a history of the released keys in a buffer of size \c 128. When a new key is released,
- its keycode is stored in the pressed keys history. When a key is pressed, \c 0 is put instead.
- This means that up to the 64 last released keys may be read from the released keys history.
- When a new value is stored, the released keys history is shifted so that the latest entry is always
- stored at position \c 0.
- - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
- your code stay portable (see cimg::keyESC).
- **/
- unsigned int& released_key(const unsigned int pos=0) const {
- static unsigned int key0;
- return pos<128?_released_keys[pos]:(key0 = 0);
- }
- //! Return keycode corresponding to the specified string.
- /**
- \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
- your code stay portable (see cimg::keyESC).
- \par Example
- \code
- const unsigned int keyTAB = CImgDisplay::keycode("TAB"); // Return cimg::keyTAB
- \endcode
- **/
- static unsigned int keycode(const char *const keycode) {
- #define _cimg_keycode(k) if (!cimg::strcasecmp(keycode,#k)) return cimg::key##k;
- _cimg_keycode(ESC); _cimg_keycode(F1); _cimg_keycode(F2); _cimg_keycode(F3);
- _cimg_keycode(F4); _cimg_keycode(F5); _cimg_keycode(F6); _cimg_keycode(F7);
- _cimg_keycode(F8); _cimg_keycode(F9); _cimg_keycode(F10); _cimg_keycode(F11);
- _cimg_keycode(F12); _cimg_keycode(PAUSE); _cimg_keycode(1); _cimg_keycode(2);
- _cimg_keycode(3); _cimg_keycode(4); _cimg_keycode(5); _cimg_keycode(6);
- _cimg_keycode(7); _cimg_keycode(8); _cimg_keycode(9); _cimg_keycode(0);
- _cimg_keycode(BACKSPACE); _cimg_keycode(INSERT); _cimg_keycode(HOME);
- _cimg_keycode(PAGEUP); _cimg_keycode(TAB); _cimg_keycode(Q); _cimg_keycode(W);
- _cimg_keycode(E); _cimg_keycode(R); _cimg_keycode(T); _cimg_keycode(Y);
- _cimg_keycode(U); _cimg_keycode(I); _cimg_keycode(O); _cimg_keycode(P);
- _cimg_keycode(DELETE); _cimg_keycode(END); _cimg_keycode(PAGEDOWN);
- _cimg_keycode(CAPSLOCK); _cimg_keycode(A); _cimg_keycode(S); _cimg_keycode(D);
- _cimg_keycode(F); _cimg_keycode(G); _cimg_keycode(H); _cimg_keycode(J);
- _cimg_keycode(K); _cimg_keycode(L); _cimg_keycode(ENTER);
- _cimg_keycode(SHIFTLEFT); _cimg_keycode(Z); _cimg_keycode(X); _cimg_keycode(C);
- _cimg_keycode(V); _cimg_keycode(B); _cimg_keycode(N); _cimg_keycode(M);
- _cimg_keycode(SHIFTRIGHT); _cimg_keycode(ARROWUP); _cimg_keycode(CTRLLEFT);
- _cimg_keycode(APPLEFT); _cimg_keycode(ALT); _cimg_keycode(SPACE); _cimg_keycode(ALTGR);
- _cimg_keycode(APPRIGHT); _cimg_keycode(MENU); _cimg_keycode(CTRLRIGHT);
- _cimg_keycode(ARROWLEFT); _cimg_keycode(ARROWDOWN); _cimg_keycode(ARROWRIGHT);
- _cimg_keycode(PAD0); _cimg_keycode(PAD1); _cimg_keycode(PAD2);
- _cimg_keycode(PAD3); _cimg_keycode(PAD4); _cimg_keycode(PAD5);
- _cimg_keycode(PAD6); _cimg_keycode(PAD7); _cimg_keycode(PAD8);
- _cimg_keycode(PAD9); _cimg_keycode(PADADD); _cimg_keycode(PADSUB);
- _cimg_keycode(PADMUL); _cimg_keycode(PADDIV);
- return 0;
- }
- //! Return the current refresh rate, in frames per second.
- /**
- \note Returns a significant value when the current instance is used to display successive frames.
- It measures the delay between successive calls to frames_per_second().
- **/
- float frames_per_second() {
- if (!_fps_timer) _fps_timer = cimg::time();
- const float delta = (float)((cimg::time() - _fps_timer)/1000.f);
- ++_fps_frames;
- if (delta>=1) {
- _fps_fps = _fps_frames/delta;
- _fps_frames = 0;
- _fps_timer = cimg::time();
- }
- return _fps_fps;
- }
- // Move current display window so that its content stays inside the current screen.
- CImgDisplay& move_inside_screen() {
- if (is_empty()) return *this;
- const int
- x0 = window_x(),
- y0 = window_y(),
- x1 = x0 + window_width() - 1,
- y1 = y0 + window_height() - 1,
- sw = CImgDisplay::screen_width(),
- sh = CImgDisplay::screen_height();
- if (x0<0 || y0<0 || x1>=sw || y1>=sh)
- move(std::max(0,std::min(x0,sw - x1 + x0)),
- std::max(0,std::min(y0,sh - y1 + y0)));
- return *this;
- }
- //@}
- //---------------------------------------
- //
- //! \name Window Manipulation
- //@{
- //---------------------------------------
- #if cimg_display==0
- //! Display image on associated window.
- /**
- \param img Input image to display.
- \note This method returns immediately.
- **/
- template<typename T>
- CImgDisplay& display(const CImg<T>& img) {
- return assign(img);
- }
- #endif
- //! Display list of images on associated window.
- /**
- \param list List of images to display.
- \param axis Axis used to append the images along, for the visualization (can be \c x, \c y, \c z or \c c).
- \param align Relative position of aligned images when displaying lists with images of different sizes
- (\c 0 for upper-left, \c 0.5 for centering and \c 1 for lower-right).
- \note This method returns immediately.
- **/
- template<typename T>
- CImgDisplay& display(const CImgList<T>& list, const char axis='x', const float align=0) {
- if (list._width==1) {
- const CImg<T>& img = list[0];
- if (img._depth==1 && (img._spectrum==1 || img._spectrum>=3) && _normalization!=1) return display(img);
- }
- CImgList<typename CImg<T>::ucharT> visu(list._width);
- unsigned int dims = 0;
- cimglist_for(list,l) {
- const CImg<T>& img = list._data[l];
- img._get_select(*this,_normalization,(img._width - 1)/2,(img._height - 1)/2,
- (img._depth - 1)/2).move_to(visu[l]);
- dims = std::max(dims,visu[l]._spectrum);
- }
- cimglist_for(list,l) if (visu[l]._spectrum<dims) visu[l].resize(-100,-100,-100,dims,1);
- visu.get_append(axis,align).display(*this);
- return *this;
- }
- #if cimg_display==0
- //! Show (closed) associated window on the screen.
- /**
- \note
- - Force the associated window of a display to be visible on the screen, even if it has been closed before.
- - Using show() on a visible display does nothing.
- **/
- CImgDisplay& show() {
- return assign();
- }
- //! Close (visible) associated window and make it disappear from the screen.
- /**
- \note
- - A closed display only means the associated window is not visible anymore. This does not mean the display has
- been destroyed.
- Use show() to make the associated window reappear.
- - Using close() on a closed display does nothing.
- **/
- CImgDisplay& close() {
- return assign();
- }
- //! Move associated window to a new location.
- /**
- \param pos_x X-coordinate of the new window location.
- \param pos_y Y-coordinate of the new window location.
- \note Depending on the window manager behavior, this method may not succeed (no exceptions are thrown
- nevertheless).
- **/
- CImgDisplay& move(const int pos_x, const int pos_y) {
- return assign(pos_x,pos_y);
- }
- #endif
- //! Resize display to the size of the associated window.
- /**
- \param force_redraw Tells if the previous window content must be updated and refreshed as well.
- \note
- - Calling this method ensures that width() and window_width() become equal, as well as height() and
- window_height().
- - The associated window is also resized to specified dimensions.
- **/
- CImgDisplay& resize(const bool force_redraw=true) {
- resize(window_width(),window_height(),force_redraw);
- return *this;
- }
- #if cimg_display==0
- //! Resize display to the specified size.
- /**
- \param width Requested display width.
- \param height Requested display height.
- \param force_redraw Tells if the previous window content must be updated and refreshed as well.
- \note The associated window is also resized to specified dimensions.
- **/
- CImgDisplay& resize(const int width, const int height, const bool force_redraw=true) {
- return assign(width,height,0,3,force_redraw);
- }
- #endif
- //! Resize display to the size of an input image.
- /**
- \param img Input image to take size from.
- \param force_redraw Tells if the previous window content must be resized and updated as well.
- \note
- - Calling this method ensures that width() and <tt>img.width()</tt> become equal, as well as height() and
- <tt>img.height()</tt>.
- - The associated window is also resized to specified dimensions.
- **/
- template<typename T>
- CImgDisplay& resize(const CImg<T>& img, const bool force_redraw=true) {
- return resize(img._width,img._height,force_redraw);
- }
- //! Resize display to the size of another CImgDisplay instance.
- /**
- \param disp Input display to take size from.
- \param force_redraw Tells if the previous window content must be resized and updated as well.
- \note
- - Calling this method ensures that width() and <tt>disp.width()</tt> become equal, as well as height() and
- <tt>disp.height()</tt>.
- - The associated window is also resized to specified dimensions.
- **/
- CImgDisplay& resize(const CImgDisplay& disp, const bool force_redraw=true) {
- return resize(disp.width(),disp.height(),force_redraw);
- }
- // [internal] Render pixel buffer with size (wd,hd) from source buffer of size (ws,hs).
- template<typename t, typename T>
- static void _render_resize(const T *ptrs, const unsigned int ws, const unsigned int hs,
- t *ptrd, const unsigned int wd, const unsigned int hd) {
- typedef typename cimg::last<T,cimg_ulong>::type ulongT;
- const ulongT one = (ulongT)1;
- CImg<ulongT> off_x(wd), off_y(hd + 1);
- if (wd==ws) off_x.fill(1);
- else {
- ulongT *poff_x = off_x._data, curr = 0;
- for (unsigned int x = 0; x<wd; ++x) {
- const ulongT old = curr;
- curr = (x + one)*ws/wd;
- *(poff_x++) = curr - old;
- }
- }
- if (hd==hs) off_y.fill(ws);
- else {
- ulongT *poff_y = off_y._data, curr = 0;
- for (unsigned int y = 0; y<hd; ++y) {
- const ulongT old = curr;
- curr = (y + one)*hs/hd;
- *(poff_y++) = ws*(curr - old);
- }
- *poff_y = 0;
- }
- ulongT *poff_y = off_y._data;
- for (unsigned int y = 0; y<hd; ) {
- const T *ptr = ptrs;
- ulongT *poff_x = off_x._data;
- for (unsigned int x = 0; x<wd; ++x) { *(ptrd++) = *ptr; ptr+=*(poff_x++); }
- ++y;
- ulongT dy = *(poff_y++);
- for ( ; !dy && y<hd; std::memcpy(ptrd,ptrd - wd,sizeof(t)*wd), ++y, ptrd+=wd, dy = *(poff_y++)) {}
- ptrs+=dy;
- }
- }
- //! Set normalization type.
- /**
- \param normalization New normalization mode.
- **/
- CImgDisplay& set_normalization(const unsigned int normalization) {
- _normalization = normalization;
- _min = _max = 0;
- return *this;
- }
- #if cimg_display==0
- //! Set title of the associated window.
- /**
- \param format C-string containing the format of the title, as with <tt>std::printf()</tt>.
- \warning As the first argument is a format string, it is highly recommended to write
- \code
- disp.set_title("%s",window_title);
- \endcode
- instead of
- \code
- disp.set_title(window_title);
- \endcode
- if \c window_title can be arbitrary, to prevent nasty memory access.
- **/
- CImgDisplay& set_title(const char *const format, ...) {
- return assign(0,0,format);
- }
- #endif
- //! Enable or disable fullscreen mode.
- /**
- \param is_fullscreen Tells is the fullscreen mode must be activated or not.
- \param force_redraw Tells if the previous window content must be displayed as well.
- \note
- - When the fullscreen mode is enabled, the associated window fills the entire screen but the size of the
- current display is not modified.
- - The screen resolution may be switched to fit the associated window size and ensure it appears the largest
- as possible.
- For X-Window (X11) users, the configuration flag \c cimg_use_xrandr has to be set to allow the screen
- resolution change (requires the X11 extensions to be enabled).
- **/
- CImgDisplay& set_fullscreen(const bool is_fullscreen, const bool force_redraw=true) {
- if (is_empty() || _is_fullscreen==is_fullscreen) return *this;
- return toggle_fullscreen(force_redraw);
- }
- #if cimg_display==0
- //! Toggle fullscreen mode.
- /**
- \param force_redraw Tells if the previous window content must be displayed as well.
- \note Enable fullscreen mode if it was not enabled, and disable it otherwise.
- **/
- CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
- return assign(_width,_height,0,3,force_redraw);
- }
- //! Show mouse pointer.
- /**
- \note Depending on the window manager behavior, this method may not succeed
- (no exceptions are thrown nevertheless).
- **/
- CImgDisplay& show_mouse() {
- return assign();
- }
- //! Hide mouse pointer.
- /**
- \note Depending on the window manager behavior, this method may not succeed
- (no exceptions are thrown nevertheless).
- **/
- CImgDisplay& hide_mouse() {
- return assign();
- }
- //! Move mouse pointer to a specified location.
- /**
- \note Depending on the window manager behavior, this method may not succeed
- (no exceptions are thrown nevertheless).
- **/
- CImgDisplay& set_mouse(const int pos_x, const int pos_y) {
- return assign(pos_x,pos_y);
- }
- #endif
- //! Simulate a mouse button release event.
- /**
- \note All mouse buttons are considered released at the same time.
- **/
- CImgDisplay& set_button() {
- _button = 0;
- _is_event = true;
- #if cimg_display==1
- pthread_cond_broadcast(&cimg::X11_attr().wait_event);
- #elif cimg_display==2
- SetEvent(cimg::Win32_attr().wait_event);
- #endif
- return *this;
- }
- //! Simulate a mouse button press or release event.
- /**
- \param button Buttons event code, where each button is associated to a single bit.
- \param is_pressed Tells if the mouse button is considered as pressed or released.
- **/
- CImgDisplay& set_button(const unsigned int button, const bool is_pressed=true) {
- const unsigned int buttoncode = button==1U?1U:button==2U?2U:button==3U?4U:0U;
- if (is_pressed) _button |= buttoncode; else _button &= ~buttoncode;
- _is_event = buttoncode?true:false;
- if (buttoncode) {
- #if cimg_display==1
- pthread_cond_broadcast(&cimg::X11_attr().wait_event);
- #elif cimg_display==2
- SetEvent(cimg::Win32_attr().wait_event);
- #endif
- }
- return *this;
- }
- //! Flush all mouse wheel events.
- /**
- \note Make wheel() to return \c 0, if called afterwards.
- **/
- CImgDisplay& set_wheel() {
- _wheel = 0;
- _is_event = true;
- #if cimg_display==1
- pthread_cond_broadcast(&cimg::X11_attr().wait_event);
- #elif cimg_display==2
- SetEvent(cimg::Win32_attr().wait_event);
- #endif
- return *this;
- }
- //! Simulate a wheel event.
- /**
- \param amplitude Amplitude of the wheel scrolling to simulate.
- \note Make wheel() to return \c amplitude, if called afterwards.
- **/
- CImgDisplay& set_wheel(const int amplitude) {
- _wheel+=amplitude;
- _is_event = amplitude?true:false;
- if (amplitude) {
- #if cimg_display==1
- pthread_cond_broadcast(&cimg::X11_attr().wait_event);
- #elif cimg_display==2
- SetEvent(cimg::Win32_attr().wait_event);
- #endif
- }
- return *this;
- }
- //! Flush all key events.
- /**
- \note Make key() to return \c 0, if called afterwards.
- **/
- CImgDisplay& set_key() {
- std::memset((void*)_keys,0,128*sizeof(unsigned int));
- std::memset((void*)_released_keys,0,128*sizeof(unsigned int));
- _is_keyESC = _is_keyF1 = _is_keyF2 = _is_keyF3 = _is_keyF4 = _is_keyF5 = _is_keyF6 = _is_keyF7 = _is_keyF8 =
- _is_keyF9 = _is_keyF10 = _is_keyF11 = _is_keyF12 = _is_keyPAUSE = _is_key1 = _is_key2 = _is_key3 = _is_key4 =
- _is_key5 = _is_key6 = _is_key7 = _is_key8 = _is_key9 = _is_key0 = _is_keyBACKSPACE = _is_keyINSERT =
- _is_keyHOME = _is_keyPAGEUP = _is_keyTAB = _is_keyQ = _is_keyW = _is_keyE = _is_keyR = _is_keyT = _is_keyY =
- _is_keyU = _is_keyI = _is_keyO = _is_keyP = _is_keyDELETE = _is_keyEND = _is_keyPAGEDOWN = _is_keyCAPSLOCK =
- _is_keyA = _is_keyS = _is_keyD = _is_keyF = _is_keyG = _is_keyH = _is_keyJ = _is_keyK = _is_keyL =
- _is_keyENTER = _is_keySHIFTLEFT = _is_keyZ = _is_keyX = _is_keyC = _is_keyV = _is_keyB = _is_keyN =
- _is_keyM = _is_keySHIFTRIGHT = _is_keyARROWUP = _is_keyCTRLLEFT = _is_keyAPPLEFT = _is_keyALT = _is_keySPACE =
- _is_keyALTGR = _is_keyAPPRIGHT = _is_keyMENU = _is_keyCTRLRIGHT = _is_keyARROWLEFT = _is_keyARROWDOWN =
- _is_keyARROWRIGHT = _is_keyPAD0 = _is_keyPAD1 = _is_keyPAD2 = _is_keyPAD3 = _is_keyPAD4 = _is_keyPAD5 =
- _is_keyPAD6 = _is_keyPAD7 = _is_keyPAD8 = _is_keyPAD9 = _is_keyPADADD = _is_keyPADSUB = _is_keyPADMUL =
- _is_keyPADDIV = false;
- _is_event = true;
- #if cimg_display==1
- pthread_cond_broadcast(&cimg::X11_attr().wait_event);
- #elif cimg_display==2
- SetEvent(cimg::Win32_attr().wait_event);
- #endif
- return *this;
- }
- //! Simulate a keyboard press/release event.
- /**
- \param keycode Keycode of the associated key.
- \param is_pressed Tells if the key is considered as pressed or released.
- \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
- your code stay portable (see cimg::keyESC).
- **/
- CImgDisplay& set_key(const unsigned int keycode, const bool is_pressed=true) {
- #define _cimg_set_key(k) if (keycode==cimg::key##k) _is_key##k = is_pressed;
- _cimg_set_key(ESC); _cimg_set_key(F1); _cimg_set_key(F2); _cimg_set_key(F3);
- _cimg_set_key(F4); _cimg_set_key(F5); _cimg_set_key(F6); _cimg_set_key(F7);
- _cimg_set_key(F8); _cimg_set_key(F9); _cimg_set_key(F10); _cimg_set_key(F11);
- _cimg_set_key(F12); _cimg_set_key(PAUSE); _cimg_set_key(1); _cimg_set_key(2);
- _cimg_set_key(3); _cimg_set_key(4); _cimg_set_key(5); _cimg_set_key(6);
- _cimg_set_key(7); _cimg_set_key(8); _cimg_set_key(9); _cimg_set_key(0);
- _cimg_set_key(BACKSPACE); _cimg_set_key(INSERT); _cimg_set_key(HOME);
- _cimg_set_key(PAGEUP); _cimg_set_key(TAB); _cimg_set_key(Q); _cimg_set_key(W);
- _cimg_set_key(E); _cimg_set_key(R); _cimg_set_key(T); _cimg_set_key(Y);
- _cimg_set_key(U); _cimg_set_key(I); _cimg_set_key(O); _cimg_set_key(P);
- _cimg_set_key(DELETE); _cimg_set_key(END); _cimg_set_key(PAGEDOWN);
- _cimg_set_key(CAPSLOCK); _cimg_set_key(A); _cimg_set_key(S); _cimg_set_key(D);
- _cimg_set_key(F); _cimg_set_key(G); _cimg_set_key(H); _cimg_set_key(J);
- _cimg_set_key(K); _cimg_set_key(L); _cimg_set_key(ENTER);
- _cimg_set_key(SHIFTLEFT); _cimg_set_key(Z); _cimg_set_key(X); _cimg_set_key(C);
- _cimg_set_key(V); _cimg_set_key(B); _cimg_set_key(N); _cimg_set_key(M);
- _cimg_set_key(SHIFTRIGHT); _cimg_set_key(ARROWUP); _cimg_set_key(CTRLLEFT);
- _cimg_set_key(APPLEFT); _cimg_set_key(ALT); _cimg_set_key(SPACE); _cimg_set_key(ALTGR);
- _cimg_set_key(APPRIGHT); _cimg_set_key(MENU); _cimg_set_key(CTRLRIGHT);
- _cimg_set_key(ARROWLEFT); _cimg_set_key(ARROWDOWN); _cimg_set_key(ARROWRIGHT);
- _cimg_set_key(PAD0); _cimg_set_key(PAD1); _cimg_set_key(PAD2);
- _cimg_set_key(PAD3); _cimg_set_key(PAD4); _cimg_set_key(PAD5);
- _cimg_set_key(PAD6); _cimg_set_key(PAD7); _cimg_set_key(PAD8);
- _cimg_set_key(PAD9); _cimg_set_key(PADADD); _cimg_set_key(PADSUB);
- _cimg_set_key(PADMUL); _cimg_set_key(PADDIV);
- if (is_pressed) {
- if (*_keys)
- std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int));
- *_keys = keycode;
- if (*_released_keys) {
- std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int));
- *_released_keys = 0;
- }
- } else {
- if (*_keys) {
- std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int));
- *_keys = 0;
- }
- if (*_released_keys)
- std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int));
- *_released_keys = keycode;
- }
- _is_event = keycode?true:false;
- if (keycode) {
- #if cimg_display==1
- pthread_cond_broadcast(&cimg::X11_attr().wait_event);
- #elif cimg_display==2
- SetEvent(cimg::Win32_attr().wait_event);
- #endif
- }
- return *this;
- }
- //! Flush all display events.
- /**
- \note Remove all passed events from the current display.
- **/
- CImgDisplay& flush() {
- set_key().set_button().set_wheel();
- _is_resized = _is_moved = _is_event = false;
- _fps_timer = _fps_frames = _timer = 0;
- _fps_fps = 0;
- return *this;
- }
- //! Wait for any user event occurring on the current display.
- CImgDisplay& wait() {
- wait(*this);
- return *this;
- }
- //! Wait for a given number of milliseconds since the last call to wait().
- /**
- \param milliseconds Number of milliseconds to wait for.
- \note Similar to cimg::wait().
- **/
- CImgDisplay& wait(const unsigned int milliseconds) {
- cimg::wait(milliseconds,&_timer);
- return *this;
- }
- //! Wait for any event occurring on the display \c disp1.
- static void wait(CImgDisplay& disp1) {
- disp1._is_event = false;
- while (!disp1._is_closed && !disp1._is_event) wait_all();
- }
- //! Wait for any event occurring either on the display \c disp1 or \c disp2.
- static void wait(CImgDisplay& disp1, CImgDisplay& disp2) {
- disp1._is_event = disp2._is_event = false;
- while ((!disp1._is_closed || !disp2._is_closed) &&
- !disp1._is_event && !disp2._is_event) wait_all();
- }
- //! Wait for any event occurring either on the display \c disp1, \c disp2 or \c disp3.
- static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3) {
- disp1._is_event = disp2._is_event = disp3._is_event = false;
- while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed) &&
- !disp1._is_event && !disp2._is_event && !disp3._is_event) wait_all();
- }
- //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3 or \c disp4.
- static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4) {
- disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = false;
- while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed) &&
- !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event) wait_all();
- }
- //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4 or \c disp5.
- static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4,
- CImgDisplay& disp5) {
- disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = false;
- while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed) &&
- !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event)
- wait_all();
- }
- //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp6.
- static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
- CImgDisplay& disp6) {
- disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
- disp6._is_event = false;
- while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
- !disp6._is_closed) &&
- !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
- !disp6._is_event) wait_all();
- }
- //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp7.
- static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
- CImgDisplay& disp6, CImgDisplay& disp7) {
- disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
- disp6._is_event = disp7._is_event = false;
- while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
- !disp6._is_closed || !disp7._is_closed) &&
- !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
- !disp6._is_event && !disp7._is_event) wait_all();
- }
- //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp8.
- static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
- CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8) {
- disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
- disp6._is_event = disp7._is_event = disp8._is_event = false;
- while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
- !disp6._is_closed || !disp7._is_closed || !disp8._is_closed) &&
- !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
- !disp6._is_event && !disp7._is_event && !disp8._is_event) wait_all();
- }
- //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp9.
- static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
- CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9) {
- disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
- disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = false;
- while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
- !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed) &&
- !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
- !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event) wait_all();
- }
- //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp10.
- static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
- CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9,
- CImgDisplay& disp10) {
- disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
- disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = disp10._is_event = false;
- while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
- !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed || !disp10._is_closed) &&
- !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
- !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event && !disp10._is_event)
- wait_all();
- }
- #if cimg_display==0
- //! Wait for any window event occurring in any opened CImgDisplay.
- static void wait_all() {
- return _no_display_exception();
- }
- //! Render image into internal display buffer.
- /**
- \param img Input image data to render.
- \note
- - Convert image data representation into the internal display buffer (architecture-dependent structure).
- - The content of the associated window is not modified, until paint() is called.
- - Should not be used for common CImgDisplay uses, since display() is more useful.
- **/
- template<typename T>
- CImgDisplay& render(const CImg<T>& img) {
- return assign(img);
- }
- //! Paint internal display buffer on associated window.
- /**
- \note
- - Update the content of the associated window with the internal display buffer, e.g. after a render() call.
- - Should not be used for common CImgDisplay uses, since display() is more useful.
- **/
- CImgDisplay& paint() {
- return assign();
- }
- //! Take a snapshot of the current screen content.
- /**
- \param x0 X-coordinate of the upper left corner.
- \param y0 Y-coordinate of the upper left corner.
- \param x1 X-coordinate of the lower right corner.
- \param y1 Y-coordinate of the lower right corner.
- \param[out] img Output screenshot. Can be empty on input
- **/
- template<typename T>
- static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) {
- cimg::unused(x0,y0,x1,y1,&img);
- _no_display_exception();
- }
- //! Take a snapshot of the associated window content.
- /**
- \param[out] img Output snapshot. Can be empty on input.
- **/
- template<typename T>
- const CImgDisplay& snapshot(CImg<T>& img) const {
- cimg::unused(img);
- _no_display_exception();
- return *this;
- }
- #endif
- // X11-based implementation
- //--------------------------
- #if cimg_display==1
- Atom _wm_window_atom, _wm_protocol_atom;
- Window _window, _background_window;
- Colormap _colormap;
- XImage *_image;
- void *_data;
- #ifdef cimg_use_xshm
- XShmSegmentInfo *_shminfo;
- #endif
- static int screen_width() {
- Display *const dpy = cimg::X11_attr().display;
- int res = 0;
- if (!dpy) {
- Display *const _dpy = XOpenDisplay(0);
- if (!_dpy)
- throw CImgDisplayException("CImgDisplay::screen_width(): Failed to open X11 display.");
- res = DisplayWidth(_dpy,DefaultScreen(_dpy));
- XCloseDisplay(_dpy);
- } else {
- #ifdef cimg_use_xrandr
- if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
- res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width;
- else res = DisplayWidth(dpy,DefaultScreen(dpy));
- #else
- res = DisplayWidth(dpy,DefaultScreen(dpy));
- #endif
- }
- return res;
- }
- static int screen_height() {
- Display *const dpy = cimg::X11_attr().display;
- int res = 0;
- if (!dpy) {
- Display *const _dpy = XOpenDisplay(0);
- if (!_dpy)
- throw CImgDisplayException("CImgDisplay::screen_height(): Failed to open X11 display.");
- res = DisplayHeight(_dpy,DefaultScreen(_dpy));
- XCloseDisplay(_dpy);
- } else {
- #ifdef cimg_use_xrandr
- if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
- res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height;
- else res = DisplayHeight(dpy,DefaultScreen(dpy));
- #else
- res = DisplayHeight(dpy,DefaultScreen(dpy));
- #endif
- }
- return res;
- }
- static void wait_all() {
- if (!cimg::X11_attr().display) return;
- pthread_mutex_lock(&cimg::X11_attr().wait_event_mutex);
- pthread_cond_wait(&cimg::X11_attr().wait_event,&cimg::X11_attr().wait_event_mutex);
- pthread_mutex_unlock(&cimg::X11_attr().wait_event_mutex);
- }
- void _handle_events(const XEvent *const pevent) {
- Display *const dpy = cimg::X11_attr().display;
- XEvent event = *pevent;
- switch (event.type) {
- case ClientMessage : {
- if ((int)event.xclient.message_type==(int)_wm_protocol_atom &&
- (int)event.xclient.data.l[0]==(int)_wm_window_atom) {
- XUnmapWindow(cimg::X11_attr().display,_window);
- _is_closed = _is_event = true;
- pthread_cond_broadcast(&cimg::X11_attr().wait_event);
- }
- } break;
- case ConfigureNotify : {
- while (XCheckWindowEvent(dpy,_window,StructureNotifyMask,&event)) {}
- const unsigned int nw = event.xconfigure.width, nh = event.xconfigure.height;
- const int nx = event.xconfigure.x, ny = event.xconfigure.y;
- if (nw && nh && (nw!=_window_width || nh!=_window_height)) {
- _window_width = nw; _window_height = nh; _mouse_x = _mouse_y = -1;
- XResizeWindow(dpy,_window,_window_width,_window_height);
- _is_resized = _is_event = true;
- pthread_cond_broadcast(&cimg::X11_attr().wait_event);
- }
- if (nx!=_window_x || ny!=_window_y) {
- _window_x = nx;
- _window_y = ny;
- _is_moved = _is_event = true;
- pthread_cond_broadcast(&cimg::X11_attr().wait_event);
- }
- } break;
- case Expose : {
- while (XCheckWindowEvent(dpy,_window,ExposureMask,&event)) {}
- _paint(false);
- if (_is_fullscreen) {
- XWindowAttributes attr;
- do {
- XGetWindowAttributes(dpy,_window,&attr);
- if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); }
- } while (attr.map_state!=IsViewable);
- XSetInputFocus(dpy,_window,RevertToParent,CurrentTime);
- }
- } break;
- case ButtonPress : {
- do {
- _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
- if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
- switch (event.xbutton.button) {
- case 1 : set_button(1); break;
- case 3 : set_button(2); break;
- case 2 : set_button(3); break;
- }
- } while (XCheckWindowEvent(dpy,_window,ButtonPressMask,&event));
- } break;
- case ButtonRelease : {
- do {
- _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
- if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
- switch (event.xbutton.button) {
- case 1 : set_button(1,false); break;
- case 3 : set_button(2,false); break;
- case 2 : set_button(3,false); break;
- case 4 : set_wheel(1); break;
- case 5 : set_wheel(-1); break;
- }
- } while (XCheckWindowEvent(dpy,_window,ButtonReleaseMask,&event));
- } break;
- case KeyPress : {
- char tmp = 0; KeySym ksym;
- XLookupString(&event.xkey,&tmp,1,&ksym,0);
- set_key((unsigned int)ksym,true);
- } break;
- case KeyRelease : {
- char keys_return[32]; // Check that the key has been physically unpressed
- XQueryKeymap(dpy,keys_return);
- const unsigned int kc = event.xkey.keycode, kc1 = kc/8, kc2 = kc%8;
- const bool is_key_pressed = kc1>=32?false:(keys_return[kc1]>>kc2)&1;
- if (!is_key_pressed) {
- char tmp = 0; KeySym ksym;
- XLookupString(&event.xkey,&tmp,1,&ksym,0);
- set_key((unsigned int)ksym,false);
- }
- } break;
- case EnterNotify: {
- while (XCheckWindowEvent(dpy,_window,EnterWindowMask,&event)) {}
- _mouse_x = event.xmotion.x;
- _mouse_y = event.xmotion.y;
- if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
- } break;
- case LeaveNotify : {
- while (XCheckWindowEvent(dpy,_window,LeaveWindowMask,&event)) {}
- _mouse_x = _mouse_y = -1; _is_event = true;
- pthread_cond_broadcast(&cimg::X11_attr().wait_event);
- } break;
- case MotionNotify : {
- while (XCheckWindowEvent(dpy,_window,PointerMotionMask,&event)) {}
- _mouse_x = event.xmotion.x;
- _mouse_y = event.xmotion.y;
- if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
- _is_event = true;
- pthread_cond_broadcast(&cimg::X11_attr().wait_event);
- } break;
- }
- }
- static void* _events_thread(void *arg) { // Thread to manage events for all opened display windows
- Display *const dpy = cimg::X11_attr().display;
- XEvent event;
- pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0);
- pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0);
- if (!arg) for ( ; ; ) {
- cimg_lock_display();
- bool event_flag = XCheckTypedEvent(dpy,ClientMessage,&event);
- if (!event_flag) event_flag = XCheckMaskEvent(dpy,
- ExposureMask | StructureNotifyMask | ButtonPressMask |
- KeyPressMask | PointerMotionMask | EnterWindowMask |
- LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask,&event);
- if (event_flag)
- for (unsigned int i = 0; i<cimg::X11_attr().nb_wins; ++i)
- if (!cimg::X11_attr().wins[i]->_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window)
- cimg::X11_attr().wins[i]->_handle_events(&event);
- cimg_unlock_display();
- pthread_testcancel();
- cimg::sleep(8);
- }
- return 0;
- }
- void _set_colormap(Colormap& cmap, const unsigned int dim) {
- XColor *const colormap = new XColor[256];
- switch (dim) {
- case 1 : { // colormap for greyscale images
- for (unsigned int index = 0; index<256; ++index) {
- colormap[index].pixel = index;
- colormap[index].red = colormap[index].green = colormap[index].blue = (unsigned short)(index<<8);
- colormap[index].flags = DoRed | DoGreen | DoBlue;
- }
- } break;
- case 2 : { // colormap for RG images
- for (unsigned int index = 0, r = 8; r<256; r+=16)
- for (unsigned int g = 8; g<256; g+=16) {
- colormap[index].pixel = index;
- colormap[index].red = colormap[index].blue = (unsigned short)(r<<8);
- colormap[index].green = (unsigned short)(g<<8);
- colormap[index++].flags = DoRed | DoGreen | DoBlue;
- }
- } break;
- default : { // colormap for RGB images
- for (unsigned int index = 0, r = 16; r<256; r+=32)
- for (unsigned int g = 16; g<256; g+=32)
- for (unsigned int b = 32; b<256; b+=64) {
- colormap[index].pixel = index;
- colormap[index].red = (unsigned short)(r<<8);
- colormap[index].green = (unsigned short)(g<<8);
- colormap[index].blue = (unsigned short)(b<<8);
- colormap[index++].flags = DoRed | DoGreen | DoBlue;
- }
- }
- }
- XStoreColors(cimg::X11_attr().display,cmap,colormap,256);
- delete[] colormap;
- }
- void _map_window() {
- Display *const dpy = cimg::X11_attr().display;
- bool is_exposed = false, is_mapped = false;
- XWindowAttributes attr;
- XEvent event;
- XMapRaised(dpy,_window);
- do { // Wait for the window to be mapped
- XWindowEvent(dpy,_window,StructureNotifyMask | ExposureMask,&event);
- switch (event.type) {
- case MapNotify : is_mapped = true; break;
- case Expose : is_exposed = true; break;
- }
- } while (!is_exposed || !is_mapped);
- do { // Wait for the window to be visible
- XGetWindowAttributes(dpy,_window,&attr);
- if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); }
- } while (attr.map_state!=IsViewable);
- _window_x = attr.x;
- _window_y = attr.y;
- }
- void _paint(const bool wait_expose=true) {
- if (_is_closed || !_image) return;
- Display *const dpy = cimg::X11_attr().display;
- if (wait_expose) { // Send an expose event sticked to display window to force repaint
- XEvent event;
- event.xexpose.type = Expose;
- event.xexpose.serial = 0;
- event.xexpose.send_event = 1;
- event.xexpose.display = dpy;
- event.xexpose.window = _window;
- event.xexpose.x = 0;
- event.xexpose.y = 0;
- event.xexpose.width = width();
- event.xexpose.height = height();
- event.xexpose.count = 0;
- XSendEvent(dpy,_window,0,0,&event);
- } else { // Repaint directly (may be called from the expose event)
- GC gc = DefaultGC(dpy,DefaultScreen(dpy));
- #ifdef cimg_use_xshm
- if (_shminfo) XShmPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height,1);
- else XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height);
- #else
- XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height);
- #endif
- }
- }
- template<typename T>
- void _resize(T pixel_type, const unsigned int ndimx, const unsigned int ndimy, const bool force_redraw) {
- Display *const dpy = cimg::X11_attr().display;
- cimg::unused(pixel_type);
- #ifdef cimg_use_xshm
- if (_shminfo) {
- XShmSegmentInfo *const nshminfo = new XShmSegmentInfo;
- XImage *const nimage = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),
- cimg::X11_attr().nb_bits,ZPixmap,0,nshminfo,ndimx,ndimy);
- if (!nimage) { delete nshminfo; return; }
- else {
- nshminfo->shmid = shmget(IPC_PRIVATE,ndimx*ndimy*sizeof(T),IPC_CREAT | 0777);
- if (nshminfo->shmid==-1) { XDestroyImage(nimage); delete nshminfo; return; }
- else {
- nshminfo->shmaddr = nimage->data = (char*)shmat(nshminfo->shmid,0,0);
- if (nshminfo->shmaddr==(char*)-1) {
- shmctl(nshminfo->shmid,IPC_RMID,0); XDestroyImage(nimage); delete nshminfo; return;
- } else {
- nshminfo->readOnly = 0;
- cimg::X11_attr().is_shm_enabled = true;
- XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
- XShmAttach(dpy,nshminfo);
- XFlush(dpy);
- XSetErrorHandler(oldXErrorHandler);
- if (!cimg::X11_attr().is_shm_enabled) {
- shmdt(nshminfo->shmaddr);
- shmctl(nshminfo->shmid,IPC_RMID,0);
- XDestroyImage(nimage);
- delete nshminfo;
- return;
- } else {
- T *const ndata = (T*)nimage->data;
- if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
- else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
- XShmDetach(dpy,_shminfo);
- XDestroyImage(_image);
- shmdt(_shminfo->shmaddr);
- shmctl(_shminfo->shmid,IPC_RMID,0);
- delete _shminfo;
- _shminfo = nshminfo;
- _image = nimage;
- _data = (void*)ndata;
- }
- }
- }
- }
- } else
- #endif
- {
- T *ndata = (T*)std::malloc(ndimx*ndimy*sizeof(T));
- if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
- else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
- _data = (void*)ndata;
- XDestroyImage(_image);
- _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),
- cimg::X11_attr().nb_bits,ZPixmap,0,(char*)_data,ndimx,ndimy,8,0);
- }
- }
- void _init_fullscreen() {
- if (!_is_fullscreen || _is_closed) return;
- Display *const dpy = cimg::X11_attr().display;
- _background_window = 0;
- #ifdef cimg_use_xrandr
- int foo;
- if (XRRQueryExtension(dpy,&foo,&foo)) {
- XRRRotations(dpy,DefaultScreen(dpy),&cimg::X11_attr().curr_rotation);
- if (!cimg::X11_attr().resolutions) {
- cimg::X11_attr().resolutions = XRRSizes(dpy,DefaultScreen(dpy),&foo);
- cimg::X11_attr().nb_resolutions = (unsigned int)foo;
- }
- if (cimg::X11_attr().resolutions) {
- cimg::X11_attr().curr_resolution = 0;
- for (unsigned int i = 0; i<cimg::X11_attr().nb_resolutions; ++i) {
- const unsigned int
- nw = (unsigned int)(cimg::X11_attr().resolutions[i].width),
- nh = (unsigned int)(cimg::X11_attr().resolutions[i].height);
- if (nw>=_width && nh>=_height &&
- nw<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width) &&
- nh<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height))
- cimg::X11_attr().curr_resolution = i;
- }
- if (cimg::X11_attr().curr_resolution>0) {
- XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy));
- XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),
- cimg::X11_attr().curr_resolution,cimg::X11_attr().curr_rotation,CurrentTime);
- XRRFreeScreenConfigInfo(config);
- XSync(dpy,0);
- }
- }
- }
- if (!cimg::X11_attr().resolutions)
- cimg::warn(_cimgdisplay_instance
- "init_fullscreen(): Xrandr extension not supported by the X server.",
- cimgdisplay_instance);
- #endif
- const unsigned int sx = screen_width(), sy = screen_height();
- if (sx==_width && sy==_height) return;
- XSetWindowAttributes attr_set;
- attr_set.background_pixel = XBlackPixel(dpy,XDefaultScreen(dpy));
- attr_set.override_redirect = 1;
- _background_window = XCreateWindow(dpy,DefaultRootWindow(dpy),0,0,sx,sy,0,0,
- InputOutput,CopyFromParent,CWBackPixel | CWOverrideRedirect,&attr_set);
- XEvent event;
- XSelectInput(dpy,_background_window,StructureNotifyMask);
- XMapRaised(dpy,_background_window);
- do XWindowEvent(dpy,_background_window,StructureNotifyMask,&event);
- while (event.type!=MapNotify);
- XWindowAttributes attr;
- do {
- XGetWindowAttributes(dpy,_background_window,&attr);
- if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); }
- } while (attr.map_state!=IsViewable);
- }
- void _desinit_fullscreen() {
- if (!_is_fullscreen) return;
- Display *const dpy = cimg::X11_attr().display;
- XUngrabKeyboard(dpy,CurrentTime);
- #ifdef cimg_use_xrandr
- if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) {
- XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy));
- XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),0,cimg::X11_attr().curr_rotation,CurrentTime);
- XRRFreeScreenConfigInfo(config);
- XSync(dpy,0);
- cimg::X11_attr().curr_resolution = 0;
- }
- #endif
- if (_background_window) XDestroyWindow(dpy,_background_window);
- _background_window = 0;
- _is_fullscreen = false;
- }
- static int _assign_xshm(Display *dpy, XErrorEvent *error) {
- cimg::unused(dpy,error);
- cimg::X11_attr().is_shm_enabled = false;
- return 0;
- }
- void _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
- const unsigned int normalization_type=3,
- const bool fullscreen_flag=false, const bool closed_flag=false) {
- cimg::mutex(14);
- // Allocate space for window title
- const char *const nptitle = ptitle?ptitle:"";
- const unsigned int s = (unsigned int)std::strlen(nptitle) + 1;
- char *const tmp_title = s?new char[s]:0;
- if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
- // Destroy previous display window if existing
- if (!is_empty()) assign();
- // Open X11 display and retrieve graphical properties.
- Display* &dpy = cimg::X11_attr().display;
- if (!dpy) {
- dpy = XOpenDisplay(0);
- if (!dpy)
- throw CImgDisplayException(_cimgdisplay_instance
- "assign(): Failed to open X11 display.",
- cimgdisplay_instance);
- cimg::X11_attr().nb_bits = DefaultDepth(dpy,DefaultScreen(dpy));
- if (cimg::X11_attr().nb_bits!=8 && cimg::X11_attr().nb_bits!=16 &&
- cimg::X11_attr().nb_bits!=24 && cimg::X11_attr().nb_bits!=32)
- throw CImgDisplayException(_cimgdisplay_instance
- "assign(): Invalid %u bits screen mode detected "
- "(only 8, 16, 24 and 32 bits modes are managed).",
- cimgdisplay_instance,
- cimg::X11_attr().nb_bits);
- XVisualInfo vtemplate;
- vtemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,DefaultScreen(dpy)));
- int nb_visuals;
- XVisualInfo *vinfo = XGetVisualInfo(dpy,VisualIDMask,&vtemplate,&nb_visuals);
- if (vinfo && vinfo->red_mask<vinfo->blue_mask) cimg::X11_attr().is_blue_first = true;
- cimg::X11_attr().byte_order = ImageByteOrder(dpy);
- XFree(vinfo);
- cimg_lock_display();
- cimg::X11_attr().events_thread = new pthread_t;
- pthread_create(cimg::X11_attr().events_thread,0,_events_thread,0);
- } else cimg_lock_display();
- // Set display variables.
- _width = std::min(dimw,(unsigned int)screen_width());
- _height = std::min(dimh,(unsigned int)screen_height());
- _normalization = normalization_type<4?normalization_type:3;
- _is_fullscreen = fullscreen_flag;
- _window_x = _window_y = cimg::type<int>::min();
- _is_closed = closed_flag;
- _title = tmp_title;
- flush();
- // Create X11 window (and LUT, if 8bits display)
- if (_is_fullscreen) {
- if (!_is_closed) _init_fullscreen();
- const unsigned int sx = screen_width(), sy = screen_height();
- XSetWindowAttributes attr_set;
- attr_set.override_redirect = 1;
- _window = XCreateWindow(dpy,DefaultRootWindow(dpy),(sx - _width)/2,(sy - _height)/2,_width,_height,0,0,
- InputOutput,CopyFromParent,CWOverrideRedirect,&attr_set);
- } else
- _window = XCreateSimpleWindow(dpy,DefaultRootWindow(dpy),0,0,_width,_height,0,0L,0L);
- XSelectInput(dpy,_window,
- ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask | PointerMotionMask |
- EnterWindowMask | LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask);
- XStoreName(dpy,_window,_title?_title:" ");
- if (cimg::X11_attr().nb_bits==8) {
- _colormap = XCreateColormap(dpy,_window,DefaultVisual(dpy,DefaultScreen(dpy)),AllocAll);
- _set_colormap(_colormap,3);
- XSetWindowColormap(dpy,_window,_colormap);
- }
- static const char *const _window_class = cimg_appname;
- XClassHint *const window_class = XAllocClassHint();
- window_class->res_name = (char*)_window_class;
- window_class->res_class = (char*)_window_class;
- XSetClassHint(dpy,_window,window_class);
- XFree(window_class);
- _window_width = _width;
- _window_height = _height;
- // Create XImage
- #ifdef cimg_use_xshm
- _shminfo = 0;
- if (XShmQueryExtension(dpy)) {
- _shminfo = new XShmSegmentInfo;
- _image = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
- ZPixmap,0,_shminfo,_width,_height);
- if (!_image) { delete _shminfo; _shminfo = 0; }
- else {
- _shminfo->shmid = shmget(IPC_PRIVATE,_image->bytes_per_line*_image->height,IPC_CREAT|0777);
- if (_shminfo->shmid==-1) { XDestroyImage(_image); delete _shminfo; _shminfo = 0; }
- else {
- _shminfo->shmaddr = _image->data = (char*)(_data = shmat(_shminfo->shmid,0,0));
- if (_shminfo->shmaddr==(char*)-1) {
- shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); delete _shminfo; _shminfo = 0;
- } else {
- _shminfo->readOnly = 0;
- cimg::X11_attr().is_shm_enabled = true;
- XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
- XShmAttach(dpy,_shminfo);
- XSync(dpy,0);
- XSetErrorHandler(oldXErrorHandler);
- if (!cimg::X11_attr().is_shm_enabled) {
- shmdt(_shminfo->shmaddr); shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image);
- delete _shminfo; _shminfo = 0;
- }
- }
- }
- }
- }
- if (!_shminfo)
- #endif
- {
- const cimg_ulong buf_size = (cimg_ulong)_width*_height*(cimg::X11_attr().nb_bits==8?1:
- (cimg::X11_attr().nb_bits==16?2:4));
- _data = std::malloc(buf_size);
- _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
- ZPixmap,0,(char*)_data,_width,_height,8,0);
- }
- _wm_window_atom = XInternAtom(dpy,"WM_DELETE_WINDOW",0);
- _wm_protocol_atom = XInternAtom(dpy,"WM_PROTOCOLS",0);
- XSetWMProtocols(dpy,_window,&_wm_window_atom,1);
- if (_is_fullscreen) XGrabKeyboard(dpy,_window,1,GrabModeAsync,GrabModeAsync,CurrentTime);
- cimg::X11_attr().wins[cimg::X11_attr().nb_wins++]=this;
- if (!_is_closed) _map_window(); else _window_x = _window_y = cimg::type<int>::min();
- cimg_unlock_display();
- cimg::mutex(14,0);
- }
- CImgDisplay& assign() {
- if (is_empty()) return flush();
- Display *const dpy = cimg::X11_attr().display;
- cimg_lock_display();
- // Remove display window from event thread list.
- unsigned int i;
- for (i = 0; i<cimg::X11_attr().nb_wins && cimg::X11_attr().wins[i]!=this; ++i) {}
- for ( ; i<cimg::X11_attr().nb_wins - 1; ++i) cimg::X11_attr().wins[i] = cimg::X11_attr().wins[i + 1];
- --cimg::X11_attr().nb_wins;
- // Destroy window, image, colormap and title.
- if (_is_fullscreen && !_is_closed) _desinit_fullscreen();
- #ifdef cimg_use_xshm
- if (_shminfo) {
- XShmDetach(dpy,_shminfo);
- shmdt(_shminfo->shmaddr);
- shmctl(_shminfo->shmid,IPC_RMID,0);
- delete _shminfo;
- _shminfo = 0;
- }
- #endif
- XDestroyImage(_image);
- if (cimg::X11_attr().nb_bits==8) XFreeColormap(dpy,_colormap);
- XDestroyWindow(dpy,_window);
- XSync(dpy,0);
- _window = 0; _colormap = 0; _data = 0; _image = 0;
- // Reset display variables.
- delete[] _title;
- _width = _height = _normalization = _window_width = _window_height = 0;
- _window_x = _window_y = cimg::type<int>::min();
- _is_fullscreen = false;
- _is_closed = true;
- _min = _max = 0;
- _title = 0;
- flush();
- cimg_unlock_display();
- return *this;
- }
- CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0,
- const unsigned int normalization_type=3,
- const bool fullscreen_flag=false, const bool closed_flag=false) {
- if (!dimw || !dimh) return assign();
- _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag);
- _min = _max = 0;
- std::memset(_data,0,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char):
- (cimg::X11_attr().nb_bits==16?sizeof(unsigned short):sizeof(unsigned int)))*
- (size_t)_width*_height);
- return paint();
- }
- template<typename T>
- CImgDisplay& assign(const CImg<T>& img, const char *const title=0,
- const unsigned int normalization_type=3,
- const bool fullscreen_flag=false, const bool closed_flag=false) {
- if (!img) return assign();
- CImg<T> tmp;
- const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
- (img._height - 1)/2,
- (img._depth - 1)/2));
- _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
- if (_normalization==2) _min = (float)nimg.min_max(_max);
- return render(nimg).paint();
- }
- template<typename T>
- CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
- const unsigned int normalization_type=3,
- const bool fullscreen_flag=false, const bool closed_flag=false) {
- if (!list) return assign();
- CImg<T> tmp;
- const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
- (img._height - 1)/2,
- (img._depth - 1)/2));
- _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
- if (_normalization==2) _min = (float)nimg.min_max(_max);
- return render(nimg).paint();
- }
- CImgDisplay& assign(const CImgDisplay& disp) {
- if (!disp) return assign();
- _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed);
- std::memcpy(_data,disp._data,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char):
- cimg::X11_attr().nb_bits==16?sizeof(unsigned short):
- sizeof(unsigned int))*(size_t)_width*_height);
- return paint();
- }
- CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) {
- if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign();
- if (is_empty()) return assign(nwidth,nheight);
- Display *const dpy = cimg::X11_attr().display;
- const unsigned int
- tmpdimx = (nwidth>0)?nwidth:(-nwidth*width()/100),
- tmpdimy = (nheight>0)?nheight:(-nheight*height()/100),
- dimx = tmpdimx?tmpdimx:1,
- dimy = tmpdimy?tmpdimy:1;
- if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) {
- show();
- cimg_lock_display();
- if (_window_width!=dimx || _window_height!=dimy) {
- XWindowAttributes attr;
- for (unsigned int i = 0; i<10; ++i) {
- XResizeWindow(dpy,_window,dimx,dimy);
- XGetWindowAttributes(dpy,_window,&attr);
- if (attr.width==(int)dimx && attr.height==(int)dimy) break;
- cimg::wait(5,&_timer);
- }
- }
- if (_width!=dimx || _height!=dimy) switch (cimg::X11_attr().nb_bits) {
- case 8 : { unsigned char pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break;
- case 16 : { unsigned short pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break;
- default : { unsigned int pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); }
- }
- _window_width = _width = dimx; _window_height = _height = dimy;
- cimg_unlock_display();
- }
- _is_resized = false;
- if (_is_fullscreen) move((screen_width() - _width)/2,(screen_height() - _height)/2);
- if (force_redraw) return paint();
- return *this;
- }
- CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
- if (is_empty()) return *this;
- if (force_redraw) {
- const cimg_ulong buf_size = (cimg_ulong)_width*_height*
- (cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4));
- void *image_data = std::malloc(buf_size);
- std::memcpy(image_data,_data,buf_size);
- assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
- std::memcpy(_data,image_data,buf_size);
- std::free(image_data);
- return paint();
- }
- return assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
- }
- CImgDisplay& show() {
- if (is_empty() || !_is_closed) return *this;
- cimg_lock_display();
- _is_closed = false;
- if (_is_fullscreen) _init_fullscreen();
- _map_window();
- cimg_unlock_display();
- return paint();
- }
- CImgDisplay& close() {
- if (is_empty() || _is_closed) return *this;
- Display *const dpy = cimg::X11_attr().display;
- cimg_lock_display();
- if (_is_fullscreen) _desinit_fullscreen();
- XUnmapWindow(dpy,_window);
- _window_x = _window_y = cimg::type<int>::min();
- _is_closed = true;
- cimg_unlock_display();
- return *this;
- }
- CImgDisplay& move(const int posx, const int posy) {
- if (is_empty()) return *this;
- show();
- if (_window_x!=posx || _window_y!=posy) {
- Display *const dpy = cimg::X11_attr().display;
- cimg_lock_display();
- XMoveWindow(dpy,_window,posx,posy);
- _window_x = posx;
- _window_y = posy;
- cimg_unlock_display();
- }
- _is_moved = false;
- return paint();
- }
- CImgDisplay& show_mouse() {
- if (is_empty()) return *this;
- Display *const dpy = cimg::X11_attr().display;
- cimg_lock_display();
- XUndefineCursor(dpy,_window);
- cimg_unlock_display();
- return *this;
- }
- CImgDisplay& hide_mouse() {
- if (is_empty()) return *this;
- Display *const dpy = cimg::X11_attr().display;
- cimg_lock_display();
- static const char pix_data[8] = {};
- XColor col;
- col.red = col.green = col.blue = 0;
- Pixmap pix = XCreateBitmapFromData(dpy,_window,pix_data,8,8);
- Cursor cur = XCreatePixmapCursor(dpy,pix,pix,&col,&col,0,0);
- XFreePixmap(dpy,pix);
- XDefineCursor(dpy,_window,cur);
- cimg_unlock_display();
- return *this;
- }
- CImgDisplay& set_mouse(const int posx, const int posy) {
- if (is_empty() || _is_closed) return *this;
- Display *const dpy = cimg::X11_attr().display;
- cimg_lock_display();
- XWarpPointer(dpy,0L,_window,0,0,0,0,posx,posy);
- _mouse_x = posx; _mouse_y = posy;
- _is_moved = false;
- XSync(dpy,0);
- cimg_unlock_display();
- return *this;
- }
- CImgDisplay& set_title(const char *const format, ...) {
- if (is_empty()) return *this;
- char *const tmp = new char[1024];
- va_list ap;
- va_start(ap, format);
- cimg_vsnprintf(tmp,1024,format,ap);
- va_end(ap);
- if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; }
- delete[] _title;
- const unsigned int s = (unsigned int)std::strlen(tmp) + 1;
- _title = new char[s];
- std::memcpy(_title,tmp,s*sizeof(char));
- Display *const dpy = cimg::X11_attr().display;
- cimg_lock_display();
- XStoreName(dpy,_window,tmp);
- cimg_unlock_display();
- delete[] tmp;
- return *this;
- }
- template<typename T>
- CImgDisplay& display(const CImg<T>& img) {
- if (!img)
- throw CImgArgumentException(_cimgdisplay_instance
- "display(): Empty specified image.",
- cimgdisplay_instance);
- if (is_empty()) return assign(img);
- return render(img).paint(false);
- }
- CImgDisplay& paint(const bool wait_expose=true) {
- if (is_empty()) return *this;
- cimg_lock_display();
- _paint(wait_expose);
- cimg_unlock_display();
- return *this;
- }
- template<typename T>
- CImgDisplay& render(const CImg<T>& img, const bool flag8=false) {
- if (!img)
- throw CImgArgumentException(_cimgdisplay_instance
- "render(): Empty specified image.",
- cimgdisplay_instance);
- if (is_empty()) return *this;
- if (img._depth!=1) return render(img.get_projections2d((img._width - 1)/2,(img._height - 1)/2,
- (img._depth - 1)/2));
- if (cimg::X11_attr().nb_bits==8 && (img._width!=_width || img._height!=_height))
- return render(img.get_resize(_width,_height,1,-100,1));
- if (cimg::X11_attr().nb_bits==8 && !flag8 && img._spectrum==3) {
- static const CImg<typename CImg<T>::ucharT> default_colormap = CImg<typename CImg<T>::ucharT>::default_LUT256();
- return render(img.get_index(default_colormap,1,false));
- }
- const T
- *data1 = img._data,
- *data2 = (img._spectrum>1)?img.data(0,0,0,1):data1,
- *data3 = (img._spectrum>2)?img.data(0,0,0,2):data1;
- if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3);
- cimg_lock_display();
- if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) {
- _min = _max = 0;
- switch (cimg::X11_attr().nb_bits) {
- case 8 : { // 256 colormap, no normalization
- _set_colormap(_colormap,img._spectrum);
- unsigned char
- *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:
- new unsigned char[(size_t)img._width*img._height],
- *ptrd = (unsigned char*)ndata;
- switch (img._spectrum) {
- case 1 :
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
- (*ptrd++) = (unsigned char)*(data1++);
- break;
- case 2 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char
- R = (unsigned char)*(data1++),
- G = (unsigned char)*(data2++);
- (*ptrd++) = (R&0xf0) | (G>>4);
- } break;
- default : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char
- R = (unsigned char)*(data1++),
- G = (unsigned char)*(data2++),
- B = (unsigned char)*(data3++);
- (*ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6);
- }
- }
- if (ndata!=_data) {
- _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height);
- delete[] ndata;
- }
- } break;
- case 16 : { // 16 bits colors, no normalization
- unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data:
- new unsigned short[(size_t)img._width*img._height];
- unsigned char *ptrd = (unsigned char*)ndata;
- const unsigned int M = 248;
- switch (img._spectrum) {
- case 1 :
- if (cimg::X11_attr().byte_order)
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char val = (unsigned char)*(data1++), G = val>>2;
- ptrd[0] = (val&M) | (G>>3);
- ptrd[1] = (G<<5) | (G>>1);
- ptrd+=2;
- } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char val = (unsigned char)*(data1++), G = val>>2;
- ptrd[0] = (G<<5) | (G>>1);
- ptrd[1] = (val&M) | (G>>3);
- ptrd+=2;
- }
- break;
- case 2 :
- if (cimg::X11_attr().byte_order)
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char G = (unsigned char)*(data2++)>>2;
- ptrd[0] = ((unsigned char)*(data1++)&M) | (G>>3);
- ptrd[1] = (G<<5);
- ptrd+=2;
- } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char G = (unsigned char)*(data2++)>>2;
- ptrd[0] = (G<<5);
- ptrd[1] = ((unsigned char)*(data1++)&M) | (G>>3);
- ptrd+=2;
- }
- break;
- default :
- if (cimg::X11_attr().byte_order)
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char G = (unsigned char)*(data2++)>>2;
- ptrd[0] = ((unsigned char)*(data1++)&M) | (G>>3);
- ptrd[1] = (G<<5) | ((unsigned char)*(data3++)>>3);
- ptrd+=2;
- } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char G = (unsigned char)*(data2++)>>2;
- ptrd[0] = (G<<5) | ((unsigned char)*(data3++)>>3);
- ptrd[1] = ((unsigned char)*(data1++)&M) | (G>>3);
- ptrd+=2;
- }
- }
- if (ndata!=_data) {
- _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height);
- delete[] ndata;
- }
- } break;
- default : { // 24 bits colors, no normalization
- unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data:
- new unsigned int[(size_t)img._width*img._height];
- if (sizeof(int)==4) { // 32 bits int uses optimized version
- unsigned int *ptrd = ndata;
- switch (img._spectrum) {
- case 1 :
- if (cimg::X11_attr().byte_order==cimg::endianness())
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char val = (unsigned char)*(data1++);
- *(ptrd++) = (val<<16) | (val<<8) | val;
- }
- else
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char val = (unsigned char)*(data1++);
- *(ptrd++) = (val<<16) | (val<<8) | val;
- }
- break;
- case 2 :
- if (cimg::X11_attr().byte_order==cimg::endianness())
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
- *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8);
- else
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
- *(ptrd++) = ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8);
- break;
- default :
- if (cimg::X11_attr().byte_order==cimg::endianness())
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
- *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) |
- (unsigned char)*(data3++);
- else
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
- *(ptrd++) = ((unsigned char)*(data3++)<<24) | ((unsigned char)*(data2++)<<16) |
- ((unsigned char)*(data1++)<<8);
- }
- } else {
- unsigned char *ptrd = (unsigned char*)ndata;
- switch (img._spectrum) {
- case 1 :
- if (cimg::X11_attr().byte_order)
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- ptrd[0] = 0;
- ptrd[1] = (unsigned char)*(data1++);
- ptrd[2] = 0;
- ptrd[3] = 0;
- ptrd+=4;
- } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- ptrd[0] = 0;
- ptrd[1] = 0;
- ptrd[2] = (unsigned char)*(data1++);
- ptrd[3] = 0;
- ptrd+=4;
- }
- break;
- case 2 :
- if (cimg::X11_attr().byte_order) cimg::swap(data1,data2);
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- ptrd[0] = 0;
- ptrd[1] = (unsigned char)*(data2++);
- ptrd[2] = (unsigned char)*(data1++);
- ptrd[3] = 0;
- ptrd+=4;
- }
- break;
- default :
- if (cimg::X11_attr().byte_order)
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- ptrd[0] = 0;
- ptrd[1] = (unsigned char)*(data1++);
- ptrd[2] = (unsigned char)*(data2++);
- ptrd[3] = (unsigned char)*(data3++);
- ptrd+=4;
- } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- ptrd[0] = (unsigned char)*(data3++);
- ptrd[1] = (unsigned char)*(data2++);
- ptrd[2] = (unsigned char)*(data1++);
- ptrd[3] = 0;
- ptrd+=4;
- }
- }
- }
- if (ndata!=_data) {
- _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height);
- delete[] ndata;
- }
- }
- }
- } else {
- if (_normalization==3) {
- if (sizeof(T)>1 && cimg::type<T>::string()!=cimg::type<bool>::string()) _min = (float)img.min_max(_max);
- else { _min = (float)cimg::type<T>::min(); _max = (float)cimg::type<T>::max(); }
- } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max);
- const float delta = _max - _min, mm = 255/(delta?delta:1.f);
- switch (cimg::X11_attr().nb_bits) {
- case 8 : { // 256 colormap, with normalization
- _set_colormap(_colormap,img._spectrum);
- unsigned char *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:
- new unsigned char[(size_t)img._width*img._height];
- unsigned char *ptrd = (unsigned char*)ndata;
- switch (img._spectrum) {
- case 1 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char R = (unsigned char)((*(data1++) - _min)*mm);
- *(ptrd++) = R;
- } break;
- case 2 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char
- R = (unsigned char)((*(data1++) - _min)*mm),
- G = (unsigned char)((*(data2++) - _min)*mm);
- (*ptrd++) = (R&0xf0) | (G>>4);
- } break;
- default :
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char
- R = (unsigned char)((*(data1++) - _min)*mm),
- G = (unsigned char)((*(data2++) - _min)*mm),
- B = (unsigned char)((*(data3++) - _min)*mm);
- *(ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6);
- }
- }
- if (ndata!=_data) {
- _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height);
- delete[] ndata;
- }
- } break;
- case 16 : { // 16 bits colors, with normalization
- unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data:
- new unsigned short[(size_t)img._width*img._height];
- unsigned char *ptrd = (unsigned char*)ndata;
- const unsigned int M = 248;
- switch (img._spectrum) {
- case 1 :
- if (cimg::X11_attr().byte_order)
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char val = (unsigned char)((*(data1++) - _min)*mm), G = val>>2;
- ptrd[0] = (val&M) | (G>>3);
- ptrd[1] = (G<<5) | (val>>3);
- ptrd+=2;
- } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char val = (unsigned char)((*(data1++) - _min)*mm), G = val>>2;
- ptrd[0] = (G<<5) | (val>>3);
- ptrd[1] = (val&M) | (G>>3);
- ptrd+=2;
- }
- break;
- case 2 :
- if (cimg::X11_attr().byte_order)
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
- ptrd[0] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
- ptrd[1] = (G<<5);
- ptrd+=2;
- } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
- ptrd[0] = (G<<5);
- ptrd[1] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
- ptrd+=2;
- }
- break;
- default :
- if (cimg::X11_attr().byte_order)
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
- ptrd[0] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
- ptrd[1] = (G<<5) | ((unsigned char)((*(data3++) - _min)*mm)>>3);
- ptrd+=2;
- } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
- ptrd[0] = (G<<5) | ((unsigned char)((*(data3++) - _min)*mm)>>3);
- ptrd[1] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
- ptrd+=2;
- }
- }
- if (ndata!=_data) {
- _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height);
- delete[] ndata;
- }
- } break;
- default : { // 24 bits colors, with normalization
- unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data:
- new unsigned int[(size_t)img._width*img._height];
- if (sizeof(int)==4) { // 32 bits int uses optimized version
- unsigned int *ptrd = ndata;
- switch (img._spectrum) {
- case 1 :
- if (cimg::X11_attr().byte_order==cimg::endianness())
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
- *(ptrd++) = (val<<16) | (val<<8) | val;
- }
- else
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
- *(ptrd++) = (val<<24) | (val<<16) | (val<<8);
- }
- break;
- case 2 :
- if (cimg::X11_attr().byte_order==cimg::endianness())
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
- *(ptrd++) =
- ((unsigned char)((*(data1++) - _min)*mm)<<16) |
- ((unsigned char)((*(data2++) - _min)*mm)<<8);
- else
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
- *(ptrd++) =
- ((unsigned char)((*(data2++) - _min)*mm)<<16) |
- ((unsigned char)((*(data1++) - _min)*mm)<<8);
- break;
- default :
- if (cimg::X11_attr().byte_order==cimg::endianness())
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
- *(ptrd++) =
- ((unsigned char)((*(data1++) - _min)*mm)<<16) |
- ((unsigned char)((*(data2++) - _min)*mm)<<8) |
- (unsigned char)((*(data3++) - _min)*mm);
- else
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
- *(ptrd++) =
- ((unsigned char)((*(data3++) - _min)*mm)<<24) |
- ((unsigned char)((*(data2++) - _min)*mm)<<16) |
- ((unsigned char)((*(data1++) - _min)*mm)<<8);
- }
- } else {
- unsigned char *ptrd = (unsigned char*)ndata;
- switch (img._spectrum) {
- case 1 :
- if (cimg::X11_attr().byte_order)
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
- ptrd[0] = 0;
- ptrd[1] = val;
- ptrd[2] = val;
- ptrd[3] = val;
- ptrd+=4;
- } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
- ptrd[0] = val;
- ptrd[1] = val;
- ptrd[2] = val;
- ptrd[3] = 0;
- ptrd+=4;
- }
- break;
- case 2 :
- if (cimg::X11_attr().byte_order) cimg::swap(data1,data2);
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- ptrd[0] = 0;
- ptrd[1] = (unsigned char)((*(data2++) - _min)*mm);
- ptrd[2] = (unsigned char)((*(data1++) - _min)*mm);
- ptrd[3] = 0;
- ptrd+=4;
- }
- break;
- default :
- if (cimg::X11_attr().byte_order)
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- ptrd[0] = 0;
- ptrd[1] = (unsigned char)((*(data1++) - _min)*mm);
- ptrd[2] = (unsigned char)((*(data2++) - _min)*mm);
- ptrd[3] = (unsigned char)((*(data3++) - _min)*mm);
- ptrd+=4;
- } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- ptrd[0] = (unsigned char)((*(data3++) - _min)*mm);
- ptrd[1] = (unsigned char)((*(data2++) - _min)*mm);
- ptrd[2] = (unsigned char)((*(data1++) - _min)*mm);
- ptrd[3] = 0;
- ptrd+=4;
- }
- }
- }
- if (ndata!=_data) {
- _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height);
- delete[] ndata;
- }
- }
- }
- }
- cimg_unlock_display();
- return *this;
- }
- template<typename T>
- static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) {
- img.assign();
- Display *dpy = cimg::X11_attr().display;
- cimg_lock_display();
- if (!dpy) {
- dpy = XOpenDisplay(0);
- if (!dpy)
- throw CImgDisplayException("CImgDisplay::screenshot(): Failed to open X11 display.");
- }
- Window root = DefaultRootWindow(dpy);
- XWindowAttributes gwa;
- XGetWindowAttributes(dpy,root,&gwa);
- const int width = gwa.width, height = gwa.height;
- int _x0 = x0, _y0 = y0, _x1 = x1, _y1 = y1;
- if (_x0>_x1) cimg::swap(_x0,_x1);
- if (_y0>_y1) cimg::swap(_y0,_y1);
- XImage *image = 0;
- if (_x1>=0 && _x0<width && _y1>=0 && _y0<height) {
- _x0 = std::max(_x0,0);
- _y0 = std::max(_y0,0);
- _x1 = std::min(_x1,width - 1);
- _y1 = std::min(_y1,height - 1);
- image = XGetImage(dpy,root,_x0,_y0,_x1 - _x0 + 1,_y1 - _y0 + 1,AllPlanes,ZPixmap);
- if (image) {
- const unsigned long
- red_mask = image->red_mask,
- green_mask = image->green_mask,
- blue_mask = image->blue_mask;
- img.assign(image->width,image->height,1,3);
- T *pR = img.data(0,0,0,0), *pG = img.data(0,0,0,1), *pB = img.data(0,0,0,2);
- cimg_forXY(img,x,y) {
- const unsigned long pixel = XGetPixel(image,x,y);
- *(pR++) = (T)((pixel & red_mask)>>16);
- *(pG++) = (T)((pixel & green_mask)>>8);
- *(pB++) = (T)(pixel & blue_mask);
- }
- XDestroyImage(image);
- }
- }
- if (!cimg::X11_attr().display) XCloseDisplay(dpy);
- cimg_unlock_display();
- if (img.is_empty())
- throw CImgDisplayException("CImgDisplay::screenshot(): Failed to take screenshot "
- "with coordinates (%d,%d)-(%d,%d).",
- x0,y0,x1,y1);
- }
- template<typename T>
- const CImgDisplay& snapshot(CImg<T>& img) const {
- if (is_empty()) { img.assign(); return *this; }
- const unsigned char *ptrs = (unsigned char*)_data;
- img.assign(_width,_height,1,3);
- T
- *data1 = img.data(0,0,0,0),
- *data2 = img.data(0,0,0,1),
- *data3 = img.data(0,0,0,2);
- if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3);
- switch (cimg::X11_attr().nb_bits) {
- case 8 : {
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char val = *(ptrs++);
- *(data1++) = (T)(val&0xe0);
- *(data2++) = (T)((val&0x1c)<<3);
- *(data3++) = (T)(val<<6);
- }
- } break;
- case 16 : {
- if (cimg::X11_attr().byte_order) for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char
- val0 = ptrs[0],
- val1 = ptrs[1];
- ptrs+=2;
- *(data1++) = (T)(val0&0xf8);
- *(data2++) = (T)((val0<<5) | ((val1&0xe0)>>5));
- *(data3++) = (T)(val1<<3);
- } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned short
- val0 = ptrs[0],
- val1 = ptrs[1];
- ptrs+=2;
- *(data1++) = (T)(val1&0xf8);
- *(data2++) = (T)((val1<<5) | ((val0&0xe0)>>5));
- *(data3++) = (T)(val0<<3);
- }
- } break;
- default : {
- if (cimg::X11_attr().byte_order) for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- ++ptrs;
- *(data1++) = (T)ptrs[0];
- *(data2++) = (T)ptrs[1];
- *(data3++) = (T)ptrs[2];
- ptrs+=3;
- } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- *(data3++) = (T)ptrs[0];
- *(data2++) = (T)ptrs[1];
- *(data1++) = (T)ptrs[2];
- ptrs+=3;
- ++ptrs;
- }
- }
- }
- return *this;
- }
- // Windows-based implementation.
- //-------------------------------
- #elif cimg_display==2
- bool _is_mouse_tracked, _is_cursor_visible;
- HANDLE _thread, _is_created, _mutex;
- HWND _window, _background_window;
- CLIENTCREATESTRUCT _ccs;
- unsigned int *_data;
- DEVMODE _curr_mode;
- BITMAPINFO _bmi;
- HDC _hdc;
- static int screen_width() {
- DEVMODE mode;
- mode.dmSize = sizeof(DEVMODE);
- mode.dmDriverExtra = 0;
- EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode);
- return (int)mode.dmPelsWidth;
- }
- static int screen_height() {
- DEVMODE mode;
- mode.dmSize = sizeof(DEVMODE);
- mode.dmDriverExtra = 0;
- EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode);
- return (int)mode.dmPelsHeight;
- }
- static void wait_all() {
- WaitForSingleObject(cimg::Win32_attr().wait_event,INFINITE);
- }
- static LRESULT APIENTRY _handle_events(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) {
- #ifdef _WIN64
- CImgDisplay *const disp = (CImgDisplay*)GetWindowLongPtr(window,GWLP_USERDATA);
- #else
- CImgDisplay *const disp = (CImgDisplay*)GetWindowLong(window,GWL_USERDATA);
- #endif
- MSG st_msg;
- switch (msg) {
- case WM_CLOSE :
- disp->_mouse_x = disp->_mouse_y = -1;
- disp->_window_x = disp->_window_y = cimg::type<int>::min();
- disp->set_button().set_key(0).set_key(0,false)._is_closed = true;
- ReleaseMutex(disp->_mutex);
- ShowWindow(disp->_window,SW_HIDE);
- disp->_is_event = true;
- SetEvent(cimg::Win32_attr().wait_event);
- return 0;
- case WM_SIZE : {
- while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {}
- WaitForSingleObject(disp->_mutex,INFINITE);
- const unsigned int nw = LOWORD(lParam),nh = HIWORD(lParam);
- if (nw && nh && (nw!=disp->_width || nh!=disp->_height)) {
- disp->_window_width = nw;
- disp->_window_height = nh;
- disp->_mouse_x = disp->_mouse_y = -1;
- disp->_is_resized = disp->_is_event = true;
- SetEvent(cimg::Win32_attr().wait_event);
- }
- ReleaseMutex(disp->_mutex);
- } break;
- case WM_MOVE : {
- while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {}
- WaitForSingleObject(disp->_mutex,INFINITE);
- const int nx = (int)(short)(LOWORD(lParam)), ny = (int)(short)(HIWORD(lParam));
- if (nx!=disp->_window_x || ny!=disp->_window_y) {
- disp->_window_x = nx;
- disp->_window_y = ny;
- disp->_is_moved = disp->_is_event = true;
- SetEvent(cimg::Win32_attr().wait_event);
- }
- ReleaseMutex(disp->_mutex);
- } break;
- case WM_PAINT :
- disp->paint();
- cimg_lock_display();
- if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE_WIN)>=0);
- cimg_unlock_display();
- break;
- case WM_ERASEBKGND :
- // return 0;
- break;
- case WM_KEYDOWN :
- disp->set_key((unsigned int)wParam);
- SetEvent(cimg::Win32_attr().wait_event);
- break;
- case WM_KEYUP :
- disp->set_key((unsigned int)wParam,false);
- SetEvent(cimg::Win32_attr().wait_event);
- break;
- case WM_MOUSEMOVE : {
- while (PeekMessage(&st_msg,window,WM_MOUSEMOVE,WM_MOUSEMOVE,PM_REMOVE)) {}
- disp->_mouse_x = LOWORD(lParam);
- disp->_mouse_y = HIWORD(lParam);
- #if (_WIN32_WINNT>=0x0400) && !defined(NOTRACKMOUSEEVENT)
- if (!disp->_is_mouse_tracked) {
- TRACKMOUSEEVENT tme;
- tme.cbSize = sizeof(TRACKMOUSEEVENT);
- tme.dwFlags = TME_LEAVE;
- tme.hwndTrack = disp->_window;
- if (TrackMouseEvent(&tme)) disp->_is_mouse_tracked = true;
- }
- #endif
- if (disp->_mouse_x<0 || disp->_mouse_y<0 || disp->_mouse_x>=disp->width() || disp->_mouse_y>=disp->height())
- disp->_mouse_x = disp->_mouse_y = -1;
- disp->_is_event = true;
- SetEvent(cimg::Win32_attr().wait_event);
- cimg_lock_display();
- if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE_WIN)>=0);
- cimg_unlock_display();
- } break;
- case WM_MOUSELEAVE : {
- disp->_mouse_x = disp->_mouse_y = -1;
- disp->_is_mouse_tracked = false;
- cimg_lock_display();
- while (ShowCursor(TRUE)<0) {}
- cimg_unlock_display();
- } break;
- case WM_LBUTTONDOWN :
- disp->set_button(1);
- SetEvent(cimg::Win32_attr().wait_event);
- break;
- case WM_RBUTTONDOWN :
- disp->set_button(2);
- SetEvent(cimg::Win32_attr().wait_event);
- break;
- case WM_MBUTTONDOWN :
- disp->set_button(3);
- SetEvent(cimg::Win32_attr().wait_event);
- break;
- case WM_LBUTTONUP :
- disp->set_button(1,false);
- SetEvent(cimg::Win32_attr().wait_event);
- break;
- case WM_RBUTTONUP :
- disp->set_button(2,false);
- SetEvent(cimg::Win32_attr().wait_event);
- break;
- case WM_MBUTTONUP :
- disp->set_button(3,false);
- SetEvent(cimg::Win32_attr().wait_event);
- break;
- case 0x020A : // WM_MOUSEWHEEL:
- disp->set_wheel((int)((short)HIWORD(wParam))/120);
- SetEvent(cimg::Win32_attr().wait_event);
- }
- return DefWindowProc(window,msg,wParam,lParam);
- }
- static DWORD WINAPI _events_thread(void* arg) {
- CImgDisplay *const disp = (CImgDisplay*)(((void**)arg)[0]);
- const char *const title = (const char*)(((void**)arg)[1]);
- MSG msg;
- delete[] (void**)arg;
- disp->_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
- disp->_bmi.bmiHeader.biWidth = disp->width();
- disp->_bmi.bmiHeader.biHeight = -disp->height();
- disp->_bmi.bmiHeader.biPlanes = 1;
- disp->_bmi.bmiHeader.biBitCount = 32;
- disp->_bmi.bmiHeader.biCompression = BI_RGB;
- disp->_bmi.bmiHeader.biSizeImage = 0;
- disp->_bmi.bmiHeader.biXPelsPerMeter = 1;
- disp->_bmi.bmiHeader.biYPelsPerMeter = 1;
- disp->_bmi.bmiHeader.biClrUsed = 0;
- disp->_bmi.bmiHeader.biClrImportant = 0;
- disp->_data = new unsigned int[(size_t)disp->_width*disp->_height];
- if (!disp->_is_fullscreen) { // Normal window
- RECT rect;
- rect.left = rect.top = 0; rect.right = (LONG)disp->_width - 1; rect.bottom = (LONG)disp->_height - 1;
- AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
- const int
- border1 = (int)((rect.right - rect.left + 1 - disp->_width)/2),
- border2 = (int)(rect.bottom - rect.top + 1 - disp->_height - border1),
- ww = disp->width() + 2*border1,
- wh = disp->height() + border1 + border2,
- sw = CImgDisplay::screen_width(),
- sh = CImgDisplay::screen_height();
- int
- wx = (int)cimg::round(cimg::rand(0,sw - ww -1)),
- wy = (int)cimg::round(cimg::rand(64,sh - wh - 65));
- if (wx + ww>=sw) wx = sw - ww;
- if (wy + wh>=sh) wy = sh - wh;
- if (wx<0) wx = 0;
- if (wy<0) wy = 0;
- disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
- (DWORD)(WS_OVERLAPPEDWINDOW | (disp->_is_closed?0:WS_VISIBLE)),
- wx,wy,ww,wh,0,0,0,&(disp->_ccs));
- if (!disp->_is_closed) {
- GetWindowRect(disp->_window,&rect);
- disp->_window_x = rect.left;
- disp->_window_y = rect.top;
- } else disp->_window_x = disp->_window_y = cimg::type<int>::min();
- } else { // Fullscreen window
- const unsigned int
- sx = (unsigned int)screen_width(),
- sy = (unsigned int)screen_height();
- disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
- (DWORD)(WS_POPUP | (disp->_is_closed?0:WS_VISIBLE)),
- (int)(sx - disp->_width)/2,
- (int)(sy - disp->_height)/2,
- disp->width(),disp->height(),0,0,0,&(disp->_ccs));
- disp->_window_x = disp->_window_y = 0;
- }
- SetForegroundWindow(disp->_window);
- disp->_hdc = GetDC(disp->_window);
- disp->_window_width = disp->_width;
- disp->_window_height = disp->_height;
- disp->flush();
- #ifdef _WIN64
- SetWindowLongPtr(disp->_window,GWLP_USERDATA,(LONG_PTR)disp);
- SetWindowLongPtr(disp->_window,GWLP_WNDPROC,(LONG_PTR)_handle_events);
- #else
- SetWindowLong(disp->_window,GWL_USERDATA,(LONG)disp);
- SetWindowLong(disp->_window,GWL_WNDPROC,(LONG)_handle_events);
- #endif
- SetEvent(disp->_is_created);
- while (GetMessage(&msg,0,0,0)) DispatchMessage(&msg);
- return 0;
- }
- CImgDisplay& _update_window_pos() {
- if (_is_closed) _window_x = _window_y = cimg::type<int>::min();
- else {
- RECT rect;
- rect.left = rect.top = 0; rect.right = (LONG)_width - 1; rect.bottom = (LONG)_height - 1;
- AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
- GetWindowRect(_window,&rect);
- _window_x = rect.left;
- _window_y = rect.top;
- }
- return *this;
- }
- void _init_fullscreen() {
- _background_window = 0;
- if (!_is_fullscreen || _is_closed) _curr_mode.dmSize = 0;
- else {
- /* DEVMODE mode;
- unsigned int imode = 0, ibest = 0, bestbpp = 0, bw = ~0U, bh = ~0U;
- for (mode.dmSize = sizeof(DEVMODE), mode.dmDriverExtra = 0; EnumDisplaySettings(0,imode,&mode); ++imode) {
- const unsigned int nw = mode.dmPelsWidth, nh = mode.dmPelsHeight;
- if (nw>=_width && nh>=_height && mode.dmBitsPerPel>=bestbpp && nw<=bw && nh<=bh) {
- bestbpp = mode.dmBitsPerPel;
- ibest = imode;
- bw = nw; bh = nh;
- }
- }
- if (bestbpp) {
- _curr_mode.dmSize = sizeof(DEVMODE); _curr_mode.dmDriverExtra = 0;
- EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&_curr_mode);
- EnumDisplaySettings(0,ibest,&mode);
- ChangeDisplaySettings(&mode,0);
- } else _curr_mode.dmSize = 0;
- */
- _curr_mode.dmSize = 0;
- const unsigned int
- sx = (unsigned int)screen_width(),
- sy = (unsigned int)screen_height();
- if (sx!=_width || sy!=_height) {
- CLIENTCREATESTRUCT background_ccs = { 0,0 };
- _background_window = CreateWindowA("MDICLIENT","",WS_POPUP | WS_VISIBLE,
- 0,0,(int)sx,(int)sy,0,0,0,&background_ccs);
- SetForegroundWindow(_background_window);
- }
- }
- }
- void _desinit_fullscreen() {
- if (!_is_fullscreen) return;
- if (_background_window) DestroyWindow(_background_window);
- _background_window = 0;
- if (_curr_mode.dmSize) ChangeDisplaySettings(&_curr_mode,0);
- _is_fullscreen = false;
- }
- CImgDisplay& _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
- const unsigned int normalization_type=3,
- const bool fullscreen_flag=false, const bool closed_flag=false) {
- // Allocate space for window title
- const char *const nptitle = ptitle?ptitle:"";
- const unsigned int s = (unsigned int)std::strlen(nptitle) + 1;
- char *const tmp_title = s?new char[s]:0;
- if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
- // Destroy previous window if existing
- if (!is_empty()) assign();
- // Set display variables
- _width = std::min(dimw,(unsigned int)screen_width());
- _height = std::min(dimh,(unsigned int)screen_height());
- _normalization = normalization_type<4?normalization_type:3;
- _is_fullscreen = fullscreen_flag;
- _window_x = _window_y = cimg::type<int>::min();
- _is_closed = closed_flag;
- _is_cursor_visible = true;
- _is_mouse_tracked = false;
- _title = tmp_title;
- flush();
- if (_is_fullscreen) _init_fullscreen();
- // Create event thread
- void *const arg = (void*)(new void*[2]);
- ((void**)arg)[0] = (void*)this;
- ((void**)arg)[1] = (void*)_title;
- _mutex = CreateMutex(0,FALSE_WIN,0);
- _is_created = CreateEvent(0,FALSE_WIN,FALSE_WIN,0);
- _thread = CreateThread(0,0,_events_thread,arg,0,0);
- WaitForSingleObject(_is_created,INFINITE);
- return *this;
- }
- CImgDisplay& assign() {
- if (is_empty()) return flush();
- DestroyWindow(_window);
- TerminateThread(_thread,0);
- delete[] _data;
- delete[] _title;
- _data = 0;
- _title = 0;
- if (_is_fullscreen) _desinit_fullscreen();
- _width = _height = _normalization = _window_width = _window_height = 0;
- _window_x = _window_y = cimg::type<int>::min();
- _is_fullscreen = false;
- _is_closed = true;
- _min = _max = 0;
- _title = 0;
- flush();
- return *this;
- }
- CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0,
- const unsigned int normalization_type=3,
- const bool fullscreen_flag=false, const bool closed_flag=false) {
- if (!dimw || !dimh) return assign();
- _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag);
- _min = _max = 0;
- std::memset(_data,0,sizeof(unsigned int)*_width*_height);
- return paint();
- }
- template<typename T>
- CImgDisplay& assign(const CImg<T>& img, const char *const title=0,
- const unsigned int normalization_type=3,
- const bool fullscreen_flag=false, const bool closed_flag=false) {
- if (!img) return assign();
- CImg<T> tmp;
- const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
- (img._height - 1)/2,
- (img._depth - 1)/2));
- _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
- if (_normalization==2) _min = (float)nimg.min_max(_max);
- return display(nimg);
- }
- template<typename T>
- CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
- const unsigned int normalization_type=3,
- const bool fullscreen_flag=false, const bool closed_flag=false) {
- if (!list) return assign();
- CImg<T> tmp;
- const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
- (img._height - 1)/2,
- (img._depth - 1)/2));
- _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
- if (_normalization==2) _min = (float)nimg.min_max(_max);
- return display(nimg);
- }
- CImgDisplay& assign(const CImgDisplay& disp) {
- if (!disp) return assign();
- _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed);
- std::memcpy(_data,disp._data,sizeof(unsigned int)*_width*_height);
- return paint();
- }
- CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) {
- if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign();
- if (is_empty()) return assign((unsigned int)nwidth,(unsigned int)nheight);
- const unsigned int
- tmpdimx = (nwidth>0)?nwidth:(-nwidth*_width/100),
- tmpdimy = (nheight>0)?nheight:(-nheight*_height/100),
- dimx = tmpdimx?tmpdimx:1,
- dimy = tmpdimy?tmpdimy:1;
- if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) {
- if (_window_width!=dimx || _window_height!=dimy) {
- RECT rect; rect.left = rect.top = 0; rect.right = (LONG)dimx - 1; rect.bottom = (LONG)dimy - 1;
- AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
- const int cwidth = rect.right - rect.left + 1, cheight = rect.bottom - rect.top + 1;
- SetWindowPos(_window,0,0,0,cwidth,cheight,SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS);
- }
- if (_width!=dimx || _height!=dimy) {
- unsigned int *const ndata = new unsigned int[dimx*dimy];
- if (force_redraw) _render_resize(_data,_width,_height,ndata,dimx,dimy);
- else std::memset(ndata,0x80,sizeof(unsigned int)*dimx*dimy);
- delete[] _data;
- _data = ndata;
- _bmi.bmiHeader.biWidth = (LONG)dimx;
- _bmi.bmiHeader.biHeight = -(int)dimy;
- _width = dimx;
- _height = dimy;
- }
- _window_width = dimx; _window_height = dimy;
- show();
- }
- _is_resized = false;
- if (_is_fullscreen) move((screen_width() - width())/2,(screen_height() - height())/2);
- if (force_redraw) return paint();
- return *this;
- }
- CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
- if (is_empty()) return *this;
- if (force_redraw) {
- const cimg_ulong buf_size = (cimg_ulong)_width*_height*4;
- void *odata = std::malloc(buf_size);
- if (odata) {
- std::memcpy(odata,_data,buf_size);
- assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
- std::memcpy(_data,odata,buf_size);
- std::free(odata);
- }
- return paint();
- }
- return assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
- }
- CImgDisplay& show() {
- if (is_empty() || !_is_closed) return *this;
- _is_closed = false;
- if (_is_fullscreen) _init_fullscreen();
- ShowWindow(_window,SW_SHOW);
- _update_window_pos();
- return paint();
- }
- CImgDisplay& close() {
- if (is_empty() || _is_closed) return *this;
- _is_closed = true;
- if (_is_fullscreen) _desinit_fullscreen();
- ShowWindow(_window,SW_HIDE);
- _window_x = _window_y = cimg::type<int>::min();
- return *this;
- }
- CImgDisplay& move(const int posx, const int posy) {
- if (is_empty()) return *this;
- if (_window_x!=posx || _window_y!=posy) {
- SetWindowPos(_window,0,posx,posy,0,0,SWP_NOSIZE | SWP_NOZORDER);
- _window_x = posx;
- _window_y = posy;
- }
- show();
- _is_moved = false;
- return *this;
- }
- CImgDisplay& show_mouse() {
- if (is_empty()) return *this;
- _is_cursor_visible = true;
- return *this;
- }
- CImgDisplay& hide_mouse() {
- if (is_empty()) return *this;
- _is_cursor_visible = false;
- return *this;
- }
- CImgDisplay& set_mouse(const int posx, const int posy) {
- if (is_empty() || _is_closed || posx<0 || posy<0) return *this;
- if (!_is_closed) {
- _update_window_pos();
- const int res = (int)SetCursorPos(_window_x + posx,_window_y + posy);
- if (res) { _mouse_x = posx; _mouse_y = posy; }
- }
- return *this;
- }
- CImgDisplay& set_title(const char *const format, ...) {
- if (is_empty()) return *this;
- char *const tmp = new char[1024];
- va_list ap;
- va_start(ap, format);
- cimg_vsnprintf(tmp,1024,format,ap);
- va_end(ap);
- if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; }
- delete[] _title;
- const unsigned int s = (unsigned int)std::strlen(tmp) + 1;
- _title = new char[s];
- std::memcpy(_title,tmp,s*sizeof(char));
- SetWindowTextA(_window, tmp);
- delete[] tmp;
- return *this;
- }
- template<typename T>
- CImgDisplay& display(const CImg<T>& img) {
- if (!img)
- throw CImgArgumentException(_cimgdisplay_instance
- "display(): Empty specified image.",
- cimgdisplay_instance);
- if (is_empty()) return assign(img);
- return render(img).paint();
- }
- CImgDisplay& paint() {
- if (_is_closed) return *this;
- WaitForSingleObject(_mutex,INFINITE);
- SetDIBitsToDevice(_hdc,0,0,_width,_height,0,0,0,_height,_data,&_bmi,DIB_RGB_COLORS);
- ReleaseMutex(_mutex);
- return *this;
- }
- template<typename T>
- CImgDisplay& render(const CImg<T>& img) {
- if (!img)
- throw CImgArgumentException(_cimgdisplay_instance
- "render(): Empty specified image.",
- cimgdisplay_instance);
- if (is_empty()) return *this;
- if (img._depth!=1) return render(img.get_projections2d((img._width - 1)/2,(img._height - 1)/2,
- (img._depth - 1)/2));
- const T
- *data1 = img._data,
- *data2 = (img._spectrum>=2)?img.data(0,0,0,1):data1,
- *data3 = (img._spectrum>=3)?img.data(0,0,0,2):data1;
- WaitForSingleObject(_mutex,INFINITE);
- unsigned int
- *const ndata = (img._width==_width && img._height==_height)?_data:
- new unsigned int[(size_t)img._width*img._height],
- *ptrd = ndata;
- if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) {
- _min = _max = 0;
- switch (img._spectrum) {
- case 1 : {
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char val = (unsigned char)*(data1++);
- *(ptrd++) = (unsigned int)((val<<16) | (val<<8) | val);
- }
- } break;
- case 2 : {
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char
- R = (unsigned char)*(data1++),
- G = (unsigned char)*(data2++);
- *(ptrd++) = (unsigned int)((R<<16) | (G<<8));
- }
- } break;
- default : {
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char
- R = (unsigned char)*(data1++),
- G = (unsigned char)*(data2++),
- B = (unsigned char)*(data3++);
- *(ptrd++) = (unsigned int)((R<<16) | (G<<8) | B);
- }
- }
- }
- } else {
- if (_normalization==3) {
- if (cimg::type<T>::is_float()) _min = (float)img.min_max(_max);
- else {
- _min = (float)cimg::type<T>::min();
- _max = (float)cimg::type<T>::max();
- }
- } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max);
- const float delta = _max - _min, mm = 255/(delta?delta:1.f);
- switch (img._spectrum) {
- case 1 : {
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
- *(ptrd++) = (unsigned int)((val<<16) | (val<<8) | val);
- }
- } break;
- case 2 : {
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char
- R = (unsigned char)((*(data1++) - _min)*mm),
- G = (unsigned char)((*(data2++) - _min)*mm);
- *(ptrd++) = (unsigned int)((R<<16) | (G<<8));
- }
- } break;
- default : {
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned char
- R = (unsigned char)((*(data1++) - _min)*mm),
- G = (unsigned char)((*(data2++) - _min)*mm),
- B = (unsigned char)((*(data3++) - _min)*mm);
- *(ptrd++) = (unsigned int)((R<<16) | (G<<8) | B);
- }
- }
- }
- }
- if (ndata!=_data) { _render_resize(ndata,img._width,img._height,_data,_width,_height); delete[] ndata; }
- ReleaseMutex(_mutex);
- return *this;
- }
- template<typename T>
- static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) {
- img.assign();
- HDC hScreen = GetDC(GetDesktopWindow());
- if (hScreen) {
- const int
- width = GetDeviceCaps(hScreen,HORZRES),
- height = GetDeviceCaps(hScreen,VERTRES);
- int _x0 = x0, _y0 = y0, _x1 = x1, _y1 = y1;
- if (_x0>_x1) cimg::swap(_x0,_x1);
- if (_y0>_y1) cimg::swap(_y0,_y1);
- if (_x1>=0 && _x0<width && _y1>=0 && _y0<height) {
- _x0 = std::max(_x0,0);
- _y0 = std::max(_y0,0);
- _x1 = std::min(_x1,width - 1);
- _y1 = std::min(_y1,height - 1);
- const int bw = _x1 - _x0 + 1, bh = _y1 - _y0 + 1;
- HDC hdcMem = CreateCompatibleDC(hScreen);
- if (hdcMem) {
- HBITMAP hBitmap = CreateCompatibleBitmap(hScreen,bw,bh);
- if (hBitmap) {
- HGDIOBJ hOld = SelectObject(hdcMem,hBitmap);
- if (hOld && BitBlt(hdcMem,0,0,bw,bh,hScreen,_x0,_y0,SRCCOPY) && SelectObject(hdcMem,hOld)) {
- BITMAPINFOHEADER bmi;
- bmi.biSize = sizeof(BITMAPINFOHEADER);
- bmi.biWidth = bw;
- bmi.biHeight = -bh;
- bmi.biPlanes = 1;
- bmi.biBitCount = 32;
- bmi.biCompression = BI_RGB;
- bmi.biSizeImage = 0;
- bmi.biXPelsPerMeter = bmi.biYPelsPerMeter = 0;
- bmi.biClrUsed = bmi.biClrImportant = 0;
- unsigned char *buf = new unsigned char[4*bw*bh];
- if (GetDIBits(hdcMem,hBitmap,0,bh,buf,(BITMAPINFO*)&bmi,DIB_RGB_COLORS)) {
- img.assign(bw,bh,1,3);
- const unsigned char *ptrs = buf;
- T *pR = img.data(0,0,0,0), *pG = img.data(0,0,0,1), *pB = img.data(0,0,0,2);
- cimg_forXY(img,x,y) {
- *(pR++) = (T)ptrs[2];
- *(pG++) = (T)ptrs[1];
- *(pB++) = (T)ptrs[0];
- ptrs+=4;
- }
- }
- delete[] buf;
- }
- DeleteObject(hBitmap);
- }
- DeleteDC(hdcMem);
- }
- }
- ReleaseDC(GetDesktopWindow(),hScreen);
- }
- if (img.is_empty())
- throw CImgDisplayException("CImgDisplay::screenshot(): Failed to take screenshot "
- "with coordinates (%d,%d)-(%d,%d).",
- x0,y0,x1,y1);
- }
- template<typename T>
- const CImgDisplay& snapshot(CImg<T>& img) const {
- if (is_empty()) { img.assign(); return *this; }
- const unsigned int *ptrs = _data;
- img.assign(_width,_height,1,3);
- T
- *data1 = img.data(0,0,0,0),
- *data2 = img.data(0,0,0,1),
- *data3 = img.data(0,0,0,2);
- for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
- const unsigned int val = *(ptrs++);
- *(data1++) = (T)(unsigned char)(val>>16);
- *(data2++) = (T)(unsigned char)((val>>8)&0xFF);
- *(data3++) = (T)(unsigned char)(val&0xFF);
- }
- return *this;
- }
- #endif
- //@}
- }; // struct CImgDisplay { ...
- /*
- #--------------------------------------
- #
- #
- #
- # Definition of the CImg<T> structure
- #
- #
- #
- #--------------------------------------
- */
- //! Class representing an image (up to 4 dimensions wide), each pixel being of type \c T.
- /**
- This is the main class of the %CImg Library. It declares and constructs
- an image, allows access to its pixel values, and is able to perform various image operations.
- \par Image representation
- A %CImg image is defined as an instance of the container \c CImg<T>, which contains a regular grid of pixels,
- each pixel value being of type \c T. The image grid can have up to 4 dimensions: width, height, depth
- and number of channels.
- Usually, the three first dimensions are used to describe spatial coordinates <tt>(x,y,z)</tt>,
- while the number of channels is rather used as a vector-valued dimension
- (it may describe the R,G,B color channels for instance).
- If you need a fifth dimension, you can use image lists \c CImgList<T> rather than simple images \c CImg<T>.
- Thus, the \c CImg<T> class is able to represent volumetric images of vector-valued pixels,
- as well as images with less dimensions (1D scalar signal, 2D color images, ...).
- Most member functions of the class CImg<\c T> are designed to handle this maximum case of (3+1) dimensions.
- Concerning the pixel value type \c T:
- fully supported template types are the basic C++ types: <tt>unsigned char, char, short, unsigned int, int,
- unsigned long, long, float, double, ... </tt>.
- Typically, fast image display can be done using <tt>CImg<unsigned char></tt> images,
- while complex image processing algorithms may be rather coded using <tt>CImg<float></tt> or <tt>CImg<double></tt>
- images that have floating-point pixel values. The default value for the template T is \c float.
- Using your own template types may be possible. However, you will certainly have to define the complete set
- of arithmetic and logical operators for your class.
- \par Image structure
- The \c CImg<T> structure contains \e six fields:
- - \c _width defines the number of \a columns of the image (size along the X-axis).
- - \c _height defines the number of \a rows of the image (size along the Y-axis).
- - \c _depth defines the number of \a slices of the image (size along the Z-axis).
- - \c _spectrum defines the number of \a channels of the image (size along the C-axis).
- - \c _data defines a \a pointer to the \a pixel \a data (of type \c T).
- - \c _is_shared is a boolean that tells if the memory buffer \c data is shared with
- another image.
- You can access these fields publicly although it is recommended to use the dedicated functions
- width(), height(), depth(), spectrum() and ptr() to do so.
- Image dimensions are not limited to a specific range (as long as you got enough available memory).
- A value of \e 1 usually means that the corresponding dimension is \a flat.
- If one of the dimensions is \e 0, or if the data pointer is null, the image is considered as \e empty.
- Empty images should not contain any pixel data and thus, will not be processed by CImg member functions
- (a CImgInstanceException will be thrown instead).
- Pixel data are stored in memory, in a non interlaced mode (See \ref cimg_storage).
- \par Image declaration and construction
- Declaring an image can be done by using one of the several available constructors.
- Here is a list of the most used:
- - Construct images from arbitrary dimensions:
- - <tt>CImg<char> img;</tt> declares an empty image.
- - <tt>CImg<unsigned char> img(128,128);</tt> declares a 128x128 greyscale image with
- \c unsigned \c char pixel values.
- - <tt>CImg<double> img(3,3);</tt> declares a 3x3 matrix with \c double coefficients.
- - <tt>CImg<unsigned char> img(256,256,1,3);</tt> declares a 256x256x1x3 (color) image
- (colors are stored as an image with three channels).
- - <tt>CImg<double> img(128,128,128);</tt> declares a 128x128x128 volumetric and greyscale image
- (with \c double pixel values).
- - <tt>CImg<> img(128,128,128,3);</tt> declares a 128x128x128 volumetric color image
- (with \c float pixels, which is the default value of the template parameter \c T).
- - \b Note: images pixels are <b>not automatically initialized to 0</b>. You may use the function \c fill() to
- do it, or use the specific constructor taking 5 parameters like this:
- <tt>CImg<> img(128,128,128,3,0);</tt> declares a 128x128x128 volumetric color image with all pixel values to 0.
- - Construct images from filenames:
- - <tt>CImg<unsigned char> img("image.jpg");</tt> reads a JPEG color image from the file "image.jpg".
- - <tt>CImg<float> img("analyze.hdr");</tt> reads a volumetric image (ANALYZE7.5 format) from the
- file "analyze.hdr".
- - \b Note: You need to install <a href="http://www.imagemagick.org">ImageMagick</a>
- to be able to read common compressed image formats (JPG,PNG, ...) (See \ref cimg_files_io).
- - Construct images from C-style arrays:
- - <tt>CImg<int> img(data_buffer,256,256);</tt> constructs a 256x256 greyscale image from a \c int* buffer
- \c data_buffer (of size 256x256=65536).
- - <tt>CImg<unsigned char> img(data_buffer,256,256,1,3);</tt> constructs a 256x256 color image
- from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels follow each others).
- The complete list of constructors can be found <a href="#constructors">here</a>.
- \par Most useful functions
- The \c CImg<T> class contains a lot of functions that operates on images.
- Some of the most useful are:
- - operator()(): Read or write pixel values.
- - display(): displays the image in a new window.
- **/
- template<typename T>
- struct CImg {
- unsigned int _width, _height, _depth, _spectrum;
- bool _is_shared;
- T *_data;
- //! Simple iterator type, to loop through each pixel value of an image instance.
- /**
- \note
- - The \c CImg<T>::iterator type is defined to be a <tt>T*</tt>.
- - You will seldom have to use iterators in %CImg, most classical operations
- being achieved (often in a faster way) using methods of \c CImg<T>.
- \par Example
- \code
- CImg<float> img("reference.jpg"); // Load image from file
- // Set all pixels to '0', with a CImg iterator.
- for (CImg<float>::iterator it = img.begin(), it<img.end(); ++it) *it = 0;
- img.fill(0); // Do the same with a built-in method
- \endcode
- **/
- typedef T* iterator;
- //! Simple const iterator type, to loop through each pixel value of a \c const image instance.
- /**
- \note
- - The \c CImg<T>::const_iterator type is defined to be a \c const \c T*.
- - You will seldom have to use iterators in %CImg, most classical operations
- being achieved (often in a faster way) using methods of \c CImg<T>.
- \par Example
- \code
- const CImg<float> img("reference.jpg"); // Load image from file
- float sum = 0;
- // Compute sum of all pixel values, with a CImg iterator.
- for (CImg<float>::iterator it = img.begin(), it<img.end(); ++it) sum+=*it;
- const float sum2 = img.sum(); // Do the same with a built-in method
- \endcode
- **/
- typedef const T* const_iterator;
- //! Pixel value type.
- /**
- Refer to the type of the pixel values of an image instance.
- \note
- - The \c CImg<T>::value_type type of a \c CImg<T> is defined to be a \c T.
- - \c CImg<T>::value_type is actually not used in %CImg methods. It has been mainly defined for
- compatibility with STL naming conventions.
- **/
- typedef T value_type;
- // Define common types related to template type T.
- typedef typename cimg::superset<T,bool>::type Tbool;
- typedef typename cimg::superset<T,unsigned char>::type Tuchar;
- typedef typename cimg::superset<T,char>::type Tchar;
- typedef typename cimg::superset<T,unsigned short>::type Tushort;
- typedef typename cimg::superset<T,short>::type Tshort;
- typedef typename cimg::superset<T,unsigned int>::type Tuint;
- typedef typename cimg::superset<T,int>::type Tint;
- typedef typename cimg::superset<T,cimg_ulong>::type Tulong;
- typedef typename cimg::superset<T,cimg_long>::type Tlong;
- typedef typename cimg::superset<T,float>::type Tfloat;
- typedef typename cimg::superset<T,double>::type Tdouble;
- typedef typename cimg::last<T,bool>::type boolT;
- typedef typename cimg::last<T,unsigned char>::type ucharT;
- typedef typename cimg::last<T,char>::type charT;
- typedef typename cimg::last<T,unsigned short>::type ushortT;
- typedef typename cimg::last<T,short>::type shortT;
- typedef typename cimg::last<T,unsigned int>::type uintT;
- typedef typename cimg::last<T,int>::type intT;
- typedef typename cimg::last<T,cimg_ulong>::type ulongT;
- typedef typename cimg::last<T,cimg_long>::type longT;
- typedef typename cimg::last<T,cimg_uint64>::type uint64T;
- typedef typename cimg::last<T,cimg_int64>::type int64T;
- typedef typename cimg::last<T,float>::type floatT;
- typedef typename cimg::last<T,double>::type doubleT;
- // Return 'dx*dy*dz*dc' as a 'size_t' and check no overflow occurs.
- static size_t safe_size(const unsigned int dx, const unsigned int dy,
- const unsigned int dz, const unsigned int dc) {
- if (!(dx && dy && dz && dc)) return 0;
- size_t siz = (size_t)dx, osiz = siz;
- if ((dy==1 || (siz*=dy)>osiz) &&
- ((osiz = siz), dz==1 || (siz*=dz)>osiz) &&
- ((osiz = siz), dc==1 || (siz*=dc)>osiz) &&
- ((osiz = siz), sizeof(T)==1 || (siz*sizeof(T))>osiz)) {
- if (siz > cimg_max_buf_size){
- throw CImgArgumentException("CImg<%s>::safe_size(): Specified size (%u,%u,%u,%u) exceeds maximum "
- "allowed buffer size of %lu ",
- pixel_type(),dx,dy,dz,dc,cimg_max_buf_size);
- }
- return siz;
- }
- throw CImgArgumentException("CImg<%s>::safe_size(): Specified size (%u,%u,%u,%u) overflows 'size_t'.",
- pixel_type(),dx,dy,dz,dc);
- }
- //@}
- //---------------------------
- //
- //! \name Plugins
- //@{
- //---------------------------
- #ifdef cimg_plugin
- #include cimg_plugin
- #endif
- #ifdef cimg_plugin1
- #include cimg_plugin1
- #endif
- #ifdef cimg_plugin2
- #include cimg_plugin2
- #endif
- #ifdef cimg_plugin3
- #include cimg_plugin3
- #endif
- #ifdef cimg_plugin4
- #include cimg_plugin4
- #endif
- #ifdef cimg_plugin5
- #include cimg_plugin5
- #endif
- #ifdef cimg_plugin6
- #include cimg_plugin6
- #endif
- #ifdef cimg_plugin7
- #include cimg_plugin7
- #endif
- #ifdef cimg_plugin8
- #include cimg_plugin8
- #endif
- //@}
- //---------------------------------------------------------
- //
- //! \name Constructors / Destructor / Instance Management
- //@{
- //---------------------------------------------------------
- //! Destroy image.
- /**
- \note
- - The pixel buffer data() is deallocated if necessary, e.g. for non-empty and non-shared image instances.
- - Destroying an empty or shared image does nothing actually.
- \warning
- - When destroying a non-shared image, make sure that you will \e not operate on a remaining shared image
- that shares its buffer with the destroyed instance, in order to avoid further invalid memory access
- (to a deallocated buffer).
- **/
- ~CImg() {
- if (!_is_shared) delete[] _data;
- }
- //! Construct empty image.
- /**
- \note
- - An empty image has no pixel data and all of its dimensions width(), height(), depth(), spectrum()
- are set to \c 0, as well as its pixel buffer pointer data().
- - An empty image may be re-assigned afterwards, e.g. with the family of
- assign(unsigned int,unsigned int,unsigned int,unsigned int) methods,
- or by operator=(const CImg<t>&). In all cases, the type of pixels stays \c T.
- - An empty image is never shared.
- \par Example
- \code
- CImg<float> img1, img2; // Construct two empty images
- img1.assign(256,256,1,3); // Re-assign 'img1' to be a 256x256x1x3 (color) image
- img2 = img1.get_rand(0,255); // Re-assign 'img2' to be a random-valued version of 'img1'
- img2.assign(); // Re-assign 'img2' to be an empty image again
- \endcode
- **/
- CImg():_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {}
- //! Construct image with specified size.
- /**
- \param size_x Image width().
- \param size_y Image height().
- \param size_z Image depth().
- \param size_c Image spectrum() (number of channels).
- \note
- - It is able to create only \e non-shared images, and allocates thus a pixel buffer data()
- for each constructed image instance.
- - Setting one dimension \c size_x,\c size_y,\c size_z or \c size_c to \c 0 leads to the construction of
- an \e empty image.
- - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated
- (e.g. when requested size is too big for available memory).
- \warning
- - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values.
- In order to initialize pixel values during construction (e.g. with \c 0), use constructor
- CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) instead.
- \par Example
- \code
- CImg<float> img1(256,256,1,3); // Construct a 256x256x1x3 (color) image, filled with garbage values
- CImg<float> img2(256,256,1,3,0); // Construct a 256x256x1x3 (color) image, filled with value '0'
- \endcode
- **/
- explicit CImg(const unsigned int size_x, const unsigned int size_y=1,
- const unsigned int size_z=1, const unsigned int size_c=1):
- _is_shared(false) {
- const size_t siz = safe_size(size_x,size_y,size_z,size_c);
- if (siz) {
- _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
- try { _data = new T[siz]; } catch (...) {
- _width = _height = _depth = _spectrum = 0; _data = 0;
- throw CImgInstanceException(_cimg_instance
- "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
- cimg_instance,
- cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
- size_x,size_y,size_z,size_c);
- }
- } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
- }
- //! Construct image with specified size and initialize pixel values.
- /**
- \param size_x Image width().
- \param size_y Image height().
- \param size_z Image depth().
- \param size_c Image spectrum() (number of channels).
- \param value Initialization value.
- \note
- - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int),
- but it also fills the pixel buffer with the specified \c value.
- \warning
- - It cannot be used to construct a vector-valued image and initialize it with \e vector-valued pixels
- (e.g. RGB vector, for color images).
- For this task, you may use fillC() after construction.
- **/
- CImg(const unsigned int size_x, const unsigned int size_y,
- const unsigned int size_z, const unsigned int size_c, const T& value):
- _is_shared(false) {
- const size_t siz = safe_size(size_x,size_y,size_z,size_c);
- if (siz) {
- _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
- try { _data = new T[siz]; } catch (...) {
- _width = _height = _depth = _spectrum = 0; _data = 0;
- throw CImgInstanceException(_cimg_instance
- "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
- cimg_instance,
- cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
- size_x,size_y,size_z,size_c);
- }
- fill(value);
- } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
- }
- //! Construct image with specified size and initialize pixel values from a sequence of integers.
- /**
- Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c,
- with pixels of type \c T, and initialize pixel
- values from the specified sequence of integers \c value0,\c value1,\c ...
- \param size_x Image width().
- \param size_y Image height().
- \param size_z Image depth().
- \param size_c Image spectrum() (number of channels).
- \param value0 First value of the initialization sequence (must be an \e integer).
- \param value1 Second value of the initialization sequence (must be an \e integer).
- \param ...
- \note
- - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
- the pixel buffer with a sequence of specified integer values.
- \warning
- - You must specify \e exactly \c size_x*\c size_y*\c size_z*\c size_c integers in the initialization sequence.
- Otherwise, the constructor may crash or fill your image pixels with garbage.
- \par Example
- \code
- const CImg<float> img(2,2,1,3, // Construct a 2x2 color (RGB) image
- 0,255,0,255, // Set the 4 values for the red component
- 0,0,255,255, // Set the 4 values for the green component
- 64,64,64,64); // Set the 4 values for the blue component
- img.resize(150,150).display();
- \endcode
- \image html ref_constructor1.jpg
- **/
- CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
- const int value0, const int value1, ...):
- _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
- #define _CImg_stdarg(img,a0,a1,N,t) { \
- size_t _siz = (size_t)N; \
- if (_siz--) { \
- va_list ap; \
- va_start(ap,a1); \
- T *ptrd = (img)._data; \
- *(ptrd++) = (T)a0; \
- if (_siz--) { \
- *(ptrd++) = (T)a1; \
- for ( ; _siz; --_siz) *(ptrd++) = (T)va_arg(ap,t); \
- } \
- va_end(ap); \
- } \
- }
- assign(size_x,size_y,size_z,size_c);
- _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),int);
- }
- #if cimg_use_cpp11==1
- //! Construct image with specified size and initialize pixel values from an initializer list of integers.
- /**
- Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c,
- with pixels of type \c T, and initialize pixel
- values from the specified initializer list of integers { \c value0,\c value1,\c ... }
- \param size_x Image width().
- \param size_y Image height().
- \param size_z Image depth().
- \param size_c Image spectrum() (number of channels).
- \param { value0, value1, ... } Initialization list
- \param repeat_values Tells if the value filling process is repeated over the image.
- \note
- - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
- the pixel buffer with a sequence of specified integer values.
- \par Example
- \code
- const CImg<float> img(2,2,1,3, // Construct a 2x2 color (RGB) image
- { 0,255,0,255, // Set the 4 values for the red component
- 0,0,255,255, // Set the 4 values for the green component
- 64,64,64,64 }); // Set the 4 values for the blue component
- img.resize(150,150).display();
- \endcode
- \image html ref_constructor1.jpg
- **/
- template<typename t>
- CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
- const std::initializer_list<t> values,
- const bool repeat_values=true):
- _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
- #define _cimg_constructor_cpp11(repeat_values) \
- auto it = values.begin(); \
- size_t siz = size(); \
- if (repeat_values) for (T *ptrd = _data; siz--; ) { \
- *(ptrd++) = (T)(*(it++)); if (it==values.end()) it = values.begin(); } \
- else { siz = std::min(siz,values.size()); for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++)); }
- assign(size_x,size_y,size_z,size_c);
- _cimg_constructor_cpp11(repeat_values);
- }
- template<typename t>
- CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z,
- std::initializer_list<t> values,
- const bool repeat_values=true):
- _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
- assign(size_x,size_y,size_z);
- _cimg_constructor_cpp11(repeat_values);
- }
- template<typename t>
- CImg(const unsigned int size_x, const unsigned int size_y,
- std::initializer_list<t> values,
- const bool repeat_values=true):
- _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
- assign(size_x,size_y);
- _cimg_constructor_cpp11(repeat_values);
- }
- template<typename t>
- CImg(const unsigned int size_x,
- std::initializer_list<t> values,
- const bool repeat_values=true):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
- assign(size_x);
- _cimg_constructor_cpp11(repeat_values);
- }
- //! Construct single channel 1D image with pixel values and width obtained from an initializer list of integers.
- /**
- Construct a new image instance of size \c width x \c 1 x \c 1 x \c 1,
- with pixels of type \c T, and initialize pixel
- values from the specified initializer list of integers { \c value0,\c value1,\c ... }. Image width is
- given by the size of the initializer list.
- \param { value0, value1, ... } Initialization list
- \note
- - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int) with height=1, depth=1, and spectrum=1,
- but it also fills the pixel buffer with a sequence of specified integer values.
- \par Example
- \code
- const CImg<float> img = {10,20,30,20,10 }; // Construct a 5x1 image with one channel, and set its pixel values
- img.resize(150,150).display();
- \endcode
- \image html ref_constructor1.jpg
- **/
- template<typename t>
- CImg(const std::initializer_list<t> values):
- _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
- assign(values.size(),1,1,1);
- auto it = values.begin();
- unsigned int siz = _width;
- for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++));
- }
- template<typename t>
- CImg<T>& operator=(std::initializer_list<t> values) {
- _cimg_constructor_cpp11(siz>values.size());
- return *this;
- }
- #endif
- //! Construct image with specified size and initialize pixel values from a sequence of doubles.
- /**
- Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, with pixels of type \c T,
- and initialize pixel values from the specified sequence of doubles \c value0,\c value1,\c ...
- \param size_x Image width().
- \param size_y Image height().
- \param size_z Image depth().
- \param size_c Image spectrum() (number of channels).
- \param value0 First value of the initialization sequence (must be a \e double).
- \param value1 Second value of the initialization sequence (must be a \e double).
- \param ...
- \note
- - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...), but
- takes a sequence of double values instead of integers.
- \warning
- - You must specify \e exactly \c dx*\c dy*\c dz*\c dc doubles in the initialization sequence.
- Otherwise, the constructor may crash or fill your image with garbage.
- For instance, the code below will probably crash on most platforms:
- \code
- const CImg<float> img(2,2,1,1, 0.5,0.5,255,255); // FAIL: The two last arguments are 'int', not 'double'!
- \endcode
- **/
- CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
- const double value0, const double value1, ...):
- _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
- assign(size_x,size_y,size_z,size_c);
- _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),double);
- }
- //! Construct image with specified size and initialize pixel values from a value string.
- /**
- Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, with pixels of type \c T,
- and initializes pixel values from the specified string \c values.
- \param size_x Image width().
- \param size_y Image height().
- \param size_z Image depth().
- \param size_c Image spectrum() (number of channels).
- \param values Value string describing the way pixel values are set.
- \param repeat_values Tells if the value filling process is repeated over the image.
- \note
- - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
- the pixel buffer with values described in the value string \c values.
- - Value string \c values may describe two different filling processes:
- - Either \c values is a sequences of values assigned to the image pixels, as in <tt>"1,2,3,7,8,2"</tt>.
- In this case, set \c repeat_values to \c true to periodically fill the image with the value sequence.
- - Either, \c values is a formula, as in <tt>"cos(x/10)*sin(y/20)"</tt>.
- In this case, parameter \c repeat_values is pointless.
- - For both cases, specifying \c repeat_values is mandatory.
- It disambiguates the possible overloading of constructor
- CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) with \c T being a <tt>const char*</tt>.
- - A \c CImgArgumentException is thrown when an invalid value string \c values is specified.
- \par Example
- \code
- const CImg<float> img1(129,129,1,3,"0,64,128,192,255",true), // Construct image from a value sequence
- img2(129,129,1,3,"if(c==0,255*abs(cos(x/10)),1.8*y)",false); // Construct image from a formula
- (img1,img2).display();
- \endcode
- \image html ref_constructor2.jpg
- **/
- CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
- const char *const values, const bool repeat_values):_is_shared(false) {
- const size_t siz = safe_size(size_x,size_y,size_z,size_c);
- if (siz) {
- _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
- try { _data = new T[siz]; } catch (...) {
- _width = _height = _depth = _spectrum = 0; _data = 0;
- throw CImgInstanceException(_cimg_instance
- "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
- cimg_instance,
- cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
- size_x,size_y,size_z,size_c);
- }
- fill(values,repeat_values);
- } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
- }
- //! Construct image with specified size and initialize pixel values from a memory buffer.
- /**
- Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c, with pixels of type \c T,
- and initializes pixel values from the specified \c t* memory buffer.
- \param values Pointer to the input memory buffer.
- \param size_x Image width().
- \param size_y Image height().
- \param size_z Image depth().
- \param size_c Image spectrum() (number of channels).
- \param is_shared Tells if input memory buffer must be shared by the current instance.
- \note
- - If \c is_shared is \c false, the image instance allocates its own pixel buffer,
- and values from the specified input buffer are copied to the instance buffer.
- If buffer types \c T and \c t are different, a regular static cast is performed during buffer copy.
- - Otherwise, the image instance does \e not allocate a new buffer, and uses the input memory buffer as its
- own pixel buffer. This case requires that types \c T and \c t are the same. Later, destroying such a shared
- image will not deallocate the pixel buffer, this task being obviously charged to the initial buffer allocator.
- - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated
- (e.g. when requested size is too big for available memory).
- \warning
- - You must take care when operating on a shared image, since it may have an invalid pixel buffer pointer data()
- (e.g. already deallocated).
- \par Example
- \code
- unsigned char tab[256*256] = {};
- CImg<unsigned char> img1(tab,256,256,1,1,false), // Construct new non-shared image from buffer 'tab'
- img2(tab,256,256,1,1,true); // Construct new shared-image from buffer 'tab'
- tab[1024] = 255; // Here, 'img2' is indirectly modified, but not 'img1'
- \endcode
- **/
- template<typename t>
- CImg(const t *const values, const unsigned int size_x, const unsigned int size_y=1,
- const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false):_is_shared(false) {
- if (is_shared) {
- _width = _height = _depth = _spectrum = 0; _data = 0;
- throw CImgArgumentException(_cimg_instance
- "CImg(): Invalid construction request of a (%u,%u,%u,%u) shared instance "
- "from a (%s*) buffer (pixel types are different).",
- cimg_instance,
- size_x,size_y,size_z,size_c,CImg<t>::pixel_type());
- }
- const size_t siz = safe_size(size_x,size_y,size_z,size_c);
- if (values && siz) {
- _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
- try { _data = new T[siz]; } catch (...) {
- _width = _height = _depth = _spectrum = 0; _data = 0;
- throw CImgInstanceException(_cimg_instance
- "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
- cimg_instance,
- cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
- size_x,size_y,size_z,size_c);
- }
- const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
- } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
- }
- //! Construct image with specified size and initialize pixel values from a memory buffer \specialization.
- CImg(const T *const values, const unsigned int size_x, const unsigned int size_y=1,
- const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false) {
- const size_t siz = safe_size(size_x,size_y,size_z,size_c);
- if (values && siz) {
- _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = is_shared;
- if (_is_shared) _data = const_cast<T*>(values);
- else {
- try { _data = new T[siz]; } catch (...) {
- _width = _height = _depth = _spectrum = 0; _data = 0;
- throw CImgInstanceException(_cimg_instance
- "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
- cimg_instance,
- cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
- size_x,size_y,size_z,size_c);
- }
- std::memcpy(_data,values,siz*sizeof(T));
- }
- } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
- }
- //! Construct image from memory buffer with specified size and pixel ordering scheme.
- template<typename t>
- CImg(const t *const values, const unsigned int size_x, const unsigned int size_y,
- const unsigned int size_z, const unsigned int size_c,
- const char *const axes_order):_data(0),_is_shared(false) {
- const size_t siz = safe_size(size_x,size_y,size_z,size_c);
- if (values && siz) {
- unsigned char s_code[4] = { 0,1,2,3 }, n_code[4] = {};
- for (unsigned int l = 0; axes_order[l]; ++l) {
- int c = cimg::lowercase(axes_order[l]);
- if (l>=4 || (c!='x' && c!='y' && c!='z' && c!='c')) { *s_code = 4; break; }
- else { ++n_code[c%=4]; s_code[l] = c; }
- }
- if (*axes_order && *s_code<4 && *n_code<=1 && n_code[1]<=1 && n_code[2]<=1 && n_code[3]<=1) {
- const unsigned int code = (s_code[0]<<12) | (s_code[1]<<8) | (s_code[2]<<4) | (s_code[3]);
- int s0 = 0, s1 = 0, s2 = 0, s3 = 0;
- const char *inv_order = 0;
- switch (code) {
- case 0x0123 : inv_order = "xyzc"; s0 = size_x; s1 = size_y; s2 = size_z; s3 = size_c; break; // xyzc
- case 0x0132 : inv_order = "xyzc"; s0 = size_x; s1 = size_y; s2 = size_c; s3 = size_z; break; // xycz
- case 0x0213 : inv_order = "xzyc"; s0 = size_x; s1 = size_z; s2 = size_y; s3 = size_c; break; // xzyc
- case 0x0231 : inv_order = "xcyz"; s0 = size_x; s1 = size_z; s2 = size_c; s3 = size_y; break; // xzcy
- case 0x0312 : inv_order = "xzcy"; s0 = size_x; s1 = size_c; s2 = size_y; s3 = size_z; break; // xcyz
- case 0x0321 : inv_order = "xczy"; s0 = size_x; s1 = size_c; s2 = size_z; s3 = size_y; break; // xczy
- case 0x1023 : inv_order = "yxzc"; s0 = size_y; s1 = size_x; s2 = size_z; s3 = size_c; break; // yxzc
- case 0x1032 : inv_order = "yxcz"; s0 = size_y; s1 = size_x; s2 = size_c; s3 = size_z; break; // yxcz
- case 0x1203 : inv_order = "zxyc"; s0 = size_y; s1 = size_z; s2 = size_x; s3 = size_c; break; // yzxc
- case 0x1230 : inv_order = "cxyz"; s0 = size_y; s1 = size_z; s2 = size_c; s3 = size_x; break; // yzcx
- case 0x1302 : inv_order = "zxcy"; s0 = size_y; s1 = size_c; s2 = size_x; s3 = size_z; break; // ycxz
- case 0x1320 : inv_order = "cxzy"; s0 = size_y; s1 = size_c; s2 = size_z; s3 = size_x; break; // yczx
- case 0x2013 : inv_order = "yzxc"; s0 = size_z; s1 = size_x; s2 = size_y; s3 = size_c; break; // zxyc
- case 0x2031 : inv_order = "ycxz"; s0 = size_z; s1 = size_x; s2 = size_c; s3 = size_y; break; // zxcy
- case 0x2103 : inv_order = "zyxc"; s0 = size_z; s1 = size_y; s2 = size_x; s3 = size_c; break; // zyxc
- case 0x2130 : inv_order = "cyxz"; s0 = size_z; s1 = size_y; s2 = size_c; s3 = size_x; break; // zycx
- case 0x2301 : inv_order = "zcxy"; s0 = size_z; s1 = size_c; s2 = size_x; s3 = size_y; break; // zcxy
- case 0x2310 : inv_order = "czxy"; s0 = size_z; s1 = size_c; s2 = size_y; s3 = size_x; break; // zcyx
- case 0x3012 : inv_order = "yzcx"; s0 = size_c; s1 = size_x; s2 = size_y; s3 = size_z; break; // cxyz
- case 0x3021 : inv_order = "yczx"; s0 = size_c; s1 = size_x; s2 = size_z; s3 = size_y; break; // cxzy
- case 0x3102 : inv_order = "zycx"; s0 = size_c; s1 = size_y; s2 = size_x; s3 = size_z; break; // cyxz
- case 0x3120 : inv_order = "cyzx"; s0 = size_c; s1 = size_y; s2 = size_z; s3 = size_x; break; // cyzx
- case 0x3201 : inv_order = "zcyx"; s0 = size_c; s1 = size_z; s2 = size_x; s3 = size_y; break; // czxy
- case 0x3210 : inv_order = "czyx"; s0 = size_c; s1 = size_z; s2 = size_y; s3 = size_x; break; // czyx
- }
- CImg<t>(values,s0,s1,s2,s3,true).get_permute_axes(inv_order).move_to(*this);
- } else {
- _width = _height = _depth = _spectrum = 0; _data = 0;
- throw CImgArgumentException(_cimg_instance
- "CImg(): Invalid specified axes order '%s'.",
- cimg_instance,
- axes_order);
- }
- } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
- }
- //! Construct image from reading an image file.
- /**
- Construct a new image instance with pixels of type \c T, and initialize pixel values with the data read from
- an image file.
- \param filename Filename, as a C-string.
- \note
- - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it reads the image
- dimensions and pixel values from the specified image file.
- - The recognition of the image file format by %CImg higlhy depends on the tools installed on your system
- and on the external libraries you used to link your code against.
- - Considered pixel type \c T should better fit the file format specification, or data loss may occur during
- file load (e.g. constructing a \c CImg<unsigned char> from a float-valued image file).
- - A \c CImgIOException is thrown when the specified \c filename cannot be read, or if the file format is not
- recognized.
- \par Example
- \code
- const CImg<float> img("reference.jpg");
- img.display();
- \endcode
- \image html ref_image.jpg
- **/
- explicit CImg(const char *const filename):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
- assign(filename);
- }
- //! Construct image copy.
- /**
- Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg<t> instance.
- \param img Input image to copy.
- \note
- - Constructed copy has the same size width() x height() x depth() x spectrum() and pixel values as the
- input image \c img.
- - If input image \c img is \e shared and if types \c T and \c t are the same, the constructed copy is also
- \e shared, and shares its pixel buffer with \c img.
- Modifying a pixel value in the constructed copy will thus also modifies it in the input image \c img.
- This behavior is needful to allow functions to return shared images.
- - Otherwise, the constructed copy allocates its own pixel buffer, and copies pixel values from the input
- image \c img into its buffer. The copied pixel values may be eventually statically casted if types \c T and
- \c t are different.
- - Constructing a copy from an image \c img when types \c t and \c T are the same is significantly faster than
- with different types.
- - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated
- (e.g. not enough available memory).
- **/
- template<typename t>
- CImg(const CImg<t>& img):_is_shared(false) {
- const size_t siz = (size_t)img.size();
- if (img._data && siz) {
- _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
- try { _data = new T[siz]; } catch (...) {
- _width = _height = _depth = _spectrum = 0; _data = 0;
- throw CImgInstanceException(_cimg_instance
- "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
- cimg_instance,
- cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
- img._width,img._height,img._depth,img._spectrum);
- }
- const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
- } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
- }
- //! Construct image copy \specialization.
- CImg(const CImg<T>& img) {
- const size_t siz = (size_t)img.size();
- if (img._data && siz) {
- _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
- _is_shared = img._is_shared;
- if (_is_shared) _data = const_cast<T*>(img._data);
- else {
- try { _data = new T[siz]; } catch (...) {
- _width = _height = _depth = _spectrum = 0; _data = 0;
- throw CImgInstanceException(_cimg_instance
- "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
- cimg_instance,
- cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
- img._width,img._height,img._depth,img._spectrum);
- }
- std::memcpy(_data,img._data,siz*sizeof(T));
- }
- } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
- }
- //! Advanced copy constructor.
- /**
- Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg<t> instance,
- while forcing the shared state of the constructed copy.
- \param img Input image to copy.
- \param is_shared Tells about the shared state of the constructed copy.
- \note
- - Similar to CImg(const CImg<t>&), except that it allows to decide the shared state of
- the constructed image, which does not depend anymore on the shared state of the input image \c img:
- - If \c is_shared is \c true, the constructed copy will share its pixel buffer with the input image \c img.
- For that case, the pixel types \c T and \c t \e must be the same.
- - If \c is_shared is \c false, the constructed copy will allocate its own pixel buffer, whether the input
- image \c img is shared or not.
- - A \c CImgArgumentException is thrown when a shared copy is requested with different pixel types \c T and \c t.
- **/
- template<typename t>
- CImg(const CImg<t>& img, const bool is_shared):_is_shared(false) {
- if (is_shared) {
- _width = _height = _depth = _spectrum = 0; _data = 0;
- throw CImgArgumentException(_cimg_instance
- "CImg(): Invalid construction request of a shared instance from a "
- "CImg<%s> image (%u,%u,%u,%u,%p) (pixel types are different).",
- cimg_instance,
- CImg<t>::pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data);
- }
- const size_t siz = (size_t)img.size();
- if (img._data && siz) {
- _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
- try { _data = new T[siz]; } catch (...) {
- _width = _height = _depth = _spectrum = 0; _data = 0;
- throw CImgInstanceException(_cimg_instance
- "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
- cimg_instance,
- cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
- img._width,img._height,img._depth,img._spectrum);
- }
- const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
- } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
- }
- //! Advanced copy constructor \specialization.
- CImg(const CImg<T>& img, const bool is_shared) {
- const size_t siz = (size_t)img.size();
- if (img._data && siz) {
- _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
- _is_shared = is_shared;
- if (_is_shared) _data = const_cast<T*>(img._data);
- else {
- try { _data = new T[siz]; } catch (...) {
- _width = _height = _depth = _spectrum = 0; _data = 0;
- throw CImgInstanceException(_cimg_instance
- "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
- cimg_instance,
- cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
- img._width,img._height,img._depth,img._spectrum);
- }
- std::memcpy(_data,img._data,siz*sizeof(T));
- }
- } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
- }
- //! Construct image with dimensions borrowed from another image.
- /**
- Construct a new image instance with pixels of type \c T, and size get from some dimensions of an existing
- \c CImg<t> instance.
- \param img Input image from which dimensions are borrowed.
- \param dimensions C-string describing the image size along the X,Y,Z and C-dimensions.
- \note
- - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it takes the image dimensions
- (\e not its pixel values) from an existing \c CImg<t> instance.
- - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values.
- In order to initialize pixel values (e.g. with \c 0), use constructor CImg(const CImg<t>&,const char*,T)
- instead.
- \par Example
- \code
- const CImg<float> img1(256,128,1,3), // 'img1' is a 256x128x1x3 image
- img2(img1,"xyzc"), // 'img2' is a 256x128x1x3 image
- img3(img1,"y,x,z,c"), // 'img3' is a 128x256x1x3 image
- img4(img1,"c,x,y,3",0), // 'img4' is a 3x128x256x3 image (with pixels initialized to '0')
- \endcode
- **/
- template<typename t>
- CImg(const CImg<t>& img, const char *const dimensions):
- _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
- assign(img,dimensions);
- }
- //! Construct image with dimensions borrowed from another image and initialize pixel values.
- /**
- Construct a new image instance with pixels of type \c T, and size get from the dimensions of an existing
- \c CImg<t> instance, and set all pixel values to specified \c value.
- \param img Input image from which dimensions are borrowed.
- \param dimensions String describing the image size along the X,Y,Z and V-dimensions.
- \param value Value used for initialization.
- \note
- - Similar to CImg(const CImg<t>&,const char*), but it also fills the pixel buffer with the specified \c value.
- **/
- template<typename t>
- CImg(const CImg<t>& img, const char *const dimensions, const T& value):
- _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
- assign(img,dimensions).fill(value);
- }
- //! Construct image from a display window.
- /**
- Construct a new image instance with pixels of type \c T, as a snapshot of an existing \c CImgDisplay instance.
- \param disp Input display window.
- \note
- - The width() and height() of the constructed image instance are the same as the specified \c CImgDisplay.
- - The depth() and spectrum() of the constructed image instance are respectively set to \c 1 and \c 3
- (i.e. a 2D color image).
- - The image pixels are read as 8-bits RGB values.
- **/
- explicit CImg(const CImgDisplay &disp):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
- disp.snapshot(*this);
- }
- // Constructor and assignment operator for rvalue references (c++11).
- // This avoids an additional image copy for methods returning new images. Can save RAM for big images !
- #if cimg_use_cpp11==1
- CImg(CImg<T>&& img):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
- swap(img);
- }
- CImg<T>& operator=(CImg<T>&& img) {
- if (_is_shared) return assign(img);
- return img.swap(*this);
- }
- #endif
- //! Construct empty image \inplace.
- /**
- In-place version of the default constructor CImg(). It simply resets the instance to an empty image.
- **/
- CImg<T>& assign() {
- if (!_is_shared) delete[] _data;
- _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0;
- return *this;
- }
- //! Construct image with specified size \inplace.
- /**
- In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int).
- **/
- CImg<T>& assign(const unsigned int size_x, const unsigned int size_y=1,
- const unsigned int size_z=1, const unsigned int size_c=1) {
- const size_t siz = safe_size(size_x,size_y,size_z,size_c);
- if (!siz) return assign();
- const size_t curr_siz = (size_t)size();
- if (siz!=curr_siz) {
- if (_is_shared)
- throw CImgArgumentException(_cimg_instance
- "assign(): Invalid assignment request of shared instance from specified "
- "image (%u,%u,%u,%u).",
- cimg_instance,
- size_x,size_y,size_z,size_c);
- else {
- delete[] _data;
- try { _data = new T[siz]; } catch (...) {
- _width = _height = _depth = _spectrum = 0; _data = 0;
- throw CImgInstanceException(_cimg_instance
- "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
- cimg_instance,
- cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
- size_x,size_y,size_z,size_c);
- }
- }
- }
- _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
- return *this;
- }
- //! Construct image with specified size and initialize pixel values \inplace.
- /**
- In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,T).
- **/
- CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
- const unsigned int size_z, const unsigned int size_c, const T& value) {
- return assign(size_x,size_y,size_z,size_c).fill(value);
- }
- //! Construct image with specified size and initialize pixel values from a sequence of integers \inplace.
- /**
- In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...).
- **/
- CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
- const unsigned int size_z, const unsigned int size_c,
- const int value0, const int value1, ...) {
- assign(size_x,size_y,size_z,size_c);
- _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),int);
- return *this;
- }
- //! Construct image with specified size and initialize pixel values from a sequence of doubles \inplace.
- /**
- In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,double,double,...).
- **/
- CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
- const unsigned int size_z, const unsigned int size_c,
- const double value0, const double value1, ...) {
- assign(size_x,size_y,size_z,size_c);
- _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),double);
- return *this;
- }
- //! Construct image with specified size and initialize pixel values from a value string \inplace.
- /**
- In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,const char*,bool).
- **/
- CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
- const unsigned int size_z, const unsigned int size_c,
- const char *const values, const bool repeat_values) {
- return assign(size_x,size_y,size_z,size_c).fill(values,repeat_values);
- }
- //! Construct image with specified size and initialize pixel values from a memory buffer \inplace.
- /**
- In-place version of the constructor CImg(const t*,unsigned int,unsigned int,unsigned int,unsigned int).
- **/
- template<typename t>
- CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y=1,
- const unsigned int size_z=1, const unsigned int size_c=1) {
- const size_t siz = safe_size(size_x,size_y,size_z,size_c);
- if (!values || !siz) return assign();
- assign(size_x,size_y,size_z,size_c);
- const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
- return *this;
- }
- //! Construct image with specified size and initialize pixel values from a memory buffer \specialization.
- CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y=1,
- const unsigned int size_z=1, const unsigned int size_c=1) {
- const size_t siz = safe_size(size_x,size_y,size_z,size_c);
- if (!values || !siz) return assign();
- const size_t curr_siz = (size_t)size();
- if (values==_data && siz==curr_siz) return assign(size_x,size_y,size_z,size_c);
- if (_is_shared || values + siz<_data || values>=_data + size()) {
- assign(size_x,size_y,size_z,size_c);
- if (_is_shared) std::memmove((void*)_data,(void*)values,siz*sizeof(T));
- else std::memcpy((void*)_data,(void*)values,siz*sizeof(T));
- } else {
- T *new_data = 0;
- try { new_data = new T[siz]; } catch (...) {
- _width = _height = _depth = _spectrum = 0; _data = 0;
- throw CImgInstanceException(_cimg_instance
- "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
- cimg_instance,
- cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
- size_x,size_y,size_z,size_c);
- }
- std::memcpy((void*)new_data,(void*)values,siz*sizeof(T));
- delete[] _data; _data = new_data; _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
- }
- return *this;
- }
- //! Construct image with specified size and initialize pixel values from a memory buffer \overloading.
- template<typename t>
- CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y,
- const unsigned int size_z, const unsigned int size_c, const bool is_shared) {
- if (is_shared)
- throw CImgArgumentException(_cimg_instance
- "assign(): Invalid assignment request of shared instance from (%s*) buffer"
- "(pixel types are different).",
- cimg_instance,
- CImg<t>::pixel_type());
- return assign(values,size_x,size_y,size_z,size_c);
- }
- //! Construct image with specified size and initialize pixel values from a memory buffer \overloading.
- CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y,
- const unsigned int size_z, const unsigned int size_c, const bool is_shared) {
- const size_t siz = safe_size(size_x,size_y,size_z,size_c);
- if (!values || !siz) return assign();
- if (!is_shared) { if (_is_shared) assign(); assign(values,size_x,size_y,size_z,size_c); }
- else {
- if (!_is_shared) {
- if (values + siz<_data || values>=_data + size()) assign();
- else cimg::warn(_cimg_instance
- "assign(): Shared image instance has overlapping memory.",
- cimg_instance);
- }
- _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = true;
- _data = const_cast<T*>(values);
- }
- return *this;
- }
- //! Construct image from memory buffer with specified size and pixel ordering scheme.
- template<typename t>
- CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y,
- const unsigned int size_z, const unsigned int size_c,
- const char *const axes_order) {
- CImg<T>(values,size_x,size_y,size_z,size_c,axes_order).move_to(*this);
- }
- //! Construct image from reading an image file \inplace.
- /**
- In-place version of the constructor CImg(const char*).
- **/
- CImg<T>& assign(const char *const filename) {
- return load(filename);
- }
- //! Construct image copy \inplace.
- /**
- In-place version of the constructor CImg(const CImg<t>&).
- **/
- template<typename t>
- CImg<T>& assign(const CImg<t>& img) {
- return assign(img._data,img._width,img._height,img._depth,img._spectrum);
- }
- //! In-place version of the advanced copy constructor.
- /**
- In-place version of the constructor CImg(const CImg<t>&,bool).
- **/
- template<typename t>
- CImg<T>& assign(const CImg<t>& img, const bool is_shared) {
- return assign(img._data,img._width,img._height,img._depth,img._spectrum,is_shared);
- }
- //! Construct image with dimensions borrowed from another image \inplace.
- /**
- In-place version of the constructor CImg(const CImg<t>&,const char*).
- **/
- template<typename t>
- CImg<T>& assign(const CImg<t>& img, const char *const dimensions) {
- if (!dimensions || !*dimensions) return assign(img._width,img._height,img._depth,img._spectrum);
- unsigned int siz[4] = { 0,1,1,1 }, k = 0;
- CImg<charT> item(256);
- for (const char *s = dimensions; *s && k<4; ++k) {
- if (cimg_sscanf(s,"%255[^0-9%xyzvwhdcXYZVWHDC]",item._data)>0) s+=std::strlen(item);
- if (*s) {
- unsigned int val = 0; char sep = 0;
- if (cimg_sscanf(s,"%u%c",&val,&sep)>0) {
- if (sep=='%') siz[k] = val*(k==0?_width:k==1?_height:k==2?_depth:_spectrum)/100;
- else siz[k] = val;
- while (*s>='0' && *s<='9') ++s;
- if (sep=='%') ++s;
- } else switch (cimg::lowercase(*s)) {
- case 'x' : case 'w' : siz[k] = img._width; ++s; break;
- case 'y' : case 'h' : siz[k] = img._height; ++s; break;
- case 'z' : case 'd' : siz[k] = img._depth; ++s; break;
- case 'c' : case 's' : siz[k] = img._spectrum; ++s; break;
- default :
- throw CImgArgumentException(_cimg_instance
- "assign(): Invalid character '%c' detected in specified dimension string '%s'.",
- cimg_instance,
- *s,dimensions);
- }
- }
- }
- return assign(siz[0],siz[1],siz[2],siz[3]);
- }
- //! Construct image with dimensions borrowed from another image and initialize pixel values \inplace.
- /**
- In-place version of the constructor CImg(const CImg<t>&,const char*,T).
- **/
- template<typename t>
- CImg<T>& assign(const CImg<t>& img, const char *const dimensions, const T& value) {
- return assign(img,dimensions).fill(value);
- }
- //! Construct image from a display window \inplace.
- /**
- In-place version of the constructor CImg(const CImgDisplay&).
- **/
- CImg<T>& assign(const CImgDisplay &disp) {
- disp.snapshot(*this);
- return *this;
- }
- //! Construct empty image \inplace.
- /**
- Equivalent to assign().
- \note
- - It has been defined for compatibility with STL naming conventions.
- **/
- CImg<T>& clear() {
- return assign();
- }
- //! Transfer content of an image instance into another one.
- /**
- Transfer the dimensions and the pixel buffer content of an image instance into another one,
- and replace instance by an empty image. It avoids the copy of the pixel buffer
- when possible.
- \param img Destination image.
- \note
- - Pixel types \c T and \c t of source and destination images can be different, though the process is
- designed to be instantaneous when \c T and \c t are the same.
- \par Example
- \code
- CImg<float> src(256,256,1,3,0), // Construct a 256x256x1x3 (color) image filled with value '0'
- dest(16,16); // Construct a 16x16x1x1 (scalar) image
- src.move_to(dest); // Now, 'src' is empty and 'dest' is the 256x256x1x3 image
- \endcode
- **/
- template<typename t>
- CImg<t>& move_to(CImg<t>& img) {
- img.assign(*this);
- assign();
- return img;
- }
- //! Transfer content of an image instance into another one \specialization.
- CImg<T>& move_to(CImg<T>& img) {
- if (_is_shared || img._is_shared) img.assign(*this);
- else swap(img);
- assign();
- return img;
- }
- //! Transfer content of an image instance into a new image in an image list.
- /**
- Transfer the dimensions and the pixel buffer content of an image instance
- into a newly inserted image at position \c pos in specified \c CImgList<t> instance.
- \param list Destination list.
- \param pos Position of the newly inserted image in the list.
- \note
- - When optional parameter \c pos is omitted, the image instance is transferred as a new
- image at the end of the specified \c list.
- - It is convenient to sequentially insert new images into image lists, with no
- additional copies of memory buffer.
- \par Example
- \code
- CImgList<float> list; // Construct an empty image list
- CImg<float> img("reference.jpg"); // Read image from filename
- img.move_to(list); // Transfer image content as a new item in the list (no buffer copy)
- \endcode
- **/
- template<typename t>
- CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos=~0U) {
- const unsigned int npos = pos>list._width?list._width:pos;
- move_to(list.insert(1,npos)[npos]);
- return list;
- }
- //! Swap fields of two image instances.
- /**
- \param img Image to swap fields with.
- \note
- - It can be used to interchange the content of two images in a very fast way. Can be convenient when dealing
- with algorithms requiring two swapping buffers.
- \par Example
- \code
- CImg<float> img1("lena.jpg"),
- img2("milla.jpg");
- img1.swap(img2); // Now, 'img1' is 'milla' and 'img2' is 'lena'
- \endcode
- **/
- CImg<T>& swap(CImg<T>& img) {
- cimg::swap(_width,img._width,_height,img._height,_depth,img._depth,_spectrum,img._spectrum);
- cimg::swap(_data,img._data);
- cimg::swap(_is_shared,img._is_shared);
- return img;
- }
- //! Return a reference to an empty image.
- /**
- \note
- This function is useful mainly to declare optional parameters having type \c CImg<T> in functions prototypes,
- e.g.
- \code
- void f(const int x=0, const int y=0, const CImg<float>& img=CImg<float>::empty());
- \endcode
- **/
- static CImg<T>& empty() {
- static CImg<T> _empty;
- return _empty.assign();
- }
- //! Return a reference to an empty image \const.
- static const CImg<T>& const_empty() {
- static const CImg<T> _empty;
- return _empty;
- }
- //@}
- //------------------------------------------
- //
- //! \name Overloaded Operators
- //@{
- //------------------------------------------
- //! Access to a pixel value.
- /**
- Return a reference to a located pixel value of the image instance,
- being possibly \e const, whether the image instance is \e const or not.
- This is the standard method to get/set pixel values in \c CImg<T> images.
- \param x X-coordinate of the pixel value.
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- \param c C-coordinate of the pixel value.
- \note
- - Range of pixel coordinates start from <tt>(0,0,0,0)</tt> to
- <tt>(width() - 1,height() - 1,depth() - 1,spectrum() - 1)</tt>.
- - Due to the particular arrangement of the pixel buffers defined in %CImg, you can omit one coordinate if the
- corresponding dimension is equal to \c 1.
- For instance, pixels of a 2D image (depth() equal to \c 1) can be accessed by <tt>img(x,y,c)</tt> instead of
- <tt>img(x,y,0,c)</tt>.
- \warning
- - There is \e no boundary checking done in this operator, to make it as fast as possible.
- You \e must take care of out-of-bounds access by yourself, if necessary.
- For debugging purposes, you may want to define macro \c 'cimg_verbosity'>=3 to enable additional boundary
- checking operations in this operator. In that case, warning messages will be printed on the error output
- when accessing out-of-bounds pixels.
- \par Example
- \code
- CImg<float> img(100,100,1,3,0); // Construct a 100x100x1x3 (color) image with pixels set to '0'
- const float
- valR = img(10,10,0,0), // Read red value at coordinates (10,10)
- valG = img(10,10,0,1), // Read green value at coordinates (10,10)
- valB = img(10,10,2), // Read blue value at coordinates (10,10) (Z-coordinate can be omitted)
- avg = (valR + valG + valB)/3; // Compute average pixel value
- img(10,10,0) = img(10,10,1) = img(10,10,2) = avg; // Replace the color pixel (10,10) by the average grey value
- \endcode
- **/
- #if cimg_verbosity>=3
- T& operator()(const unsigned int x, const unsigned int y=0,
- const unsigned int z=0, const unsigned int c=0) {
- const ulongT off = (ulongT)offset(x,y,z,c);
- if (!_data || off>=size()) {
- cimg::warn(_cimg_instance
- "operator(): Invalid pixel request, at coordinates (%d,%d,%d,%d) [offset=%u].",
- cimg_instance,
- (int)x,(int)y,(int)z,(int)c,off);
- return *_data;
- }
- else return _data[off];
- }
- //! Access to a pixel value \const.
- const T& operator()(const unsigned int x, const unsigned int y=0,
- const unsigned int z=0, const unsigned int c=0) const {
- return const_cast<CImg<T>*>(this)->operator()(x,y,z,c);
- }
- //! Access to a pixel value.
- /**
- \param x X-coordinate of the pixel value.
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- \param c C-coordinate of the pixel value.
- \param wh Precomputed offset, must be equal to <tt>width()*\ref height()</tt>.
- \param whd Precomputed offset, must be equal to <tt>width()*\ref height()*\ref depth()</tt>.
- \note
- - Similar to (but faster than) operator()().
- It uses precomputed offsets to optimize memory access. You may use it to optimize
- the reading/writing of several pixel values in the same image (e.g. in a loop).
- **/
- T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
- const ulongT wh, const ulongT whd=0) {
- cimg::unused(wh,whd);
- return (*this)(x,y,z,c);
- }
- //! Access to a pixel value \const.
- const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
- const ulongT wh, const ulongT whd=0) const {
- cimg::unused(wh,whd);
- return (*this)(x,y,z,c);
- }
- #else
- T& operator()(const unsigned int x) {
- return _data[x];
- }
- const T& operator()(const unsigned int x) const {
- return _data[x];
- }
- T& operator()(const unsigned int x, const unsigned int y) {
- return _data[x + y*_width];
- }
- const T& operator()(const unsigned int x, const unsigned int y) const {
- return _data[x + y*_width];
- }
- T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) {
- return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height];
- }
- const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) const {
- return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height];
- }
- T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) {
- return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height + c*(ulongT)_width*_height*_depth];
- }
- const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) const {
- return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height + c*(ulongT)_width*_height*_depth];
- }
- T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int,
- const ulongT wh) {
- return _data[x + y*_width + z*wh];
- }
- const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int,
- const ulongT wh) const {
- return _data[x + y*_width + z*wh];
- }
- T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
- const ulongT wh, const ulongT whd) {
- return _data[x + y*_width + z*wh + c*whd];
- }
- const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
- const ulongT wh, const ulongT whd) const {
- return _data[x + y*_width + z*wh + c*whd];
- }
- #endif
- //! Implicitly cast an image into a \c T*.
- /**
- Implicitly cast a \c CImg<T> instance into a \c T* or \c const \c T* pointer, whether the image instance
- is \e const or not. The returned pointer points on the first value of the image pixel buffer.
- \note
- - It simply returns the pointer data() to the pixel buffer.
- - This implicit conversion is convenient to test the empty state of images (data() being \c 0 in this case), e.g.
- \code
- CImg<float> img1(100,100), img2; // 'img1' is a 100x100 image, 'img2' is an empty image
- if (img1) { // Test succeeds, 'img1' is not an empty image
- if (!img2) { // Test succeeds, 'img2' is an empty image
- std::printf("'img1' is not empty, 'img2' is empty.");
- }
- }
- \endcode
- - It also allows to use brackets to access pixel values, without need for a \c CImg<T>::operator[](), e.g.
- \code
- CImg<float> img(100,100);
- const float value = img[99]; // Access to value of the last pixel on the first row
- img[510] = 255; // Set pixel value at (10,5)
- \endcode
- **/
- operator T*() {
- return _data;
- }
- //! Implicitly cast an image into a \c T* \const.
- operator const T*() const {
- return _data;
- }
- //! Assign a value to all image pixels.
- /**
- Assign specified \c value to each pixel value of the image instance.
- \param value Value that will be assigned to image pixels.
- \note
- - The image size is never modified.
- - The \c value may be casted to pixel type \c T if necessary.
- \par Example
- \code
- CImg<char> img(100,100); // Declare image (with garbage values)
- img = 0; // Set all pixel values to '0'
- img = 1.2; // Set all pixel values to '1' (cast of '1.2' as a 'char')
- \endcode
- **/
- CImg<T>& operator=(const T& value) {
- return fill(value);
- }
- //! Assign pixels values from a specified expression.
- /**
- Initialize all pixel values from the specified string \c expression.
- \param expression Value string describing the way pixel values are set.
- \note
- - String parameter \c expression may describe different things:
- - If \c expression is a list of values (as in \c "1,2,3,8,3,2"), or a formula (as in \c "(x*y)%255"),
- the pixel values are set from specified \c expression and the image size is not modified.
- - If \c expression is a filename (as in \c "reference.jpg"), the corresponding image file is loaded and
- replace the image instance. The image size is modified if necessary.
- \par Example
- \code
- CImg<float> img1(100,100), img2(img1), img3(img1); // Declare 3 scalar images 100x100 with uninitialized values
- img1 = "0,50,100,150,200,250,200,150,100,50"; // Set pixel values of 'img1' from a value sequence
- img2 = "10*((x*y)%25)"; // Set pixel values of 'img2' from a formula
- img3 = "reference.jpg"; // Set pixel values of 'img3' from a file (image size is modified)
- (img1,img2,img3).display();
- \endcode
- \image html ref_operator_eq.jpg
- **/
- CImg<T>& operator=(const char *const expression) {
- const unsigned int omode = cimg::exception_mode();
- cimg::exception_mode(0);
- try {
- _fill(expression,true,3,0,"operator=",0,0);
- } catch (CImgException&) {
- cimg::exception_mode(omode);
- load(expression);
- }
- cimg::exception_mode(omode);
- return *this;
- }
- //! Copy an image into the current image instance.
- /**
- Similar to the in-place copy constructor assign(const CImg<t>&).
- **/
- template<typename t>
- CImg<T>& operator=(const CImg<t>& img) {
- return assign(img);
- }
- //! Copy an image into the current image instance \specialization.
- CImg<T>& operator=(const CImg<T>& img) {
- return assign(img);
- }
- //! Copy the content of a display window to the current image instance.
- /**
- Similar to assign(const CImgDisplay&).
- **/
- CImg<T>& operator=(const CImgDisplay& disp) {
- disp.snapshot(*this);
- return *this;
- }
- //! In-place addition operator.
- /**
- Add specified \c value to all pixels of an image instance.
- \param value Value to add.
- \note
- - Resulting pixel values are casted to fit the pixel type \c T.
- For instance, adding \c 0.2 to a \c CImg<char> is possible but does nothing indeed.
- - Overflow values are treated as with standard C++ numeric types. For instance,
- \code
- CImg<unsigned char> img(100,100,1,1,255); // Construct a 100x100 image with pixel values '255'
- img+=1; // Add '1' to each pixels -> Overflow
- // here all pixels of image 'img' are equal to '0'.
- \endcode
- - To prevent value overflow, you may want to consider pixel type \c T as \c float or \c double,
- and use cut() after addition.
- \par Example
- \code
- CImg<unsigned char> img1("reference.jpg"); // Load a 8-bits RGB image (values in [0,255])
- CImg<float> img2(img1); // Construct a float-valued copy of 'img1'
- img2+=100; // Add '100' to pixel values -> goes out of [0,255] but no problems with floats
- img2.cut(0,255); // Cut values in [0,255] to fit the 'unsigned char' constraint
- img1 = img2; // Rewrite safe result in 'unsigned char' version 'img1'
- const CImg<unsigned char> img3 = (img1 + 100).cut(0,255); // Do the same in a more simple and elegant way
- (img1,img2,img3).display();
- \endcode
- \image html ref_operator_plus.jpg
- **/
- template<typename t>
- CImg<T>& operator+=(const t value) {
- if (is_empty()) return *this;
- cimg_openmp_for(*this,*ptr + value,524288);
- return *this;
- }
- //! In-place addition operator.
- /**
- Add values to image pixels, according to the specified string \c expression.
- \param expression Value string describing the way pixel values are added.
- \note
- - Similar to operator=(const char*), except that it adds values to the pixels of the current image instance,
- instead of assigning them.
- **/
- CImg<T>& operator+=(const char *const expression) {
- return *this+=(+*this)._fill(expression,true,3,0,"operator+=",this,0);
- }
- //! In-place addition operator.
- /**
- Add values to image pixels, according to the values of the input image \c img.
- \param img Input image to add.
- \note
- - The size of the image instance is never modified.
- - It is not mandatory that input image \c img has the same size as the image instance.
- If less values are available in \c img, then the values are added periodically. For instance, adding one
- WxH scalar image (spectrum() equal to \c 1) to one WxH color image (spectrum() equal to \c 3)
- means each color channel will be incremented with the same values at the same locations.
- \par Example
- \code
- CImg<float> img1("reference.jpg"); // Load a RGB color image (img1.spectrum()==3)
- // Construct a scalar shading (img2.spectrum()==1).
- const CImg<float> img2(img1.width(),img.height(),1,1,"255*(x/w)^2");
- img1+=img2; // Add shading to each channel of 'img1'
- img1.cut(0,255); // Prevent [0,255] overflow
- (img2,img1).display();
- \endcode
- \image html ref_operator_plus1.jpg
- **/
- template<typename t>
- CImg<T>& operator+=(const CImg<t>& img) {
- const ulongT siz = size(), isiz = img.size();
- if (siz && isiz) {
- if (is_overlapped(img)) return *this+=+img;
- T *ptrd = _data, *const ptre = _data + siz;
- if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
- for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
- *ptrd = (T)(*ptrd + *(ptrs++));
- for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd + *(ptrs++));
- }
- return *this;
- }
- //! In-place increment operator (prefix).
- /**
- Add \c 1 to all image pixels, and return a reference to the current incremented image instance.
- \note
- - Writing \c ++img is equivalent to \c img+=1.
- **/
- CImg<T>& operator++() {
- if (is_empty()) return *this;
- cimg_openmp_for(*this,*ptr + 1,524288);
- return *this;
- }
- //! In-place increment operator (postfix).
- /**
- Add \c 1 to all image pixels, and return a new copy of the initial (pre-incremented) image instance.
- \note
- - Use the prefixed version operator++() if you don't need a copy of the initial
- (pre-incremented) image instance, since a useless image copy may be expensive in terms of memory usage.
- **/
- CImg<T> operator++(int) {
- const CImg<T> copy(*this,false);
- ++*this;
- return copy;
- }
- //! Return a non-shared copy of the image instance.
- /**
- \note
- - Use this operator to ensure you get a non-shared copy of an image instance with same pixel type \c T.
- Indeed, the usual copy constructor CImg<T>(const CImg<T>&) returns a shared copy of a shared input image,
- and it may be not desirable to work on a regular copy (e.g. for a resize operation) if you have no
- information about the shared state of the input image.
- - Writing \c (+img) is equivalent to \c CImg<T>(img,false).
- **/
- CImg<T> operator+() const {
- return CImg<T>(*this,false);
- }
- //! Addition operator.
- /**
- Similar to operator+=(const t), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
- **/
- template<typename t>
- CImg<_cimg_Tt> operator+(const t value) const {
- return CImg<_cimg_Tt>(*this,false)+=value;
- }
- //! Addition operator.
- /**
- Similar to operator+=(const char*), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
- **/
- CImg<Tfloat> operator+(const char *const expression) const {
- return CImg<Tfloat>(*this,false)+=expression;
- }
- //! Addition operator.
- /**
- Similar to operator+=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
- **/
- template<typename t>
- CImg<_cimg_Tt> operator+(const CImg<t>& img) const {
- return CImg<_cimg_Tt>(*this,false)+=img;
- }
- //! In-place subtraction operator.
- /**
- Similar to operator+=(const t), except that it performs a subtraction instead of an addition.
- **/
- template<typename t>
- CImg<T>& operator-=(const t value) {
- if (is_empty()) return *this;
- cimg_openmp_for(*this,*ptr - value,524288);
- return *this;
- }
- //! In-place subtraction operator.
- /**
- Similar to operator+=(const char*), except that it performs a subtraction instead of an addition.
- **/
- CImg<T>& operator-=(const char *const expression) {
- return *this-=(+*this)._fill(expression,true,3,0,"operator-=",this,0);
- }
- //! In-place subtraction operator.
- /**
- Similar to operator+=(const CImg<t>&), except that it performs a subtraction instead of an addition.
- **/
- template<typename t>
- CImg<T>& operator-=(const CImg<t>& img) {
- const ulongT siz = size(), isiz = img.size();
- if (siz && isiz) {
- if (is_overlapped(img)) return *this-=+img;
- T *ptrd = _data, *const ptre = _data + siz;
- if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
- for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
- *ptrd = (T)(*ptrd - *(ptrs++));
- for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd - *(ptrs++));
- }
- return *this;
- }
- //! In-place decrement operator (prefix).
- /**
- Similar to operator++(), except that it performs a decrement instead of an increment.
- **/
- CImg<T>& operator--() {
- if (is_empty()) return *this;
- cimg_openmp_for(*this,*ptr - 1,524288);
- return *this;
- }
- //! In-place decrement operator (postfix).
- /**
- Similar to operator++(int), except that it performs a decrement instead of an increment.
- **/
- CImg<T> operator--(int) {
- const CImg<T> copy(*this,false);
- --*this;
- return copy;
- }
- //! Replace each pixel by its opposite value.
- /**
- \note
- - If the computed opposite values are out-of-range, they are treated as with standard C++ numeric types.
- For instance, the \c unsigned \c char opposite of \c 1 is \c 255.
- \par Example
- \code
- const CImg<unsigned char>
- img1("reference.jpg"), // Load a RGB color image
- img2 = -img1; // Compute its opposite (in 'unsigned char')
- (img1,img2).display();
- \endcode
- \image html ref_operator_minus.jpg
- **/
- CImg<T> operator-() const {
- return CImg<T>(_width,_height,_depth,_spectrum,(T)0)-=*this;
- }
- //! Subtraction operator.
- /**
- Similar to operator-=(const t), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
- **/
- template<typename t>
- CImg<_cimg_Tt> operator-(const t value) const {
- return CImg<_cimg_Tt>(*this,false)-=value;
- }
- //! Subtraction operator.
- /**
- Similar to operator-=(const char*), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
- **/
- CImg<Tfloat> operator-(const char *const expression) const {
- return CImg<Tfloat>(*this,false)-=expression;
- }
- //! Subtraction operator.
- /**
- Similar to operator-=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
- **/
- template<typename t>
- CImg<_cimg_Tt> operator-(const CImg<t>& img) const {
- return CImg<_cimg_Tt>(*this,false)-=img;
- }
- //! In-place multiplication operator.
- /**
- Similar to operator+=(const t), except that it performs a multiplication instead of an addition.
- **/
- template<typename t>
- CImg<T>& operator*=(const t value) {
- if (is_empty()) return *this;
- cimg_openmp_for(*this,*ptr * value,262144);
- return *this;
- }
- //! In-place multiplication operator.
- /**
- Similar to operator+=(const char*), except that it performs a multiplication instead of an addition.
- **/
- CImg<T>& operator*=(const char *const expression) {
- return mul((+*this)._fill(expression,true,3,0,"operator*=",this,0));
- }
- //! In-place multiplication operator.
- /**
- Replace the image instance by the matrix multiplication between the image instance and the specified matrix
- \c img.
- \param img Second operand of the matrix multiplication.
- \note
- - It does \e not compute a pointwise multiplication between two images. For this purpose, use
- mul(const CImg<t>&) instead.
- - The size of the image instance can be modified by this operator.
- \par Example
- \code
- CImg<float> A(2,2,1,1, 1,2,3,4); // Construct 2x2 matrix A = [1,2;3,4]
- const CImg<float> X(1,2,1,1, 1,2); // Construct 1x2 vector X = [1;2]
- A*=X; // Assign matrix multiplication A*X to 'A'
- // 'A' is now a 1x2 vector whose values are [5;11].
- \endcode
- **/
- template<typename t>
- CImg<T>& operator*=(const CImg<t>& img) {
- return ((*this)*img).move_to(*this);
- }
- //! Multiplication operator.
- /**
- Similar to operator*=(const t), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
- **/
- template<typename t>
- CImg<_cimg_Tt> operator*(const t value) const {
- return CImg<_cimg_Tt>(*this,false)*=value;
- }
- //! Multiplication operator.
- /**
- Similar to operator*=(const char*), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
- **/
- CImg<Tfloat> operator*(const char *const expression) const {
- return CImg<Tfloat>(*this,false)*=expression;
- }
- //! Multiplication operator.
- /**
- Similar to operator*=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
- **/
- template<typename t>
- CImg<_cimg_Tt> operator*(const CImg<t>& img) const {
- typedef _cimg_Ttdouble Ttdouble;
- typedef _cimg_Tt Tt;
- if (_width!=img._height || _depth!=1 || _spectrum!=1)
- throw CImgArgumentException(_cimg_instance
- "operator*(): Invalid multiplication of instance by specified "
- "matrix (%u,%u,%u,%u,%p).",
- cimg_instance,
- img._width,img._height,img._depth,img._spectrum,img._data);
- CImg<Tt> res(img._width,_height);
- // Check for common cases to optimize.
- if (img._width==1) { // Matrix * Vector
- if (_height==1) switch (_width) { // Vector^T * Vector
- case 1 :
- res[0] = (Tt)((Ttdouble)_data[0]*img[0]);
- return res;
- case 2 :
- res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1]);
- return res;
- case 3 :
- res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] +
- (Ttdouble)_data[2]*img[2]);
- return res;
- case 4 :
- res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] +
- (Ttdouble)_data[2]*img[2] + (Ttdouble)_data[3]*img[3]);
- return res;
- default : {
- Ttdouble val = 0;
- cimg_pragma_openmp(parallel for reduction(+:val) cimg_openmp_if_size(size(),4096))
- cimg_forX(*this,i) val+=(Ttdouble)_data[i]*img[i];
- res[0] = val;
- return res;
- }
- } else if (_height==_width) switch (_width) { // Square_matrix * Vector
- case 2 : // 2x2_matrix * Vector
- res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1]);
- res[1] = (Tt)((Ttdouble)_data[2]*img[0] + (Ttdouble)_data[3]*img[1]);
- return res;
- case 3 : // 3x3_matrix * Vector
- res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] +
- (Ttdouble)_data[2]*img[2]);
- res[1] = (Tt)((Ttdouble)_data[3]*img[0] + (Ttdouble)_data[4]*img[1] +
- (Ttdouble)_data[5]*img[2]);
- res[2] = (Tt)((Ttdouble)_data[6]*img[0] + (Ttdouble)_data[7]*img[1] +
- (Ttdouble)_data[8]*img[2]);
- return res;
- case 4 : // 4x4_matrix * Vector
- res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] +
- (Ttdouble)_data[2]*img[2] + (Ttdouble)_data[3]*img[3]);
- res[1] = (Tt)((Ttdouble)_data[4]*img[0] + (Ttdouble)_data[5]*img[1] +
- (Ttdouble)_data[6]*img[2] + (Ttdouble)_data[7]*img[3]);
- res[2] = (Tt)((Ttdouble)_data[8]*img[0] + (Ttdouble)_data[9]*img[1] +
- (Ttdouble)_data[10]*img[2] + (Ttdouble)_data[11]*img[3]);
- res[3] = (Tt)((Ttdouble)_data[12]*img[0] + (Ttdouble)_data[13]*img[1] +
- (Ttdouble)_data[14]*img[2] + (Ttdouble)_data[15]*img[3]);
- return res;
- }
- } else if (_height==_width) {
- if (img._height==img._width) switch (_width) { // Square_matrix * Square_matrix
- case 2 : // 2x2_matrix * 2x2_matrix
- res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[2]);
- res[1] = (Tt)((Ttdouble)_data[0]*img[1] + (Ttdouble)_data[1]*img[3]);
- res[2] = (Tt)((Ttdouble)_data[2]*img[0] + (Ttdouble)_data[3]*img[2]);
- res[3] = (Tt)((Ttdouble)_data[2]*img[1] + (Ttdouble)_data[3]*img[3]);
- return res;
- case 3 : // 3x3_matrix * 3x3_matrix
- res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[3] +
- (Ttdouble)_data[2]*img[6]);
- res[1] = (Tt)((Ttdouble)_data[0]*img[1] + (Ttdouble)_data[1]*img[4] +
- (Ttdouble)_data[2]*img[7]);
- res[2] = (Tt)((Ttdouble)_data[0]*img[2] + (Ttdouble)_data[1]*img[5] +
- (Ttdouble)_data[2]*img[8]);
- res[3] = (Tt)((Ttdouble)_data[3]*img[0] + (Ttdouble)_data[4]*img[3] +
- (Ttdouble)_data[5]*img[6]);
- res[4] = (Tt)((Ttdouble)_data[3]*img[1] + (Ttdouble)_data[4]*img[4] +
- (Ttdouble)_data[5]*img[7]);
- res[5] = (Tt)((Ttdouble)_data[3]*img[2] + (Ttdouble)_data[4]*img[5] +
- (Ttdouble)_data[5]*img[8]);
- res[6] = (Tt)((Ttdouble)_data[6]*img[0] + (Ttdouble)_data[7]*img[3] +
- (Ttdouble)_data[8]*img[6]);
- res[7] = (Tt)((Ttdouble)_data[6]*img[1] + (Ttdouble)_data[7]*img[4] +
- (Ttdouble)_data[8]*img[7]);
- res[8] = (Tt)((Ttdouble)_data[6]*img[2] + (Ttdouble)_data[7]*img[5] +
- (Ttdouble)_data[8]*img[8]);
- return res;
- case 4 : // 4x4_matrix * 4x4_matrix
- res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[4] +
- (Ttdouble)_data[2]*img[8] + (Ttdouble)_data[3]*img[12]);
- res[1] = (Tt)((Ttdouble)_data[0]*img[1] + (Ttdouble)_data[1]*img[5] +
- (Ttdouble)_data[2]*img[9] + (Ttdouble)_data[3]*img[13]);
- res[2] = (Tt)((Ttdouble)_data[0]*img[2] + (Ttdouble)_data[1]*img[6] +
- (Ttdouble)_data[2]*img[10] + (Ttdouble)_data[3]*img[14]);
- res[3] = (Tt)((Ttdouble)_data[0]*img[3] + (Ttdouble)_data[1]*img[7] +
- (Ttdouble)_data[2]*img[11] + (Ttdouble)_data[3]*img[15]);
- res[4] = (Tt)((Ttdouble)_data[4]*img[0] + (Ttdouble)_data[5]*img[4] +
- (Ttdouble)_data[6]*img[8] + (Ttdouble)_data[7]*img[12]);
- res[5] = (Tt)((Ttdouble)_data[4]*img[1] + (Ttdouble)_data[5]*img[5] +
- (Ttdouble)_data[6]*img[9] + (Ttdouble)_data[7]*img[13]);
- res[6] = (Tt)((Ttdouble)_data[4]*img[2] + (Ttdouble)_data[5]*img[6] +
- (Ttdouble)_data[6]*img[10] + (Ttdouble)_data[7]*img[14]);
- res[7] = (Tt)((Ttdouble)_data[4]*img[3] + (Ttdouble)_data[5]*img[7] +
- (Ttdouble)_data[6]*img[11] + (Ttdouble)_data[7]*img[15]);
- res[8] = (Tt)((Ttdouble)_data[8]*img[0] + (Ttdouble)_data[9]*img[4] +
- (Ttdouble)_data[10]*img[8] + (Ttdouble)_data[11]*img[12]);
- res[9] = (Tt)((Ttdouble)_data[8]*img[1] + (Ttdouble)_data[9]*img[5] +
- (Ttdouble)_data[10]*img[9] + (Ttdouble)_data[11]*img[13]);
- res[10] = (Tt)((Ttdouble)_data[8]*img[2] + (Ttdouble)_data[9]*img[6] +
- (Ttdouble)_data[10]*img[10] + (Ttdouble)_data[11]*img[14]);
- res[11] = (Tt)((Ttdouble)_data[8]*img[3] + (Ttdouble)_data[9]*img[7] +
- (Ttdouble)_data[10]*img[11] + (Ttdouble)_data[11]*img[15]);
- res[12] = (Tt)((Ttdouble)_data[12]*img[0] + (Ttdouble)_data[13]*img[4] +
- (Ttdouble)_data[14]*img[8] + (Ttdouble)_data[15]*img[12]);
- res[13] = (Tt)((Ttdouble)_data[12]*img[1] + (Ttdouble)_data[13]*img[5] +
- (Ttdouble)_data[14]*img[9] + (Ttdouble)_data[15]*img[13]);
- res[14] = (Tt)((Ttdouble)_data[12]*img[2] + (Ttdouble)_data[13]*img[6] +
- (Ttdouble)_data[14]*img[10] + (Ttdouble)_data[15]*img[14]);
- res[15] = (Tt)((Ttdouble)_data[12]*img[3] + (Ttdouble)_data[13]*img[7] +
- (Ttdouble)_data[14]*img[11] + (Ttdouble)_data[15]*img[15]);
- return res;
- } else switch (_width) { // Square_matrix * Matrix
- case 2 : { // 2x2_matrix * Matrix
- const t *const ps0 = img.data(), *const ps1 = img.data(0,1);
- Tt *const pd0 = res.data(), *const pd1 = res.data(0,1);
- const Ttdouble
- a0 = (Ttdouble)_data[0], a1 = (Ttdouble)_data[1],
- a2 = (Ttdouble)_data[2], a3 = (Ttdouble)_data[3];
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(img.width(),4096))
- cimg_forX(img,i) {
- const Ttdouble x = (Ttdouble)ps0[i], y = (Ttdouble)ps1[i];
- pd0[i] = (Tt)(a0*x + a1*y);
- pd1[i] = (Tt)(a2*x + a3*y);
- }
- return res;
- }
- case 3 : { // 3x3_matrix * Matrix
- const t *const ps0 = img.data(), *const ps1 = img.data(0,1), *const ps2 = img.data(0,2);
- Tt *const pd0 = res.data(), *const pd1 = res.data(0,1), *const pd2 = res.data(0,2);
- const Ttdouble
- a0 = (Ttdouble)_data[0], a1 = (Ttdouble)_data[1], a2 = (Ttdouble)_data[2],
- a3 = (Ttdouble)_data[3], a4 = (Ttdouble)_data[4], a5 = (Ttdouble)_data[5],
- a6 = (Ttdouble)_data[6], a7 = (Ttdouble)_data[7], a8 = (Ttdouble)_data[8];
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(img.width(),1024))
- cimg_forX(img,i) {
- const Ttdouble x = (Ttdouble)ps0[i], y = (Ttdouble)ps1[i], z = (Ttdouble)ps2[i];
- pd0[i] = (Tt)(a0*x + a1*y + a2*z);
- pd1[i] = (Tt)(a3*x + a4*y + a5*z);
- pd2[i] = (Tt)(a6*x + a7*y + a8*z);
- }
- return res;
- }
- case 4 : { // 4x4_matrix * Matrix
- const t
- *const ps0 = img.data(), *const ps1 = img.data(0,1),
- *const ps2 = img.data(0,2), *const ps3 = img.data(0,3);
- Tt
- *const pd0 = res.data(), *const pd1 = res.data(0,1),
- *const pd2 = res.data(0,2), *const pd3 = res.data(0,3);
- const Ttdouble
- a0 = (Ttdouble)_data[0], a1 = (Ttdouble)_data[1], a2 = (Ttdouble)_data[2], a3 = (Ttdouble)_data[3],
- a4 = (Ttdouble)_data[4], a5 = (Ttdouble)_data[5], a6 = (Ttdouble)_data[6], a7 = (Ttdouble)_data[7],
- a8 = (Ttdouble)_data[8], a9 = (Ttdouble)_data[9], a10 = (Ttdouble)_data[10], a11 = (Ttdouble)_data[11],
- a12 = (Ttdouble)_data[12], a13 = (Ttdouble)_data[13], a14 = (Ttdouble)_data[14],
- a15 = (Ttdouble)_data[15];
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(img.width(),512))
- cimg_forX(img,i) {
- const Ttdouble x = (Ttdouble)ps0[i], y = (Ttdouble)ps1[i], z = (Ttdouble)ps2[i], c = (Ttdouble)ps3[i];
- pd0[i] = (Tt)(a0*x + a1*y + a2*z + a3*c);
- pd1[i] = (Tt)(a4*x + a5*y + a6*z + a7*c);
- pd2[i] = (Tt)(a8*x + a9*y + a10*z + a11*c);
- pd3[i] = (Tt)(a12*x + a13*y + a14*z + a15*c);
- }
- return res;
- }
- }
- }
- // Fallback to generic version.
- #if cimg_use_openmp!=0
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
- cimg_openmp_if(size()>(cimg_openmp_sizefactor)*1024 &&
- img.size()>(cimg_openmp_sizefactor)*1024))
- cimg_forXY(res,i,j) {
- Ttdouble value = 0;
- cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k);
- res(i,j) = (Tt)value;
- }
- #else
- Tt *ptrd = res._data;
- cimg_forXY(res,i,j) {
- Ttdouble value = 0;
- cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k);
- *(ptrd++) = (Tt)value;
- }
- #endif
- return res;
- }
- //! In-place division operator.
- /**
- Similar to operator+=(const t), except that it performs a division instead of an addition.
- **/
- template<typename t>
- CImg<T>& operator/=(const t value) {
- if (is_empty()) return *this;
- cimg_openmp_for(*this,*ptr / value,32768);
- return *this;
- }
- //! In-place division operator.
- /**
- Similar to operator+=(const char*), except that it performs a division instead of an addition.
- **/
- CImg<T>& operator/=(const char *const expression) {
- return div((+*this)._fill(expression,true,3,0,"operator/=",this,0));
- }
- //! In-place division operator.
- /**
- Replace the image instance by the (right) matrix division between the image instance and the specified
- matrix \c img.
- \param img Second operand of the matrix division.
- \note
- - It does \e not compute a pointwise division between two images. For this purpose, use
- div(const CImg<t>&) instead.
- - It returns the matrix operation \c A*inverse(img).
- - The size of the image instance can be modified by this operator.
- **/
- template<typename t>
- CImg<T>& operator/=(const CImg<t>& img) {
- return (*this*img.get_invert()).move_to(*this);
- }
- //! Division operator.
- /**
- Similar to operator/=(const t), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
- **/
- template<typename t>
- CImg<_cimg_Tt> operator/(const t value) const {
- return CImg<_cimg_Tt>(*this,false)/=value;
- }
- //! Division operator.
- /**
- Similar to operator/=(const char*), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
- **/
- CImg<Tfloat> operator/(const char *const expression) const {
- return CImg<Tfloat>(*this,false)/=expression;
- }
- //! Division operator.
- /**
- Similar to operator/=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
- **/
- template<typename t>
- CImg<_cimg_Tt> operator/(const CImg<t>& img) const {
- return (*this)*img.get_invert();
- }
- //! In-place modulo operator.
- /**
- Similar to operator+=(const t), except that it performs a modulo operation instead of an addition.
- **/
- template<typename t>
- CImg<T>& operator%=(const t value) {
- if (is_empty()) return *this;
- cimg_openmp_for(*this,cimg::mod(*ptr,(T)value),16384);
- return *this;
- }
- //! In-place modulo operator.
- /**
- Similar to operator+=(const char*), except that it performs a modulo operation instead of an addition.
- **/
- CImg<T>& operator%=(const char *const expression) {
- return *this%=(+*this)._fill(expression,true,3,0,"operator%=",this,0);
- }
- //! In-place modulo operator.
- /**
- Similar to operator+=(const CImg<t>&), except that it performs a modulo operation instead of an addition.
- **/
- template<typename t>
- CImg<T>& operator%=(const CImg<t>& img) {
- const ulongT siz = size(), isiz = img.size();
- if (siz && isiz) {
- if (is_overlapped(img)) return *this%=+img;
- T *ptrd = _data, *const ptre = _data + siz;
- if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
- for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
- *ptrd = cimg::mod(*ptrd,(T)*(ptrs++));
- for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::mod(*ptrd,(T)*(ptrs++));
- }
- return *this;
- }
- //! Modulo operator.
- /**
- Similar to operator%=(const t), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
- **/
- template<typename t>
- CImg<_cimg_Tt> operator%(const t value) const {
- return CImg<_cimg_Tt>(*this,false)%=value;
- }
- //! Modulo operator.
- /**
- Similar to operator%=(const char*), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
- **/
- CImg<Tfloat> operator%(const char *const expression) const {
- return CImg<Tfloat>(*this,false)%=expression;
- }
- //! Modulo operator.
- /**
- Similar to operator%=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
- **/
- template<typename t>
- CImg<_cimg_Tt> operator%(const CImg<t>& img) const {
- return CImg<_cimg_Tt>(*this,false)%=img;
- }
- //! In-place bitwise AND operator.
- /**
- Similar to operator+=(const t), except that it performs a bitwise AND operation instead of an addition.
- **/
- template<typename t>
- CImg<T>& operator&=(const t value) {
- if (is_empty()) return *this;
- cimg_openmp_for(*this,(longT)*ptr & (longT)value,32768);
- return *this;
- }
- //! In-place bitwise AND operator.
- /**
- Similar to operator+=(const char*), except that it performs a bitwise AND operation instead of an addition.
- **/
- CImg<T>& operator&=(const char *const expression) {
- return *this&=(+*this)._fill(expression,true,3,0,"operator&=",this,0);
- }
- //! In-place bitwise AND operator.
- /**
- Similar to operator+=(const CImg<t>&), except that it performs a bitwise AND operation instead of an addition.
- **/
- template<typename t>
- CImg<T>& operator&=(const CImg<t>& img) {
- const ulongT siz = size(), isiz = img.size();
- if (siz && isiz) {
- if (is_overlapped(img)) return *this&=+img;
- T *ptrd = _data, *const ptre = _data + siz;
- if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
- for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
- *ptrd = (T)((longT)*ptrd & (longT)*(ptrs++));
- for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd & (longT)*(ptrs++));
- }
- return *this;
- }
- //! Bitwise AND operator.
- /**
- Similar to operator&=(const t), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image is \c T.
- **/
- template<typename t>
- CImg<T> operator&(const t value) const {
- return (+*this)&=value;
- }
- //! Bitwise AND operator.
- /**
- Similar to operator&=(const char*), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image is \c T.
- **/
- CImg<T> operator&(const char *const expression) const {
- return (+*this)&=expression;
- }
- //! Bitwise AND operator.
- /**
- Similar to operator&=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image is \c T.
- **/
- template<typename t>
- CImg<T> operator&(const CImg<t>& img) const {
- return (+*this)&=img;
- }
- //! In-place bitwise OR operator.
- /**
- Similar to operator+=(const t), except that it performs a bitwise OR operation instead of an addition.
- **/
- template<typename t>
- CImg<T>& operator|=(const t value) {
- if (is_empty()) return *this;
- cimg_openmp_for(*this,(longT)*ptr | (longT)value,32768);
- return *this;
- }
- //! In-place bitwise OR operator.
- /**
- Similar to operator+=(const char*), except that it performs a bitwise OR operation instead of an addition.
- **/
- CImg<T>& operator|=(const char *const expression) {
- return *this|=(+*this)._fill(expression,true,3,0,"operator|=",this,0);
- }
- //! In-place bitwise OR operator.
- /**
- Similar to operator+=(const CImg<t>&), except that it performs a bitwise OR operation instead of an addition.
- **/
- template<typename t>
- CImg<T>& operator|=(const CImg<t>& img) {
- const ulongT siz = size(), isiz = img.size();
- if (siz && isiz) {
- if (is_overlapped(img)) return *this|=+img;
- T *ptrd = _data, *const ptre = _data + siz;
- if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
- for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
- *ptrd = (T)((longT)*ptrd | (longT)*(ptrs++));
- for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd | (longT)*(ptrs++));
- }
- return *this;
- }
- //! Bitwise OR operator.
- /**
- Similar to operator|=(const t), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image is \c T.
- **/
- template<typename t>
- CImg<T> operator|(const t value) const {
- return (+*this)|=value;
- }
- //! Bitwise OR operator.
- /**
- Similar to operator|=(const char*), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image is \c T.
- **/
- CImg<T> operator|(const char *const expression) const {
- return (+*this)|=expression;
- }
- //! Bitwise OR operator.
- /**
- Similar to operator|=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image is \c T.
- **/
- template<typename t>
- CImg<T> operator|(const CImg<t>& img) const {
- return (+*this)|=img;
- }
- //! In-place bitwise XOR operator.
- /**
- Similar to operator+=(const t), except that it performs a bitwise XOR operation instead of an addition.
- \warning
- - It does \e not compute the \e power of pixel values. For this purpose, use pow(const t) instead.
- **/
- template<typename t>
- CImg<T>& operator^=(const t value) {
- if (is_empty()) return *this;
- cimg_openmp_for(*this,(longT)*ptr ^ (longT)value,32768);
- return *this;
- }
- //! In-place bitwise XOR operator.
- /**
- Similar to operator+=(const char*), except that it performs a bitwise XOR operation instead of an addition.
- \warning
- - It does \e not compute the \e power of pixel values. For this purpose, use pow(const char*) instead.
- **/
- CImg<T>& operator^=(const char *const expression) {
- return *this^=(+*this)._fill(expression,true,3,0,"operator^=",this,0);
- }
- //! In-place bitwise XOR operator.
- /**
- Similar to operator+=(const CImg<t>&), except that it performs a bitwise XOR operation instead of an addition.
- \warning
- - It does \e not compute the \e power of pixel values. For this purpose, use pow(const CImg<t>&) instead.
- **/
- template<typename t>
- CImg<T>& operator^=(const CImg<t>& img) {
- const ulongT siz = size(), isiz = img.size();
- if (siz && isiz) {
- if (is_overlapped(img)) return *this^=+img;
- T *ptrd = _data, *const ptre = _data + siz;
- if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
- for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
- *ptrd = (T)((longT)*ptrd ^ (longT)*(ptrs++));
- for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd ^ (longT)*(ptrs++));
- }
- return *this;
- }
- //! Bitwise XOR operator.
- /**
- Similar to operator^=(const t), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image is \c T.
- **/
- template<typename t>
- CImg<T> operator^(const t value) const {
- return (+*this)^=value;
- }
- //! Bitwise XOR operator.
- /**
- Similar to operator^=(const char*), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image is \c T.
- **/
- CImg<T> operator^(const char *const expression) const {
- return (+*this)^=expression;
- }
- //! Bitwise XOR operator.
- /**
- Similar to operator^=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image is \c T.
- **/
- template<typename t>
- CImg<T> operator^(const CImg<t>& img) const {
- return (+*this)^=img;
- }
- //! In-place bitwise left shift operator.
- /**
- Similar to operator+=(const t), except that it performs a bitwise left shift instead of an addition.
- **/
- template<typename t>
- CImg<T>& operator<<=(const t value) {
- if (is_empty()) return *this;
- cimg_openmp_for(*this,((longT)*ptr) << (int)value,65536);
- return *this;
- }
- //! In-place bitwise left shift operator.
- /**
- Similar to operator+=(const char*), except that it performs a bitwise left shift instead of an addition.
- **/
- CImg<T>& operator<<=(const char *const expression) {
- return *this<<=(+*this)._fill(expression,true,3,0,"operator<<=",this,0);
- }
- //! In-place bitwise left shift operator.
- /**
- Similar to operator+=(const CImg<t>&), except that it performs a bitwise left shift instead of an addition.
- **/
- template<typename t>
- CImg<T>& operator<<=(const CImg<t>& img) {
- const ulongT siz = size(), isiz = img.size();
- if (siz && isiz) {
- if (is_overlapped(img)) return *this^=+img;
- T *ptrd = _data, *const ptre = _data + siz;
- if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
- for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
- *ptrd = (T)((longT)*ptrd << (int)*(ptrs++));
- for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd << (int)*(ptrs++));
- }
- return *this;
- }
- //! Bitwise left shift operator.
- /**
- Similar to operator<<=(const t), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image is \c T.
- **/
- template<typename t>
- CImg<T> operator<<(const t value) const {
- return (+*this)<<=value;
- }
- //! Bitwise left shift operator.
- /**
- Similar to operator<<=(const char*), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image is \c T.
- **/
- CImg<T> operator<<(const char *const expression) const {
- return (+*this)<<=expression;
- }
- //! Bitwise left shift operator.
- /**
- Similar to operator<<=(const CImg<t>&), except that it returns a new image instance instead of
- operating in-place.
- The pixel type of the returned image is \c T.
- **/
- template<typename t>
- CImg<T> operator<<(const CImg<t>& img) const {
- return (+*this)<<=img;
- }
- //! In-place bitwise right shift operator.
- /**
- Similar to operator+=(const t), except that it performs a bitwise right shift instead of an addition.
- **/
- template<typename t>
- CImg<T>& operator>>=(const t value) {
- if (is_empty()) return *this;
- cimg_openmp_for(*this,((longT)*ptr) >> (int)value,65536);
- return *this;
- }
- //! In-place bitwise right shift operator.
- /**
- Similar to operator+=(const char*), except that it performs a bitwise right shift instead of an addition.
- **/
- CImg<T>& operator>>=(const char *const expression) {
- return *this>>=(+*this)._fill(expression,true,3,0,"operator>>=",this,0);
- }
- //! In-place bitwise right shift operator.
- /**
- Similar to operator+=(const CImg<t>&), except that it performs a bitwise right shift instead of an addition.
- **/
- template<typename t>
- CImg<T>& operator>>=(const CImg<t>& img) {
- const ulongT siz = size(), isiz = img.size();
- if (siz && isiz) {
- if (is_overlapped(img)) return *this^=+img;
- T *ptrd = _data, *const ptre = _data + siz;
- if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
- for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
- *ptrd = (T)((longT)*ptrd >> (int)*(ptrs++));
- for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd >> (int)*(ptrs++));
- }
- return *this;
- }
- //! Bitwise right shift operator.
- /**
- Similar to operator>>=(const t), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image is \c T.
- **/
- template<typename t>
- CImg<T> operator>>(const t value) const {
- return (+*this)>>=value;
- }
- //! Bitwise right shift operator.
- /**
- Similar to operator>>=(const char*), except that it returns a new image instance instead of operating in-place.
- The pixel type of the returned image is \c T.
- **/
- CImg<T> operator>>(const char *const expression) const {
- return (+*this)>>=expression;
- }
- //! Bitwise right shift operator.
- /**
- Similar to operator>>=(const CImg<t>&), except that it returns a new image instance instead of
- operating in-place.
- The pixel type of the returned image is \c T.
- **/
- template<typename t>
- CImg<T> operator>>(const CImg<t>& img) const {
- return (+*this)>>=img;
- }
- //! Bitwise inversion operator.
- /**
- Similar to operator-(), except that it compute the bitwise inverse instead of the opposite value.
- **/
- CImg<T> operator~() const {
- CImg<T> res(_width,_height,_depth,_spectrum);
- const T *ptrs = _data;
- cimg_for(res,ptrd,T) { const ulongT value = (ulongT)*(ptrs++); *ptrd = (T)~value; }
- return res;
- }
- //! Test if all pixels of an image have the same value.
- /**
- Return \c true is all pixels of the image instance are equal to the specified \c value.
- \param value Reference value to compare with.
- **/
- template<typename t>
- bool operator==(const t value) const {
- if (is_empty()) return false;
- typedef _cimg_Tt Tt;
- bool is_equal = true;
- for (T *ptrd = _data + size(); is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)value)) {}
- return is_equal;
- }
- //! Test if all pixel values of an image follow a specified expression.
- /**
- Return \c true is all pixels of the image instance are equal to the specified \c expression.
- \param expression Value string describing the way pixel values are compared.
- **/
- bool operator==(const char *const expression) const {
- return *this==(+*this)._fill(expression,true,3,0,"operator==",this,0);
- }
- //! Test if two images have the same size and values.
- /**
- Return \c true if the image instance and the input image \c img have the same pixel values,
- even if the dimensions of the two images do not match. It returns \c false otherwise.
- \param img Input image to compare with.
- \note
- - The pixel buffer pointers data() of the two compared images do not have to be the same for operator==()
- to return \c true.
- Only the dimensions and the pixel values matter. Thus, the comparison can be \c true even for different
- pixel types \c T and \c t.
- \par Example
- \code
- const CImg<float> img1(1,3,1,1, 0,1,2); // Construct a 1x3 vector [0;1;2] (with 'float' pixel values)
- const CImg<char> img2(1,3,1,1, 0,1,2); // Construct a 1x3 vector [0;1;2] (with 'char' pixel values)
- if (img1==img2) { // Test succeeds, image dimensions and values are the same
- std::printf("'img1' and 'img2' have same dimensions and values.");
- }
- \endcode
- **/
- template<typename t>
- bool operator==(const CImg<t>& img) const {
- typedef _cimg_Tt Tt;
- const ulongT siz = size();
- bool is_equal = true;
- if (siz!=img.size()) return false;
- t *ptrs = img._data + siz;
- for (T *ptrd = _data + siz; is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)*(--ptrs))) {}
- return is_equal;
- }
- //! Test if pixels of an image are all different from a value.
- /**
- Return \c true is all pixels of the image instance are different than the specified \c value.
- \param value Reference value to compare with.
- **/
- template<typename t>
- bool operator!=(const t value) const {
- return !((*this)==value);
- }
- //! Test if all pixel values of an image are different from a specified expression.
- /**
- Return \c true is all pixels of the image instance are different to the specified \c expression.
- \param expression Value string describing the way pixel values are compared.
- **/
- bool operator!=(const char *const expression) const {
- return !((*this)==expression);
- }
- //! Test if two images have different sizes or values.
- /**
- Return \c true if the image instance and the input image \c img have different dimensions or pixel values,
- and \c false otherwise.
- \param img Input image to compare with.
- \note
- - Writing \c img1!=img2 is equivalent to \c !(img1==img2).
- **/
- template<typename t>
- bool operator!=(const CImg<t>& img) const {
- return !((*this)==img);
- }
- //! Construct an image list from two images.
- /**
- Return a new list of image (\c CImgList instance) containing exactly two elements:
- - A copy of the image instance, at position [\c 0].
- - A copy of the specified image \c img, at position [\c 1].
- \param img Input image that will be the second image of the resulting list.
- \note
- - The family of operator,() is convenient to easily create list of images, but it is also \e quite \e slow
- in practice (see warning below).
- - Constructed lists contain no shared images. If image instance or input image \c img are shared, they are
- inserted as new non-shared copies in the resulting list.
- - The pixel type of the returned list may be a superset of the initial pixel type \c T, if necessary.
- \warning
- - Pipelining operator,() \c N times will perform \c N copies of the entire content of a (growing) image list.
- This may become very expensive in terms of speed and used memory. You should avoid using this technique to
- build a new CImgList instance from several images, if you are seeking for performance.
- Fast insertions of images in an image list are possible with
- CImgList<T>::insert(const CImg<t>&,unsigned int,bool) or move_to(CImgList<t>&,unsigned int).
- \par Example
- \code
- const CImg<float>
- img1("reference.jpg"),
- img2 = img1.get_mirror('x'),
- img3 = img2.get_blur(5);
- const CImgList<float> list = (img1,img2); // Create list of two elements from 'img1' and 'img2'
- (list,img3).display(); // Display image list containing copies of 'img1','img2' and 'img3'
- \endcode
- \image html ref_operator_comma.jpg
- **/
- template<typename t>
- CImgList<_cimg_Tt> operator,(const CImg<t>& img) const {
- return CImgList<_cimg_Tt>(*this,img);
- }
- //! Construct an image list from image instance and an input image list.
- /**
- Return a new list of images (\c CImgList instance) containing exactly \c list.size() \c + \c 1 elements:
- - A copy of the image instance, at position [\c 0].
- - A copy of the specified image list \c list, from positions [\c 1] to [\c list.size()].
- \param list Input image list that will be appended to the image instance.
- \note
- - Similar to operator,(const CImg<t>&) const, except that it takes an image list as an argument.
- **/
- template<typename t>
- CImgList<_cimg_Tt> operator,(const CImgList<t>& list) const {
- return CImgList<_cimg_Tt>(list,false).insert(*this,0);
- }
- //! Split image along specified axis.
- /**
- Return a new list of images (\c CImgList instance) containing the split components
- of the instance image along the specified axis.
- \param axis Splitting axis (can be '\c x','\c y','\c z' or '\c c')
- \note
- - Similar to get_split(char,int) const, with default second argument.
- \par Example
- \code
- const CImg<unsigned char> img("reference.jpg"); // Load a RGB color image
- const CImgList<unsigned char> list = (img<'c'); // Get a list of its three R,G,B channels
- (img,list).display();
- \endcode
- \image html ref_operator_less.jpg
- **/
- CImgList<T> operator<(const char axis) const {
- return get_split(axis);
- }
- //@}
- //-------------------------------------
- //
- //! \name Instance Characteristics
- //@{
- //-------------------------------------
- //! Return the type of image pixel values as a C string.
- /**
- Return a \c char* string containing the usual type name of the image pixel values
- (i.e. a stringified version of the template parameter \c T).
- \note
- - The returned string does not contain any spaces.
- - If the pixel type \c T does not correspond to a registered type, the string <tt>"unknown"</tt> is returned.
- **/
- static const char* pixel_type() {
- return cimg::type<T>::string();
- }
- //! Return the number of image columns.
- /**
- Return the image width, i.e. the image dimension along the X-axis.
- \note
- - The width() of an empty image is equal to \c 0.
- - width() is typically equal to \c 1 when considering images as \e vectors for matrix calculations.
- - width() returns an \c int, although the image width is internally stored as an \c unsigned \c int.
- Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
- \c unsigned \c int variables.
- Access to the initial \c unsigned \c int variable is possible (though not recommended) by
- <tt>(*this)._width</tt>.
- **/
- int width() const {
- return (int)_width;
- }
- //! Return the number of image rows.
- /**
- Return the image height, i.e. the image dimension along the Y-axis.
- \note
- - The height() of an empty image is equal to \c 0.
- - height() returns an \c int, although the image height is internally stored as an \c unsigned \c int.
- Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
- \c unsigned \c int variables.
- Access to the initial \c unsigned \c int variable is possible (though not recommended) by
- <tt>(*this)._height</tt>.
- **/
- int height() const {
- return (int)_height;
- }
- //! Return the number of image slices.
- /**
- Return the image depth, i.e. the image dimension along the Z-axis.
- \note
- - The depth() of an empty image is equal to \c 0.
- - depth() is typically equal to \c 1 when considering usual 2D images. When depth()\c > \c 1, the image
- is said to be \e volumetric.
- - depth() returns an \c int, although the image depth is internally stored as an \c unsigned \c int.
- Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
- \c unsigned \c int variables.
- Access to the initial \c unsigned \c int variable is possible (though not recommended) by
- <tt>(*this)._depth</tt>.
- **/
- int depth() const {
- return (int)_depth;
- }
- //! Return the number of image channels.
- /**
- Return the number of image channels, i.e. the image dimension along the C-axis.
- \note
- - The spectrum() of an empty image is equal to \c 0.
- - spectrum() is typically equal to \c 1 when considering scalar-valued images, to \c 3
- for RGB-coded color images, and to \c 4 for RGBA-coded color images (with alpha-channel).
- The number of channels of an image instance is not limited. The meaning of the pixel values is not linked
- up to the number of channels (e.g. a 4-channel image may indifferently stands for a RGBA or CMYK color image).
- - spectrum() returns an \c int, although the image spectrum is internally stored as an \c unsigned \c int.
- Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
- \c unsigned \c int variables.
- Access to the initial \c unsigned \c int variable is possible (though not recommended) by
- <tt>(*this)._spectrum</tt>.
- **/
- int spectrum() const {
- return (int)_spectrum;
- }
- //! Return the total number of pixel values.
- /**
- Return <tt>width()*\ref height()*\ref depth()*\ref spectrum()</tt>,
- i.e. the total number of values of type \c T in the pixel buffer of the image instance.
- \note
- - The size() of an empty image is equal to \c 0.
- - The allocated memory size for a pixel buffer of a non-shared \c CImg<T> instance is equal to
- <tt>size()*sizeof(T)</tt>.
- \par Example
- \code
- const CImg<float> img(100,100,1,3); // Construct new 100x100 color image
- if (img.size()==30000) // Test succeeds
- std::printf("Pixel buffer uses %lu bytes",
- img.size()*sizeof(float));
- \endcode
- **/
- ulongT size() const {
- return (ulongT)_width*_height*_depth*_spectrum;
- }
- //! Return a pointer to the first pixel value.
- /**
- Return a \c T*, or a \c const \c T* pointer to the first value in the pixel buffer of the image instance,
- whether the instance is \c const or not.
- \note
- - The data() of an empty image is equal to \c 0 (null pointer).
- - The allocated pixel buffer for the image instance starts from \c data()
- and goes to <tt>data()+\ref size() - 1</tt> (included).
- - To get the pointer to one particular location of the pixel buffer, use
- data(unsigned int,unsigned int,unsigned int,unsigned int) instead.
- **/
- T* data() {
- return _data;
- }
- //! Return a pointer to the first pixel value \const.
- const T* data() const {
- return _data;
- }
- //! Return a pointer to a located pixel value.
- /**
- Return a \c T*, or a \c const \c T* pointer to the value located at (\c x,\c y,\c z,\c c) in the pixel buffer
- of the image instance,
- whether the instance is \c const or not.
- \param x X-coordinate of the pixel value.
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- \param c C-coordinate of the pixel value.
- \note
- - Writing \c img.data(x,y,z,c) is equivalent to <tt>&(img(x,y,z,c))</tt>. Thus, this method has the same
- properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int).
- **/
- #if cimg_verbosity>=3
- T *data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
- const ulongT off = (ulongT)offset(x,y,z,c);
- if (off>=size())
- cimg::warn(_cimg_instance
- "data(): Invalid pointer request, at coordinates (%u,%u,%u,%u) [offset=%u].",
- cimg_instance,
- x,y,z,c,off);
- return _data + off;
- }
- //! Return a pointer to a located pixel value \const.
- const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
- return const_cast<CImg<T>*>(this)->data(x,y,z,c);
- }
- #else
- T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
- return _data + x + (ulongT)y*_width + (ulongT)z*_width*_height + (ulongT)c*_width*_height*_depth;
- }
- const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
- return _data + x + (ulongT)y*_width + (ulongT)z*_width*_height + (ulongT)c*_width*_height*_depth;
- }
- #endif
- //! Return the offset to a located pixel value, with respect to the beginning of the pixel buffer.
- /**
- \param x X-coordinate of the pixel value.
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- \param c C-coordinate of the pixel value.
- \note
- - Writing \c img.data(x,y,z,c) is equivalent to <tt>&(img(x,y,z,c)) - img.data()</tt>.
- Thus, this method has the same properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int).
- \par Example
- \code
- const CImg<float> img(100,100,1,3); // Define a 100x100 RGB-color image
- const long off = img.offset(10,10,0,2); // Get the offset of the blue value of the pixel located at (10,10)
- const float val = img[off]; // Get the blue value of this pixel
- \endcode
- **/
- longT offset(const int x, const int y=0, const int z=0, const int c=0) const {
- return x + (longT)y*_width + (longT)z*_width*_height + (longT)c*_width*_height*_depth;
- }
- //! Return a CImg<T>::iterator pointing to the first pixel value.
- /**
- \note
- - Equivalent to data().
- - It has been mainly defined for compatibility with STL naming conventions.
- **/
- iterator begin() {
- return _data;
- }
- //! Return a CImg<T>::iterator pointing to the first value of the pixel buffer \const.
- const_iterator begin() const {
- return _data;
- }
- //! Return a CImg<T>::iterator pointing next to the last pixel value.
- /**
- \note
- - Writing \c img.end() is equivalent to <tt>img.data() + img.size()</tt>.
- - It has been mainly defined for compatibility with STL naming conventions.
- \warning
- - The returned iterator actually points to a value located \e outside the acceptable bounds of the pixel buffer.
- Trying to read or write the content of the returned iterator will probably result in a crash.
- Use it mainly as a strict upper bound for a CImg<T>::iterator.
- \par Example
- \code
- CImg<float> img(100,100,1,3); // Define a 100x100 RGB color image
- // 'img.end()' used below as an upper bound for the iterator.
- for (CImg<float>::iterator it = img.begin(); it<img.end(); ++it)
- *it = 0;
- \endcode
- **/
- iterator end() {
- return _data + size();
- }
- //! Return a CImg<T>::iterator pointing next to the last pixel value \const.
- const_iterator end() const {
- return _data + size();
- }
- //! Return a reference to the first pixel value.
- /**
- \note
- - Writing \c img.front() is equivalent to <tt>img[0]</tt>, or <tt>img(0,0,0,0)</tt>.
- - It has been mainly defined for compatibility with STL naming conventions.
- **/
- T& front() {
- return *_data;
- }
- //! Return a reference to the first pixel value \const.
- const T& front() const {
- return *_data;
- }
- //! Return a reference to the last pixel value.
- /**
- \note
- - Writing \c img.back() is equivalent to <tt>img[img.size() - 1]</tt>, or
- <tt>img(img.width() - 1,img.height() - 1,img.depth() - 1,img.spectrum() - 1)</tt>.
- - It has been mainly defined for compatibility with STL naming conventions.
- **/
- T& back() {
- return *(_data + size() - 1);
- }
- //! Return a reference to the last pixel value \const.
- const T& back() const {
- return *(_data + size() - 1);
- }
- //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions.
- /**
- Return a reference to the pixel value of the image instance located at a specified \c offset,
- or to a specified default value in case of out-of-bounds access.
- \param offset Offset to the desired pixel value.
- \param out_value Default value returned if \c offset is outside image bounds.
- \note
- - Writing \c img.at(offset,out_value) is similar to <tt>img[offset]</tt>, except that if \c offset
- is outside bounds (e.g. \c offset<0 or \c offset>=img.size()), a reference to a value \c out_value
- is safely returned instead.
- - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
- you are \e not sure about the validity of the specified pixel offset.
- **/
- T& at(const int offset, const T& out_value) {
- return (offset<0 || offset>=(int)size())?(cimg::temporary(out_value)=out_value):(*this)[offset];
- }
- //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions \const.
- T at(const int offset, const T& out_value) const {
- return (offset<0 || offset>=(int)size())?out_value:(*this)[offset];
- }
- //! Access to a pixel value at a specified offset, using Neumann boundary conditions.
- /**
- Return a reference to the pixel value of the image instance located at a specified \c offset,
- or to the nearest pixel location in the image instance in case of out-of-bounds access.
- \param offset Offset to the desired pixel value.
- \note
- - Similar to at(int,const T), except that an out-of-bounds access returns the value of the
- nearest pixel in the image instance, regarding the specified offset, i.e.
- - If \c offset<0, then \c img[0] is returned.
- - If \c offset>=img.size(), then \c img[img.size() - 1] is returned.
- - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
- you are \e not sure about the validity of the specified pixel offset.
- - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _at(int).
- **/
- T& at(const int offset) {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "at(): Empty instance.",
- cimg_instance);
- return _at(offset);
- }
- T& _at(const int offset) {
- const unsigned int siz = (unsigned int)size();
- return (*this)[offset<0?0:(unsigned int)offset>=siz?siz - 1:offset];
- }
- //! Access to a pixel value at a specified offset, using Neumann boundary conditions \const.
- const T& at(const int offset) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "at(): Empty instance.",
- cimg_instance);
- return _at(offset);
- }
- const T& _at(const int offset) const {
- const unsigned int siz = (unsigned int)size();
- return (*this)[offset<0?0:(unsigned int)offset>=siz?siz - 1:offset];
- }
- //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate.
- /**
- Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c),
- or to a specified default value in case of out-of-bounds access along the X-axis.
- \param x X-coordinate of the pixel value.
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- \param c C-coordinate of the pixel value.
- \param out_value Default value returned if \c (\c x,\c y,\c z,\c c) is outside image bounds.
- \note
- - Similar to operator()(), except that an out-of-bounds access along the X-axis returns the specified value
- \c out_value.
- - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
- you are \e not sure about the validity of the specified pixel coordinates.
- \warning
- - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
- **/
- T& atX(const int x, const int y, const int z, const int c, const T& out_value) {
- return (x<0 || x>=width())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
- }
- //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate \const.
- T atX(const int x, const int y, const int z, const int c, const T& out_value) const {
- return (x<0 || x>=width())?out_value:(*this)(x,y,z,c);
- }
- //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate.
- /**
- Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c),
- or to the nearest pixel location in the image instance in case of out-of-bounds access along the X-axis.
- \param x X-coordinate of the pixel value.
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- \param c C-coordinate of the pixel value.
- \note
- - Similar to at(int,int,int,int,const T), except that an out-of-bounds access returns the value of the
- nearest pixel in the image instance, regarding the specified X-coordinate.
- - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
- you are \e not sure about the validity of the specified pixel coordinates.
- - If you know your image instance is \e not empty, you may rather use the slightly faster method
- \c _at(int,int,int,int).
- \warning
- - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
- **/
- T& atX(const int x, const int y=0, const int z=0, const int c=0) {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "atX(): Empty instance.",
- cimg_instance);
- return _atX(x,y,z,c);
- }
- T& _atX(const int x, const int y=0, const int z=0, const int c=0) {
- return (*this)(x<0?0:(x>=width()?width() - 1:x),y,z,c);
- }
- //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate \const.
- const T& atX(const int x, const int y=0, const int z=0, const int c=0) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "atX(): Empty instance.",
- cimg_instance);
- return _atX(x,y,z,c);
- }
- const T& _atX(const int x, const int y=0, const int z=0, const int c=0) const {
- return (*this)(x<0?0:(x>=width()?width() - 1:x),y,z,c);
- }
- //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y-coordinates.
- /**
- Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on X and Y-coordinates.
- **/
- T& atXY(const int x, const int y, const int z, const int c, const T& out_value) {
- return (x<0 || y<0 || x>=width() || y>=height())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
- }
- //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y coordinates \const.
- T atXY(const int x, const int y, const int z, const int c, const T& out_value) const {
- return (x<0 || y<0 || x>=width() || y>=height())?out_value:(*this)(x,y,z,c);
- }
- //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates.
- /**
- Similar to atX(int,int,int,int), except that boundary checking is performed both on X and Y-coordinates.
- \note
- - If you know your image instance is \e not empty, you may rather use the slightly faster method
- \c _atXY(int,int,int,int).
- **/
- T& atXY(const int x, const int y, const int z=0, const int c=0) {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "atXY(): Empty instance.",
- cimg_instance);
- return _atXY(x,y,z,c);
- }
- T& _atXY(const int x, const int y, const int z=0, const int c=0) {
- return (*this)(cimg::cut(x,0,width() - 1),
- cimg::cut(y,0,height() - 1),z,c);
- }
- //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates \const.
- const T& atXY(const int x, const int y, const int z=0, const int c=0) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "atXY(): Empty instance.",
- cimg_instance);
- return _atXY(x,y,z,c);
- }
- const T& _atXY(const int x, const int y, const int z=0, const int c=0) const {
- return (*this)(cimg::cut(x,0,width() - 1),
- cimg::cut(y,0,height() - 1),z,c);
- }
- //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates.
- /**
- Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on
- X,Y and Z-coordinates.
- **/
- T& atXYZ(const int x, const int y, const int z, const int c, const T& out_value) {
- return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?
- (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
- }
- //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates \const.
- T atXYZ(const int x, const int y, const int z, const int c, const T& out_value) const {
- return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?out_value:(*this)(x,y,z,c);
- }
- //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates.
- /**
- Similar to atX(int,int,int,int), except that boundary checking is performed both on X,Y and Z-coordinates.
- \note
- - If you know your image instance is \e not empty, you may rather use the slightly faster method
- \c _atXYZ(int,int,int,int).
- **/
- T& atXYZ(const int x, const int y, const int z, const int c=0) {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "atXYZ(): Empty instance.",
- cimg_instance);
- return _atXYZ(x,y,z,c);
- }
- T& _atXYZ(const int x, const int y, const int z, const int c=0) {
- return (*this)(cimg::cut(x,0,width() - 1),
- cimg::cut(y,0,height() - 1),
- cimg::cut(z,0,depth() - 1),c);
- }
- //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates \const.
- const T& atXYZ(const int x, const int y, const int z, const int c=0) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "atXYZ(): Empty instance.",
- cimg_instance);
- return _atXYZ(x,y,z,c);
- }
- const T& _atXYZ(const int x, const int y, const int z, const int c=0) const {
- return (*this)(cimg::cut(x,0,width() - 1),
- cimg::cut(y,0,height() - 1),
- cimg::cut(z,0,depth() - 1),c);
- }
- //! Access to a pixel value, using Dirichlet boundary conditions.
- /**
- Similar to atX(int,int,int,int,const T), except that boundary checking is performed on all
- X,Y,Z and C-coordinates.
- **/
- T& atXYZC(const int x, const int y, const int z, const int c, const T& out_value) {
- return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?
- (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
- }
- //! Access to a pixel value, using Dirichlet boundary conditions \const.
- T atXYZC(const int x, const int y, const int z, const int c, const T& out_value) const {
- return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?out_value:
- (*this)(x,y,z,c);
- }
- //! Access to a pixel value, using Neumann boundary conditions.
- /**
- Similar to atX(int,int,int,int), except that boundary checking is performed on all X,Y,Z and C-coordinates.
- \note
- - If you know your image instance is \e not empty, you may rather use the slightly faster method
- \c _atXYZC(int,int,int,int).
- **/
- T& atXYZC(const int x, const int y, const int z, const int c) {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "atXYZC(): Empty instance.",
- cimg_instance);
- return _atXYZC(x,y,z,c);
- }
- T& _atXYZC(const int x, const int y, const int z, const int c) {
- return (*this)(cimg::cut(x,0,width() - 1),
- cimg::cut(y,0,height() - 1),
- cimg::cut(z,0,depth() - 1),
- cimg::cut(c,0,spectrum() - 1));
- }
- //! Access to a pixel value, using Neumann boundary conditions \const.
- const T& atXYZC(const int x, const int y, const int z, const int c) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "atXYZC(): Empty instance.",
- cimg_instance);
- return _atXYZC(x,y,z,c);
- }
- const T& _atXYZC(const int x, const int y, const int z, const int c) const {
- return (*this)(cimg::cut(x,0,width() - 1),
- cimg::cut(y,0,height() - 1),
- cimg::cut(z,0,depth() - 1),
- cimg::cut(c,0,spectrum() - 1));
- }
- //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X-coordinate.
- /**
- Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
- or a specified default value in case of out-of-bounds access along the X-axis.
- \param fx X-coordinate of the pixel value (float-valued).
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- \param c C-coordinate of the pixel value.
- \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds.
- \note
- - Similar to atX(int,int,int,int,const T), except that the returned pixel value is approximated by
- a linear interpolation along the X-axis, if corresponding coordinates are not integers.
- - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued.
- \warning
- - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
- **/
- Tfloat linear_atX(const float fx, const int y, const int z, const int c, const T& out_value) const {
- const int
- x = (int)fx - (fx>=0?0:1), nx = x + 1;
- const float
- dx = fx - x;
- const Tfloat
- Ic = (Tfloat)atX(x,y,z,c,out_value), In = (Tfloat)atXY(nx,y,z,c,out_value);
- return Ic + dx*(In - Ic);
- }
- //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X-coordinate.
- /**
- Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
- or the value of the nearest pixel location in the image instance in case of out-of-bounds access along
- the X-axis.
- \param fx X-coordinate of the pixel value (float-valued).
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- \param c C-coordinate of the pixel value.
- \note
- - Similar to linear_atX(float,int,int,int,const T) const, except that an out-of-bounds access returns
- the value of the nearest pixel in the image instance, regarding the specified X-coordinate.
- - If you know your image instance is \e not empty, you may rather use the slightly faster method
- \c _linear_atX(float,int,int,int).
- \warning
- - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
- **/
- Tfloat linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "linear_atX(): Empty instance.",
- cimg_instance);
- return _linear_atX(fx,y,z,c);
- }
- Tfloat _linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
- const float
- nfx = cimg::cut(fx,0,width() - 1);
- const unsigned int
- x = (unsigned int)nfx;
- const float
- dx = nfx - x;
- const unsigned int
- nx = dx>0?x + 1:x;
- const Tfloat
- Ic = (Tfloat)(*this)(x,y,z,c), In = (Tfloat)(*this)(nx,y,z,c);
- return Ic + dx*(In - Ic);
- }
- //! Return pixel value, using linear interpolation and periodic boundary conditions for the X-coordinate.
- Tfloat linear_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "linear_atX_p(): Empty instance.",
- cimg_instance);
- return _linear_atX_p(fx,y,z,c);
- }
- Tfloat _linear_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const {
- const float
- nfx = cimg::mod(fx,_width - 0.5f);
- const unsigned int
- x = (unsigned int)nfx;
- const float
- dx = nfx - x;
- const unsigned int
- nx = cimg::mod(x + 1,_width);
- const Tfloat
- Ic = (Tfloat)(*this)(x,y,z,c), In = (Tfloat)(*this)(nx,y,z,c);
- return Ic + dx*(In - Ic);
- }
- //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X and Y-coordinates.
- /**
- Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the
- boundary checking are achieved both for X and Y-coordinates.
- **/
- Tfloat linear_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const {
- const int
- x = (int)fx - (fx>=0?0:1), nx = x + 1,
- y = (int)fy - (fy>=0?0:1), ny = y + 1;
- const float
- dx = fx - x,
- dy = fy - y;
- const Tfloat
- Icc = (Tfloat)atXY(x,y,z,c,out_value), Inc = (Tfloat)atXY(nx,y,z,c,out_value),
- Icn = (Tfloat)atXY(x,ny,z,c,out_value), Inn = (Tfloat)atXY(nx,ny,z,c,out_value);
- return Icc + (Inc - Icc + (Icc + Inn - Icn - Inc)*dy)*dx + (Icn - Icc)*dy;
- }
- //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X and Y-coordinates.
- /**
- Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
- are achieved both for X and Y-coordinates.
- \note
- - If you know your image instance is \e not empty, you may rather use the slightly faster method
- \c _linear_atXY(float,float,int,int).
- **/
- Tfloat linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "linear_atXY(): Empty instance.",
- cimg_instance);
- return _linear_atXY(fx,fy,z,c);
- }
- Tfloat _linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
- const float
- nfx = cimg::cut(fx,0,width() - 1),
- nfy = cimg::cut(fy,0,height() - 1);
- const unsigned int
- x = (unsigned int)nfx,
- y = (unsigned int)nfy;
- const float
- dx = nfx - x,
- dy = nfy - y;
- const unsigned int
- nx = dx>0?x + 1:x,
- ny = dy>0?y + 1:y;
- const Tfloat
- Icc = (Tfloat)(*this)(x,y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c),
- Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c);
- return Icc + (Inc - Icc + (Icc + Inn - Icn - Inc)*dy)*dx + (Icn - Icc)*dy;
- }
- //! Return pixel value, using linear interpolation and periodic boundary conditions for the X and Y-coordinates.
- Tfloat linear_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "linear_atXY_p(): Empty instance.",
- cimg_instance);
- return _linear_atXY_p(fx,fy,z,c);
- }
- Tfloat _linear_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const {
- const float
- nfx = cimg::mod(fx,_width - 0.5f),
- nfy = cimg::mod(fy,_height - 0.5f);
- const unsigned int
- x = (unsigned int)nfx,
- y = (unsigned int)nfy;
- const float
- dx = nfx - x,
- dy = nfy - y;
- const unsigned int
- nx = cimg::mod(x + 1,_width),
- ny = cimg::mod(y + 1,_height);
- const Tfloat
- Icc = (Tfloat)(*this)(x,y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c),
- Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c);
- return Icc + (Inc - Icc + (Icc + Inn - Icn - Inc)*dy)*dx + (Icn - Icc)*dy;
- }
- //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates.
- /**
- Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the
- boundary checking are achieved both for X,Y and Z-coordinates.
- **/
- Tfloat linear_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const {
- const int
- x = (int)fx - (fx>=0?0:1), nx = x + 1,
- y = (int)fy - (fy>=0?0:1), ny = y + 1,
- z = (int)fz - (fz>=0?0:1), nz = z + 1;
- const float
- dx = fx - x,
- dy = fy - y,
- dz = fz - z;
- const Tfloat
- Iccc = (Tfloat)atXYZ(x,y,z,c,out_value), Incc = (Tfloat)atXYZ(nx,y,z,c,out_value),
- Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value), Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value),
- Iccn = (Tfloat)atXYZ(x,y,nz,c,out_value), Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value),
- Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value), Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value);
- return Iccc +
- (Incc - Iccc +
- (Iccc + Innc - Icnc - Incc +
- (Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)*dz)*dy +
- (Iccc + Incn - Iccn - Incc)*dz)*dx +
- (Icnc - Iccc +
- (Iccc + Icnn - Iccn - Icnc)*dz)*dy +
- (Iccn - Iccc)*dz;
- }
- //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X,Y and Z-coordinates.
- /**
- Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
- are achieved both for X,Y and Z-coordinates.
- \note
- - If you know your image instance is \e not empty, you may rather use the slightly faster method
- \c _linear_atXYZ(float,float,float,int).
- **/
- Tfloat linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "linear_atXYZ(): Empty instance.",
- cimg_instance);
- return _linear_atXYZ(fx,fy,fz,c);
- }
- Tfloat _linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const {
- const float
- nfx = cimg::cut(fx,0,width() - 1),
- nfy = cimg::cut(fy,0,height() - 1),
- nfz = cimg::cut(fz,0,depth() - 1);
- const unsigned int
- x = (unsigned int)nfx,
- y = (unsigned int)nfy,
- z = (unsigned int)nfz;
- const float
- dx = nfx - x,
- dy = nfy - y,
- dz = nfz - z;
- const unsigned int
- nx = dx>0?x + 1:x,
- ny = dy>0?y + 1:y,
- nz = dz>0?z + 1:z;
- const Tfloat
- Iccc = (Tfloat)(*this)(x,y,z,c), Incc = (Tfloat)(*this)(nx,y,z,c),
- Icnc = (Tfloat)(*this)(x,ny,z,c), Innc = (Tfloat)(*this)(nx,ny,z,c),
- Iccn = (Tfloat)(*this)(x,y,nz,c), Incn = (Tfloat)(*this)(nx,y,nz,c),
- Icnn = (Tfloat)(*this)(x,ny,nz,c), Innn = (Tfloat)(*this)(nx,ny,nz,c);
- return Iccc +
- (Incc - Iccc +
- (Iccc + Innc - Icnc - Incc +
- (Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)*dz)*dy +
- (Iccc + Incn - Iccn - Incc)*dz)*dx +
- (Icnc - Iccc +
- (Iccc + Icnn - Iccn - Icnc)*dz)*dy +
- (Iccn - Iccc)*dz;
- }
- //! Return pixel value, using linear interpolation and periodic boundary conditions for the X,Y and Z-coordinates.
- Tfloat linear_atXYZ_p(const float fx, const float fy=0, const float fz=0, const int c=0) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "linear_atXYZ_p(): Empty instance.",
- cimg_instance);
- return _linear_atXYZ_p(fx,fy,fz,c);
- }
- Tfloat _linear_atXYZ_p(const float fx, const float fy=0, const float fz=0, const int c=0) const {
- const float
- nfx = cimg::mod(fx,_width - 0.5f),
- nfy = cimg::mod(fy,_height - 0.5f),
- nfz = cimg::mod(fz,_depth - 0.5f);
- const unsigned int
- x = (unsigned int)nfx,
- y = (unsigned int)nfy,
- z = (unsigned int)nfz;
- const float
- dx = nfx - x,
- dy = nfy - y,
- dz = nfz - z;
- const unsigned int
- nx = cimg::mod(x + 1,_width),
- ny = cimg::mod(y + 1,_height),
- nz = cimg::mod(z + 1,_depth);
- const Tfloat
- Iccc = (Tfloat)(*this)(x,y,z,c), Incc = (Tfloat)(*this)(nx,y,z,c),
- Icnc = (Tfloat)(*this)(x,ny,z,c), Innc = (Tfloat)(*this)(nx,ny,z,c),
- Iccn = (Tfloat)(*this)(x,y,nz,c), Incn = (Tfloat)(*this)(nx,y,nz,c),
- Icnn = (Tfloat)(*this)(x,ny,nz,c), Innn = (Tfloat)(*this)(nx,ny,nz,c);
- return Iccc +
- (Incc - Iccc +
- (Iccc + Innc - Icnc - Incc +
- (Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)*dz)*dy +
- (Iccc + Incn - Iccn - Incc)*dz)*dx +
- (Icnc - Iccc +
- (Iccc + Icnn - Iccn - Icnc)*dz)*dy +
- (Iccn - Iccc)*dz;
- }
- //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for all X,Y,Z,C-coordinates.
- /**
- Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the
- boundary checking are achieved for all X,Y,Z and C-coordinates.
- **/
- Tfloat linear_atXYZC(const float fx, const float fy, const float fz, const float fc, const T& out_value) const {
- const int
- x = (int)fx - (fx>=0?0:1), nx = x + 1,
- y = (int)fy - (fy>=0?0:1), ny = y + 1,
- z = (int)fz - (fz>=0?0:1), nz = z + 1,
- c = (int)fc - (fc>=0?0:1), nc = c + 1;
- const float
- dx = fx - x,
- dy = fy - y,
- dz = fz - z,
- dc = fc - c;
- const Tfloat
- Icccc = (Tfloat)atXYZC(x,y,z,c,out_value), Inccc = (Tfloat)atXYZC(nx,y,z,c,out_value),
- Icncc = (Tfloat)atXYZC(x,ny,z,c,out_value), Inncc = (Tfloat)atXYZC(nx,ny,z,c,out_value),
- Iccnc = (Tfloat)atXYZC(x,y,nz,c,out_value), Incnc = (Tfloat)atXYZC(nx,y,nz,c,out_value),
- Icnnc = (Tfloat)atXYZC(x,ny,nz,c,out_value), Innnc = (Tfloat)atXYZC(nx,ny,nz,c,out_value),
- Icccn = (Tfloat)atXYZC(x,y,z,nc,out_value), Inccn = (Tfloat)atXYZC(nx,y,z,nc,out_value),
- Icncn = (Tfloat)atXYZC(x,ny,z,nc,out_value), Inncn = (Tfloat)atXYZC(nx,ny,z,nc,out_value),
- Iccnn = (Tfloat)atXYZC(x,y,nz,nc,out_value), Incnn = (Tfloat)atXYZC(nx,y,nz,nc,out_value),
- Icnnn = (Tfloat)atXYZC(x,ny,nz,nc,out_value), Innnn = (Tfloat)atXYZC(nx,ny,nz,nc,out_value);
- return Icccc +
- dx*(Inccc - Icccc +
- dy*(Icccc + Inncc - Icncc - Inccc +
- dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc +
- dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc -
- Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) +
- dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) +
- dz*(Icccc + Incnc - Iccnc - Inccc +
- dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) +
- dc*(Icccc + Inccn - Inccc - Icccn)) +
- dy*(Icncc - Icccc +
- dz*(Icccc + Icnnc - Iccnc - Icncc +
- dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) +
- dc*(Icccc + Icncn - Icncc - Icccn)) +
- dz*(Iccnc - Icccc +
- dc*(Icccc + Iccnn - Iccnc - Icccn)) +
- dc*(Icccn -Icccc);
- }
- //! Return pixel value, using linear interpolation and Neumann boundary conditions for all X,Y,Z and C-coordinates.
- /**
- Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
- are achieved for all X,Y,Z and C-coordinates.
- \note
- - If you know your image instance is \e not empty, you may rather use the slightly faster method
- \c _linear_atXYZC(float,float,float,float).
- **/
- Tfloat linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "linear_atXYZC(): Empty instance.",
- cimg_instance);
- return _linear_atXYZC(fx,fy,fz,fc);
- }
- Tfloat _linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
- const float
- nfx = cimg::cut(fx,0,width() - 1),
- nfy = cimg::cut(fy,0,height() - 1),
- nfz = cimg::cut(fz,0,depth() - 1),
- nfc = cimg::cut(fc,0,spectrum() - 1);
- const unsigned int
- x = (unsigned int)nfx,
- y = (unsigned int)nfy,
- z = (unsigned int)nfz,
- c = (unsigned int)nfc;
- const float
- dx = nfx - x,
- dy = nfy - y,
- dz = nfz - z,
- dc = nfc - c;
- const unsigned int
- nx = dx>0?x + 1:x,
- ny = dy>0?y + 1:y,
- nz = dz>0?z + 1:z,
- nc = dc>0?c + 1:c;
- const Tfloat
- Icccc = (Tfloat)(*this)(x,y,z,c), Inccc = (Tfloat)(*this)(nx,y,z,c),
- Icncc = (Tfloat)(*this)(x,ny,z,c), Inncc = (Tfloat)(*this)(nx,ny,z,c),
- Iccnc = (Tfloat)(*this)(x,y,nz,c), Incnc = (Tfloat)(*this)(nx,y,nz,c),
- Icnnc = (Tfloat)(*this)(x,ny,nz,c), Innnc = (Tfloat)(*this)(nx,ny,nz,c),
- Icccn = (Tfloat)(*this)(x,y,z,nc), Inccn = (Tfloat)(*this)(nx,y,z,nc),
- Icncn = (Tfloat)(*this)(x,ny,z,nc), Inncn = (Tfloat)(*this)(nx,ny,z,nc),
- Iccnn = (Tfloat)(*this)(x,y,nz,nc), Incnn = (Tfloat)(*this)(nx,y,nz,nc),
- Icnnn = (Tfloat)(*this)(x,ny,nz,nc), Innnn = (Tfloat)(*this)(nx,ny,nz,nc);
- return Icccc +
- dx*(Inccc - Icccc +
- dy*(Icccc + Inncc - Icncc - Inccc +
- dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc +
- dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc -
- Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) +
- dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) +
- dz*(Icccc + Incnc - Iccnc - Inccc +
- dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) +
- dc*(Icccc + Inccn - Inccc - Icccn)) +
- dy*(Icncc - Icccc +
- dz*(Icccc + Icnnc - Iccnc - Icncc +
- dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) +
- dc*(Icccc + Icncn - Icncc - Icccn)) +
- dz*(Iccnc - Icccc +
- dc*(Icccc + Iccnn - Iccnc - Icccn)) +
- dc*(Icccn - Icccc);
- }
- //! Return pixel value, using linear interpolation and periodic boundary conditions for all X,Y,Z and C-coordinates.
- Tfloat linear_atXYZC_p(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "linear_atXYZC_p(): Empty instance.",
- cimg_instance);
- return _linear_atXYZC_p(fx,fy,fz,fc);
- }
- Tfloat _linear_atXYZC_p(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
- const float
- nfx = cimg::mod(fx,_width - 0.5f),
- nfy = cimg::mod(fy,_height - 0.5f),
- nfz = cimg::mod(fz,_depth - 0.5f),
- nfc = cimg::mod(fc,_spectrum - 0.5f);
- const unsigned int
- x = (unsigned int)nfx,
- y = (unsigned int)nfy,
- z = (unsigned int)nfz,
- c = (unsigned int)nfc;
- const float
- dx = nfx - x,
- dy = nfy - y,
- dz = nfz - z,
- dc = nfc - c;
- const unsigned int
- nx = cimg::mod(x + 1,_width),
- ny = cimg::mod(y + 1,_height),
- nz = cimg::mod(z + 1,_depth),
- nc = cimg::mod(c + 1,_spectrum);
- const Tfloat
- Icccc = (Tfloat)(*this)(x,y,z,c), Inccc = (Tfloat)(*this)(nx,y,z,c),
- Icncc = (Tfloat)(*this)(x,ny,z,c), Inncc = (Tfloat)(*this)(nx,ny,z,c),
- Iccnc = (Tfloat)(*this)(x,y,nz,c), Incnc = (Tfloat)(*this)(nx,y,nz,c),
- Icnnc = (Tfloat)(*this)(x,ny,nz,c), Innnc = (Tfloat)(*this)(nx,ny,nz,c),
- Icccn = (Tfloat)(*this)(x,y,z,nc), Inccn = (Tfloat)(*this)(nx,y,z,nc),
- Icncn = (Tfloat)(*this)(x,ny,z,nc), Inncn = (Tfloat)(*this)(nx,ny,z,nc),
- Iccnn = (Tfloat)(*this)(x,y,nz,nc), Incnn = (Tfloat)(*this)(nx,y,nz,nc),
- Icnnn = (Tfloat)(*this)(x,ny,nz,nc), Innnn = (Tfloat)(*this)(nx,ny,nz,nc);
- return Icccc +
- dx*(Inccc - Icccc +
- dy*(Icccc + Inncc - Icncc - Inccc +
- dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc +
- dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc -
- Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) +
- dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) +
- dz*(Icccc + Incnc - Iccnc - Inccc +
- dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) +
- dc*(Icccc + Inccn - Inccc - Icccn)) +
- dy*(Icncc - Icccc +
- dz*(Icccc + Icnnc - Iccnc - Icncc +
- dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) +
- dc*(Icccc + Icncn - Icncc - Icccn)) +
- dz*(Iccnc - Icccc +
- dc*(Icccc + Iccnn - Iccnc - Icccn)) +
- dc*(Icccn - Icccc);
- }
- //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate.
- /**
- Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
- or a specified default value in case of out-of-bounds access along the X-axis.
- The cubic interpolation uses Hermite splines.
- \param fx d X-coordinate of the pixel value (float-valued).
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- \param c C-coordinate of the pixel value.
- \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds.
- \note
- - Similar to linear_atX(float,int,int,int,const T) const, except that the returned pixel value is
- approximated by a \e cubic interpolation along the X-axis.
- - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued.
- \warning
- - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
- **/
- Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T& out_value) const {
- const int
- x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2;
- const float
- dx = fx - x;
- const Tfloat
- Ip = (Tfloat)atX(px,y,z,c,out_value), Ic = (Tfloat)atX(x,y,z,c,out_value),
- In = (Tfloat)atX(nx,y,z,c,out_value), Ia = (Tfloat)atX(ax,y,z,c,out_value);
- return Ic + 0.5f*(dx*(-Ip + In) + dx*dx*(2*Ip - 5*Ic + 4*In - Ia) + dx*dx*dx*(-Ip + 3*Ic - 3*In + Ia));
- }
- //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate.
- /**
- Similar to cubic_atX(float,int,int,int,const T) const, except that the return value is clamped to stay in the
- min/max range of the datatype \c T.
- **/
- T cubic_atX_c(const float fx, const int y, const int z, const int c, const T& out_value) const {
- return cimg::type<T>::cut(cubic_atX(fx,y,z,c,out_value));
- }
- //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate.
- /**
- Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
- or the value of the nearest pixel location in the image instance in case of out-of-bounds access
- along the X-axis. The cubic interpolation uses Hermite splines.
- \param fx X-coordinate of the pixel value (float-valued).
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- \param c C-coordinate of the pixel value.
- \note
- - Similar to cubic_atX(float,int,int,int,const T) const, except that the returned pixel value is
- approximated by a cubic interpolation along the X-axis.
- - If you know your image instance is \e not empty, you may rather use the slightly faster method
- \c _cubic_atX(float,int,int,int).
- \warning
- - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
- **/
- Tfloat cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "cubic_atX(): Empty instance.",
- cimg_instance);
- return _cubic_atX(fx,y,z,c);
- }
- Tfloat _cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
- const float
- nfx = cimg::type<float>::is_nan(fx)?0:cimg::cut(fx,0,width() - 1);
- const int
- x = (int)nfx;
- const float
- dx = nfx - x;
- const int
- px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2;
- const Tfloat
- Ip = (Tfloat)(*this)(px,y,z,c), Ic = (Tfloat)(*this)(x,y,z,c),
- In = (Tfloat)(*this)(nx,y,z,c), Ia = (Tfloat)(*this)(ax,y,z,c);
- return Ic + 0.5f*(dx*(-Ip + In) + dx*dx*(2*Ip - 5*Ic + 4*In - Ia) + dx*dx*dx*(-Ip + 3*Ic - 3*In + Ia));
- }
- //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate.
- /**
- Similar to cubic_atX(float,int,int,int) const, except that the return value is clamped to stay in the
- min/max range of the datatype \c T.
- **/
- T cubic_atX_c(const float fx, const int y, const int z, const int c) const {
- return cimg::type<T>::cut(cubic_atX(fx,y,z,c));
- }
- T _cubic_atX_c(const float fx, const int y, const int z, const int c) const {
- return cimg::type<T>::cut(_cubic_atX(fx,y,z,c));
- }
- //! Return pixel value, using cubic interpolation and periodic boundary conditions for the X-coordinate.
- Tfloat cubic_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "cubic_atX_p(): Empty instance.",
- cimg_instance);
- return _cubic_atX_p(fx,y,z,c);
- }
- Tfloat _cubic_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const {
- const float
- nfx = cimg::type<float>::is_nan(fx)?0:cimg::mod(fx,_width - 0.5f);
- const int
- x = (int)nfx;
- const float
- dx = nfx - x;
- const int
- px = cimg::mod(x - 1,width()), nx = cimg::mod(x + 1,width()), ax = cimg::mod(x + 2,width());
- const Tfloat
- Ip = (Tfloat)(*this)(px,y,z,c), Ic = (Tfloat)(*this)(x,y,z,c),
- In = (Tfloat)(*this)(nx,y,z,c), Ia = (Tfloat)(*this)(ax,y,z,c);
- return Ic + 0.5f*(dx*(-Ip + In) + dx*dx*(2*Ip - 5*Ic + 4*In - Ia) + dx*dx*dx*(-Ip + 3*Ic - 3*In + Ia));
- }
- T cubic_atX_pc(const float fx, const int y, const int z, const int c) const {
- return cimg::type<T>::cut(cubic_atX_p(fx,y,z,c));
- }
- T _cubic_atX_pc(const float fx, const int y, const int z, const int c) const {
- return cimg::type<T>::cut(_cubic_atX_p(fx,y,z,c));
- }
- //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X and Y-coordinates.
- /**
- Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking
- are achieved both for X and Y-coordinates.
- **/
- Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const {
- const int
- x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2,
- y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2;
- const float dx = fx - x, dy = fy - y;
- const Tfloat
- Ipp = (Tfloat)atXY(px,py,z,c,out_value), Icp = (Tfloat)atXY(x,py,z,c,out_value),
- Inp = (Tfloat)atXY(nx,py,z,c,out_value), Iap = (Tfloat)atXY(ax,py,z,c,out_value),
- Ip = Icp + 0.5f*(dx*(-Ipp + Inp) + dx*dx*(2*Ipp - 5*Icp + 4*Inp - Iap) + dx*dx*dx*(-Ipp + 3*Icp - 3*Inp + Iap)),
- Ipc = (Tfloat)atXY(px,y,z,c,out_value), Icc = (Tfloat)atXY(x, y,z,c,out_value),
- Inc = (Tfloat)atXY(nx,y,z,c,out_value), Iac = (Tfloat)atXY(ax,y,z,c,out_value),
- Ic = Icc + 0.5f*(dx*(-Ipc + Inc) + dx*dx*(2*Ipc - 5*Icc + 4*Inc - Iac) + dx*dx*dx*(-Ipc + 3*Icc - 3*Inc + Iac)),
- Ipn = (Tfloat)atXY(px,ny,z,c,out_value), Icn = (Tfloat)atXY(x,ny,z,c,out_value),
- Inn = (Tfloat)atXY(nx,ny,z,c,out_value), Ian = (Tfloat)atXY(ax,ny,z,c,out_value),
- In = Icn + 0.5f*(dx*(-Ipn + Inn) + dx*dx*(2*Ipn - 5*Icn + 4*Inn - Ian) + dx*dx*dx*(-Ipn + 3*Icn - 3*Inn + Ian)),
- Ipa = (Tfloat)atXY(px,ay,z,c,out_value), Ica = (Tfloat)atXY(x,ay,z,c,out_value),
- Ina = (Tfloat)atXY(nx,ay,z,c,out_value), Iaa = (Tfloat)atXY(ax,ay,z,c,out_value),
- Ia = Ica + 0.5f*(dx*(-Ipa + Ina) + dx*dx*(2*Ipa - 5*Ica + 4*Ina - Iaa) + dx*dx*dx*(-Ipa + 3*Ica - 3*Ina + Iaa));
- return Ic + 0.5f*(dy*(-Ip + In) + dy*dy*(2*Ip - 5*Ic + 4*In - Ia) + dy*dy*dy*(-Ip + 3*Ic - 3*In + Ia));
- }
- //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y-coordinates.
- /**
- Similar to cubic_atXY(float,float,int,int,const T) const, except that the return value is clamped to stay in the
- min/max range of the datatype \c T.
- **/
- T cubic_atXY_c(const float fx, const float fy, const int z, const int c, const T& out_value) const {
- return cimg::type<T>::cut(cubic_atXY(fx,fy,z,c,out_value));
- }
- //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X and Y-coordinates.
- /**
- Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking
- are achieved for both X and Y-coordinates.
- \note
- - If you know your image instance is \e not empty, you may rather use the slightly faster method
- \c _cubic_atXY(float,float,int,int).
- **/
- Tfloat cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "cubic_atXY(): Empty instance.",
- cimg_instance);
- return _cubic_atXY(fx,fy,z,c);
- }
- Tfloat _cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
- const float
- nfx = cimg::type<float>::is_nan(fx)?0:cimg::cut(fx,0,width() - 1),
- nfy = cimg::type<float>::is_nan(fy)?0:cimg::cut(fy,0,height() - 1);
- const int x = (int)nfx, y = (int)nfy;
- const float dx = nfx - x, dy = nfy - y;
- const int
- px = x - 1<0?0:x - 1, nx = dx<=0?x:x + 1, ax = x + 2>=width()?width() - 1:x + 2,
- py = y - 1<0?0:y - 1, ny = dy<=0?y:y + 1, ay = y + 2>=height()?height() - 1:y + 2;
- const Tfloat
- Ipp = (Tfloat)(*this)(px,py,z,c), Icp = (Tfloat)(*this)(x,py,z,c), Inp = (Tfloat)(*this)(nx,py,z,c),
- Iap = (Tfloat)(*this)(ax,py,z,c),
- Ip = Icp + 0.5f*(dx*(-Ipp + Inp) + dx*dx*(2*Ipp - 5*Icp + 4*Inp - Iap) + dx*dx*dx*(-Ipp + 3*Icp - 3*Inp + Iap)),
- Ipc = (Tfloat)(*this)(px,y,z,c), Icc = (Tfloat)(*this)(x, y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c),
- Iac = (Tfloat)(*this)(ax,y,z,c),
- Ic = Icc + 0.5f*(dx*(-Ipc + Inc) + dx*dx*(2*Ipc - 5*Icc + 4*Inc - Iac) + dx*dx*dx*(-Ipc + 3*Icc - 3*Inc + Iac)),
- Ipn = (Tfloat)(*this)(px,ny,z,c), Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c),
- Ian = (Tfloat)(*this)(ax,ny,z,c),
- In = Icn + 0.5f*(dx*(-Ipn + Inn) + dx*dx*(2*Ipn - 5*Icn + 4*Inn - Ian) + dx*dx*dx*(-Ipn + 3*Icn - 3*Inn + Ian)),
- Ipa = (Tfloat)(*this)(px,ay,z,c), Ica = (Tfloat)(*this)(x,ay,z,c), Ina = (Tfloat)(*this)(nx,ay,z,c),
- Iaa = (Tfloat)(*this)(ax,ay,z,c),
- Ia = Ica + 0.5f*(dx*(-Ipa + Ina) + dx*dx*(2*Ipa - 5*Ica + 4*Ina - Iaa) + dx*dx*dx*(-Ipa + 3*Ica - 3*Ina + Iaa));
- return Ic + 0.5f*(dy*(-Ip + In) + dy*dy*(2*Ip - 5*Ic + 4*In - Ia) + dy*dy*dy*(-Ip + 3*Ic - 3*In + Ia));
- }
- //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y-coordinates.
- /**
- Similar to cubic_atXY(float,float,int,int) const, except that the return value is clamped to stay in the
- min/max range of the datatype \c T.
- **/
- T cubic_atXY_c(const float fx, const float fy, const int z, const int c) const {
- return cimg::type<T>::cut(cubic_atXY(fx,fy,z,c));
- }
- T _cubic_atXY_c(const float fx, const float fy, const int z, const int c) const {
- return cimg::type<T>::cut(_cubic_atXY(fx,fy,z,c));
- }
- //! Return pixel value, using cubic interpolation and periodic boundary conditions for the X and Y-coordinates.
- Tfloat cubic_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "cubic_atXY_p(): Empty instance.",
- cimg_instance);
- return _cubic_atXY_p(fx,fy,z,c);
- }
- Tfloat _cubic_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const {
- const float
- nfx = cimg::type<float>::is_nan(fx)?0:cimg::mod(fx,_width - 0.5f),
- nfy = cimg::type<float>::is_nan(fy)?0:cimg::mod(fy,_height - 0.5f);
- const int x = (int)nfx, y = (int)nfy;
- const float dx = nfx - x, dy = nfy - y;
- const int
- px = cimg::mod(x - 1,width()), nx = cimg::mod(x + 1,width()), ax = cimg::mod(x + 2,width()),
- py = cimg::mod(y - 1,height()), ny = cimg::mod(y + 1,height()), ay = cimg::mod(y + 2,height());
- const Tfloat
- Ipp = (Tfloat)(*this)(px,py,z,c), Icp = (Tfloat)(*this)(x,py,z,c), Inp = (Tfloat)(*this)(nx,py,z,c),
- Iap = (Tfloat)(*this)(ax,py,z,c),
- Ip = Icp + 0.5f*(dx*(-Ipp + Inp) + dx*dx*(2*Ipp - 5*Icp + 4*Inp - Iap) + dx*dx*dx*(-Ipp + 3*Icp - 3*Inp + Iap)),
- Ipc = (Tfloat)(*this)(px,y,z,c), Icc = (Tfloat)(*this)(x, y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c),
- Iac = (Tfloat)(*this)(ax,y,z,c),
- Ic = Icc + 0.5f*(dx*(-Ipc + Inc) + dx*dx*(2*Ipc - 5*Icc + 4*Inc - Iac) + dx*dx*dx*(-Ipc + 3*Icc - 3*Inc + Iac)),
- Ipn = (Tfloat)(*this)(px,ny,z,c), Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c),
- Ian = (Tfloat)(*this)(ax,ny,z,c),
- In = Icn + 0.5f*(dx*(-Ipn + Inn) + dx*dx*(2*Ipn - 5*Icn + 4*Inn - Ian) + dx*dx*dx*(-Ipn + 3*Icn - 3*Inn + Ian)),
- Ipa = (Tfloat)(*this)(px,ay,z,c), Ica = (Tfloat)(*this)(x,ay,z,c), Ina = (Tfloat)(*this)(nx,ay,z,c),
- Iaa = (Tfloat)(*this)(ax,ay,z,c),
- Ia = Ica + 0.5f*(dx*(-Ipa + Ina) + dx*dx*(2*Ipa - 5*Ica + 4*Ina - Iaa) + dx*dx*dx*(-Ipa + 3*Ica - 3*Ina + Iaa));
- return Ic + 0.5f*(dy*(-Ip + In) + dy*dy*(2*Ip - 5*Ic + 4*In - Ia) + dy*dy*dy*(-Ip + 3*Ic - 3*In + Ia));
- }
- T cubic_atXY_pc(const float fx, const float fy, const int z, const int c) const {
- return cimg::type<T>::cut(cubic_atXY_p(fx,fy,z,c));
- }
- T _cubic_atXY_pc(const float fx, const float fy, const int z, const int c) const {
- return cimg::type<T>::cut(_cubic_atXY_p(fx,fy,z,c));
- }
- //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates.
- /**
- Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking
- are achieved both for X,Y and Z-coordinates.
- **/
- Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const {
- const int
- x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2,
- y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2,
- z = (int)fz - (fz>=0?0:1), pz = z - 1, nz = z + 1, az = z + 2;
- const float dx = fx - x, dy = fy - y, dz = fz - z;
- const Tfloat
- Ippp = (Tfloat)atXYZ(px,py,pz,c,out_value), Icpp = (Tfloat)atXYZ(x,py,pz,c,out_value),
- Inpp = (Tfloat)atXYZ(nx,py,pz,c,out_value), Iapp = (Tfloat)atXYZ(ax,py,pz,c,out_value),
- Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) +
- dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)),
- Ipcp = (Tfloat)atXYZ(px,y,pz,c,out_value), Iccp = (Tfloat)atXYZ(x, y,pz,c,out_value),
- Incp = (Tfloat)atXYZ(nx,y,pz,c,out_value), Iacp = (Tfloat)atXYZ(ax,y,pz,c,out_value),
- Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) +
- dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)),
- Ipnp = (Tfloat)atXYZ(px,ny,pz,c,out_value), Icnp = (Tfloat)atXYZ(x,ny,pz,c,out_value),
- Innp = (Tfloat)atXYZ(nx,ny,pz,c,out_value), Ianp = (Tfloat)atXYZ(ax,ny,pz,c,out_value),
- Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) +
- dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)),
- Ipap = (Tfloat)atXYZ(px,ay,pz,c,out_value), Icap = (Tfloat)atXYZ(x,ay,pz,c,out_value),
- Inap = (Tfloat)atXYZ(nx,ay,pz,c,out_value), Iaap = (Tfloat)atXYZ(ax,ay,pz,c,out_value),
- Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) +
- dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)),
- Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) +
- dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)),
- Ippc = (Tfloat)atXYZ(px,py,z,c,out_value), Icpc = (Tfloat)atXYZ(x,py,z,c,out_value),
- Inpc = (Tfloat)atXYZ(nx,py,z,c,out_value), Iapc = (Tfloat)atXYZ(ax,py,z,c,out_value),
- Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) +
- dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)),
- Ipcc = (Tfloat)atXYZ(px,y,z,c,out_value), Iccc = (Tfloat)atXYZ(x, y,z,c,out_value),
- Incc = (Tfloat)atXYZ(nx,y,z,c,out_value), Iacc = (Tfloat)atXYZ(ax,y,z,c,out_value),
- Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) +
- dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)),
- Ipnc = (Tfloat)atXYZ(px,ny,z,c,out_value), Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value),
- Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value), Ianc = (Tfloat)atXYZ(ax,ny,z,c,out_value),
- Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) +
- dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)),
- Ipac = (Tfloat)atXYZ(px,ay,z,c,out_value), Icac = (Tfloat)atXYZ(x,ay,z,c,out_value),
- Inac = (Tfloat)atXYZ(nx,ay,z,c,out_value), Iaac = (Tfloat)atXYZ(ax,ay,z,c,out_value),
- Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) +
- dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)),
- Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) +
- dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)),
- Ippn = (Tfloat)atXYZ(px,py,nz,c,out_value), Icpn = (Tfloat)atXYZ(x,py,nz,c,out_value),
- Inpn = (Tfloat)atXYZ(nx,py,nz,c,out_value), Iapn = (Tfloat)atXYZ(ax,py,nz,c,out_value),
- Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) +
- dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)),
- Ipcn = (Tfloat)atXYZ(px,y,nz,c,out_value), Iccn = (Tfloat)atXYZ(x, y,nz,c,out_value),
- Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value), Iacn = (Tfloat)atXYZ(ax,y,nz,c,out_value),
- Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) +
- dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)),
- Ipnn = (Tfloat)atXYZ(px,ny,nz,c,out_value), Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value),
- Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value), Iann = (Tfloat)atXYZ(ax,ny,nz,c,out_value),
- Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) +
- dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)),
- Ipan = (Tfloat)atXYZ(px,ay,nz,c,out_value), Ican = (Tfloat)atXYZ(x,ay,nz,c,out_value),
- Inan = (Tfloat)atXYZ(nx,ay,nz,c,out_value), Iaan = (Tfloat)atXYZ(ax,ay,nz,c,out_value),
- Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) +
- dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)),
- In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) +
- dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)),
- Ippa = (Tfloat)atXYZ(px,py,az,c,out_value), Icpa = (Tfloat)atXYZ(x,py,az,c,out_value),
- Inpa = (Tfloat)atXYZ(nx,py,az,c,out_value), Iapa = (Tfloat)atXYZ(ax,py,az,c,out_value),
- Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) +
- dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)),
- Ipca = (Tfloat)atXYZ(px,y,az,c,out_value), Icca = (Tfloat)atXYZ(x, y,az,c,out_value),
- Inca = (Tfloat)atXYZ(nx,y,az,c,out_value), Iaca = (Tfloat)atXYZ(ax,y,az,c,out_value),
- Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) +
- dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)),
- Ipna = (Tfloat)atXYZ(px,ny,az,c,out_value), Icna = (Tfloat)atXYZ(x,ny,az,c,out_value),
- Inna = (Tfloat)atXYZ(nx,ny,az,c,out_value), Iana = (Tfloat)atXYZ(ax,ny,az,c,out_value),
- Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) +
- dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)),
- Ipaa = (Tfloat)atXYZ(px,ay,az,c,out_value), Icaa = (Tfloat)atXYZ(x,ay,az,c,out_value),
- Inaa = (Tfloat)atXYZ(nx,ay,az,c,out_value), Iaaa = (Tfloat)atXYZ(ax,ay,az,c,out_value),
- Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) +
- dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)),
- Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) +
- dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa));
- return Ic + 0.5f*(dz*(-Ip + In) + dz*dz*(2*Ip - 5*Ic + 4*In - Ia) + dz*dz*dz*(-Ip + 3*Ic - 3*In + Ia));
- }
- //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the XYZ-coordinates.
- /**
- Similar to cubic_atXYZ(float,float,float,int,const T) const, except that the return value is clamped to stay
- in the min/max range of the datatype \c T.
- **/
- T cubic_atXYZ_c(const float fx, const float fy, const float fz, const int c, const T& out_value) const {
- return cimg::type<T>::cut(cubic_atXYZ(fx,fy,fz,c,out_value));
- }
- //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates.
- /**
- Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking
- are achieved both for X,Y and Z-coordinates.
- \note
- - If you know your image instance is \e not empty, you may rather use the slightly faster method
- \c _cubic_atXYZ(float,float,float,int).
- **/
- Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "cubic_atXYZ(): Empty instance.",
- cimg_instance);
- return _cubic_atXYZ(fx,fy,fz,c);
- }
- Tfloat _cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const {
- const float
- nfx = cimg::type<float>::is_nan(fx)?0:cimg::cut(fx,0,width() - 1),
- nfy = cimg::type<float>::is_nan(fy)?0:cimg::cut(fy,0,height() - 1),
- nfz = cimg::type<float>::is_nan(fz)?0:cimg::cut(fz,0,depth() - 1);
- const int x = (int)nfx, y = (int)nfy, z = (int)nfz;
- const float dx = nfx - x, dy = nfy - y, dz = nfz - z;
- const int
- px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2,
- py = y - 1<0?0:y - 1, ny = dy>0?y + 1:y, ay = y + 2>=height()?height() - 1:y + 2,
- pz = z - 1<0?0:z - 1, nz = dz>0?z + 1:z, az = z + 2>=depth()?depth() - 1:z + 2;
- const Tfloat
- Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c),
- Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c),
- Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) +
- dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)),
- Ipcp = (Tfloat)(*this)(px,y,pz,c), Iccp = (Tfloat)(*this)(x, y,pz,c),
- Incp = (Tfloat)(*this)(nx,y,pz,c), Iacp = (Tfloat)(*this)(ax,y,pz,c),
- Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) +
- dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)),
- Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c),
- Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c),
- Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) +
- dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)),
- Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c),
- Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c),
- Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) +
- dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)),
- Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) +
- dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)),
- Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c),
- Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c),
- Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) +
- dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)),
- Ipcc = (Tfloat)(*this)(px,y,z,c), Iccc = (Tfloat)(*this)(x, y,z,c),
- Incc = (Tfloat)(*this)(nx,y,z,c), Iacc = (Tfloat)(*this)(ax,y,z,c),
- Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) +
- dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)),
- Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c),
- Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c),
- Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) +
- dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)),
- Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c),
- Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c),
- Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) +
- dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)),
- Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) +
- dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)),
- Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c),
- Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c),
- Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) +
- dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)),
- Ipcn = (Tfloat)(*this)(px,y,nz,c), Iccn = (Tfloat)(*this)(x, y,nz,c),
- Incn = (Tfloat)(*this)(nx,y,nz,c), Iacn = (Tfloat)(*this)(ax,y,nz,c),
- Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) +
- dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)),
- Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c),
- Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c),
- Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) +
- dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)),
- Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c),
- Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c),
- Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) +
- dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)),
- In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) +
- dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)),
- Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c),
- Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c),
- Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) +
- dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)),
- Ipca = (Tfloat)(*this)(px,y,az,c), Icca = (Tfloat)(*this)(x, y,az,c),
- Inca = (Tfloat)(*this)(nx,y,az,c), Iaca = (Tfloat)(*this)(ax,y,az,c),
- Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) +
- dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)),
- Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c),
- Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c),
- Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) +
- dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)),
- Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c),
- Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c),
- Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) +
- dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)),
- Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) +
- dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa));
- return Ic + 0.5f*(dz*(-Ip + In) + dz*dz*(2*Ip - 5*Ic + 4*In - Ia) + dz*dz*dz*(-Ip + 3*Ic - 3*In + Ia));
- }
- //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the XYZ-coordinates.
- /**
- Similar to cubic_atXYZ(float,float,float,int) const, except that the return value is clamped to stay in the
- min/max range of the datatype \c T.
- **/
- T cubic_atXYZ_c(const float fx, const float fy, const float fz, const int c) const {
- return cimg::type<T>::cut(cubic_atXYZ(fx,fy,fz,c));
- }
- T _cubic_atXYZ_c(const float fx, const float fy, const float fz, const int c) const {
- return cimg::type<T>::cut(_cubic_atXYZ(fx,fy,fz,c));
- }
- //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates.
- /**
- Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking
- are achieved both for X,Y and Z-coordinates.
- \note
- - If you know your image instance is \e not empty, you may rather use the slightly faster method
- \c _cubic_atXYZ(float,float,float,int).
- **/
- Tfloat cubic_atXYZ_p(const float fx, const float fy, const float fz, const int c=0) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "cubic_atXYZ_p(): Empty instance.",
- cimg_instance);
- return _cubic_atXYZ_p(fx,fy,fz,c);
- }
- Tfloat _cubic_atXYZ_p(const float fx, const float fy, const float fz, const int c=0) const {
- const float
- nfx = cimg::type<float>::is_nan(fx)?0:cimg::mod(fx,_width - 0.5f),
- nfy = cimg::type<float>::is_nan(fy)?0:cimg::mod(fy,_height - 0.5f),
- nfz = cimg::type<float>::is_nan(fz)?0:cimg::mod(fz,_depth - 0.5f);
- const int x = (int)nfx, y = (int)nfy, z = (int)nfz;
- const float dx = nfx - x, dy = nfy - y, dz = nfz - z;
- const int
- px = cimg::mod(x - 1,width()), nx = cimg::mod(x + 1,width()), ax = cimg::mod(x + 2,width()),
- py = cimg::mod(y - 1,height()), ny = cimg::mod(y + 1,height()), ay = cimg::mod(y + 2,height()),
- pz = cimg::mod(z - 1,depth()), nz = cimg::mod(z + 1,depth()), az = cimg::mod(z + 2,depth());
- const Tfloat
- Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c),
- Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c),
- Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) +
- dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)),
- Ipcp = (Tfloat)(*this)(px,y,pz,c), Iccp = (Tfloat)(*this)(x, y,pz,c),
- Incp = (Tfloat)(*this)(nx,y,pz,c), Iacp = (Tfloat)(*this)(ax,y,pz,c),
- Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) +
- dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)),
- Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c),
- Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c),
- Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) +
- dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)),
- Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c),
- Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c),
- Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) +
- dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)),
- Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) +
- dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)),
- Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c),
- Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c),
- Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) +
- dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)),
- Ipcc = (Tfloat)(*this)(px,y,z,c), Iccc = (Tfloat)(*this)(x, y,z,c),
- Incc = (Tfloat)(*this)(nx,y,z,c), Iacc = (Tfloat)(*this)(ax,y,z,c),
- Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) +
- dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)),
- Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c),
- Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c),
- Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) +
- dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)),
- Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c),
- Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c),
- Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) +
- dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)),
- Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) +
- dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)),
- Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c),
- Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c),
- Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) +
- dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)),
- Ipcn = (Tfloat)(*this)(px,y,nz,c), Iccn = (Tfloat)(*this)(x, y,nz,c),
- Incn = (Tfloat)(*this)(nx,y,nz,c), Iacn = (Tfloat)(*this)(ax,y,nz,c),
- Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) +
- dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)),
- Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c),
- Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c),
- Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) +
- dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)),
- Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c),
- Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c),
- Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) +
- dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)),
- In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) +
- dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)),
- Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c),
- Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c),
- Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) +
- dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)),
- Ipca = (Tfloat)(*this)(px,y,az,c), Icca = (Tfloat)(*this)(x, y,az,c),
- Inca = (Tfloat)(*this)(nx,y,az,c), Iaca = (Tfloat)(*this)(ax,y,az,c),
- Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) +
- dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)),
- Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c),
- Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c),
- Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) +
- dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)),
- Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c),
- Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c),
- Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) +
- dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)),
- Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) +
- dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa));
- return Ic + 0.5f*(dz*(-Ip + In) + dz*dz*(2*Ip - 5*Ic + 4*In - Ia) + dz*dz*dz*(-Ip + 3*Ic - 3*In + Ia));
- }
- T cubic_atXYZ_pc(const float fx, const float fy, const float fz, const int c) const {
- return cimg::type<T>::cut(cubic_atXYZ_p(fx,fy,fz,c));
- }
- T _cubic_atXYZ_pc(const float fx, const float fy, const float fz, const int c) const {
- return cimg::type<T>::cut(_cubic_atXYZ_p(fx,fy,fz,c));
- }
- //! Set pixel value, using linear interpolation for the X-coordinates.
- /**
- Set pixel value at specified coordinates (\c fx,\c y,\c z,\c c) in the image instance, in a way that
- the value is spread amongst several neighbors if the pixel coordinates are float-valued.
- \param value Pixel value to set.
- \param fx X-coordinate of the pixel value (float-valued).
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- \param c C-coordinate of the pixel value.
- \param is_added Tells if the pixel value is added to (\c true), or simply replace (\c false) the current image
- pixel(s).
- \return A reference to the current image instance.
- \note
- - Calling this method with out-of-bounds coordinates does nothing.
- **/
- CImg<T>& set_linear_atX(const T& value, const float fx, const int y=0, const int z=0, const int c=0,
- const bool is_added=false) {
- const int
- x = (int)fx - (fx>=0?0:1), nx = x + 1;
- const float
- dx = fx - x;
- if (y>=0 && y<height() && z>=0 && z<depth() && c>=0 && c<spectrum()) {
- if (x>=0 && x<width()) {
- const float w1 = 1 - dx, w2 = is_added?1:(1 - w1);
- (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
- }
- if (nx>=0 && nx<width()) {
- const float w1 = dx, w2 = is_added?1:(1 - w1);
- (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
- }
- }
- return *this;
- }
- //! Set pixel value, using linear interpolation for the X and Y-coordinates.
- /**
- Similar to set_linear_atX(const T&,float,int,int,int,bool), except that the linear interpolation
- is achieved both for X and Y-coordinates.
- **/
- CImg<T>& set_linear_atXY(const T& value, const float fx, const float fy=0, const int z=0, const int c=0,
- const bool is_added=false) {
- const int
- x = (int)fx - (fx>=0?0:1), nx = x + 1,
- y = (int)fy - (fy>=0?0:1), ny = y + 1;
- const float
- dx = fx - x,
- dy = fy - y;
- if (z>=0 && z<depth() && c>=0 && c<spectrum()) {
- if (y>=0 && y<height()) {
- if (x>=0 && x<width()) {
- const float w1 = (1 - dx)*(1 - dy), w2 = is_added?1:(1 - w1);
- (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
- }
- if (nx>=0 && nx<width()) {
- const float w1 = dx*(1 - dy), w2 = is_added?1:(1 - w1);
- (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
- }
- }
- if (ny>=0 && ny<height()) {
- if (x>=0 && x<width()) {
- const float w1 = (1 - dx)*dy, w2 = is_added?1:(1 - w1);
- (*this)(x,ny,z,c) = (T)(w1*value + w2*(*this)(x,ny,z,c));
- }
- if (nx>=0 && nx<width()) {
- const float w1 = dx*dy, w2 = is_added?1:(1 - w1);
- (*this)(nx,ny,z,c) = (T)(w1*value + w2*(*this)(nx,ny,z,c));
- }
- }
- }
- return *this;
- }
- //! Set pixel value, using linear interpolation for the X,Y and Z-coordinates.
- /**
- Similar to set_linear_atXY(const T&,float,float,int,int,bool), except that the linear interpolation
- is achieved both for X,Y and Z-coordinates.
- **/
- CImg<T>& set_linear_atXYZ(const T& value, const float fx, const float fy=0, const float fz=0, const int c=0,
- const bool is_added=false) {
- const int
- x = (int)fx - (fx>=0?0:1), nx = x + 1,
- y = (int)fy - (fy>=0?0:1), ny = y + 1,
- z = (int)fz - (fz>=0?0:1), nz = z + 1;
- const float
- dx = fx - x,
- dy = fy - y,
- dz = fz - z;
- if (c>=0 && c<spectrum()) {
- if (z>=0 && z<depth()) {
- if (y>=0 && y<height()) {
- if (x>=0 && x<width()) {
- const float w1 = (1 - dx)*(1 - dy)*(1 - dz), w2 = is_added?1:(1 - w1);
- (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
- }
- if (nx>=0 && nx<width()) {
- const float w1 = dx*(1 - dy)*(1 - dz), w2 = is_added?1:(1 - w1);
- (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
- }
- }
- if (ny>=0 && ny<height()) {
- if (x>=0 && x<width()) {
- const float w1 = (1 - dx)*dy*(1 - dz), w2 = is_added?1:(1 - w1);
- (*this)(x,ny,z,c) = (T)(w1*value + w2*(*this)(x,ny,z,c));
- }
- if (nx>=0 && nx<width()) {
- const float w1 = dx*dy*(1 - dz), w2 = is_added?1:(1 - w1);
- (*this)(nx,ny,z,c) = (T)(w1*value + w2*(*this)(nx,ny,z,c));
- }
- }
- }
- if (nz>=0 && nz<depth()) {
- if (y>=0 && y<height()) {
- if (x>=0 && x<width()) {
- const float w1 = (1 - dx)*(1 - dy)*dz, w2 = is_added?1:(1 - w1);
- (*this)(x,y,nz,c) = (T)(w1*value + w2*(*this)(x,y,nz,c));
- }
- if (nx>=0 && nx<width()) {
- const float w1 = dx*(1 - dy)*dz, w2 = is_added?1:(1 - w1);
- (*this)(nx,y,nz,c) = (T)(w1*value + w2*(*this)(nx,y,nz,c));
- }
- }
- if (ny>=0 && ny<height()) {
- if (x>=0 && x<width()) {
- const float w1 = (1 - dx)*dy*dz, w2 = is_added?1:(1 - w1);
- (*this)(x,ny,nz,c) = (T)(w1*value + w2*(*this)(x,ny,nz,c));
- }
- if (nx>=0 && nx<width()) {
- const float w1 = dx*dy*dz, w2 = is_added?1:(1 - w1);
- (*this)(nx,ny,nz,c) = (T)(w1*value + w2*(*this)(nx,ny,nz,c));
- }
- }
- }
- }
- return *this;
- }
- //! Return a C-string containing a list of all values of the image instance.
- /**
- Return a new \c CImg<char> image whose buffer data() is a \c char* string describing the list of all pixel values
- of the image instance (written in base 10), separated by specified \c separator character.
- \param separator A \c char character which specifies the separator between values in the returned C-string.
- \param max_size Maximum size of the returned image (or \c 0 if no limits are set).
- \param format For float/double-values, tell the printf format used to generate the text representation
- of the numbers (or \c 0 for default representation).
- \note
- - The returned image is never empty.
- - For an empty image instance, the returned string is <tt>""</tt>.
- - If \c max_size is equal to \c 0, there are no limits on the size of the returned string.
- - Otherwise, if the maximum number of string characters is exceeded, the value string is cut off
- and terminated by character \c '\0'. In that case, the returned image size is <tt>max_size + 1</tt>.
- **/
- CImg<charT> value_string(const char separator=',', const unsigned int max_size=0,
- const char *const format=0) const {
- if (is_empty() || max_size==1) return CImg<charT>(1,1,1,1,0);
- CImgList<charT> items;
- CImg<charT> s_item(256); *s_item = 0;
- const T *ptrs = _data;
- unsigned int string_size = 0;
- const char *const _format = format?format:cimg::type<T>::format();
- for (ulongT off = 0, siz = size(); off<siz && (!max_size || string_size<max_size); ++off) {
- const unsigned int printed_size = 1U + cimg_snprintf(s_item,s_item._width,_format,
- cimg::type<T>::format(*(ptrs++)));
- CImg<charT> item(s_item._data,printed_size);
- item[printed_size - 1] = separator;
- item.move_to(items);
- if (max_size) string_size+=printed_size;
- }
- CImg<charT> res;
- (items>'x').move_to(res);
- if (max_size && res._width>=max_size) res.crop(0,max_size - 1);
- res.back() = 0;
- return res;
- }
- //@}
- //-------------------------------------
- //
- //! \name Instance Checking
- //@{
- //-------------------------------------
- //! Test shared state of the pixel buffer.
- /**
- Return \c true if image instance has a shared memory buffer, and \c false otherwise.
- \note
- - A shared image do not own his pixel buffer data() and will not deallocate it on destruction.
- - Most of the time, a \c CImg<T> image instance will \e not be shared.
- - A shared image can only be obtained by a limited set of constructors and methods (see list below).
- **/
- bool is_shared() const {
- return _is_shared;
- }
- //! Test if image instance is empty.
- /**
- Return \c true, if image instance is empty, i.e. does \e not contain any pixel values, has dimensions
- \c 0 x \c 0 x \c 0 x \c 0 and a pixel buffer pointer set to \c 0 (null pointer), and \c false otherwise.
- **/
- bool is_empty() const {
- return !(_data && _width && _height && _depth && _spectrum);
- }
- //! Test if image instance contains a 'inf' value.
- /**
- Return \c true, if image instance contains a 'inf' value, and \c false otherwise.
- **/
- bool is_inf() const {
- if (cimg::type<T>::is_float()) cimg_for(*this,p,T) if (cimg::type<T>::is_inf((float)*p)) return true;
- return false;
- }
- //! Test if image instance contains a NaN value.
- /**
- Return \c true, if image instance contains a NaN value, and \c false otherwise.
- **/
- bool is_nan() const {
- if (cimg::type<T>::is_float()) cimg_for(*this,p,T) if (cimg::type<T>::is_nan((float)*p)) return true;
- return false;
- }
- //! Test if image width is equal to specified value.
- bool is_sameX(const unsigned int size_x) const {
- return _width==size_x;
- }
- //! Test if image width is equal to specified value.
- template<typename t>
- bool is_sameX(const CImg<t>& img) const {
- return is_sameX(img._width);
- }
- //! Test if image width is equal to specified value.
- bool is_sameX(const CImgDisplay& disp) const {
- return is_sameX(disp._width);
- }
- //! Test if image height is equal to specified value.
- bool is_sameY(const unsigned int size_y) const {
- return _height==size_y;
- }
- //! Test if image height is equal to specified value.
- template<typename t>
- bool is_sameY(const CImg<t>& img) const {
- return is_sameY(img._height);
- }
- //! Test if image height is equal to specified value.
- bool is_sameY(const CImgDisplay& disp) const {
- return is_sameY(disp._height);
- }
- //! Test if image depth is equal to specified value.
- bool is_sameZ(const unsigned int size_z) const {
- return _depth==size_z;
- }
- //! Test if image depth is equal to specified value.
- template<typename t>
- bool is_sameZ(const CImg<t>& img) const {
- return is_sameZ(img._depth);
- }
- //! Test if image spectrum is equal to specified value.
- bool is_sameC(const unsigned int size_c) const {
- return _spectrum==size_c;
- }
- //! Test if image spectrum is equal to specified value.
- template<typename t>
- bool is_sameC(const CImg<t>& img) const {
- return is_sameC(img._spectrum);
- }
- //! Test if image width and height are equal to specified values.
- /**
- Test if is_sameX(unsigned int) const and is_sameY(unsigned int) const are both verified.
- **/
- bool is_sameXY(const unsigned int size_x, const unsigned int size_y) const {
- return _width==size_x && _height==size_y;
- }
- //! Test if image width and height are the same as that of another image.
- /**
- Test if is_sameX(const CImg<t>&) const and is_sameY(const CImg<t>&) const are both verified.
- **/
- template<typename t>
- bool is_sameXY(const CImg<t>& img) const {
- return is_sameXY(img._width,img._height);
- }
- //! Test if image width and height are the same as that of an existing display window.
- /**
- Test if is_sameX(const CImgDisplay&) const and is_sameY(const CImgDisplay&) const are both verified.
- **/
- bool is_sameXY(const CImgDisplay& disp) const {
- return is_sameXY(disp._width,disp._height);
- }
- //! Test if image width and depth are equal to specified values.
- /**
- Test if is_sameX(unsigned int) const and is_sameZ(unsigned int) const are both verified.
- **/
- bool is_sameXZ(const unsigned int size_x, const unsigned int size_z) const {
- return _width==size_x && _depth==size_z;
- }
- //! Test if image width and depth are the same as that of another image.
- /**
- Test if is_sameX(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
- **/
- template<typename t>
- bool is_sameXZ(const CImg<t>& img) const {
- return is_sameXZ(img._width,img._depth);
- }
- //! Test if image width and spectrum are equal to specified values.
- /**
- Test if is_sameX(unsigned int) const and is_sameC(unsigned int) const are both verified.
- **/
- bool is_sameXC(const unsigned int size_x, const unsigned int size_c) const {
- return _width==size_x && _spectrum==size_c;
- }
- //! Test if image width and spectrum are the same as that of another image.
- /**
- Test if is_sameX(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
- **/
- template<typename t>
- bool is_sameXC(const CImg<t>& img) const {
- return is_sameXC(img._width,img._spectrum);
- }
- //! Test if image height and depth are equal to specified values.
- /**
- Test if is_sameY(unsigned int) const and is_sameZ(unsigned int) const are both verified.
- **/
- bool is_sameYZ(const unsigned int size_y, const unsigned int size_z) const {
- return _height==size_y && _depth==size_z;
- }
- //! Test if image height and depth are the same as that of another image.
- /**
- Test if is_sameY(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
- **/
- template<typename t>
- bool is_sameYZ(const CImg<t>& img) const {
- return is_sameYZ(img._height,img._depth);
- }
- //! Test if image height and spectrum are equal to specified values.
- /**
- Test if is_sameY(unsigned int) const and is_sameC(unsigned int) const are both verified.
- **/
- bool is_sameYC(const unsigned int size_y, const unsigned int size_c) const {
- return _height==size_y && _spectrum==size_c;
- }
- //! Test if image height and spectrum are the same as that of another image.
- /**
- Test if is_sameY(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
- **/
- template<typename t>
- bool is_sameYC(const CImg<t>& img) const {
- return is_sameYC(img._height,img._spectrum);
- }
- //! Test if image depth and spectrum are equal to specified values.
- /**
- Test if is_sameZ(unsigned int) const and is_sameC(unsigned int) const are both verified.
- **/
- bool is_sameZC(const unsigned int size_z, const unsigned int size_c) const {
- return _depth==size_z && _spectrum==size_c;
- }
- //! Test if image depth and spectrum are the same as that of another image.
- /**
- Test if is_sameZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
- **/
- template<typename t>
- bool is_sameZC(const CImg<t>& img) const {
- return is_sameZC(img._depth,img._spectrum);
- }
- //! Test if image width, height and depth are equal to specified values.
- /**
- Test if is_sameXY(unsigned int,unsigned int) const and is_sameZ(unsigned int) const are both verified.
- **/
- bool is_sameXYZ(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z) const {
- return is_sameXY(size_x,size_y) && _depth==size_z;
- }
- //! Test if image width, height and depth are the same as that of another image.
- /**
- Test if is_sameXY(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
- **/
- template<typename t>
- bool is_sameXYZ(const CImg<t>& img) const {
- return is_sameXYZ(img._width,img._height,img._depth);
- }
- //! Test if image width, height and spectrum are equal to specified values.
- /**
- Test if is_sameXY(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
- **/
- bool is_sameXYC(const unsigned int size_x, const unsigned int size_y, const unsigned int size_c) const {
- return is_sameXY(size_x,size_y) && _spectrum==size_c;
- }
- //! Test if image width, height and spectrum are the same as that of another image.
- /**
- Test if is_sameXY(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
- **/
- template<typename t>
- bool is_sameXYC(const CImg<t>& img) const {
- return is_sameXYC(img._width,img._height,img._spectrum);
- }
- //! Test if image width, depth and spectrum are equal to specified values.
- /**
- Test if is_sameXZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
- **/
- bool is_sameXZC(const unsigned int size_x, const unsigned int size_z, const unsigned int size_c) const {
- return is_sameXZ(size_x,size_z) && _spectrum==size_c;
- }
- //! Test if image width, depth and spectrum are the same as that of another image.
- /**
- Test if is_sameXZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
- **/
- template<typename t>
- bool is_sameXZC(const CImg<t>& img) const {
- return is_sameXZC(img._width,img._depth,img._spectrum);
- }
- //! Test if image height, depth and spectrum are equal to specified values.
- /**
- Test if is_sameYZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
- **/
- bool is_sameYZC(const unsigned int size_y, const unsigned int size_z, const unsigned int size_c) const {
- return is_sameYZ(size_y,size_z) && _spectrum==size_c;
- }
- //! Test if image height, depth and spectrum are the same as that of another image.
- /**
- Test if is_sameYZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
- **/
- template<typename t>
- bool is_sameYZC(const CImg<t>& img) const {
- return is_sameYZC(img._height,img._depth,img._spectrum);
- }
- //! Test if image width, height, depth and spectrum are equal to specified values.
- /**
- Test if is_sameXYZ(unsigned int,unsigned int,unsigned int) const and is_sameC(unsigned int) const are both
- verified.
- **/
- bool is_sameXYZC(const unsigned int size_x, const unsigned int size_y,
- const unsigned int size_z, const unsigned int size_c) const {
- return is_sameXYZ(size_x,size_y,size_z) && _spectrum==size_c;
- }
- //! Test if image width, height, depth and spectrum are the same as that of another image.
- /**
- Test if is_sameXYZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
- **/
- template<typename t>
- bool is_sameXYZC(const CImg<t>& img) const {
- return is_sameXYZC(img._width,img._height,img._depth,img._spectrum);
- }
- //! Test if specified coordinates are inside image bounds.
- /**
- Return \c true if pixel located at (\c x,\c y,\c z,\c c) is inside bounds of the image instance,
- and \c false otherwise.
- \param x X-coordinate of the pixel value.
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- \param c C-coordinate of the pixel value.
- \note
- - Return \c true only if all these conditions are verified:
- - The image instance is \e not empty.
- - <tt>0<=x<=\ref width() - 1</tt>.
- - <tt>0<=y<=\ref height() - 1</tt>.
- - <tt>0<=z<=\ref depth() - 1</tt>.
- - <tt>0<=c<=\ref spectrum() - 1</tt>.
- **/
- bool containsXYZC(const int x, const int y=0, const int z=0, const int c=0) const {
- return !is_empty() && x>=0 && x<width() && y>=0 && y<height() && z>=0 && z<depth() && c>=0 && c<spectrum();
- }
- //! Test if pixel value is inside image bounds and get its X,Y,Z and C-coordinates.
- /**
- Return \c true, if specified reference refers to a pixel value inside bounds of the image instance,
- and \c false otherwise.
- \param pixel Reference to pixel value to test.
- \param[out] x X-coordinate of the pixel value, if test succeeds.
- \param[out] y Y-coordinate of the pixel value, if test succeeds.
- \param[out] z Z-coordinate of the pixel value, if test succeeds.
- \param[out] c C-coordinate of the pixel value, if test succeeds.
- \note
- - Useful to convert an offset to a buffer value into pixel value coordinates:
- \code
- const CImg<float> img(100,100,1,3); // Construct a 100x100 RGB color image
- const unsigned long offset = 1249; // Offset to the pixel (49,12,0,0)
- unsigned int x,y,z,c;
- if (img.contains(img[offset],x,y,z,c)) { // Convert offset to (x,y,z,c) coordinates
- std::printf("Offset %u refers to pixel located at (%u,%u,%u,%u).\n",
- offset,x,y,z,c);
- }
- \endcode
- **/
- template<typename t>
- bool contains(const T& pixel, t& x, t& y, t& z, t& c) const {
- const ulongT wh = (ulongT)_width*_height, whd = wh*_depth, siz = whd*_spectrum;
- const T *const ppixel = &pixel;
- if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false;
- ulongT off = (ulongT)(ppixel - _data);
- const ulongT nc = off/whd;
- off%=whd;
- const ulongT nz = off/wh;
- off%=wh;
- const ulongT ny = off/_width, nx = off%_width;
- x = (t)nx; y = (t)ny; z = (t)nz; c = (t)nc;
- return true;
- }
- //! Test if pixel value is inside image bounds and get its X,Y and Z-coordinates.
- /**
- Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X,Y and Z-coordinates are set.
- **/
- template<typename t>
- bool contains(const T& pixel, t& x, t& y, t& z) const {
- const ulongT wh = (ulongT)_width*_height, whd = wh*_depth, siz = whd*_spectrum;
- const T *const ppixel = &pixel;
- if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false;
- ulongT off = ((ulongT)(ppixel - _data))%whd;
- const ulongT nz = off/wh;
- off%=wh;
- const ulongT ny = off/_width, nx = off%_width;
- x = (t)nx; y = (t)ny; z = (t)nz;
- return true;
- }
- //! Test if pixel value is inside image bounds and get its X and Y-coordinates.
- /**
- Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X and Y-coordinates are set.
- **/
- template<typename t>
- bool contains(const T& pixel, t& x, t& y) const {
- const ulongT wh = (ulongT)_width*_height, siz = wh*_depth*_spectrum;
- const T *const ppixel = &pixel;
- if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false;
- ulongT off = ((unsigned int)(ppixel - _data))%wh;
- const ulongT ny = off/_width, nx = off%_width;
- x = (t)nx; y = (t)ny;
- return true;
- }
- //! Test if pixel value is inside image bounds and get its X-coordinate.
- /**
- Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X-coordinate is set.
- **/
- template<typename t>
- bool contains(const T& pixel, t& x) const {
- const T *const ppixel = &pixel;
- if (is_empty() || ppixel<_data || ppixel>=_data + size()) return false;
- x = (t)(((ulongT)(ppixel - _data))%_width);
- return true;
- }
- //! Test if pixel value is inside image bounds.
- /**
- Similar to contains(const T&,t&,t&,t&,t&) const, except that no pixel coordinates are set.
- **/
- bool contains(const T& pixel) const {
- const T *const ppixel = &pixel;
- return !is_empty() && ppixel>=_data && ppixel<_data + size();
- }
- //! Test if pixel buffers of instance and input images overlap.
- /**
- Return \c true, if pixel buffers attached to image instance and input image \c img overlap,
- and \c false otherwise.
- \param img Input image to compare with.
- \note
- - Buffer overlapping may happen when manipulating \e shared images.
- - If two image buffers overlap, operating on one of the image will probably modify the other one.
- - Most of the time, \c CImg<T> instances are \e non-shared and do not overlap between each others.
- \par Example
- \code
- const CImg<float>
- img1("reference.jpg"), // Load RGB-color image
- img2 = img1.get_shared_channel(1); // Get shared version of the green channel
- if (img1.is_overlapped(img2)) { // Test succeeds, 'img1' and 'img2' overlaps
- std::printf("Buffers overlap!\n");
- }
- \endcode
- **/
- template<typename t>
- bool is_overlapped(const CImg<t>& img) const {
- const ulongT csiz = size(), isiz = img.size();
- return !((void*)(_data + csiz)<=(void*)img._data || (void*)_data>=(void*)(img._data + isiz));
- }
- //! Test if the set {\c *this,\c primitives,\c colors,\c opacities} defines a valid 3D object.
- /**
- Return \c true is the 3D object represented by the set {\c *this,\c primitives,\c colors,\c opacities} defines a
- valid 3D object, and \c false otherwise. The vertex coordinates are defined by the instance image.
- \param primitives List of primitives of the 3D object.
- \param colors List of colors of the 3D object.
- \param opacities List (or image) of opacities of the 3D object.
- \param full_check Tells if full checking of the 3D object must be performed.
- \param[out] error_message C-string to contain the error message, if the test does not succeed
- (at least 256 bytes).
- \note
- - Set \c full_checking to \c false to speed-up the 3D object checking. In this case, only the size of
- each 3D object component is checked.
- - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message.
- **/
- template<typename tp, typename tc, typename to>
- bool is_object3d(const CImgList<tp>& primitives,
- const CImgList<tc>& colors,
- const to& opacities,
- const bool full_check=true,
- char *const error_message=0) const {
- if (error_message) *error_message = 0;
- // Check consistency for the particular case of an empty 3D object.
- if (is_empty()) {
- if (primitives || colors || opacities) {
- if (error_message) cimg_snprintf(error_message,256,
- "3D object (%u,%u) defines no vertices but %u primitives, "
- "%u colors and %lu opacities",
- _width,primitives._width,primitives._width,
- colors._width,(unsigned long)opacities.size());
- return false;
- }
- return true;
- }
- // Check consistency of vertices.
- if (_height!=3 || _depth>1 || _spectrum>1) { // Check vertices dimensions
- if (error_message) cimg_snprintf(error_message,256,
- "3D object (%u,%u) has invalid vertex dimensions (%u,%u,%u,%u)",
- _width,primitives._width,_width,_height,_depth,_spectrum);
- return false;
- }
- if (colors._width>primitives._width + 1) {
- if (error_message) cimg_snprintf(error_message,256,
- "3D object (%u,%u) defines %u colors",
- _width,primitives._width,colors._width);
- return false;
- }
- if (opacities.size()>primitives._width) {
- if (error_message) cimg_snprintf(error_message,256,
- "3D object (%u,%u) defines %lu opacities",
- _width,primitives._width,(unsigned long)opacities.size());
- return false;
- }
- if (!full_check) return true;
- // Check consistency of primitives.
- cimglist_for(primitives,l) {
- const CImg<tp>& primitive = primitives[l];
- const unsigned int psiz = (unsigned int)primitive.size();
- switch (psiz) {
- case 1 : { // Point
- const unsigned int i0 = (unsigned int)primitive(0);
- if (i0>=_width) {
- if (error_message) cimg_snprintf(error_message,256,
- "3D object (%u,%u) refers to invalid vertex index %u in "
- "point primitive [%u]",
- _width,primitives._width,i0,l);
- return false;
- }
- } break;
- case 5 : { // Sphere
- const unsigned int
- i0 = (unsigned int)primitive(0),
- i1 = (unsigned int)primitive(1);
- if (i0>=_width || i1>=_width) {
- if (error_message) cimg_snprintf(error_message,256,
- "3D object (%u,%u) refers to invalid vertex indices (%u,%u) in "
- "sphere primitive [%u]",
- _width,primitives._width,i0,i1,l);
- return false;
- }
- } break;
- case 2 : case 6 : { // Segment
- const unsigned int
- i0 = (unsigned int)primitive(0),
- i1 = (unsigned int)primitive(1);
- if (i0>=_width || i1>=_width) {
- if (error_message) cimg_snprintf(error_message,256,
- "3D object (%u,%u) refers to invalid vertex indices (%u,%u) in "
- "segment primitive [%u]",
- _width,primitives._width,i0,i1,l);
- return false;
- }
- } break;
- case 3 : case 9 : { // Triangle
- const unsigned int
- i0 = (unsigned int)primitive(0),
- i1 = (unsigned int)primitive(1),
- i2 = (unsigned int)primitive(2);
- if (i0>=_width || i1>=_width || i2>=_width) {
- if (error_message) cimg_snprintf(error_message,256,
- "3D object (%u,%u) refers to invalid vertex indices (%u,%u,%u) in "
- "triangle primitive [%u]",
- _width,primitives._width,i0,i1,i2,l);
- return false;
- }
- } break;
- case 4 : case 12 : { // Quadrangle
- const unsigned int
- i0 = (unsigned int)primitive(0),
- i1 = (unsigned int)primitive(1),
- i2 = (unsigned int)primitive(2),
- i3 = (unsigned int)primitive(3);
- if (i0>=_width || i1>=_width || i2>=_width || i3>=_width) {
- if (error_message) cimg_snprintf(error_message,256,
- "3D object (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in "
- "quadrangle primitive [%u]",
- _width,primitives._width,i0,i1,i2,i3,l);
- return false;
- }
- } break;
- default :
- if (error_message) cimg_snprintf(error_message,256,
- "3D object (%u,%u) defines an invalid primitive [%u] of size %u",
- _width,primitives._width,l,(unsigned int)psiz);
- return false;
- }
- }
- // Check consistency of colors.
- cimglist_for(colors,c) {
- const CImg<tc>& color = colors[c];
- if (!color) {
- if (error_message) cimg_snprintf(error_message,256,
- "3D object (%u,%u) defines no color for primitive [%u]",
- _width,primitives._width,c);
- return false;
- }
- }
- // Check consistency of light texture.
- if (colors._width>primitives._width) {
- const CImg<tc> &light = colors.back();
- if (!light || light._depth>1) {
- if (error_message) cimg_snprintf(error_message,256,
- "3D object (%u,%u) defines an invalid light texture (%u,%u,%u,%u)",
- _width,primitives._width,light._width,
- light._height,light._depth,light._spectrum);
- return false;
- }
- }
- return true;
- }
- //! Test if image instance represents a valid serialization of a 3D object.
- /**
- Return \c true if the image instance represents a valid serialization of a 3D object, and \c false otherwise.
- \param full_check Tells if full checking of the instance must be performed.
- \param[out] error_message C-string to contain the error message, if the test does not succeed.
- \note
- - Set \c full_check to \c false to speed-up the 3D object checking. In this case, only the size of
- each 3D object component is checked.
- - Size of the string \c error_message should be at least 256-bytes long, to be able to contain the error message.
- **/
- bool is_CImg3d(const bool full_check=true, char *const error_message=0) const {
- if (error_message) *error_message = 0;
- // Check instance dimension and header.
- if (_width!=1 || _height<8 || _depth!=1 || _spectrum!=1) {
- if (error_message) cimg_snprintf(error_message,256,
- "CImg3d has invalid dimensions (%u,%u,%u,%u)",
- _width,_height,_depth,_spectrum);
- return false;
- }
- const T *ptrs = _data, *const ptre = end();
- if (!_is_CImg3d(*(ptrs++),'C') || !_is_CImg3d(*(ptrs++),'I') || !_is_CImg3d(*(ptrs++),'m') ||
- !_is_CImg3d(*(ptrs++),'g') || !_is_CImg3d(*(ptrs++),'3') || !_is_CImg3d(*(ptrs++),'d')) {
- if (error_message) cimg_snprintf(error_message,256,
- "CImg3d header not found");
- return false;
- }
- if (!cimg::type<T>::is_finite(*ptrs) || !cimg::type<T>::is_finite(ptrs[1])) {
- if (error_message) cimg_snprintf(error_message,256,
- "Specified numbers of vertices/primitives (%g/%g) are invalid",
- (double)*ptrs,(double)ptrs[1]);
- return false;
- }
- const unsigned int
- nb_points = cimg::float2uint((float)*(ptrs++)),
- nb_primitives = cimg::float2uint((float)*(ptrs++));
- // Check consistency of number of vertices / primitives.
- if (!full_check) {
- const ulongT minimal_size = 8UL + 3*nb_points + 6*nb_primitives;
- if (_data + minimal_size>ptre) {
- if (error_message) cimg_snprintf(error_message,256,
- "CImg3d (%u,%u) has only %lu values, "
- "while at least %lu values were expected",
- nb_points,nb_primitives,(unsigned long)size(),(unsigned long)minimal_size);
- return false;
- }
- }
- // Check consistency of vertex data.
- if (!nb_points) {
- if (nb_primitives) {
- if (error_message) cimg_snprintf(error_message,256,
- "CImg3d (%u,%u) defines no vertices but %u primitives",
- nb_points,nb_primitives,nb_primitives);
- return false;
- }
- if (ptrs!=ptre) {
- if (error_message) cimg_snprintf(error_message,256,
- "CImg3d (%u,%u) is an empty object but contains %u value%s "
- "more than expected",
- nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":"");
- return false;
- }
- return true;
- }
- if (ptrs + 3*nb_points>ptre) {
- if (error_message) cimg_snprintf(error_message,256,
- "CImg3d (%u,%u) defines only %u vertices data",
- nb_points,nb_primitives,(unsigned int)(ptre - ptrs)/3);
- return false;
- }
- ptrs+=3*nb_points;
- // Check consistency of primitive data.
- if (ptrs==ptre) {
- if (error_message) cimg_snprintf(error_message,256,
- "CImg3d (%u,%u) defines %u vertices but no primitive",
- nb_points,nb_primitives,nb_points);
- return false;
- }
- if (!full_check) return true;
- for (unsigned int p = 0; p<nb_primitives; ++p) {
- const unsigned int nb_inds = (unsigned int)*(ptrs++);
- switch (nb_inds) {
- case 1 : { // Point
- const unsigned int i0 = cimg::float2uint((float)*(ptrs++));
- if (i0>=nb_points) {
- if (error_message) cimg_snprintf(error_message,256,
- "CImg3d (%u,%u) refers to invalid vertex index %u in point primitive [%u]",
- nb_points,nb_primitives,i0,p);
- return false;
- }
- } break;
- case 5 : { // Sphere
- const unsigned int
- i0 = cimg::float2uint((float)*(ptrs++)),
- i1 = cimg::float2uint((float)*(ptrs++));
- ptrs+=3;
- if (i0>=nb_points || i1>=nb_points) {
- if (error_message) cimg_snprintf(error_message,256,
- "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in "
- "sphere primitive [%u]",
- nb_points,nb_primitives,i0,i1,p);
- return false;
- }
- } break;
- case 2 : case 6 : { // Segment
- const unsigned int
- i0 = cimg::float2uint((float)*(ptrs++)),
- i1 = cimg::float2uint((float)*(ptrs++));
- if (nb_inds==6) ptrs+=4;
- if (i0>=nb_points || i1>=nb_points) {
- if (error_message) cimg_snprintf(error_message,256,
- "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in "
- "segment primitive [%u]",
- nb_points,nb_primitives,i0,i1,p);
- return false;
- }
- } break;
- case 3 : case 9 : { // Triangle
- const unsigned int
- i0 = cimg::float2uint((float)*(ptrs++)),
- i1 = cimg::float2uint((float)*(ptrs++)),
- i2 = cimg::float2uint((float)*(ptrs++));
- if (nb_inds==9) ptrs+=6;
- if (i0>=nb_points || i1>=nb_points || i2>=nb_points) {
- if (error_message) cimg_snprintf(error_message,256,
- "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u) in "
- "triangle primitive [%u]",
- nb_points,nb_primitives,i0,i1,i2,p);
- return false;
- }
- } break;
- case 4 : case 12 : { // Quadrangle
- const unsigned int
- i0 = cimg::float2uint((float)*(ptrs++)),
- i1 = cimg::float2uint((float)*(ptrs++)),
- i2 = cimg::float2uint((float)*(ptrs++)),
- i3 = cimg::float2uint((float)*(ptrs++));
- if (nb_inds==12) ptrs+=8;
- if (i0>=nb_points || i1>=nb_points || i2>=nb_points || i3>=nb_points) {
- if (error_message) cimg_snprintf(error_message,256,
- "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in "
- "quadrangle primitive [%u]",
- nb_points,nb_primitives,i0,i1,i2,i3,p);
- return false;
- }
- } break;
- default :
- if (error_message) cimg_snprintf(error_message,256,
- "CImg3d (%u,%u) defines an invalid primitive [%u] of size %u",
- nb_points,nb_primitives,p,nb_inds);
- return false;
- }
- if (ptrs>ptre) {
- if (error_message) cimg_snprintf(error_message,256,
- "CImg3d (%u,%u) has incomplete primitive data for primitive [%u], "
- "%u values missing",
- nb_points,nb_primitives,p,(unsigned int)(ptrs - ptre));
- return false;
- }
- }
- // Check consistency of color data.
- if (ptrs==ptre) {
- if (error_message) cimg_snprintf(error_message,256,
- "CImg3d (%u,%u) defines no color/texture data",
- nb_points,nb_primitives);
- return false;
- }
- for (unsigned int c = 0; c<nb_primitives; ++c) {
- if (*(ptrs++)!=(T)-128) ptrs+=2;
- else if ((ptrs+=3)<ptre) {
- const unsigned int
- w = (unsigned int)cimg::float2uint((float)*(ptrs - 3)),
- h = (unsigned int)*(ptrs - 2),
- s = (unsigned int)*(ptrs - 1);
- if (!h && !s) {
- if (w>=c) {
- if (error_message) cimg_snprintf(error_message,256,
- "CImg3d (%u,%u) refers to invalid shared sprite/texture index %u "
- "for primitive [%u]",
- nb_points,nb_primitives,w,c);
- return false;
- }
- } else ptrs+=w*h*s;
- }
- if (ptrs>ptre) {
- if (error_message) cimg_snprintf(error_message,256,
- "CImg3d (%u,%u) has incomplete color/texture data for primitive [%u], "
- "%u values missing",
- nb_points,nb_primitives,c,(unsigned int)(ptrs - ptre));
- return false;
- }
- }
- // Check consistency of opacity data.
- if (ptrs==ptre) {
- if (error_message) cimg_snprintf(error_message,256,
- "CImg3d (%u,%u) defines no opacity data",
- nb_points,nb_primitives);
- return false;
- }
- for (unsigned int o = 0; o<nb_primitives; ++o) {
- if (*(ptrs++)==(T)-128 && (ptrs+=3)<ptre) {
- const unsigned int
- w = (unsigned int)cimg::float2uint((float)*(ptrs - 3)),
- h = (unsigned int)*(ptrs - 2),
- s = (unsigned int)*(ptrs - 1);
- if (!h && !s) {
- if (w>=o) {
- if (error_message) cimg_snprintf(error_message,256,
- "CImg3d (%u,%u) refers to invalid shared opacity index %u "
- "for primitive [%u]",
- nb_points,nb_primitives,w,o);
- return false;
- }
- } else ptrs+=w*h*s;
- }
- if (ptrs>ptre) {
- if (error_message) cimg_snprintf(error_message,256,
- "CImg3d (%u,%u) has incomplete opacity data for primitive [%u]",
- nb_points,nb_primitives,o);
- return false;
- }
- }
- // Check end of data.
- if (ptrs<ptre) {
- if (error_message) cimg_snprintf(error_message,256,
- "CImg3d (%u,%u) contains %u value%s more than expected",
- nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":"");
- return false;
- }
- return true;
- }
- static bool _is_CImg3d(const T val, const char c) {
- return val>=(T)c && val<(T)(c + 1);
- }
- //@}
- //-------------------------------------
- //
- //! \name Mathematical Functions
- //@{
- //-------------------------------------
- // Define the math formula parser/compiler and expression evaluator.
- struct _cimg_math_parser {
- CImg<doubleT> mem;
- CImg<intT> memtype, memmerge;
- CImgList<ulongT> _code, &code, code_begin, code_end,
- _code_begin_t, &code_begin_t, _code_end_t, &code_end_t;
- CImg<ulongT> opcode;
- const CImg<ulongT> *p_code_end, *p_code;
- const CImg<ulongT> *const p_break;
- CImg<charT> expr, pexpr;
- const CImg<T>& imgin;
- CImg<T> &imgout;
- CImgList<T>& imglist;
- CImg<doubleT> _img_stats, &img_stats, constcache_vals;
- CImgList<doubleT> _list_stats, &list_stats, _list_median, &list_median, _list_norm, &list_norm;
- CImg<uintT> mem_img_stats, constcache_inds;
- CImg<uintT> level, variable_pos, reserved_label;
- CImgList<charT> variable_def, macro_def, macro_body;
- char *user_macro;
- unsigned int mempos, mem_img_median, mem_img_norm, mem_img_index, debug_indent,
- result_dim, result_end_dim, break_type, constcache_size;
- bool is_parallelizable, is_noncritical_run, is_end_code, is_fill, need_input_copy, return_new_comp;
- double *result, *result_end;
- cimg_uint64 rng;
- const char *const calling_function, *s_op, *ss_op;
- typedef double (*mp_func)(_cimg_math_parser&);
- #define _cimg_mp_is_scalar(arg) (memtype[arg]<2) // Is scalar value?
- #define _cimg_mp_is_const_scalar(arg) (memtype[arg]==1) // Is const scalar?
- #define _cimg_mp_is_vector(arg) (memtype[arg]>1) // Is vector?
- #define _cimg_mp_is_comp(arg) (!memtype[arg]) // Is computation value?
- #define _cimg_mp_is_reserved(arg) (memtype[arg]==-1) // Is scalar and reserved (e.g. variable)?
- #define _cimg_mp_size(arg) (_cimg_mp_is_scalar(arg)?0U:(unsigned int)memtype[arg] - 1) // Size (0=scalar, N>0=vectorN)
- #define _cimg_mp_calling_function s_calling_function()._data
- #define _cimg_mp_op(s) s_op = s; ss_op = ss
- #define _cimg_mp_check_const_scalar(arg,n_arg,mode) check_const_scalar(arg,n_arg,mode,ss,se,saved_char)
- #define _cimg_mp_check_const_index(arg) check_const_index(arg,ss,se,saved_char)
- #define _cimg_mp_check_notnan_index(arg) check_notnan_index(arg,ss,se,saved_char)
- #define _cimg_mp_check_list() check_list(ss,se,saved_char)
- #define _cimg_mp_check_matrix_square(arg,n_arg) check_matrix_square(arg,n_arg,ss,se,saved_char)
- #define _cimg_mp_check_type(arg,n_arg,mode,N) check_type(arg,n_arg,mode,N,ss,se,saved_char)
- #define _cimg_mp_defunc(mp) (*(mp_func)(*(mp).opcode))(mp)
- #define _cimg_mp_return(x) { *se = saved_char; s_op = previous_s_op; ss_op = previous_ss_op; return x; }
- #define _cimg_mp_return_nan() _cimg_mp_return(_cimg_mp_slot_nan)
- #define _cimg_mp_const_scalar(val) _cimg_mp_return(const_scalar((double)(val)))
- #define _cimg_mp_scalar0(op) _cimg_mp_return(scalar0(op))
- #define _cimg_mp_scalar1(op,i1) _cimg_mp_return(scalar1(op,i1))
- #define _cimg_mp_scalar2(op,i1,i2) _cimg_mp_return(scalar2(op,i1,i2))
- #define _cimg_mp_scalar3(op,i1,i2,i3) _cimg_mp_return(scalar3(op,i1,i2,i3))
- #define _cimg_mp_scalar4(op,i1,i2,i3,i4) _cimg_mp_return(scalar4(op,i1,i2,i3,i4))
- #define _cimg_mp_scalar5(op,i1,i2,i3,i4,i5) _cimg_mp_return(scalar5(op,i1,i2,i3,i4,i5))
- #define _cimg_mp_scalar6(op,i1,i2,i3,i4,i5,i6) _cimg_mp_return(scalar6(op,i1,i2,i3,i4,i5,i6))
- #define _cimg_mp_scalar7(op,i1,i2,i3,i4,i5,i6,i7) _cimg_mp_return(scalar7(op,i1,i2,i3,i4,i5,i6,i7))
- #define _cimg_mp_vector1_v(op,i1) _cimg_mp_return(vector1_v(op,i1))
- #define _cimg_mp_vector2_sv(op,i1,i2) _cimg_mp_return(vector2_sv(op,i1,i2))
- #define _cimg_mp_vector2_vs(op,i1,i2) _cimg_mp_return(vector2_vs(op,i1,i2))
- #define _cimg_mp_vector2_vv(op,i1,i2) _cimg_mp_return(vector2_vv(op,i1,i2))
- #define _cimg_mp_vector3_vss(op,i1,i2,i3) _cimg_mp_return(vector3_vss(op,i1,i2,i3))
- #define _cimg_mp_vector4_vvss(op,i1,i2,i3,i4) _cimg_mp_return(vector4_vvss(op,i1,i2,i3,i4))
- #define _cimg_mp_vector4_vsss(op,i1,i2,i3,i4) _cimg_mp_return(vector4_vsss(op,i1,i2,i3,i4))
- #define _cimg_mp_vector4_svss(op,i1,i2,i3,i4) _cimg_mp_return(vector4_svss(op,i1,i2,i3,i4))
- #define _cimg_mp_strerr \
- *se = saved_char; \
- for (s0 = ss; s0>expr._data && *s0!=';'; --s0) {} \
- if (*s0==';') ++s0; \
- while (cimg::is_blank(*s0)) ++s0; \
- cimg::strellipsize(s0,64)
- // Constructors / Destructors.
- ~_cimg_math_parser() {
- cimg::srand(rng);
- }
- _cimg_math_parser(const char *const expression, const char *const funcname=0,
- const CImg<T>& img_input=CImg<T>::const_empty(), CImg<T> *const img_output=0,
- CImgList<T> *const list_images=0, const bool _is_fill=false):
- code(_code),code_begin_t(_code_begin_t),code_end_t(_code_end_t),
- p_break((CImg<ulongT>*)(cimg_ulong)-2),imgin(img_input),
- imgout(img_output?*img_output:CImg<T>::empty()),imglist(list_images?*list_images:CImgList<T>::empty()),
- img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),list_norm(_list_norm),user_macro(0),
- mem_img_median(~0U),mem_img_norm(~0U),mem_img_index(~0U),debug_indent(0),result_dim(0),result_end_dim(0),
- break_type(0),constcache_size(0),is_parallelizable(true),is_noncritical_run(false),is_fill(_is_fill),
- need_input_copy(false),result_end(0),rng((cimg::_rand(),cimg::rng())),
- calling_function(funcname?funcname:"cimg_math_parser") {
- #if cimg_use_openmp!=0
- rng+=omp_get_thread_num();
- #endif
- if (!expression || !*expression)
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: Empty expression.",
- pixel_type(),_cimg_mp_calling_function);
- const char *_expression = expression;
- while (*_expression && (cimg::is_blank(*_expression) || *_expression==';')) ++_expression;
- CImg<charT>::string(_expression).move_to(expr);
- char *ps = &expr.back() - 1;
- while (ps>expr._data && (cimg::is_blank(*ps) || *ps==';')) --ps;
- *(++ps) = 0; expr._width = (unsigned int)(ps - expr._data + 1);
- // Ease the retrieval of previous non-space characters afterwards.
- pexpr.assign(expr._width);
- char c, *pe = pexpr._data;
- for (ps = expr._data, c = ' '; *ps; ++ps) {
- if (!cimg::is_blank(*ps)) c = *ps; else *ps = ' ';
- *(pe++) = c;
- }
- *pe = 0;
- level = get_level(expr);
- // Init constant values.
- #define _cimg_mp_interpolation (reserved_label[31]!=~0U?reserved_label[31]:0)
- #define _cimg_mp_boundary (reserved_label[32]!=~0U?reserved_label[32]:0)
- #define _cimg_mp_slot_t 17
- #define _cimg_mp_slot_nan 29
- #define _cimg_mp_slot_x 30
- #define _cimg_mp_slot_y 31
- #define _cimg_mp_slot_z 32
- #define _cimg_mp_slot_c 33
- mem.assign(96);
- for (unsigned int i = 0; i<=10; ++i) mem[i] = (double)i; // mem[0-10] = 0...10
- for (unsigned int i = 1; i<=5; ++i) mem[i + 10] = -(double)i; // mem[11-15] = -1...-5
- mem[16] = 0.5;
- mem[_cimg_mp_slot_t] = 0; // thread_id
- mem[18] = (double)imgin._width; // w
- mem[19] = (double)imgin._height; // h
- mem[20] = (double)imgin._depth; // d
- mem[21] = (double)imgin._spectrum; // s
- mem[22] = (double)imgin._is_shared; // r
- mem[23] = (double)imgin._width*imgin._height; // wh
- mem[24] = (double)imgin._width*imgin._height*imgin._depth; // whd
- mem[25] = (double)imgin._width*imgin._height*imgin._depth*imgin._spectrum; // whds
- mem[26] = (double)imglist._width; // l
- mem[27] = std::exp(1.); // e
- mem[28] = cimg::PI; // pi
- mem[_cimg_mp_slot_nan] = cimg::type<double>::nan(); // nan
- // Set value property :
- // { -1 = reserved (e.g. variable) | 0 = computation value |
- // 1 = compile-time constant | N>1 = constant ptr to vector[N-1] }.
- memtype.assign(mem._width,1,1,1,0);
- for (unsigned int i = 0; i<_cimg_mp_slot_x; ++i) memtype[i] = 1;
- memtype[_cimg_mp_slot_t] = memtype[_cimg_mp_slot_x] = memtype[_cimg_mp_slot_y] =
- memtype[_cimg_mp_slot_z] = memtype[_cimg_mp_slot_c] = -1;
- mempos = _cimg_mp_slot_c + 1;
- variable_pos.assign(8);
- reserved_label.assign(128,1,1,1,~0U);
- // reserved_label[0-32] are used to store the memory index of these variables:
- // [0] = wh, [1] = whd, [2] = whds, [3] = pi, [4] = im, [5] = iM, [6] = ia, [7] = iv, [8] = id,
- // [9] = is, [10] = ip, [11] = ic, [12] = in, [13] = xm, [14] = ym, [15] = zm, [16] = cm, [17] = xM,
- // [18] = yM, [19] = zM, [20] = cM, [21] = i0...[30] = i9, [31] = interpolation, [32] = boundary
- // Compile expression into a sequence of opcodes.
- s_op = ""; ss_op = expr._data;
- const unsigned int ind_result = compile(expr._data,expr._data + expr._width - 1,0,0,0);
- if (!_cimg_mp_is_const_scalar(ind_result)) {
- if (_cimg_mp_is_vector(ind_result))
- CImg<doubleT>(&mem[ind_result] + 1,_cimg_mp_size(ind_result),1,1,1,true).
- fill(cimg::type<double>::nan());
- else if (ind_result!=_cimg_mp_slot_t) mem[ind_result] = cimg::type<double>::nan();
- }
- if (mem._width>=256 && mem._width - mempos>=mem._width/2) mem.resize(mempos,1,1,1,-1);
- result_dim = _cimg_mp_size(ind_result);
- result = mem._data + ind_result;
- // Free resources used for compiling expression and prepare evaluation.
- memtype.assign();
- constcache_vals.assign();
- constcache_inds.assign();
- level.assign();
- variable_pos.assign();
- reserved_label.assign();
- expr.assign();
- pexpr.assign();
- opcode.assign();
- opcode._is_shared = true;
- // Execute begin() block if any specified.
- if (code_begin) {
- mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
- p_code_end = code_begin.end();
- for (p_code = code_begin; p_code<p_code_end; ++p_code) {
- opcode._data = p_code->_data;
- const ulongT target = opcode[1];
- mem[target] = _cimg_mp_defunc(*this);
- }
- }
- p_code_end = code.end();
- }
- _cimg_math_parser():
- code(_code),code_begin_t(_code_begin_t),code_end_t(_code_end_t),
- p_code_end(0),p_break((CImg<ulongT>*)(cimg_ulong)-2),
- imgin(CImg<T>::const_empty()),imgout(CImg<T>::empty()),imglist(CImgList<T>::empty()),
- img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),list_norm(_list_norm),debug_indent(0),
- result_dim(0),result_end_dim(0),break_type(0),constcache_size(0),is_parallelizable(true),
- is_noncritical_run(false),is_fill(false),need_input_copy(false),
- result_end(0),rng(0),calling_function(0) {
- mem.assign(1 + _cimg_mp_slot_c,1,1,1,0); // Allow to skip 'is_empty?' test in operator()()
- result = mem._data;
- }
- _cimg_math_parser(const _cimg_math_parser& mp):
- mem(mp.mem),code(mp.code),code_begin_t(mp.code_begin_t),code_end_t(mp.code_end_t),
- p_code_end(mp.p_code_end),p_break(mp.p_break),
- imgin(mp.imgin),imgout(mp.imgout),imglist(mp.imglist),
- img_stats(mp.img_stats),list_stats(mp.list_stats),list_median(mp.list_median),list_norm(mp.list_norm),
- debug_indent(0),result_dim(mp.result_dim),result_end_dim(mp.result_end_dim),break_type(0),constcache_size(0),
- is_parallelizable(mp.is_parallelizable),is_noncritical_run(mp.is_noncritical_run),is_fill(mp.is_fill),
- need_input_copy(mp.need_input_copy),
- result(mem._data + (mp.result - mp.mem._data)),
- result_end(mp.result_end?mem._data + (mp.result_end - mp.mem._data):0),
- rng((cimg::_rand(),cimg::rng())),calling_function(0) {
- #if cimg_use_openmp!=0
- mem[_cimg_mp_slot_t] = (double)omp_get_thread_num();
- rng+=omp_get_thread_num();
- #endif
- opcode.assign();
- opcode._is_shared = true;
- }
- // Compilation procedure.
- unsigned int compile(char *ss, char *se, const unsigned int depth, unsigned int *const p_ref,
- const unsigned char block_flags) {
- if (depth>256) {
- cimg::strellipsize(expr,64);
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: Call stack overflow (infinite recursion?), "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,
- (ss - 4)>expr._data?ss - 4:expr._data);
- }
- char c1, c2;
- // Simplify expression when possible.
- do {
- c2 = 0;
- if (ss<se) {
- while (*ss && (cimg::is_blank(*ss) || *ss==';')) ++ss; // Remove leading blanks and ';'
- while (se>ss && (cimg::is_blank(c1 = *(se - 1)) || c1==';')) --se; // Remove trailing blanks and ';'
- }
- while (*ss=='(' && *(se - 1)==')' && std::strchr(ss,')')==se - 1) { // Remove useless start/end parentheses
- ++ss; --se; c2 = 1;
- }
- if (*ss=='_' && ss + 1<se && ss[1]=='(') { // Remove leading '_(something)' comment.
- const unsigned int clevel = level[ss - expr._data];
- ss+=2;
- while (ss<se && (*ss!=')' || level[ss - expr._data]!=clevel)) ++ss;
- if (ss<se) ++ss;
- if (ss>=se) return _cimg_mp_slot_nan;
- c2 = 1;
- }
- } while (c2 && ss<se);
- if (se<=ss || !*ss) {
- cimg::strellipsize(expr,64);
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s%s Missing %s, in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
- *s_op=='F'?"argument":"item",
- ss_op);
- }
- static const size_t siz_ref = 7*sizeof(unsigned int);
- const char *const previous_s_op = s_op, *const previous_ss_op = ss_op;
- const unsigned int depth1 = depth + 1;
- unsigned int pos, p1, p2, p3, arg1, arg2, arg3, arg4, arg5, arg6;
- char
- *const se1 = se - 1, *const se2 = se - 2, *const se3 = se - 3,
- *const ss1 = ss + 1, *const ss2 = ss + 2, *const ss3 = ss + 3, *const ss4 = ss + 4,
- *const ss5 = ss + 5, *const ss6 = ss + 6, *const ss7 = ss + 7, *const ss8 = ss + 8,
- *s, *ps, *ns, *s0, *s1, *s2, *s3, sep = 0, end = 0;
- double val = 0, val1, val2;
- mp_func op;
- return_new_comp = false;
- // Bits of 'block_flags' tell about in which code block we currently are:
- // 0: critical(), 1: begin(), 2: begin_t(), 3: end(), 4: end_t().
- const bool
- is_inside_critical = (bool)(block_flags&1),
- is_inside_begin = (bool)(block_flags&2),
- is_inside_begin_t = (bool)(block_flags&4),
- is_inside_end = (bool)(block_flags&8),
- is_inside_end_t = (bool)(block_flags&16);
- // 'p_ref' is a 'unsigned int[7]' used to return a reference to an image or vector value
- // linked to the returned memory slot (reference that cannot be determined at compile time).
- // p_ref[0] can be { 0 = scalar (unlinked) | 1 = vector value | 2 = image value (offset) |
- // 3 = image value (coordinates) | 4 = image value as a vector (offset) |
- // 5 = image value as a vector (coordinates) }.
- // Depending on p_ref[0], the remaining p_ref[k] have the following meaning:
- // When p_ref[0]==0, p_ref is actually unlinked.
- // When p_ref[0]==1, p_ref = [ 1, vector_ind, offset ].
- // When p_ref[0]==2, p_ref = [ 2, image_ind (or ~0U), is_relative, offset ].
- // When p_ref[0]==3, p_ref = [ 3, image_ind (or ~0U), is_relative, x, y, z, c ].
- // When p_ref[0]==4, p_ref = [ 4, image_ind (or ~0U), is_relative, offset ].
- // When p_ref[0]==5, p_ref = [ 5, image_ind (or ~0U), is_relative, x, y, z ].
- if (p_ref) { *p_ref = 0; p_ref[1] = p_ref[2] = p_ref[3] = p_ref[4] = p_ref[5] = p_ref[6] = ~0U; }
- const char saved_char = *se; *se = 0;
- const unsigned int clevel = level[ss - expr._data], clevel1 = clevel + 1;
- bool is_sth, is_relative;
- CImg<uintT> ref;
- CImg<charT> variable_name;
- CImgList<ulongT> l_opcode;
- // Look for a single value or a pre-defined variable.
- int nb = 0;
- s = ss + (*ss=='+' || *ss=='-'?1:0);
- if (*s=='i' || *s=='I' || *s=='n' || *s=='N') { // Particular cases : +/-NaN and +/-Inf
- is_sth = *ss=='-';
- if (!cimg::strcasecmp(s,"inf")) { val = cimg::type<double>::inf(); nb = 1; }
- else if (!cimg::strcasecmp(s,"nan")) { val = cimg::type<double>::nan(); nb = 1; }
- if (nb==1 && is_sth) val = -val;
- } else if (*s=='0' && (s[1]=='x' || s[1]=='X')) { // Hexadecimal number
- is_sth = *ss=='-';
- if (cimg_sscanf(s + 2,"%x%c",&arg1,&sep)==1) {
- nb = 1;
- val = (double)arg1;
- if (is_sth) val = -val;
- }
- }
- if (!nb) nb = cimg_sscanf(ss,"%lf%c%c",&val,&(sep=0),&(end=0));
- if (nb==1) _cimg_mp_const_scalar(val);
- if (nb==2 && sep=='%') _cimg_mp_const_scalar(val/100);
- if (ss1==se) switch (*ss) { // One-char reserved variable
- case 'c' : _cimg_mp_return(reserved_label[(int)'c']!=~0U?reserved_label[(int)'c']:_cimg_mp_slot_c);
- case 'd' : _cimg_mp_return(reserved_label[(int)'d']!=~0U?reserved_label[(int)'d']:20);
- case 'e' : _cimg_mp_return(reserved_label[(int)'e']!=~0U?reserved_label[(int)'e']:27);
- case 'h' : _cimg_mp_return(reserved_label[(int)'h']!=~0U?reserved_label[(int)'h']:19);
- case 'k' :
- if (reserved_label[(int)'k']!=~0U) _cimg_mp_return(reserved_label[(int)'k']);
- pos = get_mem_img_index();
- if (pos!=~0U) _cimg_mp_return(pos);
- _cimg_mp_return_nan();
- case 'l' : _cimg_mp_return(reserved_label[(int)'l']!=~0U?reserved_label[(int)'l']:26);
- case 'r' : _cimg_mp_return(reserved_label[(int)'r']!=~0U?reserved_label[(int)'r']:22);
- case 's' : _cimg_mp_return(reserved_label[(int)'s']!=~0U?reserved_label[(int)'s']:21);
- case 't' : _cimg_mp_return(reserved_label[(int)'t']!=~0U?reserved_label[(int)'t']:_cimg_mp_slot_t);
- case 'n' :
- if (reserved_label[(int)'n']!=~0U) _cimg_mp_return(reserved_label[(int)'n']);
- #if cimg_use_openmp!=0
- _cimg_mp_const_scalar((double)omp_get_max_threads());
- #else
- _cimg_mp_return(1);
- #endif
- case 'w' : _cimg_mp_return(reserved_label[(int)'w']!=~0U?reserved_label[(int)'w']:18);
- case 'x' : _cimg_mp_return(reserved_label[(int)'x']!=~0U?reserved_label[(int)'x']:_cimg_mp_slot_x);
- case 'y' : _cimg_mp_return(reserved_label[(int)'y']!=~0U?reserved_label[(int)'y']:_cimg_mp_slot_y);
- case 'z' : _cimg_mp_return(reserved_label[(int)'z']!=~0U?reserved_label[(int)'z']:_cimg_mp_slot_z);
- case 'u' :
- if (reserved_label[(int)'u']!=~0U) _cimg_mp_return(reserved_label[(int)'u']);
- _cimg_mp_scalar2(mp_u,0,1);
- case 'v' :
- if (reserved_label[(int)'v']!=~0U) _cimg_mp_return(reserved_label[(int)'v']);
- _cimg_mp_scalar2(mp_u,11,1);
- case 'g' :
- if (reserved_label[(int)'g']!=~0U) _cimg_mp_return(reserved_label[(int)'g']);
- _cimg_mp_scalar0(mp_g);
- case 'i' :
- if (reserved_label[(int)'i']!=~0U) _cimg_mp_return(reserved_label[(int)'i']);
- _cimg_mp_scalar0(mp_i);
- case 'I' :
- _cimg_mp_op("Variable 'I'");
- if (reserved_label[(int)'I']!=~0U) _cimg_mp_return(reserved_label[(int)'I']);
- if (!imgin._spectrum) _cimg_mp_return(0);
- need_input_copy = true;
- pos = vector(imgin._spectrum);
- CImg<ulongT>::vector((ulongT)mp_Joff,pos,0,0,imgin._spectrum).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- case 'R' :
- if (reserved_label[(int)'R']!=~0U) _cimg_mp_return(reserved_label[(int)'R']);
- need_input_copy = true;
- _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0,0,0);
- case 'G' :
- if (reserved_label[(int)'G']!=~0U) _cimg_mp_return(reserved_label[(int)'G']);
- need_input_copy = true;
- _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1,0,0);
- case 'B' :
- if (reserved_label[(int)'B']!=~0U) _cimg_mp_return(reserved_label[(int)'B']);
- need_input_copy = true;
- _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2,0,0);
- case 'A' :
- if (reserved_label[(int)'A']!=~0U) _cimg_mp_return(reserved_label[(int)'A']);
- need_input_copy = true;
- _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3,0,0);
- }
- else if (ss2==se) { // Two-chars reserved variable
- arg1 = arg2 = ~0U;
- if (*ss=='w' && *ss1=='h') // wh
- _cimg_mp_return(reserved_label[0]!=~0U?reserved_label[0]:23);
- if (*ss=='p' && *ss1=='i') // pi
- _cimg_mp_return(reserved_label[3]!=~0U?reserved_label[3]:28);
- if (*ss=='i') {
- if (*ss1>='0' && *ss1<='9') { // i0...i9
- pos = 21 + *ss1 - '0';
- if (reserved_label[pos]!=~0U) _cimg_mp_return(reserved_label[pos]);
- need_input_copy = true;
- _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,pos - 21,0,0);
- }
- switch (*ss1) {
- case 'a' : arg1 = 6; arg2 = 2; break; // ia
- case 'c' : // ic
- if (reserved_label[11]!=~0U) _cimg_mp_return(reserved_label[11]);
- if (mem_img_median==~0U) mem_img_median = imgin?const_scalar(imgin.median()):0;
- _cimg_mp_return(mem_img_median);
- break;
- case 'd' : arg1 = 8; arg2 = 3; break; // id
- case 'm' : arg1 = 4; arg2 = 0; break; // im
- case 'M' : arg1 = 5; arg2 = 1; break; // iM
- case 'n' : // in
- if (reserved_label[12]!=~0U) _cimg_mp_return(reserved_label[12]);
- if (mem_img_norm==~0U) mem_img_norm = imgin?const_scalar(imgin.magnitude(2)):0;
- _cimg_mp_return(mem_img_norm);
- break;
- case 'p' : arg1 = 10; arg2 = 13; break; // ip
- case 's' : arg1 = 9; arg2 = 12; break; // is
- case 'v' : arg1 = 7; arg2 = 3; break; // iv
- }
- }
- else if (*ss1=='m') switch (*ss) {
- case 'x' : arg1 = 13; arg2 = 4; break; // xm
- case 'y' : arg1 = 14; arg2 = 5; break; // ym
- case 'z' : arg1 = 15; arg2 = 6; break; // zm
- case 'c' : arg1 = 16; arg2 = 7; break; // cm
- }
- else if (*ss1=='M') switch (*ss) {
- case 'x' : arg1 = 17; arg2 = 8; break; // xM
- case 'y' : arg1 = 18; arg2 = 9; break; // yM
- case 'z' : arg1 = 19; arg2 = 10; break; // zM
- case 'c' : arg1 = 20; arg2 = 11; break; // cM
- }
- if (arg1!=~0U) {
- if (reserved_label[arg1]!=~0U) _cimg_mp_return(reserved_label[arg1]);
- if (!img_stats) {
- img_stats.assign(1,14,1,1,0).fill(imgin.get_stats(),false);
- mem_img_stats.assign(1,14,1,1,~0U);
- }
- if (mem_img_stats[arg2]==~0U) mem_img_stats[arg2] = const_scalar(img_stats[arg2]);
- if (arg1==8) _cimg_mp_const_scalar(std::sqrt(img_stats[arg2])); // id: std variation
- _cimg_mp_return(mem_img_stats[arg2]);
- }
- } else if (ss3==se) { // Three-chars reserved variable
- if (*ss=='w' && *ss1=='h' && *ss2=='d') // whd
- _cimg_mp_return(reserved_label[1]!=~0U?reserved_label[1]:24);
- } else if (ss4==se) { // Four-chars reserved variable
- if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s') // whds
- _cimg_mp_return(reserved_label[2]!=~0U?reserved_label[2]:25);
- }
- pos = ~0U;
- is_sth = false;
- for (s0 = ss, s = ss1; s<se1; ++s)
- if (*s==';' && level[s - expr._data]==clevel) { // Separator ';'
- is_end_code = false;
- arg1 = compile(s0,s++,depth,0,block_flags);
- if (!is_end_code) pos = arg1; // 'end()' and 'end_t()' return void
- is_sth = true;
- while (*s && (cimg::is_blank(*s) || *s==';')) ++s;
- s0 = s;
- }
- if (is_sth) {
- is_end_code = false;
- arg1 = compile(s0,se,depth,p_ref,block_flags);
- if (!is_end_code) pos = arg1; // 'end()' and 'end_t()' return void
- _cimg_mp_return(pos!=~0U?pos:_cimg_mp_slot_nan);
- }
- // Declare / assign variable, vector value or image value.
- for (s = ss1, ps = ss, ns = ss2; s<se1; ++s, ++ps, ++ns)
- if (*s=='=' && *ns!='=' && *ps!='=' && *ps!='>' && *ps!='<' && *ps!='!' &&
- *ps!='+' && *ps!='-' && *ps!='*' && *ps!='/' && *ps!='%' &&
- *ps!='>' && *ps!='<' && *ps!='&' && *ps!='|' && *ps!='^' &&
- level[s - expr._data]==clevel) {
- variable_name.assign(ss,(unsigned int)(s + 1 - ss)).back() = 0;
- cimg::strpare(variable_name,false,true);
- const unsigned int l_variable_name = (unsigned int)std::strlen(variable_name);
- char *const ve1 = ss + l_variable_name - 1;
- _cimg_mp_op("Operator '='");
- // Assign image value (direct).
- if (l_variable_name>2 && (*ss=='i' || *ss=='j' || *ss=='I' || *ss=='J') && (*ss1=='(' || *ss1=='[') &&
- (reserved_label[(int)*ss]==~0U || *ss1=='(' || !_cimg_mp_is_vector(reserved_label[(int)*ss]))) {
- is_relative = *ss=='j' || *ss=='J';
- if (*ss1=='[' && *ve1==']') { // i/j/I/J[_#ind,offset] = value
- if (!is_inside_critical) is_parallelizable = false;
- if (*ss2=='#') { // Index specified
- s0 = ss3; while (s0<ve1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- p1 = compile(ss3,s0++,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- _cimg_mp_check_list();
- } else { p1 = ~0U; s0 = ss2; }
- arg1 = compile(s0,ve1,depth1,0,block_flags); // Offset
- _cimg_mp_check_type(arg1,0,1,0);
- arg2 = compile(s + 1,se,depth1,0,block_flags); // Value to assign
- _cimg_mp_check_type(arg2,2,*ss>='i'?1:3,0);
- if (_cimg_mp_is_vector(arg2)) {
- if (p1!=~0U) {
- _cimg_mp_check_const_index(p1);
- p3 = (unsigned int)cimg::mod((int)mem[p1],imglist.width());
- p2 = imglist[p3]._spectrum;
- } else p2 = imgin._spectrum;
- if (!p2) _cimg_mp_return(0);
- _cimg_mp_check_type(arg2,2,2,p2);
- } else p2 = 0;
- if (p_ref) {
- *p_ref = *ss=='I' || *ss=='J'?4:2;
- p_ref[1] = p1;
- p_ref[2] = (unsigned int)is_relative;
- p_ref[3] = arg1;
- if (_cimg_mp_is_vector(arg2))
- set_reserved_vector(arg2); // Prevent from being used in further optimization
- else if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1;
- if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1;
- }
- if (p1!=~0U) {
- if (!imglist) _cimg_mp_return(arg2);
- if (*ss>='i')
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
- arg2,p1,arg1).move_to(code);
- else if (_cimg_mp_is_scalar(arg2))
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s),
- arg2,p1,arg1).move_to(code);
- else
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
- arg2,p1,arg1,_cimg_mp_size(arg2)).move_to(code);
- } else {
- if (!imgout) _cimg_mp_return(arg2);
- if (*ss>='i')
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
- arg2,arg1).move_to(code);
- else if (_cimg_mp_is_scalar(arg2))
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s),
- arg2,arg1).move_to(code);
- else
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
- arg2,arg1,_cimg_mp_size(arg2)).move_to(code);
- }
- _cimg_mp_return(arg2);
- }
- if (*ss1=='(' && *ve1==')') { // i/j/I/J(_#ind,_x,_y,_z,_c) = value
- if (!is_inside_critical) is_parallelizable = false;
- if (*ss2=='#') { // Index specified
- s0 = ss3; while (s0<ve1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- p1 = compile(ss3,s0++,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- _cimg_mp_check_list();
- } else { p1 = ~0U; s0 = ss2; }
- arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
- arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
- arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
- arg4 = is_relative?0U:(unsigned int)_cimg_mp_slot_c;
- arg5 = compile(s + 1,se,depth1,0,block_flags); // Value to assign
- _cimg_mp_check_type(arg5,2,*ss>='i'?1:3,0);
- if (s0<ve1) { // X or [ X,_Y,_Z,_C ]
- s1 = s0; while (s1<ve1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(s0,s1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
- p2 = _cimg_mp_size(arg1); // Vector size
- ++arg1;
- if (p2>1) {
- arg2 = arg1 + 1;
- if (p2>2) {
- arg3 = arg2 + 1;
- if (p2>3) arg4 = arg3 + 1;
- }
- }
- } else if (s1<ve1) { // Y
- s2 = ++s1; while (s2<ve1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,0,block_flags);
- if (s2<ve1) { // Z
- s3 = ++s2; while (s3<ve1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
- arg3 = compile(s2,s3,depth1,0,block_flags);
- if (s3<ve1) arg4 = compile(++s3,ve1,depth1,0,block_flags); // C
- }
- }
- }
- if (_cimg_mp_is_vector(arg5)) {
- if (p1!=~0U) {
- _cimg_mp_check_const_index(p1);
- p3 = (unsigned int)cimg::mod((int)mem[p1],imglist.width());
- p2 = imglist[p3]._spectrum;
- } else p2 = imgin._spectrum;
- if (!p2) _cimg_mp_return(0);
- _cimg_mp_check_type(arg5,2,2,p2);
- } else p2 = 0;
- if (p_ref) {
- *p_ref = *ss=='I' || *ss=='J'?5:3;
- p_ref[1] = p1;
- p_ref[2] = (unsigned int)is_relative;
- p_ref[3] = arg1;
- p_ref[4] = arg2;
- p_ref[5] = arg3;
- p_ref[6] = arg4;
- if (_cimg_mp_is_vector(arg5))
- set_reserved_vector(arg5); // Prevent from being used in further optimization
- else if (_cimg_mp_is_comp(arg5)) memtype[arg5] = -1;
- if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -1;
- if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1;
- if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1;
- if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1;
- if (_cimg_mp_is_comp(arg4)) memtype[arg4] = -1;
- }
- if (p1!=~0U) {
- if (!imglist) _cimg_mp_return(arg5);
- if (*ss>='i')
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
- arg5,p1,arg1,arg2,arg3,arg4).move_to(code);
- else if (_cimg_mp_is_scalar(arg5))
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s),
- arg5,p1,arg1,arg2,arg3).move_to(code);
- else
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
- arg5,p1,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code);
- } else {
- if (!imgout) _cimg_mp_return(arg5);
- if (*ss>='i')
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
- arg5,arg1,arg2,arg3,arg4).move_to(code);
- else if (_cimg_mp_is_scalar(arg5))
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s),
- arg5,arg1,arg2,arg3).move_to(code);
- else
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
- arg5,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code);
- }
- _cimg_mp_return(arg5);
- }
- }
- // Assign vector value (direct).
- if (l_variable_name>3 && *ve1==']' && *ss!='[') {
- s0 = ve1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0;
- if (s0>ss && cimg::is_varname(ss,s0 - ss)) {
- variable_name[s0 - ss] = 0; // Remove brackets in variable name
- get_variable_pos(variable_name,arg1,arg2);
- arg1 = arg2!=~0U?reserved_label[arg2]:arg1!=~0U?variable_pos[arg1]:~0U; // Vector slot
- if (arg1==~0U || _cimg_mp_is_scalar(arg1))
- compile(ss,s0,depth1,0,block_flags); // Variable does not exist or is not a vector -> error
- arg2 = compile(++s0,ve1,depth1,0,block_flags); // Index
- arg3 = compile(s + 1,se,depth1,0,block_flags); // Value to assign
- _cimg_mp_check_type(arg3,2,1,0);
- if (_cimg_mp_is_const_scalar(arg2)) { // Constant index -> return corresponding variable slot directly
- nb = (int)mem[arg2];
- if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) {
- arg1+=nb + 1;
- CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg3).move_to(code);
- _cimg_mp_return(arg1);
- }
- compile(ss,s,depth1,0,block_flags); // Out-of-bounds reference -> error
- }
- // Case of non-constant index -> return assigned value + linked reference
- if (p_ref) {
- *p_ref = 1;
- p_ref[1] = arg1;
- p_ref[2] = arg2;
- if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1; // Prevent from being used in further optimization
- if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1;
- }
- CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg3,arg1,(ulongT)_cimg_mp_size(arg1),arg2).
- move_to(code);
- _cimg_mp_return(arg3);
- }
- }
- // Assign user-defined macro.
- if (l_variable_name>2 && *ve1==')' && *ss!='(') {
- s0 = ve1; while (s0>ss && *s0!='(') --s0;
- if (cimg::is_varname(ss,s0 - ss) && std::strncmp(variable_name,"debug(",6) &&
- std::strncmp(variable_name,"print(",6)) { // Valid macro name
- s0 = variable_name._data + (s0 - ss);
- *s0 = 0;
- s1 = variable_name._data + l_variable_name - 1; // Pointer to closing parenthesis
- CImg<charT>(variable_name._data,(unsigned int)(s0 - variable_name._data + 1)).move_to(macro_def,0);
- ++s; while (*s && cimg::is_blank(*s)) ++s;
- CImg<charT>(s,(unsigned int)(se - s + 1)).move_to(macro_body,0);
- bool is_variadic = false;
- p1 = 1; // Index of current parsed argument
- for (s = s0 + 1; s<=s1; ++p1, s = ns + 1) { // Parse function arguments
- if (is_variadic && p1>1) {
- _cimg_mp_strerr;
- cimg::strellipsize(variable_name,64);
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Multiple arguments not allowed when first one is "
- "variadic, in macro definition '%s()', in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- variable_name._data,s0);
- }
- if (p1>24) {
- _cimg_mp_strerr;
- cimg::strellipsize(variable_name,64);
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Too much specified arguments (>24), in macro "
- "definition '%s()', in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- variable_name._data,s0);
- }
- while (*s && cimg::is_blank(*s)) ++s;
- if (*s==')' && p1==1) break; // Function has no arguments
- s2 = s; // Start of the argument name
- is_sth = true; // is_valid_argument_name?
- if (*s2>='0' && *s2<='9') is_sth = false;
- else for (ns = s2; ns<s1 && *ns!=',' && *ns!='.' && !cimg::is_blank(*ns); ++ns)
- if (!cimg::is_varchar(*ns)) { is_sth = false; break; }
- s3 = ns; // End of the argument name
- if (is_sth && ns>s2 && *ns=='.' && ns[1]=='.' && ns[2]=='.') { is_variadic = true; ns+=3; }
- else if (*ns=='.') is_sth = false;
- while (*ns && cimg::is_blank(*ns)) ++ns;
- if (!is_sth || s2==s3 || (*ns!=',' && ns!=s1)) {
- _cimg_mp_strerr;
- cimg::strellipsize(variable_name,64);
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: %s name specified for argument %u when defining "
- "macro '%s()', in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- is_sth?"Empty":"Invalid",p1,
- variable_name._data,s0);
- }
- if (ns==s1 || *ns==',' || (is_variadic && *ns=='.')) { // New argument found
- *s3 = 0;
- p2 = (unsigned int)(s3 - s2); // Argument length
- for (ps = std::strstr(macro_body[0],s2); ps; ps = std::strstr(ps,s2)) { // Replace by arg number
- if (!((ps>macro_body[0]._data && cimg::is_varchar(*(ps - 1))) ||
- (ps + p2<macro_body[0].end() && cimg::is_varchar(*(ps + p2))))) {
- if (ps>macro_body[0]._data && *(ps - 1)=='#') { // Remove pre-number sign
- *(ps - 1) = (char)p1;
- if (ps + p2<macro_body[0].end() && *(ps + p2)=='#') { // Has pre & post number signs
- std::memmove(ps,ps + p2 + 1,macro_body[0].end() - ps - p2 - 1);
- macro_body[0]._width-=p2 + 1;
- } else { // Has pre number sign only
- std::memmove(ps,ps + p2,macro_body[0].end() - ps - p2);
- macro_body[0]._width-=p2;
- }
- } else if (ps + p2<macro_body[0].end() && *(ps + p2)=='#') { // Remove post-number sign
- *(ps++) = (char)p1;
- std::memmove(ps,ps + p2,macro_body[0].end() - ps - p2);
- macro_body[0]._width-=p2;
- } else if (is_variadic) { // Replace variadic argument
- *(ps++) = (char)p1;
- std::memmove(ps,ps + p2 - 1,macro_body[0].end() - ps - p2 + 1);
- macro_body[0]._width-=p2 - 1;
- } else { // Not near a number sign
- if (p2<3) {
- ps-=(ulongT)macro_body[0]._data;
- macro_body[0].resize(macro_body[0]._width - p2 + 3,1,1,1,0);
- ps+=(ulongT)macro_body[0]._data;
- } else macro_body[0]._width-=p2 - 3;
- std::memmove(ps + 3,ps + p2,macro_body[0].end() - ps - 3);
- *(ps++) = '(';
- *(ps++) = (char)p1;
- *(ps++) = ')';
- }
- } else ++ps;
- }
- }
- }
- // Store number of arguments.
- macro_def[0].resize(macro_def[0]._width + 1,1,1,1,0).back() = is_variadic?(char)-1:(char)(p1 - 1);
- _cimg_mp_return_nan();
- }
- }
- // Check if the variable name could be valid. If not, this is probably an lvalue assignment.
- const bool is_const = l_variable_name>6 && !std::strncmp(variable_name,"const ",6);
- s0 = variable_name._data;
- if (is_const) {
- s0+=6; while (cimg::is_blank(*s0)) ++s0;
- variable_name.resize(variable_name.end() - s0,1,1,1,0,0,1);
- }
- if (cimg::is_varname(variable_name)) { // Valid variable name
- // Assign variable (direct).
- get_variable_pos(variable_name,arg1,arg2);
- arg3 = compile(s + 1,se,depth1,0,block_flags);
- is_sth = return_new_comp; // is arg3 a new blank object?
- if (is_const) _cimg_mp_check_const_scalar(arg3,2,0);
- arg1 = arg2!=~0U?reserved_label[arg2]:arg1!=~0U?variable_pos[arg1]:~0U;
- if (arg1==~0U) { // Create new variable
- if (_cimg_mp_is_vector(arg3)) { // Vector variable
- arg1 = is_sth || is_comp_vector(arg3)?arg3:vector_copy(arg3);
- set_reserved_vector(arg1); // Prevent from being used in further optimization
- } else { // Scalar variable
- if (is_const) arg1 = arg3;
- else {
- arg1 = is_sth || _cimg_mp_is_comp(arg3)?arg3:scalar1(mp_copy,arg3);
- memtype[arg1] = -1;
- }
- }
- if (arg2!=~0U) reserved_label[arg2] = arg1;
- else {
- if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0);
- variable_pos[variable_def._width] = arg1;
- variable_name.move_to(variable_def);
- }
- } else { // Variable already exists -> assign a new value
- if (is_const || _cimg_mp_is_const_scalar(arg1)) {
- _cimg_mp_strerr;
- cimg::strellipsize(variable_name,64);
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Invalid assignment of %sconst variable '%s'%s, "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- _cimg_mp_is_const_scalar(arg1)?"":"non-",
- variable_name._data,
- !_cimg_mp_is_const_scalar(arg1) && is_const?" as a const variable":"",
- s0);
- }
- _cimg_mp_check_type(arg3,2,_cimg_mp_is_vector(arg1)?3:1,_cimg_mp_size(arg1));
- if (_cimg_mp_is_vector(arg1)) { // Vector
- if (_cimg_mp_is_vector(arg3)) // From vector
- CImg<ulongT>::vector((ulongT)mp_vector_copy,arg1,arg3,(ulongT)_cimg_mp_size(arg1)).
- move_to(code);
- else // From scalar
- CImg<ulongT>::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg3).
- move_to(code);
- } else // Scalar
- CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg3).move_to(code);
- }
- return_new_comp = false;
- _cimg_mp_return(arg1);
- }
- // Assign lvalue (variable name was not valid for a direct assignment).
- arg1 = ~0U;
- is_sth = (bool)std::strchr(variable_name,'?'); // Contains_ternary_operator?
- if (is_sth) break; // Do nothing and make ternary operator priority over assignment
- if (l_variable_name>2 && (std::strchr(variable_name,'(') || std::strchr(variable_name,'['))) {
- ref.assign(7);
- arg1 = compile(ss,s,depth1,ref,block_flags); // Lvalue slot
- arg2 = compile(s + 1,se,depth1,0,block_flags); // Value to assign
- if (*ref==1) { // Vector value (scalar): V[k] = scalar
- _cimg_mp_check_type(arg2,2,1,0);
- arg3 = ref[1]; // Vector slot
- arg4 = ref[2]; // Index
- if (p_ref) std::memcpy(p_ref,ref,siz_ref);
- CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg2,arg3,(ulongT)_cimg_mp_size(arg3),arg4).
- move_to(code);
- _cimg_mp_return(arg2);
- }
- if (*ref==2) { // Image value (scalar): i/j[_#ind,off] = scalar
- if (!is_inside_critical) is_parallelizable = false;
- _cimg_mp_check_type(arg2,2,1,0);
- p1 = ref[1]; // Index
- is_relative = (bool)ref[2];
- arg3 = ref[3]; // Offset
- if (p_ref) std::memcpy(p_ref,ref,siz_ref);
- if (p1!=~0U) {
- if (!imglist) _cimg_mp_return(arg2);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
- arg2,p1,arg3).move_to(code);
- } else {
- if (!imgout) _cimg_mp_return(arg2);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
- arg2,arg3).move_to(code);
- }
- _cimg_mp_return(arg2);
- }
- if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) = scalar
- if (!is_inside_critical) is_parallelizable = false;
- _cimg_mp_check_type(arg2,2,1,0);
- p1 = ref[1]; // Index
- is_relative = (bool)ref[2];
- arg3 = ref[3]; // X
- arg4 = ref[4]; // Y
- arg5 = ref[5]; // Z
- arg6 = ref[6]; // C
- if (p_ref) std::memcpy(p_ref,ref,siz_ref);
- if (p1!=~0U) {
- if (!imglist) _cimg_mp_return(arg2);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
- arg2,p1,arg3,arg4,arg5,arg6).move_to(code);
- } else {
- if (!imgout) _cimg_mp_return(arg2);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
- arg2,arg3,arg4,arg5,arg6).move_to(code);
- }
- _cimg_mp_return(arg2);
- }
- if (*ref==4) { // Image value (vector): I/J[_#ind,off] = value
- if (!is_inside_critical) is_parallelizable = false;
- _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
- p1 = ref[1]; // Index
- is_relative = (bool)ref[2];
- arg3 = ref[3]; // Offset
- if (p_ref) std::memcpy(p_ref,ref,siz_ref);
- if (p1!=~0U) {
- if (!imglist) _cimg_mp_return(arg2);
- if (_cimg_mp_is_scalar(arg2))
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s),
- arg2,p1,arg3).move_to(code);
- else {
- _cimg_mp_check_const_index(p1);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
- arg2,p1,arg3,_cimg_mp_size(arg2)).move_to(code);
- }
- } else {
- if (!imgout) _cimg_mp_return(arg2);
- if (_cimg_mp_is_scalar(arg2))
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s),
- arg2,arg3).move_to(code);
- else
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
- arg2,arg3,_cimg_mp_size(arg2)).move_to(code);
- }
- _cimg_mp_return(arg2);
- }
- if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) = value
- if (!is_inside_critical) is_parallelizable = false;
- _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
- p1 = ref[1]; // Index
- is_relative = (bool)ref[2];
- arg3 = ref[3]; // X
- arg4 = ref[4]; // Y
- arg5 = ref[5]; // Z
- if (p_ref) std::memcpy(p_ref,ref,siz_ref);
- if (p1!=~0U) {
- if (!imglist) _cimg_mp_return(arg2);
- if (_cimg_mp_is_scalar(arg2))
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s),
- arg2,p1,arg3,arg4,arg5).move_to(code);
- else {
- _cimg_mp_check_const_index(p1);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
- arg2,p1,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code);
- }
- } else {
- if (!imgout) _cimg_mp_return(arg2);
- if (_cimg_mp_is_scalar(arg2))
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s),
- arg2,arg3,arg4,arg5).move_to(code);
- else
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
- arg2,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code);
- }
- _cimg_mp_return(arg2);
- }
- if (_cimg_mp_is_vector(arg1)) { // Vector variable: V = value
- _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
- if (_cimg_mp_is_vector(arg2)) // From vector
- CImg<ulongT>::vector((ulongT)mp_vector_copy,arg1,arg2,(ulongT)_cimg_mp_size(arg1)).
- move_to(code);
- else // From scalar
- CImg<ulongT>::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg2).
- move_to(code);
- _cimg_mp_return(arg1);
- }
- if (_cimg_mp_is_reserved(arg1)) { // Scalar variable: s = scalar
- _cimg_mp_check_type(arg2,2,1,0);
- CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg2).move_to(code);
- _cimg_mp_return(arg1);
- }
- }
- // No assignment expressions match -> error
- _cimg_mp_strerr;
- cimg::strellipsize(variable_name,64);
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Invalid %slvalue '%s', "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- arg1!=~0U && _cimg_mp_is_const_scalar(arg1)?"const ":"",
- variable_name._data,s0);
- }
- // Apply unary/binary/ternary operators. The operator precedences should be the same as in C++.
- for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1
- if (*s=='=' && (*ps=='*' || *ps=='/' || *ps=='^') && *ns==*ps &&
- level[s - expr._data]==clevel) { // Self-operators for complex numbers only (**=,//=,^^=)
- _cimg_mp_op(*ps=='*'?"Operator '**='":*ps=='/'?"Operator '//='":"Operator '^^='");
- ref.assign(7);
- arg1 = compile(ss,ns,depth1,ref,block_flags); // Vector slot
- arg2 = compile(s + 1,se,depth1,0,block_flags); // Right operand
- _cimg_mp_check_type(arg1,1,2,2);
- _cimg_mp_check_type(arg2,2,3,2);
- if (_cimg_mp_is_vector(arg2)) { // Complex **= complex
- if (*ps=='*')
- CImg<ulongT>::vector((ulongT)mp_complex_mul,arg1,arg1,arg2).move_to(code);
- else if (*ps=='/')
- CImg<ulongT>::vector((ulongT)mp_complex_div_vv,arg1,arg1,arg2).move_to(code);
- else
- CImg<ulongT>::vector((ulongT)mp_complex_pow_vv,arg1,arg1,arg2).move_to(code);
- } else { // Complex **= scalar
- if (*ps=='*') {
- if (arg2==1) _cimg_mp_return(arg1);
- self_vector_s(arg1,mp_self_mul,arg2);
- } else if (*ps=='/') {
- if (arg2==1) _cimg_mp_return(arg1);
- self_vector_s(arg1,mp_self_div,arg2);
- } else {
- if (arg2==1) _cimg_mp_return(arg1);
- CImg<ulongT>::vector((ulongT)mp_complex_pow_vs,arg1,arg1,arg2).move_to(code);
- }
- }
- if (*ref==4) { // Image value (vector): I/J[_#ind,off] **= value
- if (!is_inside_critical) is_parallelizable = false;
- p1 = ref[1]; // Index
- is_relative = (bool)ref[2];
- arg3 = ref[3]; // Offset
- if (p_ref) std::memcpy(p_ref,ref,siz_ref);
- if (p1!=~0U) {
- if (!imglist) _cimg_mp_return(arg1);
- _cimg_mp_check_const_index(p1);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
- arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
- } else {
- if (!imgout) _cimg_mp_return(arg1);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
- arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
- }
- } else if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) **= value
- if (!is_inside_critical) is_parallelizable = false;
- p1 = ref[1]; // Index
- is_relative = (bool)ref[2];
- arg3 = ref[3]; // X
- arg4 = ref[4]; // Y
- arg5 = ref[5]; // Z
- if (p_ref) std::memcpy(p_ref,ref,siz_ref);
- if (p1!=~0U) {
- if (!imglist) _cimg_mp_return(arg1);
- _cimg_mp_check_const_index(p1);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
- arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
- } else {
- if (!imgout) _cimg_mp_return(arg1);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
- arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
- }
- }
- _cimg_mp_return(arg1);
- }
- for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1
- if (*s=='=' && (*ps=='+' || *ps=='-' || *ps=='*' || *ps=='/' || (*ps=='%' && s[1]!='=') ||
- *ps=='&' || *ps=='^' || *ps=='|' ||
- (*ps=='>' && *ns=='>') || (*ps=='<' && *ns=='<')) &&
- level[s - expr._data]==clevel) { // Self-operators (+=,-=,*=,/=,%=,>>=,<<=,&=,^=,|=)
- switch (*ps) {
- case '+' : op = mp_self_add; _cimg_mp_op("Operator '+='"); break;
- case '-' : op = mp_self_sub; _cimg_mp_op("Operator '-='"); break;
- case '*' : op = mp_self_mul; _cimg_mp_op("Operator '*='"); break;
- case '/' : op = mp_self_div; _cimg_mp_op("Operator '/='"); break;
- case '%' : op = mp_self_modulo; _cimg_mp_op("Operator '%='"); break;
- case '<' : op = mp_self_bitwise_left_shift; _cimg_mp_op("Operator '<<='"); break;
- case '>' : op = mp_self_bitwise_right_shift; _cimg_mp_op("Operator '>>='"); break;
- case '&' : op = mp_self_bitwise_and; _cimg_mp_op("Operator '&='"); break;
- case '|' : op = mp_self_bitwise_or; _cimg_mp_op("Operator '|='"); break;
- default : op = mp_self_pow; _cimg_mp_op("Operator '^='"); break;
- }
- s1 = *ps=='>' || *ps=='<'?ns:ps;
- ref.assign(7);
- arg1 = compile(ss,s1,depth1,ref,block_flags); // Variable slot
- arg2 = compile(s + 1,se,depth1,0,block_flags); // Value to apply
- // Check for particular case to be simplified.
- if ((op==mp_self_add || op==mp_self_sub) && !arg2) _cimg_mp_return(arg1);
- if ((op==mp_self_mul || op==mp_self_div) && arg2==1) _cimg_mp_return(arg1);
- // Apply operator on a copy to prevent modifying a constant or a variable.
- if (*ref && (_cimg_mp_is_const_scalar(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_reserved(arg1))) {
- if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1);
- else arg1 = scalar1(mp_copy,arg1);
- }
- if (*ref==1) { // Vector value (scalar): V[k] += scalar
- _cimg_mp_check_type(arg2,2,1,0);
- arg3 = ref[1]; // Vector slot
- arg4 = ref[2]; // Index
- if (p_ref) std::memcpy(p_ref,ref,siz_ref);
- CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
- CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4).
- move_to(code);
- _cimg_mp_return(arg1);
- }
- if (*ref==2) { // Image value (scalar): i/j[_#ind,off] += scalar
- if (!is_inside_critical) is_parallelizable = false;
- _cimg_mp_check_type(arg2,2,1,0);
- p1 = ref[1]; // Index
- is_relative = (bool)ref[2];
- arg3 = ref[3]; // Offset
- if (p_ref) std::memcpy(p_ref,ref,siz_ref);
- CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
- if (p1!=~0U) {
- if (!imglist) _cimg_mp_return(arg1);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
- arg1,p1,arg3).move_to(code);
- } else {
- if (!imgout) _cimg_mp_return(arg1);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
- arg1,arg3).move_to(code);
- }
- _cimg_mp_return(arg1);
- }
- if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) += scalar
- if (!is_inside_critical) is_parallelizable = false;
- _cimg_mp_check_type(arg2,2,1,0);
- p1 = ref[1]; // Index
- is_relative = (bool)ref[2];
- arg3 = ref[3]; // X
- arg4 = ref[4]; // Y
- arg5 = ref[5]; // Z
- arg6 = ref[6]; // C
- if (p_ref) std::memcpy(p_ref,ref,siz_ref);
- CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
- if (p1!=~0U) {
- if (!imglist) _cimg_mp_return(arg1);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
- arg1,p1,arg3,arg4,arg5,arg6).move_to(code);
- } else {
- if (!imgout) _cimg_mp_return(arg1);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
- arg1,arg3,arg4,arg5,arg6).move_to(code);
- }
- _cimg_mp_return(arg1);
- }
- if (*ref==4) { // Image value (vector): I/J[_#ind,off] += value
- if (!is_inside_critical) is_parallelizable = false;
- _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
- p1 = ref[1]; // Index
- is_relative = (bool)ref[2];
- arg3 = ref[3]; // Offset
- if (p_ref) std::memcpy(p_ref,ref,siz_ref);
- if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2);
- if (p1!=~0U) {
- if (!imglist) _cimg_mp_return(arg1);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
- arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
- } else {
- if (!imgout) _cimg_mp_return(arg1);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
- arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
- }
- _cimg_mp_return(arg1);
- }
- if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) += value
- if (!is_inside_critical) is_parallelizable = false;
- _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
- p1 = ref[1]; // Index
- is_relative = (bool)ref[2];
- arg3 = ref[3]; // X
- arg4 = ref[4]; // Y
- arg5 = ref[5]; // Z
- if (p_ref) std::memcpy(p_ref,ref,siz_ref);
- if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2);
- if (p1!=~0U) {
- if (!imglist) _cimg_mp_return(arg1);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
- arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
- } else {
- if (!imgout) _cimg_mp_return(arg1);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
- arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
- }
- _cimg_mp_return(arg1);
- }
- if (_cimg_mp_is_vector(arg1)) { // Vector variable: V += value
- _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
- if (_cimg_mp_is_vector(arg2)) self_vector_v(arg1,op,arg2); // Vector += vector
- else self_vector_s(arg1,op,arg2); // Vector += scalar
- _cimg_mp_return(arg1);
- }
- if (_cimg_mp_is_reserved(arg1)) { // Scalar variable: s += scalar
- _cimg_mp_check_type(arg2,2,1,0);
- CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
- _cimg_mp_return(arg1);
- }
- variable_name.assign(ss,(unsigned int)(s - ss)).back() = 0;
- cimg::strpare(variable_name,false,true);
- _cimg_mp_strerr;
- cimg::strellipsize(variable_name,64);
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Invalid %slvalue '%s', "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- _cimg_mp_is_const_scalar(arg1)?"const ":"",
- variable_name._data,s0);
- }
- for (s = ss1; s<se1; ++s)
- if (*s=='?' && level[s - expr._data]==clevel) { // Ternary operator 'cond?expr1:expr2'
- _cimg_mp_op("Operator '?:'");
- s1 = s + 1; while (s1<se1 && (*s1!=':' || level[s1 - expr._data]!=clevel)) ++s1;
- arg1 = compile(ss,s,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,1,1,0);
- if (_cimg_mp_is_const_scalar(arg1)) {
- if ((bool)mem[arg1]) return compile(s + 1,*s1!=':'?se:s1,depth1,0,block_flags);
- else return *s1!=':'?0:compile(++s1,se,depth1,0,block_flags);
- }
- p2 = code._width;
- arg2 = compile(s + 1,*s1!=':'?se:s1,depth1,0,block_flags);
- p3 = code._width;
- arg3 = *s1==':'?compile(++s1,se,depth1,0,block_flags):
- _cimg_mp_is_vector(arg2)?vector(_cimg_mp_size(arg2),0):0;
- _cimg_mp_check_type(arg3,3,_cimg_mp_is_vector(arg2)?2:1,_cimg_mp_size(arg2));
- arg4 = _cimg_mp_size(arg2);
- if (arg4) pos = vector(arg4); else pos = scalar();
- CImg<ulongT>::vector((ulongT)mp_if,pos,arg1,arg2,arg3,
- p3 - p2,code._width - p3,arg4).move_to(code,p2);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- for (s = se3, ns = se2; s>ss; --s, --ns)
- if (*s=='|' && *ns=='|' && level[s - expr._data]==clevel) { // Logical or ('||')
- _cimg_mp_op("Operator '||'");
- arg1 = compile(ss,s,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,1,1,0);
- if (arg1>0 && arg1<=16) _cimg_mp_return(1);
- p2 = code._width;
- arg2 = compile(s + 2,se,depth1,0,block_flags);
- _cimg_mp_check_type(arg2,2,1,0);
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
- _cimg_mp_const_scalar(mem[arg1] || mem[arg2]);
- if (!arg1) _cimg_mp_return(arg2);
- pos = scalar();
- CImg<ulongT>::vector((ulongT)mp_logical_or,pos,arg1,arg2,code._width - p2).
- move_to(code,p2);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- for (s = se3, ns = se2; s>ss; --s, --ns)
- if (*s=='&' && *ns=='&' && level[s - expr._data]==clevel) { // Logical and ('&&')
- _cimg_mp_op("Operator '&&'");
- arg1 = compile(ss,s,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,1,1,0);
- if (!arg1) _cimg_mp_return(0);
- p2 = code._width;
- arg2 = compile(s + 2,se,depth1,0,block_flags);
- _cimg_mp_check_type(arg2,2,1,0);
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
- _cimg_mp_const_scalar(mem[arg1] && mem[arg2]);
- if (arg1>0 && arg1<=16) _cimg_mp_return(arg2);
- pos = scalar();
- CImg<ulongT>::vector((ulongT)mp_logical_and,pos,arg1,arg2,code._width - p2).
- move_to(code,p2);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- for (s = se2; s>ss; --s)
- if (*s=='|' && level[s - expr._data]==clevel) { // Bitwise or ('|')
- _cimg_mp_op("Operator '|'");
- arg1 = compile(ss,s,depth1,0,block_flags);
- arg2 = compile(s + 1,se,depth1,0,block_flags);
- _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_or,arg1,arg2);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
- if (!arg2) _cimg_mp_return(arg1);
- _cimg_mp_vector2_vs(mp_bitwise_or,arg1,arg2);
- }
- if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
- if (!arg1) _cimg_mp_return(arg2);
- _cimg_mp_vector2_sv(mp_bitwise_or,arg1,arg2);
- }
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
- _cimg_mp_const_scalar((longT)mem[arg1] | (longT)mem[arg2]);
- if (!arg2) _cimg_mp_return(arg1);
- if (!arg1) _cimg_mp_return(arg2);
- _cimg_mp_scalar2(mp_bitwise_or,arg1,arg2);
- }
- for (s = se2; s>ss; --s)
- if (*s=='&' && level[s - expr._data]==clevel) { // Bitwise and ('&')
- _cimg_mp_op("Operator '&'");
- arg1 = compile(ss,s,depth1,0,block_flags);
- arg2 = compile(s + 1,se,depth1,0,block_flags);
- _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_and,arg1,arg2);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_and,arg1,arg2);
- if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_and,arg1,arg2);
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
- _cimg_mp_const_scalar((longT)mem[arg1] & (longT)mem[arg2]);
- if (!arg1 || !arg2) _cimg_mp_return(0);
- _cimg_mp_scalar2(mp_bitwise_and,arg1,arg2);
- }
- for (s = se3, ns = se2; s>ss; --s, --ns)
- if (*s=='!' && *ns=='=' && level[s - expr._data]==clevel) { // Not equal to ('!=')
- _cimg_mp_op("Operator '!='");
- arg1 = compile(ss,s,depth1,0,block_flags);
- arg2 = compile(s + 2,se,depth1,0,block_flags);
- if (arg1==arg2) _cimg_mp_return(0);
- p1 = _cimg_mp_size(arg1);
- p2 = _cimg_mp_size(arg2);
- if (p1 || p2) {
- if (p1 && p2 && p1!=p2) _cimg_mp_return(1);
- pos = scalar();
- CImg<ulongT>::vector((ulongT)mp_vector_neq,pos,arg1,p1,arg2,p2,11,1).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
- _cimg_mp_const_scalar(mem[arg1]!=mem[arg2]);
- _cimg_mp_scalar2(mp_neq,arg1,arg2);
- }
- for (s = se3, ns = se2; s>ss; --s, --ns)
- if (*s=='=' && *ns=='=' && level[s - expr._data]==clevel) { // Equal to ('==')
- _cimg_mp_op("Operator '=='");
- arg1 = compile(ss,s,depth1,0,block_flags);
- arg2 = compile(s + 2,se,depth1,0,block_flags);
- if (arg1==arg2) _cimg_mp_return(1);
- p1 = _cimg_mp_size(arg1);
- p2 = _cimg_mp_size(arg2);
- if (p1 || p2) {
- if (p1 && p2 && p1!=p2) _cimg_mp_return(0);
- pos = scalar();
- CImg<ulongT>::vector((ulongT)mp_vector_eq,pos,arg1,p1,arg2,p2,11,1).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
- _cimg_mp_const_scalar(mem[arg1]==mem[arg2]);
- _cimg_mp_scalar2(mp_eq,arg1,arg2);
- }
- for (s = se3, ns = se2; s>ss; --s, --ns)
- if (*s=='<' && *ns=='=' && level[s - expr._data]==clevel) { // Less or equal than ('<=')
- _cimg_mp_op("Operator '<='");
- arg1 = compile(ss,s,depth1,0,block_flags);
- arg2 = compile(s + 2,se,depth1,0,block_flags);
- _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lte,arg1,arg2);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lte,arg1,arg2);
- if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lte,arg1,arg2);
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
- _cimg_mp_const_scalar(mem[arg1]<=mem[arg2]);
- if (arg1==arg2) _cimg_mp_return(1);
- _cimg_mp_scalar2(mp_lte,arg1,arg2);
- }
- for (s = se3, ns = se2; s>ss; --s, --ns)
- if (*s=='>' && *ns=='=' && level[s - expr._data]==clevel) { // Greater or equal than ('>=')
- _cimg_mp_op("Operator '>='");
- arg1 = compile(ss,s,depth1,0,block_flags);
- arg2 = compile(s + 2,se,depth1,0,block_flags);
- _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gte,arg1,arg2);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gte,arg1,arg2);
- if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gte,arg1,arg2);
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
- _cimg_mp_const_scalar(mem[arg1]>=mem[arg2]);
- if (arg1==arg2) _cimg_mp_return(1);
- _cimg_mp_scalar2(mp_gte,arg1,arg2);
- }
- for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps)
- if (*s=='<' && *ns!='<' && *ps!='<' && level[s - expr._data]==clevel) { // Less than ('<')
- _cimg_mp_op("Operator '<'");
- arg1 = compile(ss,s,depth1,0,block_flags);
- arg2 = compile(s + 1,se,depth1,0,block_flags);
- _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lt,arg1,arg2);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lt,arg1,arg2);
- if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lt,arg1,arg2);
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
- _cimg_mp_const_scalar(mem[arg1]<mem[arg2]);
- if (arg1==arg2) _cimg_mp_return(0);
- _cimg_mp_scalar2(mp_lt,arg1,arg2);
- }
- for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps)
- if (*s=='>' && *ns!='>' && *ps!='>' && level[s - expr._data]==clevel) { // Greater than ('>')
- _cimg_mp_op("Operator '>'");
- arg1 = compile(ss,s,depth1,0,block_flags);
- arg2 = compile(s + 1,se,depth1,0,block_flags);
- _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gt,arg1,arg2);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gt,arg1,arg2);
- if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gt,arg1,arg2);
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
- _cimg_mp_const_scalar(mem[arg1]>mem[arg2]);
- if (arg1==arg2) _cimg_mp_return(0);
- _cimg_mp_scalar2(mp_gt,arg1,arg2);
- }
- for (s = se3, ns = se2; s>ss; --s, --ns)
- if (*s=='<' && *ns=='<' && level[s - expr._data]==clevel) { // Left bit shift ('<<')
- _cimg_mp_op("Operator '<<'");
- arg1 = compile(ss,s,depth1,0,block_flags);
- arg2 = compile(s + 2,se,depth1,0,block_flags);
- _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2))
- _cimg_mp_vector2_vv(mp_bitwise_left_shift,arg1,arg2);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
- if (!arg2) _cimg_mp_return(arg1);
- _cimg_mp_vector2_vs(mp_bitwise_left_shift,arg1,arg2);
- }
- if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2))
- _cimg_mp_vector2_sv(mp_bitwise_left_shift,arg1,arg2);
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
- _cimg_mp_const_scalar((longT)mem[arg1]<<(unsigned int)mem[arg2]);
- if (!arg1) _cimg_mp_return(0);
- if (!arg2) _cimg_mp_return(arg1);
- _cimg_mp_scalar2(mp_bitwise_left_shift,arg1,arg2);
- }
- for (s = se3, ns = se2; s>ss; --s, --ns)
- if (*s=='>' && *ns=='>' && level[s - expr._data]==clevel) { // Right bit shift ('>>')
- _cimg_mp_op("Operator '>>'");
- arg1 = compile(ss,s,depth1,0,block_flags);
- arg2 = compile(s + 2,se,depth1,0,block_flags);
- _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2))
- _cimg_mp_vector2_vv(mp_bitwise_right_shift,arg1,arg2);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
- if (!arg2) _cimg_mp_return(arg1);
- _cimg_mp_vector2_vs(mp_bitwise_right_shift,arg1,arg2);
- }
- if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2))
- _cimg_mp_vector2_sv(mp_bitwise_right_shift,arg1,arg2);
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
- _cimg_mp_const_scalar((longT)mem[arg1]>>(unsigned int)mem[arg2]);
- if (!arg1) _cimg_mp_return(0);
- if (!arg2) _cimg_mp_return(arg1);
- _cimg_mp_scalar2(mp_bitwise_right_shift,arg1,arg2);
- }
- for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps)
- if (*s=='+' && (*ns!='+' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' &&
- *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' &&
- (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' &&
- *(ps - 1)<='9')))) &&
- level[s - expr._data]==clevel) { // Addition ('+')
- _cimg_mp_op("Operator '+'");
- arg1 = compile(ss,s,depth1,0,block_flags);
- arg2 = compile(s + 1,se,depth1,0,block_flags);
- _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
- if (!arg2) _cimg_mp_return(arg1);
- if (!arg1) _cimg_mp_return(arg2);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_add,arg1,arg2);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_add,arg1,arg2);
- if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_add,arg1,arg2);
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
- _cimg_mp_const_scalar(mem[arg1] + mem[arg2]);
- if (code) { // Try to spot linear case 'a*b + c'
- CImg<ulongT> &pop = code.back();
- if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) {
- arg3 = (unsigned int)pop[1];
- arg4 = (unsigned int)pop[2];
- arg5 = (unsigned int)pop[3];
- code.remove();
- CImg<ulongT>::vector((ulongT)mp_linear_add,arg3,arg4,arg5,arg3==arg2?arg1:arg2).move_to(code);
- _cimg_mp_return(arg3);
- }
- }
- if (arg2==1) _cimg_mp_scalar1(mp_increment,arg1);
- if (arg1==1) _cimg_mp_scalar1(mp_increment,arg2);
- _cimg_mp_scalar2(mp_add,arg1,arg2);
- }
- for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps)
- if (*s=='-' && (*ns!='-' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' &&
- *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' &&
- (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' &&
- *(ps - 1)<='9')))) &&
- level[s - expr._data]==clevel) { // Subtraction ('-')
- _cimg_mp_op("Operator '-'");
- arg1 = compile(ss,s,depth1,0,block_flags);
- arg2 = compile(s + 1,se,depth1,0,block_flags);
- _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
- if (!arg2) _cimg_mp_return(arg1);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_sub,arg1,arg2);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_sub,arg1,arg2);
- if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
- if (!arg1) _cimg_mp_vector1_v(mp_minus,arg2);
- _cimg_mp_vector2_sv(mp_sub,arg1,arg2);
- }
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
- _cimg_mp_const_scalar(mem[arg1] - mem[arg2]);
- if (!arg1) _cimg_mp_scalar1(mp_minus,arg2);
- if (code) { // Try to spot linear cases 'a*b - c' and 'c - a*b'
- CImg<ulongT> &pop = code.back();
- if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) {
- arg3 = (unsigned int)pop[1];
- arg4 = (unsigned int)pop[2];
- arg5 = (unsigned int)pop[3];
- code.remove();
- CImg<ulongT>::vector((ulongT)(arg3==arg1?mp_linear_sub_left:mp_linear_sub_right),
- arg3,arg4,arg5,arg3==arg1?arg2:arg1).move_to(code);
- _cimg_mp_return(arg3);
- }
- }
- if (arg2==1) _cimg_mp_scalar1(mp_decrement,arg1);
- _cimg_mp_scalar2(mp_sub,arg1,arg2);
- }
- for (s = se3, ns = se2; s>ss; --s, --ns)
- if (*s=='*' && *ns=='*' && level[s - expr._data]==clevel) { // Complex multiplication ('**')
- _cimg_mp_op("Operator '**'");
- arg1 = compile(ss,s,depth1,0,block_flags);
- arg2 = compile(s + 2,se,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,1,3,2);
- _cimg_mp_check_type(arg2,2,3,2);
- if (arg2==1) _cimg_mp_return(arg1);
- if (arg1==1) _cimg_mp_return(arg2);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) {
- pos = vector(2);
- CImg<ulongT>::vector((ulongT)mp_complex_mul,pos,arg1,arg2).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2);
- if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2);
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
- _cimg_mp_const_scalar(mem[arg1]*mem[arg2]);
- if (!arg1 || !arg2) _cimg_mp_return(0);
- _cimg_mp_scalar2(mp_mul,arg1,arg2);
- }
- for (s = se3, ns = se2; s>ss; --s, --ns)
- if (*s=='/' && *ns=='/' && level[s - expr._data]==clevel) { // Complex division ('//')
- _cimg_mp_op("Operator '//'");
- arg1 = compile(ss,s,depth1,0,block_flags);
- arg2 = compile(s + 2,se,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,1,3,2);
- _cimg_mp_check_type(arg2,2,3,2);
- if (arg2==1) _cimg_mp_return(arg1);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) {
- pos = vector(2);
- CImg<ulongT>::vector((ulongT)mp_complex_div_vv,pos,arg1,arg2).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2);
- if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
- pos = vector(2);
- CImg<ulongT>::vector((ulongT)mp_complex_div_sv,pos,arg1,arg2).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
- _cimg_mp_const_scalar(mem[arg1]/mem[arg2]);
- if (!arg1) _cimg_mp_return(0);
- _cimg_mp_scalar2(mp_div,arg1,arg2);
- }
- for (s = se2; s>ss; --s) if (*s=='*' && level[s - expr._data]==clevel) { // Multiplication ('*')
- _cimg_mp_op("Operator '*'");
- arg1 = compile(ss,s,depth1,0,block_flags);
- arg2 = compile(s + 1,se,depth1,0,block_flags);
- p2 = _cimg_mp_size(arg2);
- if (p2>0 && (ulongT)_cimg_mp_size(arg1)==(ulongT)p2*p2) { // Particular case of matrix multiplication
- pos = vector(p2);
- CImg<ulongT>::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,p2,p2,1).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
- if (arg2==1) _cimg_mp_return(arg1);
- if (arg1==1) _cimg_mp_return(arg2);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_mul,arg1,arg2);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2);
- if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2);
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
- _cimg_mp_const_scalar(mem[arg1]*mem[arg2]);
- if (code) { // Try to spot double multiplication 'a*b*c'
- CImg<ulongT> &pop = code.back();
- if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) {
- arg3 = (unsigned int)pop[1];
- arg4 = (unsigned int)pop[2];
- arg5 = (unsigned int)pop[3];
- code.remove();
- CImg<ulongT>::vector((ulongT)mp_mul2,arg3,arg4,arg5,arg3==arg2?arg1:arg2).move_to(code);
- _cimg_mp_return(arg3);
- }
- }
- if (!arg1 || !arg2) _cimg_mp_return(0);
- _cimg_mp_scalar2(mp_mul,arg1,arg2);
- }
- for (s = se2; s>ss; --s) if (*s=='/' && level[s - expr._data]==clevel) { // Division ('/')
- _cimg_mp_op("Operator '/'");
- arg1 = compile(ss,s,depth1,0,block_flags);
- arg2 = compile(s + 1,se,depth1,0,block_flags);
- _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
- if (arg2==1) _cimg_mp_return(arg1);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_div,arg1,arg2);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2);
- if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_div,arg1,arg2);
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
- _cimg_mp_const_scalar(mem[arg1]/mem[arg2]);
- if (!arg1) _cimg_mp_return(0);
- _cimg_mp_scalar2(mp_div,arg1,arg2);
- }
- for (s = se2, ns = se1; s>ss; --s, --ns)
- if (*s=='%' && *ns!='^' && level[s - expr._data]==clevel) { // Modulo ('%')
- _cimg_mp_op("Operator '%'");
- arg1 = compile(ss,s,depth1,0,block_flags);
- arg2 = compile(s + 1,se,depth1,0,block_flags);
- _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_modulo,arg1,arg2);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_modulo,arg1,arg2);
- if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_modulo,arg1,arg2);
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
- _cimg_mp_const_scalar(cimg::mod(mem[arg1],mem[arg2]));
- _cimg_mp_scalar2(mp_modulo,arg1,arg2);
- }
- if (se1>ss) {
- if (*ss=='+' && (*ss1!='+' || (ss2<se && *ss2>='0' && *ss2<='9'))) { // Unary plus ('+')
- _cimg_mp_op("Operator '+'");
- _cimg_mp_return(compile(ss1,se,depth1,0,block_flags));
- }
- if (*ss=='-' && (*ss1!='-' || (ss2<se && *ss2>='0' && *ss2<='9'))) { // Unary minus ('-')
- _cimg_mp_op("Operator '-'");
- arg1 = compile(ss1,se,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_minus,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(-mem[arg1]);
- _cimg_mp_scalar1(mp_minus,arg1);
- }
- if (*ss=='!') { // Logical not ('!')
- _cimg_mp_op("Operator '!'");
- if (*ss1=='!') { // '!!expr' optimized as 'bool(expr)'
- arg1 = compile(ss2,se,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar((bool)mem[arg1]);
- _cimg_mp_scalar1(mp_bool,arg1);
- }
- arg1 = compile(ss1,se,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_logical_not,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(!mem[arg1]);
- _cimg_mp_scalar1(mp_logical_not,arg1);
- }
- if (*ss=='~') { // Bitwise not ('~')
- _cimg_mp_op("Operator '~'");
- arg1 = compile(ss1,se,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bitwise_not,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(~(unsigned int)mem[arg1]);
- _cimg_mp_scalar1(mp_bitwise_not,arg1);
- }
- }
- for (s = se3, ns = se2; s>ss; --s, --ns)
- if (*s=='^' && *ns=='^' && level[s - expr._data]==clevel) { // Complex power ('^^')
- _cimg_mp_op("Operator '^^'");
- arg1 = compile(ss,s,depth1,0,block_flags);
- arg2 = compile(s + 2,se,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,1,3,2);
- _cimg_mp_check_type(arg2,2,3,2);
- if (arg2==1) _cimg_mp_return(arg1);
- pos = vector(2);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2))
- CImg<ulongT>::vector((ulongT)mp_complex_pow_vv,pos,arg1,arg2).move_to(code);
- else if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2))
- CImg<ulongT>::vector((ulongT)mp_complex_pow_vs,pos,arg1,arg2).move_to(code);
- else if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2))
- CImg<ulongT>::vector((ulongT)mp_complex_pow_sv,pos,arg1,arg2).move_to(code);
- else
- CImg<ulongT>::vector((ulongT)mp_complex_pow_ss,pos,arg1,arg2).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- for (s = se2; s>ss; --s)
- if (*s=='^' && level[s - expr._data]==clevel) { // Power ('^')
- _cimg_mp_op("Operator '^'");
- arg1 = compile(ss,s,depth1,0,block_flags);
- arg2 = compile(s + 1,se,depth1,0,block_flags);
- _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
- if (arg2==1) _cimg_mp_return(arg1);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_pow,arg1,arg2);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_pow,arg1,arg2);
- if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_pow,arg1,arg2);
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
- _cimg_mp_const_scalar(std::pow(mem[arg1],mem[arg2]));
- switch (arg2) {
- case 0 : _cimg_mp_return(1);
- case 2 : _cimg_mp_scalar1(mp_sqr,arg1);
- case 3 : _cimg_mp_scalar1(mp_pow3,arg1);
- case 4 : _cimg_mp_scalar1(mp_pow4,arg1);
- default :
- if (_cimg_mp_is_const_scalar(arg2)) {
- if (mem[arg2]==0.5) { _cimg_mp_scalar1(mp_sqrt,arg1); }
- else if (mem[arg2]==0.25) { _cimg_mp_scalar1(mp_pow0_25,arg1); }
- }
- _cimg_mp_scalar2(mp_pow,arg1,arg2);
- }
- }
- // Percentage computation.
- if (*se1=='%') {
- arg1 = compile(ss,se1,depth1,0,block_flags);
- arg2 = _cimg_mp_is_const_scalar(arg1)?0:const_scalar(100);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector2_vs(mp_div,arg1,arg2);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(mem[arg1]/100);
- _cimg_mp_scalar2(mp_div,arg1,arg2);
- }
- // Degree to radian postfix operator ('°' in UTF-8).
- if (se2>ss && (unsigned char)*se2==0xC2 && (unsigned char)*se1==0xB0) {
- arg1 = compile(ss,se2,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_deg2rad,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(mem[arg1]*cimg::PI/180);
- _cimg_mp_scalar1(mp_deg2rad,arg1);
- }
- // Pre/post-decrement and increment.
- is_sth = ss1<se1 && (*ss=='+' || *ss=='-') && *ss1==*ss; // is pre-?
- if (is_sth || (se2>ss && (*se1=='+' || *se1=='-') && *se2==*se1)) {
- if ((is_sth && *ss=='+') || (!is_sth && *se1=='+')) {
- _cimg_mp_op("Operator '++'");
- op = mp_self_increment;
- } else {
- _cimg_mp_op("Operator '--'");
- op = mp_self_decrement;
- }
- ref.assign(7);
- arg1 = is_sth?compile(ss2,se,depth1,ref,block_flags):
- compile(ss,se2,depth1,ref,block_flags); // Variable slot
- // Apply operator on a copy to prevent modifying a constant or a variable.
- if (*ref && (_cimg_mp_is_const_scalar(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_reserved(arg1))) {
- if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1);
- else arg1 = scalar1(mp_copy,arg1);
- }
- if (is_sth) pos = arg1; // Determine return index, depending on pre/post action
- else {
- if (_cimg_mp_is_vector(arg1)) pos = vector_copy(arg1);
- else pos = scalar1(mp_copy,arg1);
- }
- if (*ref==1) { // Vector value (scalar): V[k]++
- arg3 = ref[1]; // Vector slot
- arg4 = ref[2]; // Index
- if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
- CImg<ulongT>::vector((ulongT)op,arg1,1).move_to(code);
- CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4).
- move_to(code);
- _cimg_mp_return(pos);
- }
- if (*ref==2) { // Image value (scalar): i/j[_#ind,off]++
- if (!is_inside_critical) is_parallelizable = false;
- p1 = ref[1]; // Index
- is_relative = (bool)ref[2];
- arg3 = ref[3]; // Offset
- if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
- CImg<ulongT>::vector((ulongT)op,arg1).move_to(code);
- if (p1!=~0U) {
- if (!imglist) _cimg_mp_return(pos);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
- arg1,p1,arg3).move_to(code);
- } else {
- if (!imgout) _cimg_mp_return(pos);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
- arg1,arg3).move_to(code);
- }
- _cimg_mp_return(pos);
- }
- if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c)++
- if (!is_inside_critical) is_parallelizable = false;
- p1 = ref[1]; // Index
- is_relative = (bool)ref[2];
- arg3 = ref[3]; // X
- arg4 = ref[4]; // Y
- arg5 = ref[5]; // Z
- arg6 = ref[6]; // C
- if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
- CImg<ulongT>::vector((ulongT)op,arg1).move_to(code);
- if (p1!=~0U) {
- if (!imglist) _cimg_mp_return(pos);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
- arg1,p1,arg3,arg4,arg5,arg6).move_to(code);
- } else {
- if (!imgout) _cimg_mp_return(pos);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
- arg1,arg3,arg4,arg5,arg6).move_to(code);
- }
- _cimg_mp_return(pos);
- }
- if (*ref==4) { // Image value (vector): I/J[_#ind,off]++
- if (!is_inside_critical) is_parallelizable = false;
- p1 = ref[1]; // Index
- is_relative = (bool)ref[2];
- arg3 = ref[3]; // Offset
- if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
- self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1);
- if (p1!=~0U) {
- if (!imglist) _cimg_mp_return(pos);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
- arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
- } else {
- if (!imgout) _cimg_mp_return(pos);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
- arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
- }
- _cimg_mp_return(pos);
- }
- if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c)++
- if (!is_inside_critical) is_parallelizable = false;
- p1 = ref[1]; // Index
- is_relative = (bool)ref[2];
- arg3 = ref[3]; // X
- arg4 = ref[4]; // Y
- arg5 = ref[5]; // Z
- if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
- self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1);
- if (p1!=~0U) {
- if (!imglist) _cimg_mp_return(pos);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
- arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
- } else {
- if (!imgout) _cimg_mp_return(pos);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
- arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
- }
- _cimg_mp_return(pos);
- }
- if (_cimg_mp_is_vector(arg1)) { // Vector variable: V++
- self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1);
- _cimg_mp_return(pos);
- }
- if (_cimg_mp_is_reserved(arg1)) { // Scalar variable: s++
- CImg<ulongT>::vector((ulongT)op,arg1).move_to(code);
- _cimg_mp_return(pos);
- }
- if (is_sth) variable_name.assign(ss2,(unsigned int)(se - ss1));
- else variable_name.assign(ss,(unsigned int)(se1 - ss));
- variable_name.back() = 0;
- cimg::strpare(variable_name,false,true);
- _cimg_mp_strerr;
- cimg::strellipsize(variable_name,64);
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Invalid %slvalue '%s', "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- _cimg_mp_is_const_scalar(arg1)?"const ":"",
- variable_name._data,s0);
- }
- // Array-like access to vectors and image values 'i/j/I/J[_#ind,offset,_boundary]' and 'vector[offset]'.
- if (*se1==']') {
- _cimg_mp_op("Value accessor '[]'");
- // Find opening bracket for the offset.
- s0 = se1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0;
- if (s0>ss) { s1 = s0; do { --s1; } while (cimg::is_blank(*s1)); cimg::swap(*s0,*++s1); }
- is_sth=s0>ss && *(s0-1)==']'; // Particular case s.a. '..[..][..]' ?
- is_relative = *ss=='j' || *ss=='J';
- if (!is_sth && (*ss=='I' || *ss=='J') && *ss1=='[' &&
- (reserved_label[(int)*ss]==~0U ||
- !_cimg_mp_is_vector(reserved_label[(int)*ss]))) { // Image value as a vector
- if (*ss2=='#') { // Index specified
- s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- p1 = compile(ss3,s0++,depth1,0,block_flags);
- _cimg_mp_check_const_index(p1);
- _cimg_mp_check_notnan_index(p1);
- _cimg_mp_check_list();
- } else { p1 = ~0U; s0 = ss2; }
- s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- p2 = 1 + (p1!=~0U);
- arg1 = compile(s0,s1,depth1,0,block_flags); // Offset
- _cimg_mp_check_type(arg1,p2,1,0);
- arg2 = ~0U;
- if (s1<se1) {
- arg2 = compile(++s1,se1,depth1,0,block_flags); // Boundary
- _cimg_mp_check_type(arg2,p2 + 1,1,0);
- }
- if (p_ref && arg2==~0U) {
- *p_ref = 4;
- p_ref[1] = p1;
- p_ref[2] = (unsigned int)is_relative;
- p_ref[3] = arg1;
- if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1;
- }
- p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any
- if (p1==~0U) p2 = imgin._spectrum;
- else {
- p3 = (unsigned int)cimg::mod((int)mem[p1],imglist.width());
- p2 = imglist[p3]._spectrum;
- }
- if (!p2) _cimg_mp_return(0);
- pos = vector(p2);
- if (p1!=~0U) {
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_Joff:mp_list_Ioff),
- pos,p1,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code);
- } else {
- need_input_copy = true;
- CImg<ulongT>::vector((ulongT)(is_relative?mp_Joff:mp_Ioff),
- pos,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code);
- }
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!is_sth && (*ss=='i' || *ss=='j') && *ss1=='[' &&
- (reserved_label[(int)*ss]==~0U ||
- !_cimg_mp_is_vector(reserved_label[(int)*ss]))) { // Image value as a scalar
- if (*ss2=='#') { // Index specified
- s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- p1 = compile(ss3,s0++,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- } else { p1 = ~0U; s0 = ss2; }
- s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(s0,s1,depth1,0,block_flags); // Offset
- arg2 = s1<se1?compile(++s1,se1,depth1,0,block_flags):~0U; // Boundary
- if (p_ref && arg2==~0U) {
- *p_ref = 2;
- p_ref[1] = p1;
- p_ref[2] = (unsigned int)is_relative;
- p_ref[3] = arg1;
- if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -1; // Prevent from being used in further optimization
- if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1;
- }
- if (p1!=~0U) {
- if (!imglist) _cimg_mp_return(0);
- pos = scalar3(is_relative?mp_list_joff:mp_list_ioff,p1,arg1,arg2==~0U?_cimg_mp_boundary:arg2);
- } else {
- if (!imgin) _cimg_mp_return(0);
- need_input_copy = true;
- pos = scalar2(is_relative?mp_joff:mp_ioff,arg1,arg2==~0U?_cimg_mp_boundary:arg2);
- }
- memtype[pos] = -1; // Prevent from being used in further optimization
- _cimg_mp_return(pos);
- }
- s0 = se1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0;
- if (s0>ss) { // Vector element
- arg1 = compile(ss,s0,depth1,0,block_flags);
- if (_cimg_mp_is_scalar(arg1)) {
- variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0;
- _cimg_mp_strerr;
- cimg::strellipsize(variable_name,64);
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Array brackets used on non-vector variable '%s', "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- variable_name._data,s0);
- }
- s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- if (s1<se1) { // Two or three arguments -> sub-vector extraction
- p1 = _cimg_mp_size(arg1);
- arg2 = compile(++s0,s1,depth1,0,block_flags); // Starting index
- s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- arg3 = compile(s1,s0,depth1,0,block_flags); // Length
- arg4 = s0<se1?compile(++s0,se1,depth1,0,block_flags):1; // Step
- _cimg_mp_check_const_scalar(arg3,2,3);
- arg3 = (unsigned int)mem[arg3];
- pos = vector(arg3);
- CImg<ulongT>::vector((ulongT)mp_vector_crop,pos,arg1,p1,arg2,arg3,arg4).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- // One argument -> vector value reference
- arg2 = compile(++s0,se1,depth1,0,block_flags);
- if (_cimg_mp_is_const_scalar(arg2)) { // Constant index
- nb = (int)mem[arg2];
- if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) _cimg_mp_return(arg1 + 1 + nb);
- variable_name.assign(ss,(unsigned int)(s0 - ss)).back() = 0;
- _cimg_mp_strerr;
- cimg::strellipsize(variable_name,64);
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: Out-of-bounds reference '%s[%d]' "
- "(vector '%s' has dimension %u), "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,
- variable_name._data,nb,
- variable_name._data,_cimg_mp_size(arg1),s0);
- }
- if (p_ref) {
- *p_ref = 1;
- p_ref[1] = arg1;
- p_ref[2] = arg2;
- if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1; // Prevent from being used in further optimization
- }
- pos = scalar3(mp_vector_off,arg1,_cimg_mp_size(arg1),arg2);
- memtype[pos] = -1; // Prevent from being used in further optimization
- _cimg_mp_return(pos);
- }
- }
- // Look for a function call, an access to image value, or a parenthesis.
- if (*se1==')') {
- if (*ss=='(') _cimg_mp_return(compile(ss1,se1,depth1,p_ref,block_flags)); // Simple parentheses
- _cimg_mp_op("Value accessor '()'");
- is_relative = *ss=='j' || *ss=='J';
- s0 = s1 = std::strchr(ss,'('); if (s0) { do { --s1; } while (cimg::is_blank(*s1)); cimg::swap(*s0,*++s1); }
- // I/J(_#ind,_x,_y,_z,_interpolation,_boundary_conditions)
- if ((*ss=='I' || *ss=='J') && *ss1=='(') { // Image value as scalar
- if (*ss2=='#') { // Index specified
- s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- p1 = compile(ss3,s0++,depth1,0,block_flags);
- _cimg_mp_check_const_index(p1);
- _cimg_mp_check_notnan_index(p1);
- _cimg_mp_check_list();
- } else { p1 = ~0U; s0 = ss2; }
- arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
- arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
- arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
- arg4 = arg5 = ~0U;
- if (s0<se1) {
- s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(s0,s1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
- p2 = _cimg_mp_size(arg1);
- ++arg1;
- if (p2>1) {
- arg2 = arg1 + 1;
- if (p2>2) arg3 = arg2 + 1;
- }
- if (s1<se1) {
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg4 = compile(s1,s2,depth1,0,block_flags);
- arg5 = s2<se1?compile(++s2,se1,depth1,0,block_flags):~0U;
- }
- } else if (s1<se1) {
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,0,block_flags);
- if (s2<se1) {
- s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
- arg3 = compile(s2,s3,depth1,0,block_flags);
- if (s3<se1) {
- s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg4 = compile(s3,s2,depth1,0,block_flags);
- arg5 = s2<se1?compile(++s2,se1,depth1,0,block_flags):~0U;
- }
- }
- }
- }
- if (p_ref && arg4==~0U && arg5==~0U) {
- *p_ref = 5;
- p_ref[1] = p1;
- p_ref[2] = (unsigned int)is_relative;
- p_ref[3] = arg1;
- p_ref[4] = arg2;
- p_ref[5] = arg3;
- if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -1; // Prevent from being used in further optimization
- if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1;
- if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1;
- if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1;
- }
- p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any
- if (p1==~0U) p2 = imgin._spectrum;
- else if (_cimg_mp_is_const_scalar(p1)) {
- p3 = (unsigned int)cimg::mod((int)mem[p1],imglist.width());
- p2 = imglist[p3]._spectrum;
- }
- if (!p2) _cimg_mp_return(0);
- pos = vector(p2);
- if (p1!=~0U)
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_Jxyz:mp_list_Ixyz),
- pos,p1,arg1,arg2,arg3,
- arg4==~0U?_cimg_mp_interpolation:arg4,
- arg5==~0U?_cimg_mp_boundary:arg5,p2).move_to(code);
- else {
- need_input_copy = true;
- CImg<ulongT>::vector((ulongT)(is_relative?mp_Jxyz:mp_Ixyz),
- pos,arg1,arg2,arg3,
- arg4==~0U?_cimg_mp_interpolation:arg4,
- arg5==~0U?_cimg_mp_boundary:arg5,p2).move_to(code);
- }
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- // i/j(_#ind,_x,_y,_z,_c,_interpolation,_boundary_conditions)
- if ((*ss=='i' || *ss=='j') && *ss1=='(') { // Image value as scalar
- if (*ss2=='#') { // Index specified
- s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- p1 = compile(ss3,s0++,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- } else { p1 = ~0U; s0 = ss2; }
- arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
- arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
- arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
- arg4 = is_relative?0U:(unsigned int)_cimg_mp_slot_c;
- arg5 = arg6 = ~0U;
- if (s0<se1) {
- s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(s0,s1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
- p2 = _cimg_mp_size(arg1);
- ++arg1;
- if (p2>1) {
- arg2 = arg1 + 1;
- if (p2>2) {
- arg3 = arg2 + 1;
- if (p2>3) arg4 = arg3 + 1;
- }
- }
- if (s1<se1) {
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg5 = compile(s1,s2,depth1,0,block_flags);
- arg6 = s2<se1?compile(++s2,se1,depth1,0,block_flags):~0U;
- }
- } else if (s1<se1) {
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,0,block_flags);
- if (s2<se1) {
- s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
- arg3 = compile(s2,s3,depth1,0,block_flags);
- if (s3<se1) {
- s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg4 = compile(s3,s2,depth1,0,block_flags);
- if (s2<se1) {
- s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
- arg5 = compile(s2,s3,depth1,0,block_flags);
- arg6 = s3<se1?compile(++s3,se1,depth1,0,block_flags):~0U;
- }
- }
- }
- }
- }
- if (p_ref && arg5==~0U && arg6==~0U) {
- *p_ref = 3;
- p_ref[1] = p1;
- p_ref[2] = (unsigned int)is_relative;
- p_ref[3] = arg1;
- p_ref[4] = arg2;
- p_ref[5] = arg3;
- p_ref[6] = arg4;
- if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -1; // Prevent from being used in further optimization
- if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1;
- if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1;
- if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1;
- if (_cimg_mp_is_comp(arg4)) memtype[arg4] = -1;
- }
- if (p1!=~0U) {
- if (!imglist) _cimg_mp_return(0);
- pos = scalar7(is_relative?mp_list_jxyzc:mp_list_ixyzc,
- p1,arg1,arg2,arg3,arg4,
- arg5==~0U?_cimg_mp_interpolation:arg5,
- arg6==~0U?_cimg_mp_boundary:arg6);
- } else {
- if (!imgin) _cimg_mp_return(0);
- need_input_copy = true;
- pos = scalar6(is_relative?mp_jxyzc:mp_ixyzc,
- arg1,arg2,arg3,arg4,
- arg5==~0U?_cimg_mp_interpolation:arg5,
- arg6==~0U?_cimg_mp_boundary:arg6);
- }
- memtype[pos] = -1; // Prevent from being used in further optimization
- _cimg_mp_return(pos);
- }
- // Mathematical functions.
- switch (*ss) {
- case 'a' :
- #ifdef cimg_mp_func_abort
- if (!std::strncmp(ss,"abort(",6)) { // Abort
- _cimg_mp_op("Function 'abort()'");
- if (pexpr[se2 - expr._data]=='(') { // no arguments?
- CImg<ulongT>::vector((ulongT)mp_abort,_cimg_mp_slot_nan).move_to(code);
- _cimg_mp_return_nan();
- }
- }
- #endif
- if (!std::strncmp(ss,"abs(",4)) { // Absolute value
- _cimg_mp_op("Function 'abs()'");
- arg1 = compile(ss4,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_abs,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::abs(mem[arg1]));
- _cimg_mp_scalar1(mp_abs,arg1);
- }
- if (!std::strncmp(ss,"addr(",5)) { // Pointer address
- _cimg_mp_op("Function 'addr()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- _cimg_mp_const_scalar((double)arg1);
- }
- if (!std::strncmp(ss,"acos(",5)) { // Arccos
- _cimg_mp_op("Function 'acos()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_acos,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::acos(mem[arg1]));
- _cimg_mp_scalar1(mp_acos,arg1);
- }
- if (!std::strncmp(ss,"acosh(",6)) { // Hyperbolic arccosine
- _cimg_mp_op("Function 'acosh()'");
- arg1 = compile(ss6,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_acosh,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::acosh(mem[arg1]));
- _cimg_mp_scalar1(mp_acosh,arg1);
- }
- if (!std::strncmp(ss,"asinh(",6)) { // Hyperbolic arcsine
- _cimg_mp_op("Function 'asinh()'");
- arg1 = compile(ss6,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_asinh,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::asinh(mem[arg1]));
- _cimg_mp_scalar1(mp_asinh,arg1);
- }
- if (!std::strncmp(ss,"atanh(",6)) { // Hyperbolic arctangent
- _cimg_mp_op("Function 'atanh()'");
- arg1 = compile(ss6,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_atanh,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::atanh(mem[arg1]));
- _cimg_mp_scalar1(mp_atanh,arg1);
- }
- if (!std::strncmp(ss,"arg(",4) ||
- !std::strncmp(ss,"arg0(",5) ||
- !std::strncmp(ss,"arg1(",5)) { // Nth argument
- _cimg_mp_op(*ss3=='('?"Function 'arg()'":*ss3=='0'?"Function 'arg0()'":"Function 'arg1()'");
- s0 = ss4 + (*ss3!='('?1:0);
- s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(s0,s1,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,1,1,0);
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,0,block_flags);
- p2 = _cimg_mp_size(arg2);
- p3 = 3;
- CImg<ulongT>::vector((ulongT)(*ss3=='0'?mp_arg0:mp_arg),0,0,p2,arg1,arg2).move_to(l_opcode);
- for (s = ++s2; s<se; ++s) {
- ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
- (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
- arg3 = compile(s,ns,depth1,0,block_flags);
- _cimg_mp_check_type(arg3,p3,p2?2:1,p2);
- CImg<ulongT>::vector(arg3).move_to(l_opcode);
- ++p3;
- s = ns;
- }
- (l_opcode>'y').move_to(opcode);
- opcode[2] = opcode._height;
- if (_cimg_mp_is_const_scalar(arg1)) {
- p3-=1; // Number of args
- if (*ss3=='0') arg1 = (unsigned int)(mem[arg1]<0?mem[arg1] + p3:mem[arg1] + 1);
- else arg1 = (unsigned int)(mem[arg1]<0?mem[arg1] + p3:mem[arg1]);
- if (arg1<p3) _cimg_mp_return(opcode[4 + arg1]);
- if (p2) {
- pos = vector(p2);
- std::memset(&mem[pos] + 1,0,p2*sizeof(double));
- return_new_comp = true;
- _cimg_mp_return(pos);
- } else _cimg_mp_return(0);
- }
- pos = opcode[1] = p2?vector(p2):scalar();
- opcode.move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"asin(",5)) { // Arcsin
- _cimg_mp_op("Function 'asin()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_asin,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::asin(mem[arg1]));
- _cimg_mp_scalar1(mp_asin,arg1);
- }
- if (!std::strncmp(ss,"atan(",5)) { // Arctan
- _cimg_mp_op("Function 'atan()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_atan,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::atan(mem[arg1]));
- _cimg_mp_scalar1(mp_atan,arg1);
- }
- if (!std::strncmp(ss,"atan2(",6)) { // Arctan2
- _cimg_mp_op("Function 'atan2()'");
- s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss6,s1,depth1,0,block_flags);
- arg2 = compile(++s1,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_atan2,arg1,arg2);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_atan2,arg1,arg2);
- if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_atan2,arg1,arg2);
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
- _cimg_mp_const_scalar(std::atan2(mem[arg1],mem[arg2]));
- _cimg_mp_scalar2(mp_atan2,arg1,arg2);
- }
- break;
- case 'b' :
- if (!std::strncmp(ss,"break(",6)) { // Break current block
- if (pexpr[se2 - expr._data]=='(') { // no arguments?
- CImg<ulongT>::vector((ulongT)mp_break,_cimg_mp_slot_nan).move_to(code);
- _cimg_mp_return_nan();
- }
- }
- if (!std::strncmp(ss,"breakpoint(",11)) { // Break point (for abort test)
- _cimg_mp_op("Function 'breakpoint()'");
- if (pexpr[se2 - expr._data]=='(') { // no arguments?
- CImg<ulongT>::vector((ulongT)mp_breakpoint,_cimg_mp_slot_nan).move_to(code);
- _cimg_mp_return_nan();
- }
- }
- if (!std::strncmp(ss,"bool(",5)) { // Boolean cast
- _cimg_mp_op("Function 'bool()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar((bool)mem[arg1]);
- _cimg_mp_scalar1(mp_bool,arg1);
- }
- if (!std::strncmp(ss,"begin(",6)) { // Begin
- _cimg_mp_op("Function 'begin()'");
- s1 = ss6; while (s1<se1 && cimg::is_blank(*s1)) ++s1;
- if (s1!=se1) {
- if (!is_inside_begin) code.swap(code_begin);
- arg1 = compile(s1,se1,depth1,p_ref,2);
- if (!is_inside_begin) code.swap(code_begin);
- _cimg_mp_return(arg1);
- } else _cimg_mp_return_nan();
- }
- if (!std::strncmp(ss,"begin_t(",8)) { // Begin thread
- _cimg_mp_op("Function 'begin_t()'");
- s1 = ss8; while (s1<se1 && cimg::is_blank(*s1)) ++s1;
- if (s1!=se1) {
- if (!is_inside_begin_t) code.swap(code_begin_t);
- arg1 = compile(s1,se1,depth1,p_ref,4);
- if (!is_inside_begin_t) code.swap(code_begin_t);
- _cimg_mp_return(arg1);
- } else _cimg_mp_return_nan();
- }
- break;
- case 'c' :
- if (!std::strncmp(ss,"c2o(",4)) { // Coordinates to offset
- _cimg_mp_op("Function 'c2o()'");
- if (*ss4=='#') { // Index specified
- s0 = ss5; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- p1 = compile(ss5,s0++,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- _cimg_mp_check_list();
- } else { p1 = ~0U; s0 = ss4; }
- s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- pos = compile(s0,s1,depth1,0,block_flags);
- p2 = _cimg_mp_size(pos);
- if (p2) { // Coordinates specified as a vector
- if (s1!=se1) compile(s0,se1,depth1,0,block_flags); // -> Error too much arguments
- if (p2>4) {
- *s1 = 0; s1 = s0; _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Argument '%s' is a vector of size %u (should be <=4), "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,s1,p2,s0);
- }
- arg1 = pos + 1;
- arg2 = p2>1?pos + 2:0;
- arg3 = p2>2?pos + 3:0;
- arg4 = p2>3?pos + 4:0;
- } else { // Coordinates specified as scalars
- arg1 = pos; arg2 = arg3 = arg4 = 0;
- if (s1<se1) {
- s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- arg2 = compile(s1,s0,depth1,0,block_flags);
- if (s0<se1) {
- s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg3 = compile(s0,s1,depth1,0,block_flags);
- arg4 = s1<se1?compile(++s1,se1,depth1,0,block_flags):0;
- } else arg3 = 0;
- } else arg2 = 0;
- }
- _cimg_mp_scalar5(mp_c2o,p1,arg1,arg2,arg3,arg4);
- }
- if (!std::strncmp(ss,"cabs(",5)) { // Complex absolute value
- _cimg_mp_op("Function 'cabs()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,0,3,2);
- if (_cimg_mp_is_scalar(arg1)) _cimg_mp_scalar2(mp_complex_abs,arg1,0);
- _cimg_mp_scalar2(mp_complex_abs,arg1 + 1,arg1 + 2);
- }
- if (!std::strncmp(ss,"carg(",5)) { // Complex argument
- _cimg_mp_op("Function 'carg()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,0,3,2);
- if (_cimg_mp_is_scalar(arg1)) _cimg_mp_scalar2(mp_atan2,0,arg1);
- _cimg_mp_scalar2(mp_atan2,arg1 + 2,arg1 + 1);
- }
- if (!std::strncmp(ss,"cbrt(",5)) { // Cubic root
- _cimg_mp_op("Function 'cbrt()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cbrt,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::cbrt(mem[arg1]));
- _cimg_mp_scalar1(mp_cbrt,arg1);
- }
- if (!std::strncmp(ss,"cconj(",6)) { // Complex conjugate
- _cimg_mp_op("Function 'cconj()'");
- arg1 = compile(ss6,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,0,3,2);
- pos = vector(2);
- if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_conj,pos,arg1,0).move_to(code);
- else CImg<ulongT>::vector((ulongT)mp_complex_conj,pos,arg1 + 1,arg1 + 2).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"ceil(",5)) { // Ceil
- _cimg_mp_op("Function 'ceil()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_ceil,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::ceil(mem[arg1]));
- _cimg_mp_scalar1(mp_ceil,arg1);
- }
- if (!std::strncmp(ss,"cexp(",5)) { // Complex exponential
- _cimg_mp_op("Function 'cexp()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,0,3,2);
- pos = vector(2);
- if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_exp,pos,arg1,0).move_to(code);
- else CImg<ulongT>::vector((ulongT)mp_complex_exp,pos,arg1 + 1,arg1 + 2).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"clog(",5)) { // Complex logarithm
- _cimg_mp_op("Function 'clog()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,0,3,2);
- pos = vector(2);
- if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_log,pos,arg1,0).move_to(code);
- else CImg<ulongT>::vector((ulongT)mp_complex_log,pos,arg1 + 1,arg1 + 2).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"ccos(",5)) { // Complex cosine
- _cimg_mp_op("Function 'ccos()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,0,3,2);
- pos = vector(2);
- if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_cos,pos,arg1,0).move_to(code);
- else CImg<ulongT>::vector((ulongT)mp_complex_cos,pos,arg1 + 1,arg1 + 2).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"csin(",5)) { // Complex sine
- _cimg_mp_op("Function 'csin()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,0,3,2);
- pos = vector(2);
- if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_sin,pos,arg1,0).move_to(code);
- else CImg<ulongT>::vector((ulongT)mp_complex_sin,pos,arg1 + 1,arg1 + 2).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"csqrt(",6)) { // Complex square root
- _cimg_mp_op("Function 'csqrt()'");
- arg1 = compile(ss6,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,0,3,2);
- pos = vector(2);
- if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_sqrt,pos,arg1,0).move_to(code);
- else CImg<ulongT>::vector((ulongT)mp_complex_sqrt,pos,arg1 + 1,arg1 + 2).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"ctan(",5)) { // Complex tangent
- _cimg_mp_op("Function 'ctan()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,0,3,2);
- pos = vector(2);
- if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_tan,pos,arg1,0).move_to(code);
- else CImg<ulongT>::vector((ulongT)mp_complex_tan,pos,arg1 + 1,arg1 + 2).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"ccosh(",6)) { // Complex hyperbolic cosine
- _cimg_mp_op("Function 'ccosh()'");
- arg1 = compile(ss6,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,0,3,2);
- pos = vector(2);
- if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_cosh,pos,arg1,0).move_to(code);
- else CImg<ulongT>::vector((ulongT)mp_complex_cosh,pos,arg1 + 1,arg1 + 2).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"csinh(",6)) { // Complex hyperbolic sine
- _cimg_mp_op("Function 'csinh()'");
- arg1 = compile(ss6,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,0,3,2);
- pos = vector(2);
- if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_sinh,pos,arg1,0).move_to(code);
- else CImg<ulongT>::vector((ulongT)mp_complex_sinh,pos,arg1 + 1,arg1 + 2).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"ctanh(",6)) { // Complex hyperbolic tangent
- _cimg_mp_op("Function 'ctanh()'");
- arg1 = compile(ss6,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,0,3,2);
- pos = vector(2);
- if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_tanh,pos,arg1,0).move_to(code);
- else CImg<ulongT>::vector((ulongT)mp_complex_tanh,pos,arg1 + 1,arg1 + 2).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"continue(",9)) { // Continue loop
- if (pexpr[se2 - expr._data]=='(') { // no arguments?
- CImg<ulongT>::vector((ulongT)mp_continue,_cimg_mp_slot_nan).move_to(code);
- _cimg_mp_return_nan();
- }
- }
- if (!std::strncmp(ss,"copy(",5)) { // Memory copy
- _cimg_mp_op("Function 'copy()'");
- ref.assign(14);
- s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = p1 = compile(ss5,s1,depth1,ref,block_flags);
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,ref._data + 7,block_flags);
- arg3 = arg4 = arg5 = ~0U; arg6 = 1;
- if (s2<se1) {
- s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
- arg3 = compile(s2,s3,depth1,0,block_flags);
- if (s3<se1) {
- s1 = ++s3; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg4 = compile(s3,s1,depth1,0,block_flags);
- if (s1<se1) {
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg5 = compile(s1,s2,depth1,0,block_flags);
- arg6 = s2<se1?compile(++s2,se1,depth1,0,block_flags):1;
- }
- }
- }
- if (_cimg_mp_is_vector(arg1)) {
- if (!ref[0]) ++arg1;
- else if (ref[0]>=4 && arg4==~0U) arg4 = scalar1(mp_image_whd,ref[1]);
- }
- if (_cimg_mp_is_vector(arg2)) {
- if (arg3==~0U) arg3 = const_scalar(_cimg_mp_size(arg2));
- if (!ref[7]) ++arg2;
- if (ref[7]>=4 && arg5==~0U) arg5 = scalar1(mp_image_whd,ref[8]);
- }
- if (arg3==~0U) arg3 = 1;
- if (arg4==~0U) arg4 = 1;
- if (arg5==~0U) arg5 = 1;
- _cimg_mp_check_type(arg3,3,1,0);
- _cimg_mp_check_type(arg4,4,1,0);
- _cimg_mp_check_type(arg5,5,1,0);
- _cimg_mp_check_type(arg6,5,1,0);
- CImg<ulongT>(1,22).move_to(code);
- code.back().get_shared_rows(0,7).fill((ulongT)mp_memcopy,p1,arg1,arg2,arg3,arg4,arg5,arg6);
- code.back().get_shared_rows(8,21).fill(ref);
- _cimg_mp_return_nan();
- }
- if (!std::strncmp(ss,"cos(",4)) { // Cosine
- _cimg_mp_op("Function 'cos()'");
- arg1 = compile(ss4,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cos,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::cos(mem[arg1]));
- _cimg_mp_scalar1(mp_cos,arg1);
- }
- if (!std::strncmp(ss,"cosh(",5)) { // Hyperbolic cosine
- _cimg_mp_op("Function 'cosh()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cosh,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::cosh(mem[arg1]));
- _cimg_mp_scalar1(mp_cosh,arg1);
- }
- if (!std::strncmp(ss,"cov(",4)) { // Covariance
- _cimg_mp_op("Function 'cov()'");
- s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss4,s1,depth1,0,block_flags);
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,0,block_flags);
- arg3 = arg4 = ~0U;
- if (s2<se1) {
- s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg3 = compile(s2,s1,depth1,0,block_flags);
- arg4 = s1<se1?compile(++s1,se1,depth1,0,block_flags):~0U;
- }
- _cimg_mp_check_type(arg2,2,_cimg_mp_is_scalar(arg1)?1:2,_cimg_mp_size(arg1));
- if (arg3!=~0U) _cimg_mp_check_type(arg3,3,1,0);
- if (arg4!=~0U) _cimg_mp_check_type(arg4,4,1,0);
- if (arg3==~0U && arg4==~0U && _cimg_mp_size(arg1)<2) _cimg_mp_const_scalar(0);
- _cimg_mp_scalar5(mp_cov,arg1,arg2,_cimg_mp_size(arg1),arg3,arg4);
- }
- if (!std::strncmp(ss,"critical(",9)) { // Critical section (single thread at a time)
- _cimg_mp_op("Function 'critical()'");
- p1 = code._width;
- arg1 = compile(ss + 9,se1,depth1,p_ref,block_flags | 1);
- CImg<ulongT>::vector((ulongT)mp_critical,arg1,code._width - p1).move_to(code,p1);
- _cimg_mp_return(arg1);
- }
- if (!std::strncmp(ss,"crop(",5)) { // Image or vector crop
- _cimg_mp_op("Function 'crop()'");
- is_sth = false; // is image crop ?
- arg1 = 0;
- if (*ss5=='#') { // Index specified
- s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- p1 = compile(ss6,s0++,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- _cimg_mp_check_list();
- pos = 2;
- is_sth = true;
- } else { p1 = ~0U; s0 = ss5; need_input_copy = true; pos = 1; }
- if (s0<se1) for (s = s0; s<se; ++s, ++pos) {
- ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
- (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
- arg1 = compile(s,ns,depth1,0,block_flags);
- if (pos==1 && _cimg_mp_is_scalar(arg1)) is_sth = true;
- else if (pos>1) _cimg_mp_check_type(arg1,pos,1,0);
- CImg<ulongT>::vector(arg1).move_to(l_opcode);
- s = ns;
- }
- (l_opcode>'y').move_to(opcode);
- if (!opcode || is_sth) { // Image crop
- arg1 = 0; arg2 = (p1!=~0U);
- switch (opcode._height) {
- case 0 : case 1 :
- CImg<ulongT>::vector(0,0,0,0,~0U,~0U,~0U,~0U,0).move_to(opcode);
- break;
- case 2 :
- CImg<ulongT>::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,_cimg_mp_boundary).move_to(opcode);
- arg1 = 2 + arg2; break;
- case 3 :
- CImg<ulongT>::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,opcode[2]).move_to(opcode);
- arg1 = 2 + arg2; break;
- case 4 :
- CImg<ulongT>::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,_cimg_mp_boundary).
- move_to(opcode);
- arg1 = 3 + arg2; break;
- case 5 :
- CImg<ulongT>::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,opcode[4]).
- move_to(opcode);
- arg1 = 3 + arg2; break;
- case 6 :
- CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U,
- _cimg_mp_boundary).move_to(opcode);
- arg1 = 4 + arg2; break;
- case 7 :
- CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U,
- opcode[6]).move_to(opcode);
- arg1 = 4 + arg2; break;
- case 8 :
- CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],opcode[5],opcode[6],
- opcode[7],_cimg_mp_boundary).move_to(opcode);
- arg1 = 5 + arg2; break;
- case 9 :
- arg1 = 5 + arg2; break;
- default : // Error -> too much arguments
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Too much arguments specified, "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,s0);
- }
- if (opcode[4]!=(ulongT)~0U) {
- _cimg_mp_check_const_scalar((unsigned int)opcode[4],arg1,3);
- opcode[4] = (ulongT)mem[opcode[4]];
- }
- if (opcode[5]!=(ulongT)~0U) {
- _cimg_mp_check_const_scalar((unsigned int)opcode[5],arg1 + 1,3);
- opcode[5] = (ulongT)mem[opcode[5]];
- }
- if (opcode[6]!=(ulongT)~0U) {
- _cimg_mp_check_const_scalar((unsigned int)opcode[6],arg1 + 2,3);
- opcode[6] = (ulongT)mem[opcode[6]];
- }
- if (opcode[7]!=(ulongT)~0U) {
- _cimg_mp_check_const_scalar((unsigned int)opcode[7],arg1 + 3,3);
- opcode[7] = (ulongT)mem[opcode[7]];
- }
- _cimg_mp_check_type((unsigned int)opcode[8],arg1 + 4,1,0);
- if (opcode[4]==(ulongT)~0U || opcode[5]==(ulongT)~0U ||
- opcode[6]==(ulongT)~0U || opcode[7]==(ulongT)~0U) {
- p2 = 0;
- if (p1!=~0U) {
- _cimg_mp_check_const_scalar(p1,1,1);
- p2 = (unsigned int)cimg::mod((int)mem[p1],imglist.width());
- }
- const CImg<T> &img = p1!=~0U?imglist[p2]:imgin;
- if (!img) {
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Cannot crop empty image when "
- "some xyzc-coordinates are unspecified, in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,s0);
- }
- if (opcode[4]==(ulongT)~0U) opcode[4] = (ulongT)img._width;
- if (opcode[5]==(ulongT)~0U) opcode[5] = (ulongT)img._height;
- if (opcode[6]==(ulongT)~0U) opcode[6] = (ulongT)img._depth;
- if (opcode[7]==(ulongT)~0U) opcode[7] = (ulongT)img._spectrum;
- }
- pos = vector((unsigned int)(opcode[4]*opcode[5]*opcode[6]*opcode[7]));
- CImg<ulongT>::vector((ulongT)mp_image_crop,
- pos,p1, // 1-2: res,#ind
- *opcode,opcode[1],opcode[2],opcode[3], // 3-6: x,y,z,c
- opcode[4],opcode[5],opcode[6],opcode[7], // 7-10: dx,dy,dz,dc
- opcode[8]).move_to(code); // 11: boundary conditions
- } else { // Vector crop
- switch (opcode._height) {
- case 5 : case 6 :
- CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],
- 0,0,0,0,~0U,~0U,~0U,~0U,0).move_to(opcode);
- break;
- case 7 :
- CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],
- opcode[5],0,0,0,opcode[6],~0U,~0U,~0U,_cimg_mp_boundary).move_to(opcode);
- arg1 = 7; break;
- case 8 :
- CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],
- opcode[5],0,0,0,opcode[6],~0U,~0U,~0U,opcode[7]).move_to(opcode);
- arg1 = 7; break;
- case 9 :
- CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],
- opcode[5],opcode[6],0,0,opcode[7],opcode[8],~0U,~0U,_cimg_mp_boundary).
- move_to(opcode);
- arg1 = 8; break;
- case 10 :
- CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],
- opcode[5],opcode[6],0,0,opcode[7],opcode[8],~0U,~0U,opcode[9]).
- move_to(opcode);
- arg1 = 8; break;
- case 11 :
- CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],
- opcode[5],opcode[6],opcode[7],0,opcode[8],opcode[9],opcode[10],~0U,
- _cimg_mp_boundary).move_to(opcode);
- arg1 = 9; break;
- case 12 :
- CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],
- opcode[5],opcode[6],opcode[7],0,opcode[8],opcode[9],opcode[10],~0U,
- opcode[11]).move_to(opcode);
- arg1 = 9; break;
- case 13 :
- CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],
- opcode[5],opcode[6],opcode[7],opcode[8],opcode[9],opcode[10],opcode[11],
- opcode[12],_cimg_mp_boundary).move_to(opcode);
- arg1 = 10; break;
- case 14 :
- arg1 = 10; break;
- default : // Error -> too few or too much arguments
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Too %s arguments specified, "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- opcode._height<5?"few":"much",s0);
- }
- _cimg_mp_check_const_scalar((unsigned int)opcode[1],2,3); // w
- opcode[1] = (ulongT)mem[opcode[1]];
- _cimg_mp_check_const_scalar((unsigned int)opcode[2],3,3); // h
- opcode[2] = (ulongT)mem[opcode[2]];
- _cimg_mp_check_const_scalar((unsigned int)opcode[3],4,3); // d
- opcode[3] = (ulongT)mem[opcode[3]];
- _cimg_mp_check_const_scalar((unsigned int)opcode[4],5,3); // s
- opcode[4] = (ulongT)mem[opcode[4]];
- p1 = _cimg_mp_size((unsigned int)opcode[0]);
- arg2 = (unsigned int)opcode[1];
- arg3 = (unsigned int)opcode[2];
- arg4 = (unsigned int)opcode[3];
- arg5 = (unsigned int)opcode[4];
- if (arg2*arg3*arg4*arg5!=p1)
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Input vector size (%lu values) and its specified "
- "geometry (%u,%u,%u,%u) (%lu values) do not match.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- p1,arg2,arg3,arg4,arg5,(ulongT)arg2*arg3*arg4*arg5);
- if (opcode[9]!=(ulongT)~0U) {
- _cimg_mp_check_const_scalar((unsigned int)opcode[9],arg1,3);
- opcode[9] = (ulongT)mem[opcode[9]];
- } else opcode[9] = opcode[1];
- if (opcode[10]!=(ulongT)~0U) {
- _cimg_mp_check_const_scalar((unsigned int)opcode[10],arg1 + 1,3);
- opcode[10] = (ulongT)mem[opcode[10]];
- } else opcode[10] = opcode[2];
- if (opcode[11]!=(ulongT)~0U) {
- _cimg_mp_check_const_scalar((unsigned int)opcode[11],arg1 + 2,3);
- opcode[11] = (ulongT)mem[opcode[11]];
- } else opcode[11] = opcode[3];
- if (opcode[12]!=(ulongT)~0U) {
- _cimg_mp_check_const_scalar((unsigned int)opcode[12],arg1 + 3,3);
- opcode[12] = (ulongT)mem[opcode[12]];
- } else opcode[12] = opcode[4];
- _cimg_mp_check_type((unsigned int)opcode[13],arg1 + 4,1,0);
- pos = vector((unsigned int)(opcode[9]*opcode[10]*opcode[11]*opcode[12]));
- CImg<ulongT>::vector((ulongT)mp_vector_crop_ext,
- pos,*opcode, // 1-2: res,S
- opcode[1],opcode[2],opcode[3],opcode[4], // 3-6: w,h,d,s
- opcode[5],opcode[6],opcode[7],opcode[8], // 7-10: x,y,z,c
- opcode[9],opcode[10],opcode[11],opcode[12], // 11-14: dx,dy,dz,dc
- opcode[13]).move_to(code); // 15: boundary conditions
- }
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"cross(",6)) { // Cross product
- _cimg_mp_op("Function 'cross()'");
- s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss6,s1,depth1,0,block_flags);
- arg2 = compile(++s1,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,1,2,3);
- _cimg_mp_check_type(arg2,2,2,3);
- pos = vector(3);
- CImg<ulongT>::vector((ulongT)mp_cross,pos,arg1,arg2).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"cut(",4)) { // Cut
- _cimg_mp_op("Function 'cut()'");
- s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss4,s1,depth1,0,block_flags);
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,0,block_flags);
- arg3 = compile(++s2,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg2,2,1,0);
- _cimg_mp_check_type(arg3,3,1,0);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_cut,arg1,arg2,arg3);
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2) && _cimg_mp_is_const_scalar(arg3)) {
- val = mem[arg1];
- val1 = mem[arg2];
- val2 = mem[arg3];
- _cimg_mp_const_scalar(val<val1?val1:val>val2?val2:val);
- }
- _cimg_mp_scalar3(mp_cut,arg1,arg2,arg3);
- }
- if (!std::strncmp(ss,"convolve(",9) || !std::strncmp(ss,"correlate(",10)) { // Convolve & Correlate
- is_sth = *ss2=='n'; // is_convolve?
- _cimg_mp_op(is_sth?"Function 'convolve()'":"Function 'correlate()'");
- op = is_sth?mp_convolve:mp_correlate;
- const ulongT default_params[] = { (ulongT)op,0, // [0]=function, [1]=result vector
- 0,0,0,0,0, // [2]=A, [3]=wA, [4]=hA, [5]=dA, [6]=sA
- 0,0,0,0,0, // [7]=M, [8]=wM, [9]=hM, [10]=dM, [11]=sM
- 1,0,1, // [12]=boundary_conditions, [13]=is_normalized, [14]=chan._mode
- ~0U,~0U,~0U, // [15]=xcenter, [16]=ycenter, [17]=zcenter
- 0,0,0, // [18]=xstart, [19]=ystart, [20]=zstart
- ~0U,~0U,~0U, // [21]=xend, [22]=yend, [23]=zend
- 1,1,1, // [24]=xstride, [25]=ystride, [26]=zstride
- 1,1,1, // [27]=xdilation, [28]=ydilation, [29]=zdilation,
- 0 }; // [30]=interpolation_type
- l_opcode.assign(); // Don't use 'opcode': it could be modified by further calls to 'compile()'!
- CImg<ulongT>(default_params,1,sizeof(default_params)/sizeof(ulongT)).move_to(l_opcode);
- arg1 = 2;
- for (s = std::strchr(ss,'(') + 1; s<se && arg1<l_opcode[0]._height; ++s) {
- ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
- (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
- l_opcode(0,arg1++) = compile(s,ns,depth1,0,block_flags);
- s = ns;
- }
- l_opcode[0].move_to(opcode);
- if (arg1<12 || arg1>opcode._height) {
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: %s arguments provided, in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- arg1<12?"Not enough":"Too much",s0);
- }
- _cimg_mp_check_type(opcode[2],1,2,0); // A
- _cimg_mp_check_const_scalar(opcode[3],2,3); // wA
- _cimg_mp_check_const_scalar(opcode[4],3,3); // hA
- _cimg_mp_check_const_scalar(opcode[5],4,3); // dA
- _cimg_mp_check_const_scalar(opcode[6],5,3); // sA
- _cimg_mp_check_type(opcode[7],6,2,0); // M
- _cimg_mp_check_const_scalar(opcode[8],7,3); // wM
- _cimg_mp_check_const_scalar(opcode[9],8,3); // hM
- _cimg_mp_check_const_scalar(opcode[10],9,3); // dM
- _cimg_mp_check_const_scalar(opcode[11],10,3); // sM
- _cimg_mp_check_type(opcode[12],11,1,0); // boundary_conditions
- _cimg_mp_check_type(opcode[13],12,1,0); // is_normalized
- _cimg_mp_check_const_scalar(opcode[14],13,1); // channel_mode
- if (opcode[15]!=~0U) _cimg_mp_check_type(opcode[15],14,1,0); // xcenter
- if (opcode[16]!=~0U) _cimg_mp_check_type(opcode[16],15,1,0); // ycenter
- if (opcode[17]!=~0U) _cimg_mp_check_type(opcode[17],16,1,0); // zcenter
- _cimg_mp_check_const_scalar(opcode[18],17,1); // xstart
- _cimg_mp_check_const_scalar(opcode[19],18,1); // ystart
- _cimg_mp_check_const_scalar(opcode[20],19,1); // zstart
- if (opcode[21]!=~0U) _cimg_mp_check_const_scalar(opcode[21],20,1); // xend
- if (opcode[22]!=~0U) _cimg_mp_check_const_scalar(opcode[22],21,1); // yend
- if (opcode[23]!=~0U) _cimg_mp_check_const_scalar(opcode[23],22,1); // zend
- _cimg_mp_check_const_scalar(opcode[24],23,0); // xstride
- _cimg_mp_check_const_scalar(opcode[25],24,0); // ystride
- _cimg_mp_check_const_scalar(opcode[26],25,0); // zstride
- _cimg_mp_check_type(opcode[27],26,1,0); // xdilation
- _cimg_mp_check_type(opcode[28],27,1,0); // ydilation
- _cimg_mp_check_type(opcode[29],28,1,0); // zdilation
- _cimg_mp_check_type(opcode[30],29,1,0); // interpolation_type
- const unsigned int
- wA = (unsigned int)mem[opcode[3]],
- hA = (unsigned int)mem[opcode[4]],
- dA = (unsigned int)mem[opcode[5]],
- sA = (unsigned int)mem[opcode[6]],
- wM = (unsigned int)mem[opcode[8]],
- hM = (unsigned int)mem[opcode[9]],
- dM = (unsigned int)mem[opcode[10]],
- sM = (unsigned int)mem[opcode[11]],
- channel_mode = (unsigned int)mem[opcode[14]];
- const int
- xstart = (int)mem[opcode[18]],
- ystart = (int)mem[opcode[19]],
- zstart = (int)mem[opcode[20]],
- xend = opcode[21]!=~0U?(int)mem[opcode[21]]:wA - 1,
- yend = opcode[22]!=~0U?(int)mem[opcode[22]]:hA - 1,
- zend = opcode[23]!=~0U?(int)mem[opcode[23]]:dA - 1;
- if (xstart>xend || ystart>yend || zstart>zend) {
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Invalid xyz-start/end arguments "
- "(start = (%d,%d,%d), end = (%d,%d,%d)), in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- xstart,ystart,zstart,xend,yend,zend,s0);
- }
- const float
- xstride = (float)mem[opcode[24]],
- ystride = (float)mem[opcode[25]],
- zstride = (float)mem[opcode[26]];
- if (xstride<=0 || ystride<=0 || zstride<=0) {
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Invalid stride arguments (%g,%g,%g), "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- xstride,ystride,zstride,s0);
- }
- arg2 = xend - xstart + 1;
- arg3 = yend - ystart + 1;
- arg4 = zend - zstart + 1;
- arg5 = !channel_mode?sA*sM:channel_mode==1?std::max(sA,sM):
- channel_mode==2?std::max(sA,sM)/std::min(sA,sM):1U;
- opcode[1] = pos = vector(arg2*arg3*arg4*arg5);
- opcode[3] = (ulongT)wA;
- opcode[4] = (ulongT)hA;
- opcode[5] = (ulongT)dA;
- opcode[6] = (ulongT)sA;
- opcode[8] = (ulongT)wM;
- opcode[9] = (ulongT)hM;
- opcode[10] = (ulongT)dM;
- opcode[11] = (ulongT)sM;
- opcode[14] = (ulongT)channel_mode;
- opcode[18] = (ulongT)xstart;
- opcode[19] = (ulongT)ystart;
- opcode[20] = (ulongT)zstart;
- opcode[21] = (ulongT)xend;
- opcode[22] = (ulongT)yend;
- opcode[23] = (ulongT)zend;
- opcode.move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- break;
- case 'd' :
- if (*ss1=='(') { // Image depth
- _cimg_mp_op("Function 'd()'");
- if (*ss2=='#') { // Index specified
- p1 = compile(ss3,se1,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- _cimg_mp_check_list();
- } else { if (ss2!=se1) break; p1 = ~0U; }
- _cimg_mp_scalar1(mp_image_d,p1);
- }
- if (!std::strncmp(ss,"da_back(",8) ||
- !std::strncmp(ss,"da_pop(",7) ||
- !std::strncmp(ss,"da_pop_heap(",12)) { // Get latest element in a dynamic array
- if (!is_inside_critical) is_parallelizable = false;
- const bool is_pop = *ss3=='p', is_pop_heap = *ss6=='_';
- _cimg_mp_op(is_pop_heap?"Function 'da_pop_heap()":
- is_pop?"Function 'da_pop()'":"Function 'da_back()'");
- s0 = ss + (is_pop_heap?12:is_pop?7:8);
- if (*s0=='#') { // Index specified
- s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- p1 = compile(s0,s1++,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- } else { p1 = 11; s1 = s0; }
- _cimg_mp_check_list();
- _cimg_mp_check_const_scalar(p1,1,1);
- p3 = (unsigned int)cimg::mod((int)mem[p1],imglist.width());
- p2 = imglist[p3]._spectrum;
- if (p2>1) pos = vector(p2); else pos = scalar(); // Return vector or scalar result
- CImg<ulongT>::vector((ulongT)mp_da_back_or_pop,pos,p2,p1,is_pop_heap?2:is_pop?1:0).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"da_insert(",10) ||
- !std::strncmp(ss,"da_push(",8) ||
- !std::strncmp(ss,"da_push_heap(",13)) { // Insert element(s) in a dynamic array
- if (!is_inside_critical) is_parallelizable = false;
- const bool is_push = *ss3=='p', is_push_heap = *ss7=='_';
- _cimg_mp_op(is_push_heap?"Function 'da_push_heap()'":
- is_push?"Function 'da_push()'":"Function 'da_insert()'");
- s0 = ss + (is_push_heap?13:is_push?8:10);
- if (*s0=='#') { // Index specified
- s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- p1 = compile(s0,s1++,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- } else { p1 = 11; s1 = s0; }
- _cimg_mp_check_list();
- if (!is_push) {
- s0 = s1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(s0,s1++,depth1,0,block_flags); // Position
- } else if (is_push_heap) arg1 = ~0U - 1;
- else arg1 = ~0U;
- CImg<ulongT>::vector((ulongT)mp_da_insert_or_push,_cimg_mp_slot_nan,p1,arg1,0,0).move_to(l_opcode);
- p3 = p1==~0U?2:3;
- p1 = ~0U;
- for (s = s1; s<se; ++s) {
- ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
- (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
- arg2 = compile(s,ns,depth1,0,block_flags); // Element
- p2 = _cimg_mp_size(arg2);
- if (p1==~0U) p1 = p2;
- else {
- if (!p1) _cimg_mp_check_type(arg2,p3,1,0);
- else _cimg_mp_check_type(arg2,p3,2,p1);
- }
- CImg<ulongT>::vector(arg2).move_to(l_opcode);
- s = ns;
- ++p3;
- }
- if (p1==~0U) compile(++s1,se1,depth1,0,block_flags); // Missing element -> error
- (l_opcode>'y').move_to(opcode);
- opcode[4] = p1;
- opcode[5] = opcode._height;
- opcode.move_to(code);
- _cimg_mp_return_nan();
- }
- if (!std::strncmp(ss,"da_freeze(",10)) { // Freeze dynamic array
- if (!is_inside_critical) is_parallelizable = false;
- _cimg_mp_op("Function 'da_freeze()'");
- s0 = ss + 10;
- if (*s0=='#') { // Index specified
- s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- p1 = compile(s0,s1++,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- } else { p1 = 11; s1 = s0; }
- _cimg_mp_check_list();
- CImg<ulongT>::vector((ulongT)mp_da_freeze,_cimg_mp_slot_nan,p1).move_to(code);
- _cimg_mp_return_nan();
- }
- if (!std::strncmp(ss,"da_remove(",10)) { // Remove element(s) in a dynamic array
- if (!is_inside_critical) is_parallelizable = false;
- _cimg_mp_op("Function 'da_remove()'");
- if (ss[10]=='#') { // Index specified
- s0 = ss + 11; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- p1 = compile(ss + 11,s0++,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- } else { p1 = 11; s0 = ss + 10; }
- _cimg_mp_check_list();
- arg1 = arg2 = ~0U;
- if (s0<se1) {
- s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(s0,s1,depth1,0,block_flags); // Starting position
- arg2 = s1<se1?compile(++s1,se1,depth1,0,block_flags):~0U; // Ending position
- }
- CImg<ulongT>::vector((ulongT)mp_da_remove,_cimg_mp_slot_nan,p1,arg1,arg2).move_to(code);
- _cimg_mp_return_nan();
- }
- if (!std::strncmp(ss,"da_size(",8)) { // Size of a dynamic array
- if (!is_inside_critical) is_parallelizable = false;
- _cimg_mp_op("Function 'da_size()'");
- if (ss[8]=='#') { // Index specified
- s0 = ss + 9; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- p1 = compile(ss + 9,s0++,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- } else { p1 = 11; s0 = ss + 8; }
- _cimg_mp_check_list();
- _cimg_mp_scalar1(mp_da_size,p1);
- }
- if (!std::strncmp(ss,"date(",5)) { // Current date or file date
- _cimg_mp_op("Function 'date()'");
- s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = ss5!=se1?compile(ss5,s1,depth1,0,block_flags):~0U;
- arg2 = s1<se1?compile(++s1,se1,depth1,0,block_flags):~0U;
- if (arg2!=~0U) _cimg_mp_check_type(arg2,1,2,0);
- pos = arg1==~0U || _cimg_mp_is_vector(arg1)?vector(arg1==~0U?7:_cimg_mp_size(arg1)):scalar();
- CImg<ulongT>::vector((ulongT)mp_date,pos,_cimg_mp_size(pos),
- arg1,arg1==~0U?~0U:_cimg_mp_size(arg1),
- arg2,arg2==~0U?~0U:_cimg_mp_size(arg2)).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"debug(",6)) { // Print debug info
- _cimg_mp_op("Function 'debug()'");
- p1 = code._width;
- arg1 = compile(ss6,se1,depth1,p_ref,block_flags);
- *se1 = 0;
- variable_name.assign(CImg<charT>::string(ss6,true,true).unroll('y'),true);
- cimg::strpare(variable_name,false,true);
- ((CImg<ulongT>::vector((ulongT)mp_debug,arg1,0,code._width - p1),
- variable_name)>'y').move_to(opcode);
- opcode[2] = opcode._height;
- opcode.move_to(code,p1);
- *se1 = ')';
- _cimg_mp_return(arg1);
- }
- if (!std::strncmp(ss,"deg2rad(",8)) { // Degrees to radians
- _cimg_mp_op("Function 'deg2rad()'");
- arg1 = compile(ss8,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_deg2rad,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(mem[arg1]*cimg::PI/180);
- _cimg_mp_scalar1(mp_deg2rad,arg1);
- }
- if (!std::strncmp(ss,"display(",8)) { // Display memory, vector or image
- _cimg_mp_op("Function 'display()'");
- if (pexpr[se2 - expr._data]=='(') { // no arguments?
- CImg<ulongT>::vector((ulongT)mp_display_memory,_cimg_mp_slot_nan).move_to(code);
- _cimg_mp_return_nan();
- }
- if (*ss8!='#') { // Vector
- s1 = ss8; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss8,s1,depth1,0,block_flags);
- arg2 = 0; arg3 = arg4 = arg5 = 1;
- if (s1<se1) {
- s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1 + 1,s2,depth1,0,block_flags);
- if (s2<se1) {
- s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
- arg3 = compile(s2,s3,depth1,0,block_flags);
- if (s3<se1) {
- s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg4 = compile(s3,s2,depth1,0,block_flags);
- arg5 = s2<se1?compile(++s2,se1,depth1,0,block_flags):0;
- }
- }
- }
- _cimg_mp_check_type(arg2,2,1,0);
- _cimg_mp_check_type(arg3,3,1,0);
- _cimg_mp_check_type(arg4,4,1,0);
- _cimg_mp_check_type(arg5,5,1,0);
- c1 = *s1; *s1 = 0;
- variable_name.assign(CImg<charT>::string(ss8,true,true).unroll('y'),true);
- cimg::strpare(variable_name,false,true);
- if (_cimg_mp_is_vector(arg1))
- ((CImg<ulongT>::vector((ulongT)mp_vector_print,arg1,0,(ulongT)_cimg_mp_size(arg1),0),
- variable_name)>'y').move_to(opcode);
- else
- ((CImg<ulongT>::vector((ulongT)mp_print,arg1,0,0),
- variable_name)>'y').move_to(opcode);
- opcode[2] = opcode._height;
- opcode.move_to(code);
- ((CImg<ulongT>::vector((ulongT)mp_display,arg1,0,(ulongT)_cimg_mp_size(arg1),
- arg2,arg3,arg4,arg5),
- variable_name)>'y').move_to(opcode);
- opcode[2] = opcode._height;
- opcode.move_to(code);
- *s1 = c1;
- _cimg_mp_return(arg1);
- } else { // Image
- p1 = compile(ss8 + 1,se1,depth1,0,block_flags);
- _cimg_mp_check_list();
- CImg<ulongT>::vector((ulongT)mp_image_display,_cimg_mp_slot_nan,p1).move_to(code);
- _cimg_mp_return_nan();
- }
- }
- if (!std::strncmp(ss,"det(",4)) { // Matrix determinant
- _cimg_mp_op("Function 'det()'");
- arg1 = compile(ss4,se1,depth1,0,block_flags);
- _cimg_mp_check_matrix_square(arg1,1);
- p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
- _cimg_mp_scalar2(mp_det,arg1,p1);
- }
- if (!std::strncmp(ss,"diag(",5)) { // Diagonal matrix
- _cimg_mp_op("Function 'diag()'");
- CImg<ulongT>::vector((ulongT)mp_diag,0,0).move_to(l_opcode);
- for (s = ss5; s<se; ++s) {
- ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
- (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
- arg2 = compile(s,ns,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg2))
- CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
- arg2 + (ulongT)_cimg_mp_size(arg2)).
- move_to(l_opcode);
- else CImg<ulongT>::vector(arg2).move_to(l_opcode);
- s = ns;
- }
- (l_opcode>'y').move_to(opcode);
- arg1 = opcode._height - 3;
- pos = vector(arg1*arg1);
- opcode[1] = pos;
- opcode[2] = opcode._height;
- opcode.move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"dot(",4)) { // Dot product
- _cimg_mp_op("Function 'dot()'");
- s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss4,s1,depth1,0,block_flags);
- arg2 = compile(++s1,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) {
- _cimg_mp_check_type(arg2,2,2,_cimg_mp_size(arg1));
- _cimg_mp_scalar3(mp_dot,arg1,arg2,_cimg_mp_size(arg1));
- }
- _cimg_mp_check_type(arg2,2,1,0);
- _cimg_mp_scalar2(mp_mul,arg1,arg2);
- }
- if (!std::strncmp(ss,"do(",3)) { // Do..while
- _cimg_mp_op("Function 'do()'");
- s0 = *ss2=='('?ss3:ss8;
- s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = code._width;
- arg6 = mempos;
- p1 = compile(s0,s1,depth1,0,block_flags); // Body
- arg2 = code._width;
- p2 = s1<se1?compile(++s1,se1,depth1,0,block_flags):p1; // Condition
- _cimg_mp_check_type(p2,2,1,0);
- CImg<ulongT>::vector((ulongT)mp_do,p1,p2,arg2 - arg1,code._width - arg2,_cimg_mp_size(p1),
- p1>=arg6 && !_cimg_mp_is_const_scalar(p1),
- p2>=arg6 && !_cimg_mp_is_const_scalar(p2)).move_to(code,arg1);
- _cimg_mp_return(p1);
- }
- if (!std::strncmp(ss,"draw(",5)) { // Draw image
- _cimg_mp_op("Function 'draw()'");
- if (*ss5=='#') { // Index specified
- s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- p1 = compile(ss6,s0++,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- _cimg_mp_check_list();
- } else { p1 = ~0U; s0 = ss5; }
- for (s = s0; s<se; ++s, ++pos) {
- ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
- (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
- arg1 = compile(s,ns,depth1,0,block_flags);
- CImg<ulongT>::vector(arg1).move_to(l_opcode);
- s = ns;
- }
- (l_opcode>'y').move_to(opcode);
- is_sth = p1==~0U && opcode._height>5 && _cimg_mp_is_vector((unsigned int)opcode[5]); // Is vector drawing?
- if ((is_sth && (opcode._height<6 || opcode._height>17)) ||
- (!is_sth && (opcode._height<1 || opcode._height>12))) {
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Too %s arguments specified, "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- opcode._height>12?"much":"few",s0);
- }
- if (is_sth) { // Drawing in a vector
- _cimg_mp_check_type((unsigned int)*opcode,1,2,0); // D
- _cimg_mp_check_type((unsigned int)opcode[1],2,1,0); // w
- _cimg_mp_check_type((unsigned int)opcode[2],3,1,0); // h
- _cimg_mp_check_type((unsigned int)opcode[3],4,1,0); // d
- _cimg_mp_check_type((unsigned int)opcode[4],5,1,0); // s
- if (opcode._height<8 || (opcode._height<10 && _cimg_mp_is_vector((unsigned int)opcode[7]))) {
- // D,w,h,d,s,S[,opac,M,maxM]
- if (opcode._height>6) _cimg_mp_check_type((unsigned int)opcode[6],7,1,0); // opac
- if (opcode._height>8) _cimg_mp_check_type((unsigned int)opcode[8],9,1,0); // maxM
- CImg<ulongT>::vector((ulongT)mp_vector_draw,
- *opcode,_cimg_mp_size((unsigned int)*opcode), // 1-2: D,sizD
- opcode[1],opcode[2],opcode[3],opcode[4], // 3-6: w,h,d,s
- opcode[5],_cimg_mp_size((unsigned int)opcode[5]), // 7-8: S,sizS
- 0,0,0,0, // 9-12: x,y,z,c
- ~0U,~0U,~0U,~0U, // 13-16: dx,dy,dz,dc
- opcode._height<7?1:opcode[6], // 17: opac
- opcode._height<8?~0U:opcode[7], // 18: M
- opcode._height<8?0:_cimg_mp_size((unsigned int)opcode[7]), // 19: sizM
- opcode._height<9?1:opcode[8]).move_to(code); // 20: maxM
- } else if (opcode._height<10 || (opcode._height<12 && _cimg_mp_is_vector((unsigned int)opcode[9]))) {
- // D,w,h,d,s,S,x,dx[,opac,M,maxM]
- _cimg_mp_check_type((unsigned int)opcode[6],7,1,0); // x
- _cimg_mp_check_type((unsigned int)opcode[7],8,1,0); // dx
- if (opcode._height>8) _cimg_mp_check_type((unsigned int)opcode[8],9,1,0); // opac
- if (opcode._height>10) _cimg_mp_check_type((unsigned int)opcode[10],11,1,0); // maxM
- CImg<ulongT>::vector((ulongT)mp_vector_draw,
- *opcode,_cimg_mp_size((unsigned int)*opcode), // 1-2: D,sizD
- opcode[1],opcode[2],opcode[3],opcode[4], // 3-6: w,h,d,s
- opcode[5],_cimg_mp_size((unsigned int)opcode[5]), // 7-8: S,sizS
- opcode[6],0,0,0, // 9-12: x,y,z,c
- opcode[7],~0U,~0U,~0U, // 13-16: dx,dy,dz,dc
- opcode._height<9?1:opcode[8], // 17: opac
- opcode._height<10?~0U:opcode[9], // 18: M
- opcode._height<10?0:_cimg_mp_size((unsigned int)opcode[9]), // 19: sizM
- opcode._height<11?1:opcode[10]).move_to(code); // 20: maxM
- } else if (opcode._height<12 || (opcode._height<14 && _cimg_mp_is_vector((unsigned int)opcode[11]))) {
- // D,w,h,d,s,S,x,y,dx,dy[,opac,M,maxM]
- _cimg_mp_check_type((unsigned int)opcode[6],7,1,0); // x
- _cimg_mp_check_type((unsigned int)opcode[7],8,1,0); // y
- _cimg_mp_check_type((unsigned int)opcode[8],9,1,0); // dx
- _cimg_mp_check_type((unsigned int)opcode[9],10,1,0); // dy
- if (opcode._height>10) _cimg_mp_check_type((unsigned int)opcode[10],11,1,0); // opac
- if (opcode._height>12) _cimg_mp_check_type((unsigned int)opcode[12],13,1,0); // maxM
- CImg<ulongT>::vector((ulongT)mp_vector_draw,
- *opcode,_cimg_mp_size((unsigned int)*opcode), // 1-2: D,sizD
- opcode[1],opcode[2],opcode[3],opcode[4], // 3-6: w,h,d,s
- opcode[5],_cimg_mp_size((unsigned int)opcode[5]), // 7-8: S,sizS
- opcode[6],opcode[7],0,0, // 9-12: x,y,z,c
- opcode[8],opcode[9],~0U,~0U, // 13-16: dx,dy,dz,dc
- opcode._height<11?1:opcode[10], // 17: opac
- opcode._height<12?~0U:opcode[11], // 18: M
- opcode._height<12?0:_cimg_mp_size((unsigned int)opcode[11]), // 19: sizM
- opcode._height<13?1:opcode[12]).move_to(code); // 20: maxM
- } else if (opcode._height<14 || (opcode._height<16 && _cimg_mp_is_vector((unsigned int)opcode[13]))) {
- // D,w,h,d,s,S,x,y,z,dx,dy,dz[,opac,M,maxM]
- _cimg_mp_check_type((unsigned int)opcode[6],7,1,0); // x
- _cimg_mp_check_type((unsigned int)opcode[7],8,1,0); // y
- _cimg_mp_check_type((unsigned int)opcode[8],9,1,0); // z
- _cimg_mp_check_type((unsigned int)opcode[9],10,1,0); // dx
- _cimg_mp_check_type((unsigned int)opcode[10],11,1,0); // dy
- _cimg_mp_check_type((unsigned int)opcode[11],12,1,0); // dz
- if (opcode._height>12) _cimg_mp_check_type((unsigned int)opcode[12],13,1,0); // opac
- if (opcode._height>14) _cimg_mp_check_type((unsigned int)opcode[14],15,1,0); // maxM
- CImg<ulongT>::vector((ulongT)mp_vector_draw,
- *opcode,_cimg_mp_size((unsigned int)*opcode), // 1-2: D,sizD
- opcode[1],opcode[2],opcode[3],opcode[4], // 3-6: w,h,d,s
- opcode[5],_cimg_mp_size((unsigned int)opcode[5]), // 7-8: S,sizS
- opcode[6],opcode[7],opcode[8],0, // 9-12: x,y,z,c
- opcode[9],opcode[10],opcode[11],~0U, // 13-16: dx,dy,dz,dc
- opcode._height<13?1:opcode[12], // 17: opac
- opcode._height<14?~0U:opcode[13], // 18: M
- opcode._height<14?0:_cimg_mp_size((unsigned int)opcode[13]), // 19: sizM
- opcode._height<15?1:opcode[14]).move_to(code); // 20: maxM
- } else if (opcode._height<16 || (opcode._height<18 && _cimg_mp_is_vector((unsigned int)opcode[15]))) {
- // D,w,h,d,s,S,x,y,z,c,dx,dy,dz,dc[,opac,M,maxM]
- _cimg_mp_check_type((unsigned int)opcode[6],7,1,0); // x
- _cimg_mp_check_type((unsigned int)opcode[7],8,1,0); // y
- _cimg_mp_check_type((unsigned int)opcode[8],9,1,0); // z
- _cimg_mp_check_type((unsigned int)opcode[9],10,1,0); // c
- _cimg_mp_check_type((unsigned int)opcode[10],11,1,0); // dx
- _cimg_mp_check_type((unsigned int)opcode[11],12,1,0); // dy
- _cimg_mp_check_type((unsigned int)opcode[12],13,1,0); // dz
- _cimg_mp_check_type((unsigned int)opcode[13],14,1,0); // dc
- if (opcode._height>14) _cimg_mp_check_type((unsigned int)opcode[14],15,1,0); // opac
- if (opcode._height>16) _cimg_mp_check_type((unsigned int)opcode[16],17,1,0); // maxM
- CImg<ulongT>::vector((ulongT)mp_vector_draw,
- *opcode,_cimg_mp_size((unsigned int)*opcode), // 1-2: D,sizD
- opcode[1],opcode[2],opcode[3],opcode[4], // 3-6: w,h,d,s
- opcode[5],_cimg_mp_size((unsigned int)opcode[5]), // 7-8: S,sizS
- opcode[6],opcode[7],opcode[8],opcode[9], // 9-12: x,y,z,c
- opcode[10],opcode[11],opcode[12],opcode[13], // 13-16: dx,dy,dz,dc
- opcode._height<15?1:opcode[14], // 17: opac
- opcode._height<16?~0U:opcode[15], // 18: M
- opcode._height<16?0:_cimg_mp_size((unsigned int)opcode[15]), // 19: sizM
- opcode._height<17?1:opcode[16]).move_to(code); // 20: maxM
- } else {
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Invalid types in specified arguments, "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,s0);
- }
- } else { // Drawing in an image
- if (!is_inside_critical) is_parallelizable = false;
- arg1 = p1!=~0U;
- _cimg_mp_check_type((unsigned int)*opcode,1 + arg1,2,0); // S
- if (opcode._height<3 || (opcode._height<5 && _cimg_mp_is_vector((unsigned int)opcode[2]))) {
- // S[,opac,M,maxM]
- if (opcode._height>1) _cimg_mp_check_type((unsigned int)opcode[1],2 + arg1,1,0); // opac
- if (opcode._height>3) _cimg_mp_check_type((unsigned int)opcode[3],4 + arg1,1,0); // maxM
- CImg<ulongT>::vector((ulongT)mp_image_draw,
- *opcode,_cimg_mp_size((unsigned int)*opcode),p1, // 1-3: S,sizS,#ind
- 0,0,0,0, // 4-7: x,y,z,c
- ~0U,~0U,~0U,~0U, // 8-11: dx,dy,dz,dc
- opcode._height<2?1:opcode[1], // 12: opac
- opcode._height<3?~0U:opcode[2], // 13: M
- opcode._height<3?0:_cimg_mp_size((unsigned int)opcode[2]), // 14: sizM
- opcode._height<4?1:opcode[3]).move_to(code); // 15: maxM
- } else if (opcode._height<5 || (opcode._height<7 && _cimg_mp_is_vector((unsigned int)opcode[4]))) {
- // x,dx,S[,opac,M,maxM]
- _cimg_mp_check_type((unsigned int)opcode[1],2 + arg1,1,0); // x
- _cimg_mp_check_type((unsigned int)opcode[2],3 + arg1,1,0); // dx
- if (opcode._height>3) _cimg_mp_check_type((unsigned int)opcode[3],4 + arg1,1,0); // opac
- if (opcode._height>5) _cimg_mp_check_type((unsigned int)opcode[5],6 + arg1,1,0); // maxM
- CImg<ulongT>::vector((ulongT)mp_image_draw,
- *opcode,_cimg_mp_size((unsigned int)*opcode),p1, // 1-3: S,sizS,#ind
- opcode[1],0,0,0, // 4-7: x,y,z,c
- opcode[2],~0U,~0U,~0U, // 8-11: dx,dy,dz,dc
- opcode._height<4?1:opcode[3], // 12: opac
- opcode._height<5?~0U:opcode[4], // 13: M
- opcode._height<5?0:_cimg_mp_size((unsigned int)opcode[4]), // 14: sizM
- opcode._height<6?1:opcode[5]).move_to(code); // 15: maxM
- } else if (opcode._height<7 || (opcode._height<9 && _cimg_mp_is_vector((unsigned int)opcode[6]))) {
- // x,y,dx,dy,S[,opac,M,maxM]
- _cimg_mp_check_type((unsigned int)opcode[1],2 + arg1,1,0); // x
- _cimg_mp_check_type((unsigned int)opcode[2],3 + arg1,1,0); // y
- _cimg_mp_check_type((unsigned int)opcode[3],4 + arg1,1,0); // dx
- _cimg_mp_check_type((unsigned int)opcode[4],5 + arg1,1,0); // dy
- if (opcode._height>5) _cimg_mp_check_type((unsigned int)opcode[5],6 + arg1,1,0); // opac
- if (opcode._height>7) _cimg_mp_check_type((unsigned int)opcode[7],8 + arg1,1,0); // maxM
- CImg<ulongT>::vector((ulongT)mp_image_draw,
- *opcode,_cimg_mp_size((unsigned int)*opcode),p1, // 1-3: S,sizS,#ind
- opcode[1],opcode[2],0,0, // 4-7: x,y,z,c
- opcode[3],opcode[4],~0U,~0U, // 8-11: dx,dy,dz,dc
- opcode._height<6?1:opcode[5], // 12: opac
- opcode._height<7?~0U:opcode[6], // 13: M
- opcode._height<7?0:_cimg_mp_size((unsigned int)opcode[6]), // 14: sizM
- opcode._height<8?1:opcode[7]).move_to(code); // 15: maxM
- } else if (opcode._height<9 || (opcode._height<11 && _cimg_mp_is_vector((unsigned int)opcode[8]))) {
- // x,y,z,dx,dy,dz,S[,opac,M,maxM]
- _cimg_mp_check_type((unsigned int)opcode[1],2 + arg1,1,0); // x
- _cimg_mp_check_type((unsigned int)opcode[2],3 + arg1,1,0); // y
- _cimg_mp_check_type((unsigned int)opcode[3],4 + arg1,1,0); // z
- _cimg_mp_check_type((unsigned int)opcode[4],5 + arg1,1,0); // dx
- _cimg_mp_check_type((unsigned int)opcode[5],6 + arg1,1,0); // dy
- _cimg_mp_check_type((unsigned int)opcode[6],7 + arg1,1,0); // dz
- if (opcode._height>7) _cimg_mp_check_type((unsigned int)opcode[7],8 + arg1,1,0); // opac
- if (opcode._height>9) _cimg_mp_check_type((unsigned int)opcode[9],10 + arg1,1,0); // maxM
- CImg<ulongT>::vector((ulongT)mp_image_draw,
- *opcode,_cimg_mp_size((unsigned int)*opcode),p1, // 1-3: S,sizS,#ind
- opcode[1],opcode[2],opcode[3],0, // 4-7: x,y,z,c
- opcode[4],opcode[5],opcode[6],~0U, // 8-11: dx,dy,dz,dc
- opcode._height<8?1:opcode[7], // 12: opac
- opcode._height<9?~0U:opcode[8], // 13: M
- opcode._height<9?0:_cimg_mp_size((unsigned int)opcode[8]), // 14: sizM
- opcode._height<10?1:opcode[9]).move_to(code); // 15: maxM
- } else if (opcode._height<11 || (opcode._height<13 && _cimg_mp_is_vector((unsigned int)opcode[10]))) {
- // x,y,z,c,dx,dy,dz,dc,S[,opac,M,maxM]
- _cimg_mp_check_type((unsigned int)opcode[1],2 + arg1,1,0); // x
- _cimg_mp_check_type((unsigned int)opcode[2],3 + arg1,1,0); // y
- _cimg_mp_check_type((unsigned int)opcode[3],4 + arg1,1,0); // z
- _cimg_mp_check_type((unsigned int)opcode[4],5 + arg1,1,0); // c
- _cimg_mp_check_type((unsigned int)opcode[5],6 + arg1,1,0); // dx
- _cimg_mp_check_type((unsigned int)opcode[6],7 + arg1,1,0); // dy
- _cimg_mp_check_type((unsigned int)opcode[7],8 + arg1,1,0); // dz
- _cimg_mp_check_type((unsigned int)opcode[8],9 + arg1,1,0); // dc
- if (opcode._height>9) _cimg_mp_check_type((unsigned int)opcode[9],10 + arg1,1,0); // opac
- if (opcode._height>11) _cimg_mp_check_type((unsigned int)opcode[11],12 + arg1,1,0); // maxM
- CImg<ulongT>::vector((ulongT)mp_image_draw,
- *opcode,_cimg_mp_size((unsigned int)*opcode),p1, // 1-3: S,sizS,#ind
- opcode[1],opcode[2],opcode[3],opcode[4], // 4-7: x,y,z,c
- opcode[5],opcode[6],opcode[7],opcode[8], // 8-11: dx,dy,dz,dc
- opcode._height<10?1:opcode[9], // 12: opac
- opcode._height<11?~0U:opcode[10], // 13: M
- opcode._height<11?0:_cimg_mp_size((unsigned int)opcode[10]), // 14: sizM
- opcode._height<12?1:opcode[11]).move_to(code); // 15: maxM
- } else {
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Invalid types in specified arguments, "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,s0);
- }
- }
- _cimg_mp_return_nan();
- }
- break;
- case 'e' :
- if (!std::strncmp(ss,"echo(",5)) { // Echo
- _cimg_mp_op("Function 'echo()'");
- CImg<ulongT>::vector((ulongT)mp_echo,_cimg_mp_slot_nan,0).move_to(l_opcode);
- for (s = ss5; s<se1; ++s) {
- ns = s; while (ns<se1 && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
- (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
- arg1 = compile(s,ns,depth1,0,block_flags);
- CImg<ulongT>::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode);
- s = ns;
- }
- (l_opcode>'y').move_to(opcode);
- opcode[2] = opcode._height;
- opcode.move_to(code);
- _cimg_mp_return_nan();
- }
- if (!std::strncmp(ss,"eig(",4)) { // Matrix eigenvalues/eigenvector
- _cimg_mp_op("Function 'eig()'");
- arg1 = compile(ss4,se1,depth1,0,block_flags);
- _cimg_mp_check_matrix_square(arg1,1);
- p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
- pos = vector((p1 + 1)*p1);
- CImg<ulongT>::vector((ulongT)mp_matrix_eig,pos,arg1,p1).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"ellipse(",8)) { // Ellipse/circle drawing
- if (!is_inside_critical) is_parallelizable = false;
- _cimg_mp_op("Function 'ellipse()'");
- if (*ss8=='#') { // Index specified
- s0 = ss + 9; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- p1 = compile(ss + 9,s0++,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- _cimg_mp_check_list();
- } else { p1 = ~0U; s0 = ss8; }
- if (s0==se1) compile(s0,se1,depth1,0,block_flags); // 'missing' argument error
- CImg<ulongT>::vector((ulongT)mp_ellipse,_cimg_mp_slot_nan,0,p1).move_to(l_opcode);
- for (s = s0; s<se; ++s) {
- ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
- (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
- arg2 = compile(s,ns,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg2)) // Vector argument allowed to specify cordinates and color
- CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
- arg2 + (ulongT)_cimg_mp_size(arg2)).
- move_to(l_opcode);
- else
- CImg<ulongT>::vector(arg2).move_to(l_opcode);
- s = ns;
- }
- (l_opcode>'y').move_to(opcode);
- opcode[2] = opcode._height;
- opcode.move_to(code);
- _cimg_mp_return_nan();
- }
- #if cimg_use_cpp11==1
- if (!std::strncmp(ss,"erf(",4)) { // Error function
- _cimg_mp_op("Function 'erf()'");
- arg1 = compile(ss4,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_erf,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::erf(mem[arg1]));
- _cimg_mp_scalar1(mp_erf,arg1);
- }
- #endif
- if (!std::strncmp(ss,"erfinv(",7)) { // Inverse of error function
- _cimg_mp_op("Function 'erfinv()'");
- arg1 = compile(ss7,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_erfinv,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::erfinv(mem[arg1]));
- _cimg_mp_scalar1(mp_erfinv,arg1);
- }
- if (!std::strncmp(ss,"exp(",4)) { // Exponential
- _cimg_mp_op("Function 'exp()'");
- arg1 = compile(ss4,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_exp,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::exp(mem[arg1]));
- _cimg_mp_scalar1(mp_exp,arg1);
- }
- if (!std::strncmp(ss,"expr(",5)) { // Vector from expression
- _cimg_mp_op("Function 'expr()'");
- s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss5,s1,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,1,2,0);
- p1 = _cimg_mp_size(arg1);
- arg2 = arg3 = arg4 = arg5 = 0;
- if (s1<se1) {
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,0,block_flags);
- _cimg_mp_check_const_scalar(arg2,2,2);
- arg2 = (unsigned int)mem[arg2];
- if (arg2) arg3 = arg4 = arg5 = 1;
- if (s2<se1) {
- s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg3 = compile(s2,s1,depth1,0,block_flags);
- _cimg_mp_check_const_scalar(arg3,3,3);
- arg3 = (unsigned int)mem[arg3];
- if (s1<se1) {
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg4 = compile(s1,s2,depth1,0,block_flags);
- _cimg_mp_check_const_scalar(arg4,4,3);
- arg4 = (unsigned int)mem[arg4];
- arg5 = s2<se1?compile(++s2,se1,depth1,0,block_flags):0;
- _cimg_mp_check_const_scalar(arg5,5,3);
- arg5 = (unsigned int)mem[arg5];
- }
- }
- }
- p2 = arg2*arg3*arg4*arg5;
- if (p2) pos = vector(p2); else pos = scalar();
- CImg<ulongT>::vector((ulongT)mp_expr,pos,arg1,p1,arg2,arg3,arg4,arg5).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"eye(",4)) { // Identity matrix
- _cimg_mp_op("Function 'eye()'");
- arg1 = compile(ss4,se1,depth1,0,block_flags);
- _cimg_mp_check_const_scalar(arg1,1,3);
- p1 = (unsigned int)mem[arg1];
- pos = vector(p1*p1);
- CImg<ulongT>::vector((ulongT)mp_eye,pos,p1).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"end(",4)) { // End
- _cimg_mp_op("Function 'end()'");
- s1 = ss4; while (s1<se1 && cimg::is_blank(*s1)) ++s1;
- if (s1!=se1) {
- if (!is_inside_end) code.swap(code_end);
- pos = compile(s1,se1,depth1,p_ref,8);
- if (!is_inside_end) code.swap(code_end);
- result_end_dim = _cimg_mp_size(pos);
- result_end = mem._data + pos;
- is_end_code = true;
- }
- _cimg_mp_return_nan();
- }
- if (!std::strncmp(ss,"end_t(",6)) { // End thread
- _cimg_mp_op("Function 'end_t()'");
- s1 = ss6; while (s1<se1 && cimg::is_blank(*s1)) ++s1;
- if (s1!=se1) {
- if (!is_inside_end_t) code.swap(code_end_t);
- compile(s1,se1,depth1,p_ref,16);
- if (!is_inside_end_t) code.swap(code_end_t);
- is_end_code = true;
- }
- _cimg_mp_return_nan();
- }
- break;
- case 'f' :
- if (!std::strncmp(ss,"f2ui(",5)) { // Special float->uint conversion
- _cimg_mp_op("Function 'f2ui()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_f2ui,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar((double)cimg::float2uint((float)mem[arg1]));
- _cimg_mp_scalar1(mp_f2ui,arg1);
- }
- if (!std::strncmp(ss,"fact(",5)) { // Factorial
- _cimg_mp_op("Function 'fact()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_factorial,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::factorial((int)mem[arg1]));
- _cimg_mp_scalar1(mp_factorial,arg1);
- }
- if (!std::strncmp(ss,"fibo(",5)) { // Fibonacci
- _cimg_mp_op("Function 'fibo()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_fibonacci,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::fibonacci((int)mem[arg1]));
- _cimg_mp_scalar1(mp_fibonacci,arg1);
- }
- if (!std::strncmp(ss,"fill(",5)) { // Fill
- _cimg_mp_op("Function 'fill()'");
- s0 = ss5; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- arg1 = compile(ss5,s0,depth1,0,block_flags); // Object to fill
- if (_cimg_mp_is_const_scalar(arg1))
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Target scalar is constant, "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,ss);
- s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- p1 = code._width;
- if (s1<se1) { // Version with 3 arguments
- variable_name.assign(s0,(unsigned int)(s1 + 1 - s0)).back() = 0;
- cimg::strpare(variable_name,false,true);
- if (!cimg::is_varname(variable_name)) { // Invalid variable name
- cimg::strellipsize(variable_name,64);
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Invalid loop variable name '%s', "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- variable_name._data,s0);
- }
- get_variable_pos(variable_name,arg2,arg3);
- arg2 = arg3!=~0U?reserved_label[arg3]:arg2!=~0U?variable_pos[arg2]:~0U; // Variable slot
- if (arg2!=~0U && (!_cimg_mp_is_scalar(arg2) ||
- _cimg_mp_is_const_scalar(arg2))) { // Variable is not a vector or is a constant->error
- cimg::strellipsize(variable_name,64);
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Invalid type '%s' for variable '%s' "
- "(expected 'scalar'), in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- s_type(arg2)._data,variable_name._data,s0);
- } else if (arg2==~0U) { // Variable does not exist -> create it
- arg2 = scalar();
- if (arg3!=~0U) reserved_label[arg3] = arg2;
- else {
- if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0);
- variable_pos[variable_def._width] = arg2;
- variable_name.move_to(variable_def);
- }
- memtype[arg2] = -1;
- }
- arg3 = compile(++s1,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg3,3,1,0);
- } else { // Version with 2 arguments
- arg2 = ~0U;
- arg3 = compile(s0,se1,depth1,0,block_flags);
- }
- // arg2 = variable slot, arg3 = fill expression.
- _cimg_mp_check_type(arg3,3,1,0);
- CImg<ulongT>::vector((ulongT)mp_fill,arg1,_cimg_mp_size(arg1),arg2,arg3,code._width - p1).
- move_to(code,p1);
- _cimg_mp_return_nan();
- }
- if (!std::strncmp(ss,"find(",5)) { // Find
- _cimg_mp_op("Function 'find()'");
- // First argument: data to look at.
- s0 = ss5; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- if (*ss5=='#') { // Index specified
- p1 = compile(ss6,s0,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- _cimg_mp_check_list();
- arg1 = ~0U;
- } else { // Vector specified
- arg1 = compile(ss5,s0,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,1,2,0);
- p1 = ~0U;
- }
- // Second argument: data to find.
- s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg2 = compile(s0,s1,depth1,0,block_flags);
- // Third and fourth arguments: starting index and search direction.
- arg3 = _cimg_mp_slot_nan; arg4 = 1;
- if (s1<se1) {
- s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- arg3 = compile(s1,s0,depth1,0,block_flags);
- _cimg_mp_check_type(arg3,3,1,0);
- if (s0<se1) {
- arg4 = compile(++s0,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg4,4,1,0);
- }
- }
- if (p1!=~0U) {
- if (_cimg_mp_size(arg2)>1)
- _cimg_mp_scalar5(mp_list_find_seq,p1,arg2,_cimg_mp_size(arg2),arg3,arg4);
- _cimg_mp_scalar4(mp_list_find,p1,arg2 + (_cimg_mp_size(arg2)?1:0),arg3,arg4);
- }
- if (_cimg_mp_size(arg2)>1)
- _cimg_mp_scalar6(mp_find_seq,arg1,_cimg_mp_size(arg1),arg2,_cimg_mp_size(arg2),arg3,arg4);
- _cimg_mp_scalar5(mp_find,arg1,_cimg_mp_size(arg1),arg2 + (_cimg_mp_size(arg2)?1:0),arg3,arg4);
- }
- if (*ss1=='o' && *ss2=='r' && *ss3=='(') { // For loop
- _cimg_mp_op("Function 'for()'");
- s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- s3 = s2 + 1; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
- arg1 = code._width;
- p1 = compile(ss4,s1,depth1,0,block_flags); // Init
- arg2 = code._width;
- p2 = compile(++s1,s2,depth1,0,block_flags); // Cond
- arg3 = code._width;
- arg6 = mempos;
- if (s3<se1) { // Body + post
- p3 = compile(s3 + 1,se1,depth1,0,block_flags); // Body
- arg4 = code._width;
- pos = compile(++s2,s3,depth1,0,block_flags); // Post
- } else {
- p3 = compile(++s2,se1,depth1,0,block_flags); // Body only
- arg4 = pos = code._width;
- }
- _cimg_mp_check_type(p2,2,1,0);
- arg5 = _cimg_mp_size(pos);
- CImg<ulongT>::vector((ulongT)mp_for,p3,(ulongT)_cimg_mp_size(p3),p2,arg2 - arg1,arg3 - arg2,
- arg4 - arg3,code._width - arg4,
- p3>=arg6 && !_cimg_mp_is_const_scalar(p3),
- p2>=arg6 && !_cimg_mp_is_const_scalar(p2)).move_to(code,arg1);
- _cimg_mp_return(p3);
- }
- if (!std::strncmp(ss,"floor(",6)) { // Floor
- _cimg_mp_op("Function 'floor()'");
- arg1 = compile(ss6,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_floor,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::floor(mem[arg1]));
- _cimg_mp_scalar1(mp_floor,arg1);
- }
- if (!std::strncmp(ss,"fsize(",6)) { // File size
- _cimg_mp_op("Function 'fsize()'");
- arg1 = compile(ss6,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,1,2,0);
- pos = scalar();
- CImg<ulongT>::vector((ulongT)mp_fsize,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- break;
- case 'g' :
- #if cimg_use_cpp11==1
- if (!std::strncmp(ss,"gamma(",6)) { // Gamma
- _cimg_mp_op("Function 'gamma()'");
- arg1 = compile(ss6,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_gamma,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::tgamma(mem[arg1]));
- _cimg_mp_scalar1(mp_gamma,arg1);
- }
- #endif
- if (!std::strncmp(ss,"gauss(",6)) { // Gaussian function
- _cimg_mp_op("Function 'gauss()'");
- s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss6,s1,depth1,0,block_flags);
- arg2 = arg3 = 1;
- if (s1<se1) {
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,0,block_flags);
- arg3 = s2<se1?compile(++s2,se1,depth1,0,block_flags):1;
- }
- _cimg_mp_check_type(arg2,2,1,0);
- _cimg_mp_check_type(arg3,3,1,0);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_gauss,arg1,arg2,arg3);
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2) && _cimg_mp_is_const_scalar(arg3)) {
- val1 = mem[arg1];
- val2 = mem[arg2];
- _cimg_mp_const_scalar(std::exp(-val1*val1/(2*val2*val2))/(mem[arg3]?std::sqrt(2*val2*val2*cimg::PI):1));
- }
- _cimg_mp_scalar3(mp_gauss,arg1,arg2,arg3);
- }
- if (!std::strncmp(ss,"gcd(",4)) { // Gcd
- _cimg_mp_op("Function 'gcd()'");
- s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss4,s1,depth1,0,block_flags);
- arg2 = compile(++s1,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,1,1,0);
- _cimg_mp_check_type(arg2,2,1,0);
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
- _cimg_mp_const_scalar(cimg::gcd((long)mem[arg1],(long)mem[arg2]));
- _cimg_mp_scalar2(mp_gcd,arg1,arg2);
- }
- #ifdef cimg_mp_func_get
- if (!std::strncmp(ss,"get(",4)) { // Get value/vector from external variable
- _cimg_mp_op("Function 'get()'");
- s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss4,s1,depth1,0,block_flags);
- arg2 = arg3 = 0;
- if (s1<se1) {
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,0,block_flags);
- arg3 = s2<se1?compile(++s2,se1,depth1,0,block_flags):0;
- }
- _cimg_mp_check_type(arg1,1,2,0);
- _cimg_mp_check_const_scalar(arg2,2,2);
- _cimg_mp_check_type(arg3,3,1,0);
- p1 = _cimg_mp_size(arg1);
- const int siz_max = cimg::type<int>::max();
- if (mem[arg2]>=siz_max) {
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Specified variable size %g is larger than %d.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- mem[arg2],siz_max);
- }
- arg2 = (unsigned int)mem[arg2];
- if (arg2) pos = vector(arg2); else pos = scalar();
- CImg<ulongT>::vector((ulongT)mp_get,pos,arg1,p1,arg2,arg3).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- #endif
- break;
- case 'h' :
- if (*ss1=='(') { // Image height
- _cimg_mp_op("Function 'h()'");
- if (*ss2=='#') { // Index specified
- p1 = compile(ss3,se1,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- _cimg_mp_check_list();
- } else { if (ss2!=se1) break; p1 = ~0U; }
- _cimg_mp_scalar1(mp_image_h,p1);
- }
- break;
- case 'i' :
- if (*ss1=='c' && *ss2=='(') { // Image median
- _cimg_mp_op("Function 'ic()'");
- if (*ss3=='#') { // Index specified
- p1 = compile(ss4,se1,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- _cimg_mp_check_list();
- } else { if (ss3!=se1) break; p1 = ~0U; }
- pos = scalar();
- CImg<ulongT>::vector((ulongT)mp_image_median,pos,p1).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (*ss1=='n' && *ss2=='(') { // Image norm
- _cimg_mp_op("Function 'in()'");
- if (*ss3=='#') { // Index specified
- p1 = compile(ss4,se1,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- _cimg_mp_check_list();
- } else { if (ss3!=se1) break; p1 = ~0U; }
- pos = scalar();
- CImg<ulongT>::vector((ulongT)mp_image_norm,pos,p1).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (*ss1=='f' && *ss2=='(') { // If..then[..else.]
- _cimg_mp_op("Function 'if()'");
- s1 = ss3; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg1 = compile(ss3,s1,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,1,1,0);
- if (_cimg_mp_is_const_scalar(arg1)) {
- if ((bool)mem[arg1]) return compile(++s1,s2,depth1,0,block_flags);
- else return s2<se1?compile(++s2,se1,depth1,0,block_flags):0;
- }
- p2 = code._width;
- arg2 = compile(++s1,s2,depth1,0,block_flags);
- p3 = code._width;
- arg3 = s2<se1?compile(++s2,se1,depth1,0,block_flags):
- _cimg_mp_is_vector(arg2)?vector(_cimg_mp_size(arg2),0):0;
- _cimg_mp_check_type(arg3,3,_cimg_mp_is_vector(arg2)?2:1,_cimg_mp_size(arg2));
- arg4 = _cimg_mp_size(arg2);
- if (arg4) pos = vector(arg4); else pos = scalar();
- CImg<ulongT>::vector((ulongT)mp_if,pos,arg1,arg2,arg3,
- p3 - p2,code._width - p3,arg4).move_to(code,p2);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"inrange(",8)) { // Check value range
- _cimg_mp_op("Function 'inrange()'");
- s1 = ss8; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss8,s1,depth1,0,block_flags);
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,0,block_flags);
- s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg3 = compile(s2,s1,depth1,0,block_flags);
- arg4 = arg5 = 1;
- if (s1<se1) {
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg4 = compile(s1,s2,depth1,0,block_flags);
- arg5 = s2<se1?compile(++s2,se1,depth1,0,block_flags):arg4;
- _cimg_mp_check_type(arg4,4,1,0);
- _cimg_mp_check_type(arg5,5,1,0);
- }
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2) &&
- _cimg_mp_is_const_scalar(arg3) && _cimg_mp_is_const_scalar(arg4) &&
- _cimg_mp_is_const_scalar(arg5)) { // Optimize constant case
- val = mem[arg1]; val1 = mem[arg2]; val2 = mem[arg3];
- if (val2>=val1)
- is_sth = (mem[arg4]?(val>=val1):(val>val1)) && (mem[arg5]?(val<=val2):(val<val2));
- else
- is_sth = (mem[arg5]?(val>=val2):(val>val2)) && (mem[arg4]?(val<=val1):(val<val1));
- _cimg_mp_return(is_sth?1:0);
- }
- p1 = _cimg_mp_size(arg1);
- p2 = _cimg_mp_size(arg2);
- p3 = _cimg_mp_size(arg3);
- arg6 = ~0U; // Size of return value
- if (!p1) {
- arg6 = p2?p2:p3;
- if (arg6) _cimg_mp_check_type(arg3,3,3,arg6);
- } else {
- arg6 = p1;
- _cimg_mp_check_type(arg2,2,3,arg6);
- _cimg_mp_check_type(arg3,3,3,arg6);
- }
- pos = arg6?vector(arg6):scalar();
- CImg<ulongT>::vector((ulongT)mp_inrange,pos,arg6,arg1,p1,arg2,p2,arg3,p3,arg4,arg5).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"int(",4)) { // Integer cast
- _cimg_mp_op("Function 'int()'");
- arg1 = compile(ss4,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_int,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar((longT)mem[arg1]);
- _cimg_mp_scalar1(mp_int,arg1);
- }
- if (!std::strncmp(ss,"invert(",7)) { // Matrix/scalar inverse (or pseudoinverse)
- _cimg_mp_op("Function 'invert()'");
- s1 = ss7; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss7,s1,depth1,0,block_flags); // A
- arg2 = ~0U; // nb_colsA
- arg3 = 0; // use_LU
- arg4 = 0; // lambda
- if (s1<se1) {
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,0,block_flags);
- if (s2<se1) {
- s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg3 = compile(s2,s1,depth1,0,block_flags);
- arg4 = s1<se1?compile(++s1,se1,depth1,0,block_flags):0;
- }
- }
- if (arg2!=~0U) {
- _cimg_mp_check_const_scalar(arg2,2,3);
- arg2 = (unsigned int)mem[arg2];
- }
- _cimg_mp_check_type(arg3,3,1,0);
- _cimg_mp_check_type(arg4,4,1,0);
- if (_cimg_mp_is_vector(arg1)) {
- p1 = _cimg_mp_size(arg1);
- if (arg2==~0U) { // nb_colsA not specified: assuming square matrix
- _cimg_mp_check_matrix_square(arg1,1);
- p2 = p3 = (unsigned int)cimg::round(std::sqrt((float)p1));
- } else {
- p2 = arg2;
- p3 = p1/p2;
- if (p3*p2!=p1) {
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Type of first argument ('%s') "
- "does not match with second argument 'nb_colsA=%u', "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- s_type(arg1)._data,p2,s0);
- }
- }
- pos = vector(p1);
- CImg<ulongT>::vector((ulongT)mp_matrix_invert,pos,arg1,p2,p3,arg3,arg4).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(1/mem[arg1]);
- _cimg_mp_scalar2(mp_div,1,arg1);
- }
- if (*ss1=='s') { // Family of 'is_?()' functions
- if (!std::strncmp(ss,"isbool(",7)) { // Is boolean?
- _cimg_mp_op("Function 'isbool()'");
- if (ss7==se1) _cimg_mp_return(0);
- try { arg1 = compile(ss7,se1,depth1,0,block_flags); }
- catch(CImgException&) { _cimg_mp_return(0); }
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isbool,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_return(mem[arg1]==0. || mem[arg1]==1.);
- _cimg_mp_scalar1(mp_isbool,arg1);
- }
- if (!std::strncmp(ss,"isconst(",8)) { // Is constant?
- _cimg_mp_op("Function 'isconst()'");
- if (ss8==se1) _cimg_mp_return(0);
- try { arg1 = compile(ss8,se1,depth1,0,block_flags); }
- catch(CImgException&) { _cimg_mp_return(0); }
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_return(1);
- _cimg_mp_return(0);
- }
- if (!std::strncmp(ss,"isdir(",6)) { // Is directory?
- _cimg_mp_op("Function 'isdir()'");
- arg1 = compile(ss6,se1,depth1,0,block_flags);
- pos = scalar();
- CImg<ulongT>::vector((ulongT)mp_isdir,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"isfile(",7)) { // Is file?
- _cimg_mp_op("Function 'isfile()'");
- arg1 = compile(ss7,se1,depth1,0,block_flags);
- pos = scalar();
- CImg<ulongT>::vector((ulongT)mp_isfile,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"isin(",5)) { // Is in sequence/vector?
- if (ss5>=se1) _cimg_mp_return(0);
- _cimg_mp_op("Function 'isin()'");
- pos = scalar();
- CImg<ulongT>::vector((ulongT)mp_isin,pos,0).move_to(l_opcode);
- for (s = ss5; s<se; ++s) {
- ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
- (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
- arg1 = compile(s,ns,depth1,0,block_flags);
- CImg<ulongT>::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode);
- s = ns;
- }
- (l_opcode>'y').move_to(opcode);
- opcode[2] = opcode._height;
- opcode.move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"isinf(",6)) { // Is infinite?
- _cimg_mp_op("Function 'isinf()'");
- if (ss6==se1) _cimg_mp_return(0);
- arg1 = compile(ss6,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isinf,arg1);
- if (_cimg_mp_is_const_scalar(arg1))
- _cimg_mp_return((unsigned int)cimg::type<double>::is_inf(mem[arg1]));
- _cimg_mp_scalar1(mp_isinf,arg1);
- }
- if (!std::strncmp(ss,"isint(",6)) { // Is integer?
- _cimg_mp_op("Function 'isint()'");
- if (ss6==se1) _cimg_mp_return(0);
- try { arg1 = compile(ss6,se1,depth1,0,block_flags); }
- catch(CImgException&) { _cimg_mp_return(0); }
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isint,arg1);
- if (_cimg_mp_is_const_scalar(arg1))
- _cimg_mp_return((unsigned int)((double)(longT)mem[arg1]==mem[arg1]));
- _cimg_mp_scalar1(mp_isint,arg1);
- }
- if (!std::strncmp(ss,"isnan(",6)) { // Is NaN?
- _cimg_mp_op("Function 'isnan()'");
- if (ss6==se1) _cimg_mp_return(0);
- arg1 = compile(ss6,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isnan,arg1);
- if (_cimg_mp_is_const_scalar(arg1))
- _cimg_mp_return((unsigned int)cimg::type<double>::is_nan(mem[arg1]));
- _cimg_mp_scalar1(mp_isnan,arg1);
- }
- if (!std::strncmp(ss,"isnum(",6)) { // Is number?
- _cimg_mp_op("Function 'isnum()'");
- val = 0;
- if (cimg_sscanf(ss6,"%lf%c%c",&val,&sep,&end)==2 && sep==')') _cimg_mp_return(1);
- _cimg_mp_return(0);
- }
- if (!std::strncmp(ss,"isexpr(",7)) { // Is valid expression?
- _cimg_mp_op("Function 'isexpr()'");
- if (ss7==se1) _cimg_mp_return(0);
- try { arg1 = compile(ss7,se1,depth1,0,block_flags); }
- catch (CImgException&) { _cimg_mp_return(0); }
- _cimg_mp_return(1);
- }
- if (!std::strncmp(ss,"ispercentage(",13)) { // Does argument ends with '%'?
- _cimg_mp_op("Function 'ispercentage()'");
- _cimg_mp_return((unsigned int)(*se2=='%'));
- }
- if (!std::strncmp(ss,"isvarname(",10)) { // Is variable name?
- _cimg_mp_op("Function 'isvarname()'");
- arg1 = compile(ss + 10,se1,depth1,0,block_flags);
- pos = scalar();
- CImg<ulongT>::vector((ulongT)mp_isvarname,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- }
- break;
- case 'l' :
- if (*ss1=='(') { // Size of image list
- _cimg_mp_op("Function 'l()'");
- if (ss2!=se1) break;
- _cimg_mp_scalar0(mp_list_l);
- }
- if (!std::strncmp(ss,"lerp(",5)) { // Linear interpolation
- _cimg_mp_op("Function 'lerp()'");
- s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss5,s1,depth1,0,block_flags);
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,0,block_flags);
- arg3 = s2<se1?compile(++s2,se1,depth1,0,block_flags):16; // Default value is 0.5
- _cimg_mp_check_type(arg3,3,1,0);
- if (_cimg_mp_is_const_scalar(arg3)) { // Optimize constant cases
- if (!arg3) _cimg_mp_return(arg1);
- if (arg3==1) _cimg_mp_return(arg2);
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) {
- const double t = mem[arg3];
- _cimg_mp_const_scalar(mem[arg1]*(1-t) + mem[arg2]*t);
- }
- }
- if (_cimg_mp_is_scalar(arg1)) {
- _cimg_mp_check_type(arg2,2,1,0);
- _cimg_mp_scalar3(mp_lerp,arg1,arg2,arg3);
- }
- p1 = _cimg_mp_size(arg1);
- _cimg_mp_check_type(arg2,2,2,p1);
- pos = vector(p1);
- CImg<ulongT>::vector((ulongT)mp_vector_lerp,pos,p1,arg1,arg2,arg3).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"log(",4)) { // Natural logarithm
- _cimg_mp_op("Function 'log()'");
- arg1 = compile(ss4,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::log(mem[arg1]));
- _cimg_mp_scalar1(mp_log,arg1);
- }
- if (!std::strncmp(ss,"log2(",5)) { // Base-2 logarithm
- _cimg_mp_op("Function 'log2()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log2,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::log2(mem[arg1]));
- _cimg_mp_scalar1(mp_log2,arg1);
- }
- if (!std::strncmp(ss,"log10(",6)) { // Base-10 logarithm
- _cimg_mp_op("Function 'log10()'");
- arg1 = compile(ss6,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log10,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::log10(mem[arg1]));
- _cimg_mp_scalar1(mp_log10,arg1);
- }
- if (!std::strncmp(ss,"lowercase(",10)) { // Lower case
- _cimg_mp_op("Function 'lowercase()'");
- arg1 = compile(ss + 10,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_lowercase,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::lowercase(mem[arg1]));
- _cimg_mp_scalar1(mp_lowercase,arg1);
- }
- break;
- case 'm' :
- if (!std::strncmp(ss,"map(",4)) { // Map vector
- _cimg_mp_op("Function 'map()'");
- s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss4,s1,depth1,0,block_flags);
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,0,block_flags);
- arg3 = arg4 = 1; arg5 = 0;
- if (s2<se1) {
- s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg3 = compile(s2,s1,depth1,0,block_flags);
- if (s1<se1) {
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg4 = compile(s1,s2,depth1,0,block_flags);
- arg5 = s2<se1?compile(++s2,se1,depth1,0,block_flags):0;
- }
- }
- _cimg_mp_check_type(arg1,1,2,0);
- _cimg_mp_check_type(arg2,2,2,0);
- _cimg_mp_check_const_scalar(arg3,3,3);
- _cimg_mp_check_const_scalar(arg4,4,3);
- _cimg_mp_check_type(arg5,5,1,0);
- p1 = _cimg_mp_size(arg1);
- p2 = _cimg_mp_size(arg2);
- arg3 = (unsigned int)mem[arg3];
- arg4 = (unsigned int)mem[arg4];
- if (p1%arg3) {
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Type of first arguments ('%s') "
- "does not match with third argument 'nb_channelsX=%u', "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- s_type(arg1)._data,arg3,s0);
- }
- if (p2%arg4) {
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Type of second arguments ('%s') "
- "does not match with fourth argument 'nb_channelsP=%u', "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- s_type(arg2)._data,arg4,s0);
- }
- pos = vector(p1*arg4);
- CImg<ulongT>::vector((ulongT)mp_map,pos,arg1,arg2,p1,p2,arg3,arg4,arg5).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"mul(",4)) { // Matrix multiplication
- _cimg_mp_op("Function 'mul()'");
- s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss4,s1,depth1,0,block_flags);
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,0,block_flags);
- arg3 = s2<se1?compile(++s2,se1,depth1,0,block_flags):1;
- _cimg_mp_check_type(arg1,1,2,0);
- _cimg_mp_check_type(arg2,2,2,0);
- _cimg_mp_check_const_scalar(arg3,3,3);
- p1 = _cimg_mp_size(arg1);
- p2 = _cimg_mp_size(arg2);
- p3 = (unsigned int)mem[arg3];
- arg5 = p2/p3;
- arg4 = p1/arg5;
- if (arg4*arg5!=p1 || arg5*p3!=p2) {
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Types of first and second arguments ('%s' and '%s') "
- "do not match with third argument 'nb_colsB=%u', "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- s_type(arg1)._data,s_type(arg2)._data,p3,s0);
- }
- pos = vector(arg4*p3);
- CImg<ulongT>::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,arg4,arg5,p3).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"mproj(",6)) { // Project matrix onto dictionary
- _cimg_mp_op("Function 'mproj()'");
- s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss6,s1,depth1,0,block_flags); // S
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,0,block_flags); // ncolS
- s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg3 = compile(s2,s1,depth1,0,block_flags); // D
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg4 = compile(s1,s2,depth1,0,block_flags); // ncolD
- arg5 = arg6 = p3 = 0;
- if (s2<se1) {
- s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg5 = compile(s2,s1,depth1,0,block_flags); // method
- if (s1<se1) {
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg6 = compile(s1,s2,depth1,0,block_flags); // max_iter
- p3 = s2<se1?compile(++s2,se1,depth1,0,block_flags):0; // method
- }
- }
- _cimg_mp_check_type(arg1,1,2,0);
- _cimg_mp_check_const_scalar(arg2,2,3);
- _cimg_mp_check_type(arg3,3,2,0);
- _cimg_mp_check_const_scalar(arg4,4,3);
- _cimg_mp_check_type(arg5,5,1,0);
- _cimg_mp_check_type(arg6,6,1,0);
- _cimg_mp_check_type(p3,7,1,0);
- p1 = _cimg_mp_size(arg1);
- p2 = _cimg_mp_size(arg3);
- const unsigned int
- wS = (unsigned int)mem[arg2],
- wD = (unsigned int)mem[arg4],
- hS = p1/wS,
- hD = p2/wD;
- if (wS*hS!=p1) {
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Type of first argument ('%s') "
- "do not match with second argument 'nb_colsS=%u', "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- s_type(arg1)._data,wS,s0);
- }
- if (wD*hD!=p2) {
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Type of third argument ('%s') "
- "do not match with fourth argument 'nb_colsD=%u', "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- s_type(arg3)._data,wD,s0);
- }
- if (hS!=hD) {
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Type of first argument ('%s') "
- "do not match with third argument ('%s'), "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- s_type(arg1)._data,s_type(arg3)._data,s0);
- }
- pos = vector(wS*wD);
- CImg<ulongT>::vector((ulongT)mp_mproj,pos,arg1,wS,hS,arg3,wD,arg5,arg6,p3).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"mse(",4)) { // Mean-squared error
- _cimg_mp_op("Function 'mse()'");
- s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss4,s1,depth1,0,block_flags);
- arg2 = compile(++s1,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg2,2,_cimg_mp_is_scalar(arg1)?1:2,_cimg_mp_size(arg1));
- _cimg_mp_scalar3(mp_mse,arg1,arg2,_cimg_mp_size(arg1));
- }
- if (!std::strncmp(ss,"merge(",6)) { // Merge inter-thread variables
- _cimg_mp_op("Function 'merge()'");
- s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- ref.assign(7);
- pos = compile(ss6,s1,depth1,ref,block_flags);
- if (*ref) {
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: First argument cannot be a linked reference, "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- s0);
- }
- arg1 = ~0U; // Merge operator
- // (0='=',1='+',2='-',3='*',4='/',5='&',6='|',7='xor',8='&&',9=='||',10='min',11='max')
- if (s1<se1) {
- ++s1;
- char st_op[4] = {};
- is_sth = false; // blank after operator?
- if (cimg_sscanf(s1," %3[=+-*/&|minaxor]%c",st_op,&sep)==2 && (sep==')' ||
- (is_sth=cimg::is_blank(sep)))) {
- if (!is_sth || (is_sth && cimg_sscanf(s1," %*[=+-*/&|minaxor ]%c",&sep)==1 && sep==')')) {
- cimg::strpare(st_op,' ',false,true);
- if (!st_op[1])
- arg1 = *st_op=='='?0:*st_op=='+'?1:*st_op=='-'?2:*st_op=='*'?3:*st_op=='/'?4:
- *st_op=='&'?5:*st_op=='|'?6:~0U;
- else if (*st_op=='x' && st_op[1]=='o' && st_op[2]=='r' && !st_op[3]) arg1 = 7;
- else if (*st_op=='&' && st_op[1]=='&' && !st_op[2]) arg1 = 8;
- else if (*st_op=='|' && st_op[1]=='|' && !st_op[2]) arg1 = 9;
- else if (*st_op=='m' && st_op[1]=='i' && st_op[2]=='n' && !st_op[3]) arg1 = 10;
- else if (*st_op=='m' && st_op[1]=='a' && st_op[2]=='x' && !st_op[3]) arg1 = 11;
- }
- }
- }
- cimg_rofY(memmerge,k) if (memmerge(0,k)==(int)pos) {
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Merge has already been requested before "
- "for specified variable "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,s0);
- }
- if (arg1==~0U) {
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Invalid specified operator "
- "(should be one of '=,+,-,*,/,&,|,xor,&&,||,min,max'), "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,s0);
- }
- memmerge.resize(3,memmerge._height + 1,1,1,0,0);
- memmerge(0,memmerge._height - 1) = (int)pos;
- memmerge(1,memmerge._height - 1) = (int)_cimg_mp_size(pos);
- memmerge(2,memmerge._height - 1) = (int)arg1;
- _cimg_mp_return_nan();
- }
- break;
- case 'n' :
- #ifdef cimg_mp_func_name
- if (!std::strncmp(ss,"name(",5)) { // Get image name as a string vector
- _cimg_mp_op("Function 'name()'");
- if (*ss5=='#') { // Index specified
- s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- p1 = compile(ss6,s0++,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- _cimg_mp_check_list();
- is_sth = true; // is_index_specified?
- } else { s0 = ss5; p1 = get_mem_img_index(); is_sth = false; }
- arg1 = s0<se1?compile(s0,se1,depth1,0,block_flags):~0U;
- if (arg1!=~0U) {
- _cimg_mp_check_const_scalar(arg1,is_sth?2:1,3);
- arg1 = (unsigned int)mem[arg1];
- } else arg1 = 1024;
- pos = vector(arg1);
- CImg<ulongT>::vector((ulongT)mp_name,pos,p1,arg1).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- #endif
- if (!std::strncmp(ss,"narg(",5)) { // Number of arguments
- _cimg_mp_op("Function 'narg()'");
- if (ss5>=se1) _cimg_mp_return(0);
- arg1 = 0;
- for (s = ss5; s<se; ++s) {
- ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
- (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
- ++arg1; s = ns;
- }
- _cimg_mp_const_scalar((double)arg1);
- }
- if (!std::strncmp(ss,"normp(",6)) { // Lp norm, with variable argument p.
- _cimg_mp_op("Function 'normp()'");
- s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss6,s1,depth1,0,block_flags);
- arg2 = s1<se1?compile(++s1,se1,depth1,0,block_flags):2;
- _cimg_mp_check_type(arg2,0,1,0);
- p1 = _cimg_mp_size(arg1);
- _cimg_mp_scalar3(mp_vector_normp,arg1,p1,arg2);
- }
- break;
- case 'o' :
- if (!std::strncmp(ss,"o2c(",4)) { // Offset to coordinates
- _cimg_mp_op("Function 'o2c()'");
- if (*ss4=='#') { // Index specified
- s0 = ss5; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- p1 = compile(ss5,s0++,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- _cimg_mp_check_list();
- } else { p1 = ~0U; s0 = ss4; }
- arg1 = compile(s0,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,1,1,0);
- pos = vector(4);
- CImg<ulongT>::vector((ulongT)mp_o2c,pos,p1,arg1).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- break;
- case 'p' :
- if (!std::strncmp(ss,"permut(",7)) { // Number of permutations
- _cimg_mp_op("Function 'permut()'");
- s1 = ss7; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss7,s1,depth1,0,block_flags);
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,0,block_flags);
- arg3 = compile(++s2,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,1,1,0);
- _cimg_mp_check_type(arg2,2,1,0);
- _cimg_mp_check_type(arg3,3,1,0);
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2) && _cimg_mp_is_const_scalar(arg3))
- _cimg_mp_const_scalar(cimg::permutations((int)mem[arg1],(int)mem[arg2],(bool)mem[arg3]));
- _cimg_mp_scalar3(mp_permutations,arg1,arg2,arg3);
- }
- if (!std::strncmp(ss,"polygon(",8)) { // Polygon/line drawing
- if (!is_inside_critical) is_parallelizable = false;
- _cimg_mp_op("Function 'polygon()'");
- if (*ss8=='#') { // Index specified
- s0 = ss + 9; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- p1 = compile(ss + 9,s0++,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- _cimg_mp_check_list();
- pos = 2;
- } else { p1 = ~0U; s0 = ss8; pos = 1; }
- if (s0==se1) compile(s0,se1,depth1,0,block_flags); // 'missing' argument error
- CImg<ulongT>::vector((ulongT)mp_polygon,_cimg_mp_slot_nan,0,p1).move_to(l_opcode);
- for (s = s0; s<se; ++s, ++pos) {
- ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
- (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
- arg2 = compile(s,ns,depth1,0,block_flags);
- if (pos>1 && _cimg_mp_is_vector(arg2)) // Vector argument allowed to specify coordinates and color
- CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
- arg2 + (ulongT)_cimg_mp_size(arg2)).
- move_to(l_opcode);
- else {
- _cimg_mp_check_type(arg2,pos,1,0);
- CImg<ulongT>::vector(arg2).move_to(l_opcode);
- }
- s = ns;
- }
- (l_opcode>'y').move_to(opcode);
- opcode[2] = opcode._height;
- opcode.move_to(code);
- _cimg_mp_return_nan();
- }
- if (!std::strncmp(ss,"print(",6) ||
- !std::strncmp(ss,"prints(",7)) { // Print expressions
- s0 = ss6 + (*ss5=='('?0:1);
- is_sth = *ss5=='s'; // corresponding string must be printed?
- _cimg_mp_op(is_sth?"Function 'prints()'":"Function 'print()'");
- if (!is_sth && *s0=='#') { // Image
- p1 = compile(ss7,se1,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- _cimg_mp_check_list();
- CImg<ulongT>::vector((ulongT)mp_image_print,_cimg_mp_slot_nan,p1).move_to(code);
- _cimg_mp_return_nan();
- }
- // Regular expression
- for (s = s0; s<se; ++s) {
- ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
- (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
- ref.assign(7);
- pos = compile(s,ns,depth1,ref,block_flags);
- c1 = *ns; *ns = 0;
- variable_name.assign(CImg<charT>::string(s,true,true).unroll('y'),true);
- cimg::strpare(variable_name,false,true);
- if (_cimg_mp_is_const_scalar(pos)) // Const scalar
- std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %.17g "
- "(mem[%u]: %s%s)",
- variable_name._data,mem[pos],pos,s_type(pos)._data,s_ref(ref)._data);
- else // Vector or non-const scalar
- std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = (uninitialized) "
- "(mem[%u]: %s%s)",
- variable_name._data,pos,s_type(pos)._data,s_ref(ref)._data);
- if (p_ref) std::memcpy(p_ref,ref,siz_ref);
- if (_cimg_mp_is_vector(pos)) // Vector
- ((CImg<ulongT>::vector((ulongT)mp_vector_print,pos,0,(ulongT)_cimg_mp_size(pos),is_sth?1:0),
- variable_name)>'y').move_to(opcode);
- else // Scalar
- ((CImg<ulongT>::vector((ulongT)mp_print,pos,0,is_sth?1:0),
- variable_name)>'y').move_to(opcode);
- opcode[2] = opcode._height;
- opcode.move_to(code);
- *ns = c1; s = ns;
- }
- _cimg_mp_return(pos);
- }
- break;
- case 'r' :
- if (!std::strncmp(ss,"rad2deg(",8)) { // Degrees to radians
- _cimg_mp_op("Function 'rad2deg()'");
- arg1 = compile(ss8,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_rad2deg,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(mem[arg1]*180/cimg::PI);
- _cimg_mp_scalar1(mp_rad2deg,arg1);
- }
- if (!std::strncmp(ss,"ref(",4)) { // Variable declaration
- _cimg_mp_op("Function 'ref()'");
- s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- if (s1>=se1 || !*s1) compile(s1,s1,depth1,0,block_flags); // Will throw missing argument error
- arg3 = compile(ss4,s1++,depth1,p_ref,block_flags);
- *se1 = 0;
- if (!cimg::is_varname(s1)) { // Invalid variable name
- variable_name.assign(s1,(unsigned int)(se1 + 1 - s1)).back() = 0;
- cimg::strellipsize(variable_name,64);
- *se1 = ')';
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Invalid specified variable name '%s', "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- variable_name._data,s0);
- }
- get_variable_pos(s1,arg1,arg2);
- if (arg2!=~0U) reserved_label[arg2] = arg3;
- else if (arg1!=~0U) variable_pos[arg1] = arg3;
- else { // New variable
- if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0);
- variable_pos[variable_def._width] = arg3;
- CImg<char>::string(s1).move_to(variable_def);
- }
- if (_cimg_mp_is_vector(arg3))
- set_reserved_vector(arg3); // Prevent from being used in further optimization
- else if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1;
- *se1 = ')';
- _cimg_mp_return(arg3);
- }
- if (!std::strncmp(ss,"repeat(",7)) { // Repeat
- _cimg_mp_op("Function 'repeat()'");
- s0 = ss7; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- arg1 = compile(ss7,s0,depth1,0,block_flags); // Number of iterations
- _cimg_mp_check_type(arg1,1,1,0);
- s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- p1 = code._width;
- if (s1<se1) { // Version with 3 arguments
- variable_name.assign(s0,(unsigned int)(s1 + 1 - s0)).back() = 0;
- cimg::strpare(variable_name,false,true);
- if (!cimg::is_varname(variable_name)) { // Invalid variable name
- cimg::strellipsize(variable_name,64);
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Invalid loop variable name '%s', "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- variable_name._data,s0);
- }
- get_variable_pos(variable_name,arg2,arg3);
- arg2 = arg3!=~0U?reserved_label[arg3]:arg2!=~0U?variable_pos[arg2]:~0U; // Variable slot
- if (arg2!=~0U && (!_cimg_mp_is_scalar(arg2) ||
- _cimg_mp_is_const_scalar(arg2))) { // Variable is not a vector or is a constant->error
- cimg::strellipsize(variable_name,64);
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Invalid type '%s' for variable '%s' "
- "(expected 'scalar'), in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- s_type(arg2)._data,variable_name._data,s0);
- } else if (arg2==~0U) { // Variable does not exist -> create it
- arg2 = scalar();
- if (arg3!=~0U) reserved_label[arg3] = arg2;
- else {
- if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0);
- variable_pos[variable_def._width] = arg2;
- variable_name.move_to(variable_def);
- }
- memtype[arg2] = -1;
- }
- arg3 = compile(++s1,se1,depth1,0,block_flags);
- } else { // Version with 2 arguments
- arg2 = ~0U;
- arg3 = compile(s0,se1,depth1,0,block_flags);
- }
- // arg2 = variable slot, arg3 = fill expression.
- CImg<ulongT>::vector((ulongT)mp_repeat,arg3,arg1,arg2,code._width - p1).move_to(code,p1);
- _cimg_mp_return_nan();
- }
- if (!std::strncmp(ss,"resize(",7)) { // Vector or image resize
- _cimg_mp_op("Function 'resize()'");
- if (*ss7!='#') { // Vector
- pos = 1;
- for (s = ss7; s<se; ++s, ++pos) {
- ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
- (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
- arg2 = compile(s,ns,depth1,0,block_flags);
- if (s!=ss7) _cimg_mp_check_type(arg2,pos,1,0);
- CImg<ulongT>::vector(arg2).move_to(l_opcode);
- s = ns;
- }
- (l_opcode>'y').move_to(opcode);
- if (opcode.height()<2) compile(s,se1,depth1,0,block_flags); // Not enough arguments -> throw exception
- arg1 = (unsigned int)opcode[0]; // Vector to resize
- p1 = _cimg_mp_size(arg1);
- if (opcode.height()<=4) { // Simple vector resize
- arg2 = (unsigned int)opcode[1];
- _cimg_mp_check_const_scalar(arg2,2,3);
- arg2 = (unsigned int)mem[arg2];
- arg3 = opcode.height()<3?1U:(unsigned int)opcode[2];
- _cimg_mp_check_type(arg3,3,1,0);
- arg4 = opcode.height()<4?0U:(unsigned int)opcode[3];
- _cimg_mp_check_type(arg4,4,1,0);
- pos = vector(arg2);
- CImg<ulongT>::vector((ulongT)mp_vector_resize,pos,arg2,arg1,p1,arg3,arg4).move_to(code);
- } else { // Advanced vector resize (vector viewed as an image)
- // opcode = [ A, ow,oh,od,os, nw,nh,nd,ns, interp, boundary_cond, ax,ay,az,ac ]
- // [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ]
- if (opcode.height()<6) compile(s,se1,depth1,0,block_flags); // Not enough arguments -> throw exception
- p2 = opcode.height();
- opcode.resize(1,15,1,1,0);
- if (p2<7) opcode[6] = opcode[2];
- if (p2<8) opcode[7] = opcode[3];
- if (p2<9) opcode[8] = opcode[4];
- if (p2<10) opcode[9] = 1;
- _cimg_mp_check_const_scalar(opcode[1],2,3);
- _cimg_mp_check_const_scalar(opcode[2],3,3);
- _cimg_mp_check_const_scalar(opcode[3],4,3);
- _cimg_mp_check_const_scalar(opcode[4],5,3);
- _cimg_mp_check_const_scalar(opcode[5],6,3);
- _cimg_mp_check_const_scalar(opcode[6],7,3);
- _cimg_mp_check_const_scalar(opcode[7],8,3);
- _cimg_mp_check_const_scalar(opcode[8],9,3);
- arg2 = (unsigned int)mem[opcode[1]]; opcode[1] = arg2;
- arg3 = (unsigned int)mem[opcode[2]]; opcode[2] = arg3;
- arg4 = (unsigned int)mem[opcode[3]]; opcode[3] = arg4;
- arg5 = (unsigned int)mem[opcode[4]]; opcode[4] = arg5;
- if (arg2*arg3*arg4*arg5!=std::max(1U,p1))
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Input vector size (%lu values) and its specified "
- "geometry (%u,%u,%u,%u) (%lu values) do not match.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- std::max(p1,1U),arg2,arg3,arg4,arg5,(ulongT)arg2*arg3*arg4*arg5);
- arg2 = (unsigned int)mem[opcode[5]]; opcode[5] = arg2;
- arg3 = (unsigned int)mem[opcode[6]]; opcode[6] = arg3;
- arg4 = (unsigned int)mem[opcode[7]]; opcode[7] = arg4;
- arg5 = (unsigned int)mem[opcode[8]]; opcode[8] = arg5;
- pos = vector(arg2*arg3*arg4*arg5);
- opcode.resize(1,18,1,1,0,0,0,1);
- opcode[0] = (ulongT)mp_vector_resize_ext;
- opcode[1] = (ulongT)pos;
- opcode[2] = (ulongT)p1;
- opcode.move_to(code);
- }
- return_new_comp = true;
- _cimg_mp_return(pos);
- } else { // Image
- if (!is_inside_critical) is_parallelizable = false;
- s0 = ss8; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- p1 = compile(ss8,s0++,depth1,0,block_flags);
- _cimg_mp_check_list();
- l_opcode.assign(); // Don't use 'opcode': it can be modified by further calls to 'compile()'!
- CImg<ulongT>::vector((ulongT)mp_image_resize,_cimg_mp_slot_nan,p1,~0U,~0U,~0U,~0U,1,0,0,0,0,0).
- move_to(l_opcode);
- pos = 0;
- for (s = s0; s<se && pos<10; ++s) {
- ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
- (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
- arg1 = compile(s,ns,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,pos + 2,1,0);
- l_opcode(0,pos + 3) = arg1;
- s = ns;
- ++pos;
- }
- if (pos<1 || pos>10) {
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: %s arguments, in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- pos<1?"Missing":"Too much",s0);
- }
- l_opcode[0].move_to(code);
- _cimg_mp_return_nan();
- }
- }
- if (!std::strncmp(ss,"reverse(",8)) { // Vector reverse
- _cimg_mp_op("Function 'reverse()'");
- arg1 = compile(ss8,se1,depth1,0,block_flags);
- if (!_cimg_mp_is_vector(arg1)) _cimg_mp_return(arg1);
- p1 = _cimg_mp_size(arg1);
- pos = vector(p1);
- CImg<ulongT>::vector((ulongT)mp_vector_reverse,pos,arg1,p1).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) { // Bitwise rotation
- _cimg_mp_op(ss[2]=='l'?"Function 'rol()'":"Function 'ror()'");
- s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss4,s1,depth1,0,block_flags);
- arg2 = s1<se1?compile(++s1,se1,depth1,0,block_flags):1;
- _cimg_mp_check_type(arg2,2,1,0);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector2_vs(*ss2=='l'?mp_rol:mp_ror,arg1,arg2);
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
- _cimg_mp_const_scalar(*ss2=='l'?cimg::rol(mem[arg1],(unsigned int)mem[arg2]):
- cimg::ror(mem[arg1],(unsigned int)mem[arg2]));
- _cimg_mp_scalar2(*ss2=='l'?mp_rol:mp_ror,arg1,arg2);
- }
- if (!std::strncmp(ss,"rot(",4)) { // 2D/3D rotation matrix
- _cimg_mp_op("Function 'rot()'");
- s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss4,s1,depth1,0,block_flags);
- if (s1<se1) { // 3D rotation
- _cimg_mp_check_type(arg1,1,3,3);
- is_sth = false; // Is coordinates as vector?
- if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
- is_sth = true;
- p2 = _cimg_mp_size(arg1);
- ++arg1;
- arg2 = arg3 = 0;
- if (p2>1) {
- arg2 = arg1 + 1;
- if (p2>2) arg3 = arg2 + 1;
- }
- arg4 = compile(++s1,se1,depth1,0,block_flags);
- } else {
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,0,block_flags);
- s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
- arg3 = compile(s2,s3,depth1,0,block_flags);
- arg4 = compile(++s3,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg2,2,1,0);
- _cimg_mp_check_type(arg3,3,1,0);
- }
- _cimg_mp_check_type(arg4,is_sth?2:4,1,0);
- pos = vector(9);
- CImg<ulongT>::vector((ulongT)mp_rot3d,pos,arg1,arg2,arg3,arg4).move_to(code);
- } else { // 2D rotation
- _cimg_mp_check_type(arg1,1,1,0);
- pos = vector(4);
- CImg<ulongT>::vector((ulongT)mp_rot2d,pos,arg1).move_to(code);
- }
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"round(",6)) { // Value rounding
- _cimg_mp_op("Function 'round()'");
- s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss6,s1,depth1,0,block_flags);
- arg2 = 1;
- arg3 = 0;
- if (s1<se1) {
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,0,block_flags);
- arg3 = s2<se1?compile(++s2,se1,depth1,0,block_flags):0;
- }
- _cimg_mp_check_type(arg2,2,1,0);
- _cimg_mp_check_type(arg3,3,1,0);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_round,arg1,arg2,arg3);
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2) && _cimg_mp_is_const_scalar(arg3))
- _cimg_mp_const_scalar(cimg::round(mem[arg1],mem[arg2],(int)mem[arg3]));
- _cimg_mp_scalar3(mp_round,arg1,arg2,arg3);
- }
- #ifdef cimg_mp_func_run
- if (!std::strncmp(ss,"run(",4)) { // Run external command
- _cimg_mp_op("Function 'run()'");
- if (!is_inside_critical && !is_inside_begin && !is_inside_end) is_noncritical_run = true;
- CImg<ulongT>::vector((ulongT)mp_run,0,0).move_to(l_opcode);
- pos = 1;
- for (s = ss4; s<se; ++s) {
- ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
- (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
- arg1 = compile(s,ns,depth1,0,block_flags);
- CImg<ulongT>::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode);
- s = ns;
- }
- (l_opcode>'y').move_to(opcode);
- pos = scalar();
- opcode[1] = pos;
- opcode[2] = opcode._height;
- opcode.move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- #endif
- break;
- case 's' :
- if (*ss1=='(') { // Image spectrum
- _cimg_mp_op("Function 's()'");
- if (*ss2=='#') { // Index specified
- p1 = compile(ss3,se1,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- _cimg_mp_check_list();
- } else { if (ss2!=se1) break; p1 = ~0U; }
- _cimg_mp_scalar1(mp_image_s,p1);
- }
- if (!std::strncmp(ss,"same(",5)) { // Test if operands have the same values
- _cimg_mp_op("Function 'same()'");
- s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss5,s1,depth1,0,block_flags);
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,0,block_flags);
- arg3 = 11;
- arg4 = 1;
- if (s2<se1) {
- s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
- arg3 = compile(s2,s3,depth1,0,block_flags);
- _cimg_mp_check_type(arg3,3,1,0);
- arg4 = s3<se1?compile(++s3,se1,depth1,0,block_flags):1;
- }
- p1 = _cimg_mp_size(arg1);
- p2 = _cimg_mp_size(arg2);
- _cimg_mp_scalar6(mp_vector_eq,arg1,p1,arg2,p2,arg3,arg4);
- }
- #ifdef cimg_mp_func_set
- if (!std::strncmp(ss,"set(",4)) { // Set value/vector to external variable
- _cimg_mp_op("Function 'set()'");
- s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss4,s1,depth1,0,block_flags);
- arg2 = compile(++s1,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,2,2,0);
- p1 = _cimg_mp_size(arg1);
- p2 = _cimg_mp_size(arg2);
- CImg<ulongT>::vector((ulongT)mp_set,arg2,p2,arg1,p1).move_to(code);
- _cimg_mp_return_nan();
- }
- #endif
- if (!std::strncmp(ss,"shift(",6)) { // Shift vector
- _cimg_mp_op("Function 'shift()'");
- s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss6,s1,depth1,0,block_flags);
- arg2 = 1; arg3 = 0;
- if (s1<se1) {
- s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- arg2 = compile(s1,s0,depth1,0,block_flags);
- arg3 = s0<se1?compile(++s0,se1,depth1,0,block_flags):0;
- }
- _cimg_mp_check_type(arg1,1,2,0);
- _cimg_mp_check_type(arg2,2,1,0);
- _cimg_mp_check_type(arg3,3,1,0);
- p1 = _cimg_mp_size(arg1);
- pos = vector(p1);
- CImg<ulongT>::vector((ulongT)mp_shift,pos,arg1,p1,arg2,arg3).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"sign(",5)) { // Sign
- _cimg_mp_op("Function 'sign()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sign,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::sign(mem[arg1]));
- _cimg_mp_scalar1(mp_sign,arg1);
- }
- if (!std::strncmp(ss,"sin(",4)) { // Sine
- _cimg_mp_op("Function 'sin()'");
- arg1 = compile(ss4,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sin,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::sin(mem[arg1]));
- _cimg_mp_scalar1(mp_sin,arg1);
- }
- if (!std::strncmp(ss,"sinc(",5)) { // Sine cardinal
- _cimg_mp_op("Function 'sinc()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinc,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::sinc(mem[arg1]));
- _cimg_mp_scalar1(mp_sinc,arg1);
- }
- if (!std::strncmp(ss,"sinh(",5)) { // Hyperbolic sine
- _cimg_mp_op("Function 'sinh()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinh,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::sinh(mem[arg1]));
- _cimg_mp_scalar1(mp_sinh,arg1);
- }
- if (!std::strncmp(ss,"size(",5)) { // Vector size
- _cimg_mp_op("Function 'size()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- _cimg_mp_const_scalar(_cimg_mp_is_scalar(arg1)?0:_cimg_mp_size(arg1));
- }
- if (!std::strncmp(ss,"solve(",6)) { // Solve square linear system
- _cimg_mp_op("Function 'solve()'");
- s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss6,s1,depth1,0,block_flags);
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,0,block_flags);
- arg3 = 1;
- arg4 = 0;
- if (s2<se1) {
- s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg3 = compile(s2,s1,depth1,0,block_flags);
- arg4 = s1<se1?compile(++s1,se1,depth1,0,block_flags):0;
- }
- _cimg_mp_check_type(arg1,1,2,0);
- _cimg_mp_check_type(arg2,2,2,0);
- _cimg_mp_check_const_scalar(arg3,3,3);
- _cimg_mp_check_type(arg4,4,1,0);
- p1 = _cimg_mp_size(arg1);
- p2 = _cimg_mp_size(arg2);
- p3 = (unsigned int)mem[arg3];
- arg5 = p2/p3;
- arg6 = p1/arg5;
- if (arg6*arg5!=p1 || arg5*p3!=p2) {
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Types of first and second arguments ('%s' and '%s') "
- "do not match with third argument 'nb_colsB=%u', "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- s_type(arg1)._data,s_type(arg2)._data,p3,s0);
- }
- pos = vector(arg6*p3);
- CImg<ulongT>::vector((ulongT)mp_solve,pos,arg1,arg2,arg6,arg5,p3,arg4).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"sort(",5)) { // Sort vector
- _cimg_mp_op("Function 'sort()'");
- s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss5,s1,depth1,0,block_flags);
- arg2 = arg4 = 1; arg3 = ~0U;
- if (s1<se1) {
- s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- arg2 = compile(s1,s0,depth1,0,block_flags);
- if (s0<se1) {
- s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg3 = compile(s0,s1,depth1,0,block_flags);
- arg4 = s1<se1?compile(++s1,se1,depth1,0,block_flags):1;
- }
- }
- _cimg_mp_check_type(arg1,1,2,0);
- _cimg_mp_check_type(arg2,2,1,0);
- if (arg3!=~0U) _cimg_mp_check_type(arg3,3,1,0);
- _cimg_mp_check_type(arg4,4,1,0);
- p1 = _cimg_mp_size(arg1);
- pos = vector(p1);
- CImg<ulongT>::vector((ulongT)mp_sort,pos,arg1,p1,arg2,arg3,arg4).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"sqr(",4)) { // Square
- _cimg_mp_op("Function 'sqr()'");
- arg1 = compile(ss4,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqr,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::sqr(mem[arg1]));
- _cimg_mp_scalar1(mp_sqr,arg1);
- }
- if (!std::strncmp(ss,"sqrt(",5)) { // Square root
- _cimg_mp_op("Function 'sqrt()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqrt,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::sqrt(mem[arg1]));
- _cimg_mp_scalar1(mp_sqrt,arg1);
- }
- if (!std::strncmp(ss,"srand(",6)) { // Set RNG seed
- _cimg_mp_op("Function 'srand()'");
- arg1 = ss6<se1?compile(ss6,se1,depth1,0,block_flags):~0U;
- if (arg1!=~0U) { _cimg_mp_check_type(arg1,1,1,0); _cimg_mp_scalar1(mp_srand,arg1); }
- _cimg_mp_scalar0(mp_srand0);
- }
- if (!std::strncmp(ss,"stats(",6)) { // Image statistics
- _cimg_mp_op("Function 'stats()'");
- if (*ss6=='#') { // Index specified
- p1 = compile(ss7,se1,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- _cimg_mp_check_list();
- } else { if (ss6!=se1) break; p1 = ~0U; }
- pos = vector(14);
- CImg<ulongT>::vector((ulongT)mp_image_stats,pos,p1).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- #ifdef cimg_mp_func_store
- if (!std::strncmp(ss,"store(",6)) { // Store vector to variable
- _cimg_mp_op("Function 'store()'");
- s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss6,s1,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,2,2,0);
- p1 = _cimg_mp_size(arg1);
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,0,block_flags);
- p2 = _cimg_mp_size(arg2);
- p3 = std::max(1U,p2);
- arg3 = ~0U; arg4 = arg5 = arg6 = 1U; pos = 0;
- if (s2<se1) {
- s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg3 = compile(s2,s1,depth1,0,block_flags);
- _cimg_mp_check_type(arg3,3,1,0);
- arg4 = arg5 = arg6 = 1U;
- if (s1<se1) {
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg4 = compile(s1,s2,depth1,0,block_flags);
- _cimg_mp_check_type(arg4,4,1,0);
- if (s2<se1) {
- s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg5 = compile(s2,s1,depth1,0,block_flags);
- _cimg_mp_check_type(arg5,5,1,0);
- if (s1<se1) {
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg6 = compile(s1,s2,depth1,0,block_flags);
- pos = s2<se1?compile(++s2,se1,depth1,0,block_flags):0;
- _cimg_mp_check_type(arg6,6,1,0);
- _cimg_mp_check_type(pos,7,1,0);
- }
- }
- }
- }
- if (arg3==~0U) arg3 = const_scalar(p3);
- CImg<ulongT>::vector((ulongT)mp_store,_cimg_mp_slot_nan,arg2,p2,arg1,p1,
- arg3,arg4,arg5,arg6,pos).move_to(code);
- _cimg_mp_return_nan();
- }
- #endif
- if (!std::strncmp(ss,"s2v(",4)) { // String to double
- _cimg_mp_op("Function 's2v()'");
- s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss4,s1,depth1,0,block_flags);
- arg2 = arg3 = 0;
- if (s1<se1) {
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,0,block_flags);
- arg3 = s2<se1?compile(++s2,se1,depth1,0,block_flags):0;
- }
- _cimg_mp_check_type(arg2,2,1,0);
- _cimg_mp_check_type(arg3,3,1,0);
- p1 = _cimg_mp_size(arg1);
- pos = scalar();
- CImg<ulongT>::vector((ulongT)mp_s2v,pos,arg1,p1,arg2,arg3).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"string(",7)) { // Construct string from list of arguments
- _cimg_mp_op("Function 'string()'");
- CImg<ulongT>::vector((ulongT)mp_string,0,0,0).move_to(l_opcode);
- if (*ss7=='#') { // Output vector size specified, with '#'
- s0 = ss8; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- arg1 = compile(ss8,s0++,depth1,0,block_flags);
- _cimg_mp_check_const_scalar(arg1,1,3);
- arg1 = (unsigned int)mem[arg1];
- s = s0;
- } else { arg1=~0U; s = ss7; }
- p1 = 0;
- for (; s<se; ++s) {
- ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
- (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
- arg2 = compile(s,ns,depth1,0,block_flags);
- p2 = _cimg_mp_size(arg2);
- if (p2) p1+=p2;
- else {
- if (_cimg_mp_is_const_scalar(arg2)) p1+=cimg_snprintf(variable_name.assign(24),24,"%.17g",mem[arg2]);
- else p1+=23;
- }
- CImg<ulongT>::vector(arg2,p2).move_to(l_opcode);
- s = ns;
- }
- if (arg1==~0U) arg1 = p1;
- pos = vector(arg1,0);
- (l_opcode>'y').move_to(opcode);
- opcode[1] = pos;
- opcode[2] = arg1;
- opcode[3] = opcode._height;
- opcode.move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"svd(",4)) { // Matrix SVD
- _cimg_mp_op("Function 'svd()'");
- s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss4,s1,depth1,0,block_flags);
- arg2 = s1<se1?compile(++s1,se1,depth1,0,block_flags):1;
- _cimg_mp_check_type(arg1,1,2,0);
- _cimg_mp_check_const_scalar(arg2,2,3);
- p1 = _cimg_mp_size(arg1);
- p2 = (unsigned int)mem[arg2];
- p3 = p1/p2;
- if (p3*p2!=p1) {
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Type of first argument ('%s') "
- "does not match with second argument 'nb_colsA=%u', "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- s_type(arg1)._data,p2,s0);
- }
- pos = vector(p1 + p2 + p2*p2);
- CImg<ulongT>::vector((ulongT)mp_matrix_svd,pos,arg1,p2,p3).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"swap(",5)) { // Swap values
- _cimg_mp_op("Function 'swap()'");
- s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- ref.assign(14);
- arg1 = compile(ss5,s1,depth1,ref,block_flags);
- arg2 = compile(++s1,se1,depth1,ref._data + 7,block_flags);
- p1 = _cimg_mp_size(arg1);
- _cimg_mp_check_type(arg2,2,p1?2:1,p1);
- if (_cimg_mp_is_const_scalar(arg1) || _cimg_mp_is_const_scalar(arg2)) {
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: %s argument cannot be a constant, "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- _cimg_mp_is_const_scalar(arg1)?"First":"Second",s0);
- }
- CImg<ulongT>::vector((ulongT)mp_swap,arg1,arg2,p1).move_to(code);
- // Write back values of linked arg1 and arg2.
- const unsigned int *_ref = ref;
- is_sth = true; // Is first argument?
- do {
- switch (*_ref) {
- case 1 : // arg1: V[k]
- arg3 = _ref[1]; // Vector slot
- arg4 = _ref[2]; // Index
- CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4).
- move_to(code);
- break;
- case 2 : // arg1: i/j[_#ind,off]
- if (!is_inside_critical) is_parallelizable = false;
- p1 = _ref[1]; // Index
- is_relative = (bool)_ref[2];
- arg3 = _ref[3]; // Offset
- if (p1!=~0U) {
- if (imglist)
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
- arg1,p1,arg3).move_to(code);
- } else {
- if (imgout)
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
- arg1,arg3).move_to(code);
- }
- break;
- case 3 : // arg1: i/j(_#ind,_x,_y,_z,_c)
- if (!is_inside_critical) is_parallelizable = false;
- p1 = _ref[1]; // Index
- is_relative = (bool)_ref[2];
- arg3 = _ref[3]; // X
- arg4 = _ref[4]; // Y
- arg5 = _ref[5]; // Z
- arg6 = _ref[6]; // C
- if (p1!=~0U) {
- if (imglist)
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
- arg1,p1,arg3,arg4,arg5,arg6).move_to(code);
- } else {
- if (imgout)
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
- arg1,arg3,arg4,arg5,arg6).move_to(code);
- }
- break;
- case 4: // arg1: I/J[_#ind,off]
- if (!is_inside_critical) is_parallelizable = false;
- p1 = _ref[1]; // Index
- is_relative = (bool)_ref[2];
- arg3 = _ref[3]; // Offset
- if (p1!=~0U) {
- if (imglist) {
- if (_cimg_mp_is_scalar(arg1))
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s),
- arg1,p1,arg3).move_to(code);
- else {
- _cimg_mp_check_const_index(p1);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
- arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
- }
- }
- } else {
- if (imgout) {
- if (_cimg_mp_is_scalar(arg1))
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s),
- arg1,arg3).move_to(code);
- else
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
- arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
- }
- }
- break;
- case 5 : // arg1: I/J(_#ind,_x,_y,_z,_c)
- if (!is_inside_critical) is_parallelizable = false;
- p1 = _ref[1]; // Index
- is_relative = (bool)_ref[2];
- arg3 = _ref[3]; // X
- arg4 = _ref[4]; // Y
- arg5 = _ref[5]; // Z
- if (p1!=~0U) {
- if (imglist) {
- if (_cimg_mp_is_scalar(arg1))
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s),
- arg1,p1,arg3,arg4,arg5).move_to(code);
- else {
- _cimg_mp_check_const_index(p1);
- CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
- arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
- }
- }
- } else {
- if (imgout) {
- if (_cimg_mp_is_scalar(arg1))
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s),
- arg1,arg3,arg4,arg5).move_to(code);
- else
- CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
- arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
- }
- }
- break;
- }
- _ref+=7;
- arg1 = arg2;
- is_sth = !is_sth;
- } while (!is_sth);
- if (p_ref) std::memcpy(p_ref,ref,siz_ref);
- _cimg_mp_return_nan();
- }
- break;
- case 't' :
- if (!std::strncmp(ss,"tan(",4)) { // Tangent
- _cimg_mp_op("Function 'tan()'");
- arg1 = compile(ss4,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tan,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::tan(mem[arg1]));
- _cimg_mp_scalar1(mp_tan,arg1);
- }
- if (!std::strncmp(ss,"tanh(",5)) { // Hyperbolic tangent
- _cimg_mp_op("Function 'tanh()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tanh,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::tanh(mem[arg1]));
- _cimg_mp_scalar1(mp_tanh,arg1);
- }
- if (!std::strncmp(ss,"trace(",6)) { // Matrix trace
- _cimg_mp_op("Function 'trace()'");
- arg1 = compile(ss6,se1,depth1,0,block_flags);
- _cimg_mp_check_matrix_square(arg1,1);
- p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
- _cimg_mp_scalar2(mp_trace,arg1,p1);
- }
- if (!std::strncmp(ss,"transpose(",10)) { // Matrix transpose
- _cimg_mp_op("Function 'transpose()'");
- s1 = ss + 10; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss + 10,s1,depth1,0,block_flags);
- arg2 = compile(++s1,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,1,2,0);
- _cimg_mp_check_const_scalar(arg2,2,3);
- p1 = _cimg_mp_size(arg1);
- p2 = (unsigned int)mem[arg2];
- p3 = p1/p2;
- if (p2*p3!=p1) {
- _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Size of first argument ('%s') does not match "
- "second argument 'nb_cols=%u', in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- s_type(arg1)._data,p2,s0);
- }
- pos = vector(p3*p2);
- CImg<ulongT>::vector((ulongT)mp_transpose,pos,arg1,p2,p3).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- break;
- case 'u' :
- if (*ss1=='(') { // Random value with uniform distribution in specified range
- _cimg_mp_op("Function 'u()'");
- if (*ss2==')') _cimg_mp_scalar2(mp_u,0,1);
- s1 = ss2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss2,s1,depth1,0,block_flags);
- arg3 = arg4 = 1;
- if (s1<se1) {
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,0,block_flags);
- if (s2<se1) {
- s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg3 = compile(s2,s1,depth1,0,block_flags);
- arg4 = s1<se1?compile(++s1,se1,depth1,0,block_flags):1;
- }
- } else { arg2 = arg1; arg1 = 0; }
- _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
- _cimg_mp_check_type(arg3,3,1,0);
- _cimg_mp_check_type(arg4,4,1,0);
- if (arg3==1 && arg4==1) { // Fastest version (closed set)
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2))
- _cimg_mp_vector2_vv(mp_u,arg1,arg2);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2))
- _cimg_mp_vector2_vs(mp_u,arg1,arg2);
- if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2))
- _cimg_mp_vector2_sv(mp_u,arg1,arg2);
- _cimg_mp_scalar2(mp_u,arg1,arg2);
- } else { // Slower version (open set)
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2))
- _cimg_mp_vector4_vvss(mp_u_ext,arg1,arg2,arg3,arg4);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2))
- _cimg_mp_vector4_vsss(mp_u_ext,arg1,arg2,arg3,arg4);
- if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2))
- _cimg_mp_vector4_svss(mp_u_ext,arg1,arg2,arg3,arg4);
- _cimg_mp_scalar4(mp_u_ext,arg1,arg2,arg3,arg4);
- }
- }
- if (!std::strncmp(ss,"ui2f(",5)) { // Special uint->float conversion
- _cimg_mp_op("Function 'ui2f()'");
- arg1 = compile(ss5,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_ui2f,arg1);
- if (_cimg_mp_is_const_scalar(arg1))
- _cimg_mp_const_scalar((double)cimg::uint2float((unsigned int)mem[arg1]));
- _cimg_mp_scalar1(mp_ui2f,arg1);
- }
- if (!std::strncmp(ss,"unitnorm(",9)) { // Normalize vector to unit norm
- _cimg_mp_op("Function 'unitnorm()'");
- s0 = ss + 9;
- s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(s0,s1,depth1,0,block_flags);
- arg2 = s1<se1?compile(++s1,se1,depth1,0,block_flags):2;
- _cimg_mp_check_type(arg2,0,1,0);
- p1 = _cimg_mp_size(arg1);
- if (p1>0) pos = is_comp_vector(arg1)?arg1:((return_new_comp = true), vector(p1));
- else {
- pos = scalar();
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) {
- val = mem[arg1];
- _cimg_mp_const_scalar(val?(mem[arg2]?1:val):0);
- }
- }
- CImg<ulongT>::vector((ulongT)mp_vector_unitnorm,pos,arg1,p1,arg2).move_to(code);
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"unref(",6)) { // Un-reference variable
- _cimg_mp_op("Function 'unref()'");
- arg1=~0U;
- for (s0 = ss6; s0<se1; s0 = s1) {
- if (s0>ss6 && *s0==',') ++s0;
- s1 = s0; while (s1<se1 && *s1!=',') ++s1;
- c1 = *s1;
- if (s1>s0) {
- *s1 = 0;
- get_variable_pos(s0,arg1,arg2);
- if (arg2!=~0U) reserved_label[arg2] = ~0U;
- else if (arg1!=~0U) {
- variable_def.remove(arg1);
- if (arg1<variable_pos._width - 1)
- std::memmove(variable_pos._data + arg1,variable_pos._data + arg1 + 1,
- sizeof(uintT)*(variable_pos._width - arg1 - 1));
- --variable_pos._width;
- }
- *s1 = c1;
- } else compile(s0,s1,depth1,0,block_flags); // Will throw a 'missing argument' exception
- }
- _cimg_mp_return(arg1!=~0U?arg1:_cimg_mp_slot_nan); // Return value of last specified variable
- }
- if (!std::strncmp(ss,"uppercase(",10)) { // Upper case
- _cimg_mp_op("Function 'uppercase()'");
- arg1 = compile(ss + 10,se1,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_uppercase,arg1);
- if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::uppercase(mem[arg1]));
- _cimg_mp_scalar1(mp_uppercase,arg1);
- }
- break;
- case 'v' :
- if ((cimg_sscanf(ss,"vector%u%c",&(arg1=~0U),&sep)==2 && sep=='(' && arg1>0) ||
- !std::strncmp(ss,"vector(",7) ||
- (!std::strncmp(ss,"vector",6) && ss7<se1 && (s=std::strchr(ss7,'('))!=0)) { // Vector
- _cimg_mp_op("Function 'vector()'");
- arg2 = 0; // Number of specified values
- if (arg1==~0U && *ss6!='(') {
- arg1 = compile(ss6,s++,depth1,0,block_flags);
- _cimg_mp_check_const_scalar(arg1,0,3);
- arg1 = (unsigned int)mem[arg1];
- } else s = std::strchr(ss6,'(') + 1;
- if (arg1==~0U && *s=='#') { // Number of elements specified as first argument with '#'
- s0 = ++s; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
- arg1 = compile(s,s0++,depth1,0,block_flags);
- _cimg_mp_check_const_scalar(arg1,1,3);
- arg1 = (unsigned int)mem[arg1];
- s = s0;
- }
- if (s<se1 || arg1==~0U) for ( ; s<se; ++s) {
- ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
- (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
- arg3 = compile(s,ns,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg3)) {
- arg4 = _cimg_mp_size(arg3);
- CImg<ulongT>::sequence(arg4,arg3 + 1,arg3 + arg4).move_to(l_opcode);
- arg2+=arg4;
- } else { CImg<ulongT>::vector(arg3).move_to(l_opcode); ++arg2; }
- s = ns;
- }
- if (arg1==~0U) arg1 = arg2;
- if (!arg1) _cimg_mp_return(0);
- pos = vector(arg1);
- l_opcode.insert(CImg<ulongT>::vector((ulongT)mp_vector_init,pos,0,arg1),0);
- (l_opcode>'y').move_to(opcode);
- opcode[2] = opcode._height;
- opcode.move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"vmax(",5) || !std::strncmp(ss,"vmin(",5) ||
- !std::strncmp(ss,"vmaxabs(",8) || !std::strncmp(ss,"vminabs(",8) ||
- !std::strncmp(ss,"vmed(",5) || !std::strncmp(ss,"vkth(",5) ||
- !std::strncmp(ss,"vsum(",5) || !std::strncmp(ss,"vavg(",5) ||
- !std::strncmp(ss,"vstd(",5) || !std::strncmp(ss,"vvar(",5) ||
- !std::strncmp(ss,"vprod(",6) ||
- !std::strncmp(ss,"vargmin(",8) || !std::strncmp(ss,"vargmax(",8) ||
- !std::strncmp(ss,"vargminabs(",11) || !std::strncmp(ss,"vargmaxabs(",11) ||
- !std::strncmp(ss,"vargkth(",8)) { // Multi-argument vector functions
- _cimg_mp_op(ss[1]=='a'?(ss[2]=='v'?"Function 'vavg()'":
- ss[4]=='k'?"Function 'vargkth()'":
- ss[5]=='i' && ss[7]=='('?"Function 'vargmin()'":
- ss[5]=='i'?"Function vargminabs()'":
- ss[7]=='('?"Function 'vargmax()'":
- "Function 'vargmaxabs()'"):
- ss[1]=='s'?(ss[2]=='u'?"Function 'vsum()'":"Function 'vstd()'"):
- ss[1]=='k'?"Function 'vkth()'":
- ss[1]=='p'?"Function 'vprod()'":
- ss[1]=='v'?"Function 'vvar()'":
- ss[2]=='i'?(ss[4]=='('?"Function 'vmin()'":
- "Function 'vminabs()'"):
- ss[2]=='a'?(ss[4]=='('?"Function 'vmax()'":
- "Function 'vmaxabs()'"):
- "Function 'vmed()'");
- op = ss[1]=='a'?(ss[2]=='v'?mp_vavg:
- ss[4]=='k'?mp_vargkth:
- ss[5]=='i' && ss[7]=='('?mp_vargmin:
- ss[5]=='i'?mp_vargminabs:
- ss[7]=='('?mp_vargmax:mp_vargmaxabs):
- ss[1]=='s'?(ss[2]=='u'?mp_vsum:mp_vstd):
- ss[1]=='k'?mp_vkth:
- ss[1]=='p'?mp_vprod:
- ss[1]=='v'?mp_vvar:
- ss[2]=='i'?(ss[4]=='('?mp_vmin:mp_vminabs):
- ss[2]=='a'?(ss[4]=='('?mp_vmax:mp_vmaxabs):
- mp_vmedian;
- CImg<ulongT>::vector((ulongT)op,0,0,0).move_to(l_opcode);
- p1 = ~0U;
- p3 = 1;
- for (s = std::strchr(ss,'(') + 1; s<se; ++s) {
- ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
- (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
- arg2 = compile(s,ns,depth1,0,block_flags);
- p2 = _cimg_mp_size(arg2);
- if (p1==~0U) { if (_cimg_mp_is_vector(arg2)) p1 = p2; }
- else _cimg_mp_check_type(arg2,p3,3,p1);
- CImg<ulongT>::vector(arg2,p2).move_to(l_opcode);
- s = ns;
- ++p3;
- }
- (l_opcode>'y').move_to(opcode);
- if (p1==~0U) { pos = scalar(); p1 = 0; } else pos = vector(p1);
- opcode[1] = pos;
- opcode[2] = p1;
- opcode[3] = opcode._height;
- opcode.move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"v2s(",4)) { // Double(s) to string
- _cimg_mp_op("Function 'v2s()'");
- s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss4,s1,depth1,0,block_flags);
- arg2 = 0; arg3 = ~0U;
- if (s1<se1) {
- s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
- arg2 = compile(s1,s2,depth1,0,block_flags);
- arg3 = s2<se1?compile(++s2,se1,depth1,0,block_flags):~0U;
- }
- _cimg_mp_check_type(arg2,2,1,0);
- if (arg3==~0U) { // Auto-guess best output vector size
- p1 = _cimg_mp_size(arg1);
- p1 = p1?25*p1 - 1:24;
- } else {
- _cimg_mp_check_const_scalar(arg3,3,3);
- p1 = (unsigned int)mem[arg3];
- }
- pos = vector(p1);
- CImg<ulongT>::vector((ulongT)mp_v2s,pos,p1,arg1,_cimg_mp_size(arg1),arg2).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- break;
- case 'w' :
- if (*ss1=='(') { // Image width
- _cimg_mp_op("Function 'w()'");
- if (*ss2=='#') { // Index specified
- p1 = compile(ss3,se1,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- _cimg_mp_check_list();
- } else { if (ss2!=se1) break; p1 = ~0U; }
- _cimg_mp_scalar1(mp_image_w,p1);
- }
- if (*ss1=='h' && *ss2=='(') { // Image width*height
- _cimg_mp_op("Function 'wh()'");
- if (*ss3=='#') { // Index specified
- p1 = compile(ss4,se1,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- _cimg_mp_check_list();
- } else { if (ss3!=se1) break; p1 = ~0U; }
- _cimg_mp_scalar1(mp_image_wh,p1);
- }
- if (*ss1=='h' && *ss2=='d' && *ss3=='(') { // Image width*height*depth
- _cimg_mp_op("Function 'whd()'");
- if (*ss4=='#') { // Index specified
- p1 = compile(ss5,se1,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- _cimg_mp_check_list();
- } else { if (ss4!=se1) break; p1 = ~0U; }
- _cimg_mp_scalar1(mp_image_whd,p1);
- }
- if (*ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='(') { // Image width*height*depth*spectrum
- _cimg_mp_op("Function 'whds()'");
- if (*ss5=='#') { // Index specified
- p1 = compile(ss6,se1,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(p1);
- _cimg_mp_check_list();
- } else { if (ss5!=se1) break; p1 = ~0U; }
- _cimg_mp_scalar1(mp_image_whds,p1);
- }
- if (!std::strncmp(ss,"while(",6)) { // While...do
- _cimg_mp_op("Function 'while()'");
- s0 = *ss5=='('?ss6:ss8;
- s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- p1 = code._width;
- arg1 = compile(s0,s1,depth1,0,block_flags);
- p2 = code._width;
- arg6 = mempos;
- pos = compile(++s1,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg1,1,1,0);
- arg2 = _cimg_mp_size(pos);
- CImg<ulongT>::vector((ulongT)mp_while,pos,arg1,p2 - p1,code._width - p2,arg2,
- pos>=arg6 && !_cimg_mp_is_const_scalar(pos),
- arg1>=arg6 && !_cimg_mp_is_const_scalar(arg1)).move_to(code,p1);
- _cimg_mp_return(pos);
- }
- break;
- case 'x' :
- if (!std::strncmp(ss,"xor(",4)) { // Xor
- _cimg_mp_op("Function 'xor()'");
- s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
- arg1 = compile(ss4,s1,depth1,0,block_flags);
- arg2 = compile(++s1,se1,depth1,0,block_flags);
- _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_xor,arg1,arg2);
- if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_xor,arg1,arg2);
- if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_xor,arg1,arg2);
- if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
- _cimg_mp_const_scalar((longT)mem[arg1] ^ (longT)mem[arg2]);
- _cimg_mp_scalar2(mp_bitwise_xor,arg1,arg2);
- }
- break;
- }
- if ((!std::strncmp(ss,"norm",4) && ss4<se1 && (s = std::strchr(ss4,'('))!=0) || // Lp norm (constant p)
- (!std::strncmp(ss,"hypot(",6) && (s = ss5))) {
- const bool is_hypot = *ss=='h';
- _cimg_mp_op(is_hypot?"Function 'hypot()'":"Function 'norm()'");
- arg1 = !is_hypot && s!=ss4?compile(ss4,s,depth1,0,block_flags):2;
- _cimg_mp_check_const_scalar(arg1,0,0);
- val = mem[arg1];
- is_sth = true; // Tell if all arguments are constant
- CImg<ulongT>::vector(0,0,0,arg1).move_to(l_opcode);
- for (++s; s<se; ++s) {
- ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
- (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
- arg2 = compile(s,ns,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg2))
- CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,arg2 + (ulongT)_cimg_mp_size(arg2)).
- move_to(l_opcode);
- else CImg<ulongT>::vector(arg2).move_to(l_opcode);
- is_sth&=_cimg_mp_is_const_scalar(arg2);
- s = ns;
- }
- (l_opcode>'y').move_to(opcode);
- op = val==2?(is_hypot && opcode._height<8?_mp_vector_hypot:_mp_vector_norm2):
- val==1?_mp_vector_norm1:!val?_mp_vector_norm0:
- cimg::type<double>::is_inf(val)?_mp_vector_norminf:_mp_vector_normp;
- opcode[0] = (ulongT)op;
- opcode[2] = opcode._height;
- if (is_sth) _cimg_mp_const_scalar(op(*this));
- if (opcode._height==5) { // Single argument
- if (arg1) { _cimg_mp_scalar1(mp_abs,opcode[4]); }
- else { _cimg_mp_scalar2(mp_neq,opcode[4],0); }
- }
- opcode[1] = pos = scalar();
- opcode.move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- if (!std::strncmp(ss,"max(",4) || !std::strncmp(ss,"min(",4) ||
- !std::strncmp(ss,"maxabs(",7) || !std::strncmp(ss,"minabs(",7) ||
- !std::strncmp(ss,"med(",4) || !std::strncmp(ss,"kth(",4) ||
- !std::strncmp(ss,"sum(",4) || !std::strncmp(ss,"avg(",4) ||
- !std::strncmp(ss,"std(",4) || !std::strncmp(ss,"var(",4) ||
- !std::strncmp(ss,"prod(",5) ||
- !std::strncmp(ss,"argmin(",7) || !std::strncmp(ss,"argmax(",7) ||
- !std::strncmp(ss,"argminabs(",10) || !std::strncmp(ss,"argmaxabs(",10) ||
- !std::strncmp(ss,"argkth(",7)) { // Multi-argument functions
- _cimg_mp_op(*ss=='a'?(ss[1]=='v'?"Function 'avg()'":
- ss[3]=='k'?"Function 'argkth()'":
- ss[4]=='i' && ss[6]=='('?"Function 'argmin()'":
- ss[4]=='i'?"Function argminabs()'":
- ss[6]=='('?"Function 'argmax()'":
- "Function 'argmaxabs()'"):
- *ss=='s'?(ss[1]=='u'?"Function 'sum()'":"Function 'std()'"):
- *ss=='k'?"Function 'kth()'":
- *ss=='p'?"Function 'prod()'":
- *ss=='v'?"Function 'var()'":
- ss[1]=='i'?(ss[3]=='('?"Function 'min()'":
- "Function 'minabs()'"):
- ss[1]=='a'?(ss[3]=='('?"Function 'max()'":
- "Function 'maxabs()'"):
- "Function 'med()'");
- op = *ss=='a'?(ss[1]=='v'?mp_avg:
- ss[3]=='k'?mp_argkth:
- ss[4]=='i' && ss[6]=='('?mp_argmin:
- ss[4]=='i'?mp_argminabs:
- ss[6]=='('?mp_argmax:mp_argmaxabs):
- *ss=='s'?(ss[1]=='u'?mp_sum:mp_std):
- *ss=='k'?mp_kth:
- *ss=='p'?mp_prod:
- *ss=='v'?mp_var:
- ss[1]=='i'?(ss[3]=='('?mp_min:mp_minabs):
- ss[1]=='a'?(ss[3]=='('?mp_max:mp_maxabs):
- mp_med;
- is_sth = true; // Tell if all arguments are constant
- pos = scalar();
- CImg<ulongT>::vector((ulongT)op,pos,0).move_to(l_opcode);
- for (s = std::strchr(ss,'(') + 1; s<se; ++s) {
- ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
- (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
- arg2 = compile(s,ns,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg2)) CImg<ulongT>::vector(arg2 + 1,_cimg_mp_size(arg2)).move_to(l_opcode);
- else CImg<ulongT>::vector(arg2,1).move_to(l_opcode);
- is_sth&=_cimg_mp_is_const_scalar(arg2);
- s = ns;
- }
- (l_opcode>'y').move_to(opcode);
- opcode[2] = opcode._height;
- if (is_sth) _cimg_mp_const_scalar(op(*this));
- opcode.move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- // No corresponding built-in function -> Look for a user-defined macro call.
- s0 = strchr(ss,'(');
- if (s0) {
- variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0;
- // Count number of specified arguments.
- p1 = 0;
- for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) {
- while (*s && cimg::is_blank(*s)) ++s;
- if (*s==')' && !p1) break;
- ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
- (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
- }
- char mb = 0;
- arg3 = 0; // Number of possible name matches
- cimglist_for(macro_def,l) if (!std::strcmp(macro_def[l],variable_name) && ++arg3 &&
- ((mb = macro_def[l].back())==(char)p1 || mb==(char)-1)) {
- const bool is_variadic = mb==(char)-1;
- p2 = is_variadic?1U:(unsigned int)mb; // Number of required arguments
- CImg<charT> _expr = macro_body[l]; // Expression to be substituted
- p1 = 1; // Index of current parsed argument
- for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) { // Parse function arguments
- while (*s && cimg::is_blank(*s)) ++s;
- if (!is_variadic && *s==')' && p1==1) break; // Function has no arguments
- if (p1>p2) { ++p1; break; }
- if (is_variadic) ns = se1;
- else {
- ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
- (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
- }
- variable_name.assign(s,(unsigned int)(ns - s + 1)).back() = 0; // Argument to write
- arg2 = 0;
- cimg_forX(_expr,k) {
- if (_expr[k]==(char)p1) { // Perform argument substitution
- arg1 = _expr._width;
- if (variable_name._width>1) {
- _expr.resize(arg1 + variable_name._width - 2,1,1,1,0);
- std::memmove(_expr._data + k + variable_name._width - 1,_expr._data + k + 1,arg1 - k - 1);
- std::memcpy(_expr._data + k,variable_name,variable_name._width - 1);
- k+=variable_name._width - 2;
- } else {
- std::memmove(_expr._data + k,_expr._data + k + 1,arg1 - k - 1);
- --k;
- }
- }
- ++arg2;
- }
- }
- // Recompute 'pexpr' and 'level' for evaluating substituted expression.
- CImg<charT> _pexpr(_expr._width);
- ns = _pexpr._data;
- for (ps = _expr._data, c1 = ' '; *ps; ++ps) {
- if (!cimg::is_blank(*ps)) c1 = *ps;
- *(ns++) = c1;
- }
- *ns = 0;
- CImg<uintT> _level = get_level(_expr);
- expr.swap(_expr);
- pexpr.swap(_pexpr);
- level.swap(_level);
- s0 = user_macro;
- user_macro = macro_def[l];
- pos = compile(expr._data,expr._data + expr._width - 1,depth1,p_ref,block_flags);
- user_macro = s0;
- level.swap(_level);
- pexpr.swap(_pexpr);
- expr.swap(_expr);
- _cimg_mp_return(pos);
- }
- if (arg3) { // Macro name matched but number of arguments does not
- CImg<uintT> sig_nargs(arg3);
- arg1 = 0;
- cimglist_for(macro_def,l) if (!std::strcmp(macro_def[l],variable_name))
- sig_nargs[arg1++] = (unsigned int)macro_def[l].back();
- _cimg_mp_strerr;
- cimg::strellipsize(variable_name,64);
- if (sig_nargs._width>1) {
- sig_nargs.sort();
- arg1 = sig_nargs.back();
- --sig_nargs._width;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) "
- "does not match macro declaration (defined for %s or %u arguments), "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,variable_name._data,
- p1,sig_nargs.value_string()._data,arg1,s0);
- } else
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) "
- "does not match macro declaration (defined for %u argument%s), "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,variable_name._data,
- p1,*sig_nargs,*sig_nargs!=1?"s":"",s0);
- }
- }
- } // if (se1==')')
- // Char / string initializer.
- if (*se1=='\'' &&
- ((se1>ss && *ss=='\'') ||
- (se1>ss1 && *ss=='_' && *ss1=='\''))) {
- if (*ss=='_') { _cimg_mp_op("Char initializer"); s1 = ss2; }
- else { _cimg_mp_op("String initializer"); s1 = ss1; }
- arg1 = (unsigned int)(se1 - s1); // Original string length
- if (arg1) {
- CImg<charT>(s1,arg1 + 1).move_to(variable_name).back() = 0;
- cimg::strunescape(variable_name);
- arg1 = (unsigned int)std::strlen(variable_name);
- }
- if (!arg1) _cimg_mp_return(0); // Empty string -> 0
- if (*ss=='_') {
- if (arg1==1) _cimg_mp_const_scalar((unsigned char)*variable_name);
- _cimg_mp_strerr;
- cimg::strellipsize(variable_name,64);
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s: Literal %s contains more than one byte, "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,
- ss1,s0);
- }
- pos = vector(arg1);
- CImg<ulongT>::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode);
- CImg<ulongT>(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode);
- std::memcpy((char*)l_opcode[1]._data,variable_name,arg1);
- (l_opcode>'y').move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- // Vector initializer [ ... ].
- if (*ss=='[' && *se1==']') {
- _cimg_mp_op("Vector initializer");
- s1 = ss1; while (s1<se2 && cimg::is_blank(*s1)) ++s1;
- s2 = se2; while (s2>s1 && cimg::is_blank(*s2)) --s2;
- if (s2>s1 && *s1=='\'' && *s2=='\'') { // Vector values provided as a string
- arg1 = (unsigned int)(s2 - s1 - 1); // Original string length
- if (arg1) {
- CImg<charT>(s1 + 1,arg1 + 1).move_to(variable_name).back() = 0;
- cimg::strunescape(variable_name);
- arg1 = (unsigned int)std::strlen(variable_name);
- }
- if (!arg1) _cimg_mp_return(0); // Empty string -> 0
- pos = vector(arg1);
- CImg<ulongT>::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode);
- CImg<ulongT>(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode);
- std::memcpy((char*)l_opcode[1]._data,variable_name,arg1);
- (l_opcode>'y').move_to(code);
- } else { // Vector values provided as list of items
- arg1 = 0; // Number of specified values
- if (*ss1!=']') for (s = ss1; s<se; ++s) {
- ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
- (*ns!=']' || level[ns - expr._data]!=clevel)) ++ns;
- arg2 = compile(s,ns,depth1,0,block_flags);
- if (_cimg_mp_is_vector(arg2)) {
- arg3 = _cimg_mp_size(arg2);
- CImg<ulongT>::sequence(arg3,arg2 + 1,arg2 + arg3).move_to(l_opcode);
- arg1+=arg3;
- } else { CImg<ulongT>::vector(arg2).move_to(l_opcode); ++arg1; }
- s = ns;
- }
- if (!arg1) _cimg_mp_return(0);
- pos = vector(arg1);
- l_opcode.insert(CImg<ulongT>::vector((ulongT)mp_vector_init,pos,0,arg1),0);
- (l_opcode>'y').move_to(opcode);
- opcode[2] = opcode._height;
- opcode.move_to(code);
- }
- return_new_comp = true;
- _cimg_mp_return(pos);
- }
- // Variables related to the input list of images.
- if (*ss1=='#' && ss2<se) {
- arg1 = compile(ss2,se,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(arg1);
- p1 = (unsigned int)(imglist._width && _cimg_mp_is_const_scalar(arg1)?
- cimg::mod((int)mem[arg1],imglist.width()):~0U);
- switch (*ss) {
- case 'w' : // w#ind
- if (!imglist) _cimg_mp_return(0);
- if (p1!=~0U) _cimg_mp_const_scalar(imglist[p1]._width);
- _cimg_mp_scalar1(mp_list_width,arg1);
- case 'h' : // h#ind
- if (!imglist) _cimg_mp_return(0);
- if (p1!=~0U) _cimg_mp_const_scalar(imglist[p1]._height);
- _cimg_mp_scalar1(mp_list_height,arg1);
- case 'd' : // d#ind
- if (!imglist) _cimg_mp_return(0);
- if (p1!=~0U) _cimg_mp_const_scalar(imglist[p1]._depth);
- _cimg_mp_scalar1(mp_list_depth,arg1);
- case 'r' : // r#ind
- if (!imglist) _cimg_mp_return(0);
- if (p1!=~0U) _cimg_mp_const_scalar(imglist[p1]._is_shared);
- _cimg_mp_scalar1(mp_list_is_shared,arg1);
- case 's' : // s#ind
- if (!imglist) _cimg_mp_return(0);
- if (p1!=~0U) _cimg_mp_const_scalar(imglist[p1]._spectrum);
- _cimg_mp_scalar1(mp_list_spectrum,arg1);
- case 'i' : // i#ind
- if (!imglist) _cimg_mp_return(0);
- _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,_cimg_mp_slot_c,
- 0,_cimg_mp_boundary);
- case 'I' : // I#ind
- p2 = p1!=~0U?imglist[p1]._spectrum:imglist._width?~0U:0;
- if (!p2) _cimg_mp_return(0);
- pos = vector(p2);
- CImg<ulongT>::vector((ulongT)mp_list_Joff,pos,p1,0,0,p2).move_to(code);
- return_new_comp = true;
- _cimg_mp_return(pos);
- case 'R' : // R#ind
- if (!imglist) _cimg_mp_return(0);
- _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0,
- 0,_cimg_mp_boundary);
- case 'G' : // G#ind
- if (!imglist) _cimg_mp_return(0);
- _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1,
- 0,_cimg_mp_boundary);
- case 'B' : // B#ind
- if (!imglist) _cimg_mp_return(0);
- _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2,
- 0,_cimg_mp_boundary);
- case 'A' : // A#ind
- if (!imglist) _cimg_mp_return(0);
- _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3,
- 0,_cimg_mp_boundary);
- }
- }
- if (*ss1 && *ss2=='#' && ss3<se) {
- arg1 = compile(ss3,se,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(arg1);
- p1 = (unsigned int)(imglist._width && _cimg_mp_is_const_scalar(arg1)?
- cimg::mod((int)mem[arg1],imglist.width()):~0U);
- if (*ss=='w' && *ss1=='h') { // wh#ind
- if (!imglist) _cimg_mp_return(0);
- if (p1!=~0U) _cimg_mp_const_scalar(imglist[p1]._width*imglist[p1]._height);
- _cimg_mp_scalar1(mp_list_wh,arg1);
- }
- arg2 = ~0U;
- if (*ss=='i') {
- if (*ss1>='0' && *ss1<='9') { // i0#ind...i9#ind
- if (!imglist) _cimg_mp_return(0);
- _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,*ss1 - '0',
- 0,_cimg_mp_boundary);
- }
- if (*ss1=='c') { // ic#ind
- if (!imglist) _cimg_mp_return(0);
- if (_cimg_mp_is_const_scalar(arg1)) {
- if (!list_median) list_median.assign(imglist._width);
- if (!list_median[p1]) CImg<doubleT>::vector(imglist[p1].median()).move_to(list_median[p1]);
- _cimg_mp_const_scalar(*list_median[p1]);
- }
- _cimg_mp_scalar1(mp_list_id,arg1);
- }
- if (*ss1=='d') { // id#ind
- if (!imglist) _cimg_mp_return(0);
- if (_cimg_mp_is_const_scalar(arg1)) {
- if (!list_stats) list_stats.assign(imglist._width);
- if (!list_stats[p1]) list_stats[p1].assign(1,14,1,1,0).fill(imglist[p1].get_stats(),false);
- _cimg_mp_const_scalar(std::sqrt(list_stats(p1,3)));
- }
- _cimg_mp_scalar1(mp_list_id,arg1);
- }
- if (*ss1=='n') { // in#ind
- if (!imglist) _cimg_mp_return(0);
- if (_cimg_mp_is_const_scalar(arg1)) {
- if (!list_norm) list_norm.assign(imglist._width);
- if (!list_norm[p1]) CImg<doubleT>::vector(imglist[p1].magnitude(2)).move_to(list_norm[p1]);
- _cimg_mp_const_scalar(*list_norm[p1]);
- }
- _cimg_mp_scalar1(mp_list_norm,arg1);
- }
- switch (*ss1) {
- case 'a' : arg2 = 2; break; // ia#ind
- case 'm' : arg2 = 0; break; // im#ind
- case 'M' : arg2 = 1; break; // iM#ind
- case 'p' : arg2 = 13; break; // ip#ind
- case 's' : arg2 = 12; break; // is#ind
- case 'v' : arg2 = 3; break; // iv#ind
- }
- } else if (*ss1=='m') switch (*ss) {
- case 'x' : arg2 = 4; break; // xm#ind
- case 'y' : arg2 = 5; break; // ym#ind
- case 'z' : arg2 = 6; break; // zm#ind
- case 'c' : arg2 = 7; break; // cm#ind
- } else if (*ss1=='M') switch (*ss) {
- case 'x' : arg2 = 8; break; // xM#ind
- case 'y' : arg2 = 9; break; // yM#ind
- case 'z' : arg2 = 10; break; // zM#ind
- case 'c' : arg2 = 11; break; // cM#ind
- }
- if (arg2!=~0U) {
- if (!imglist) _cimg_mp_return(0);
- if (_cimg_mp_is_const_scalar(arg1)) {
- if (!list_stats) list_stats.assign(imglist._width);
- if (!list_stats[p1]) list_stats[p1].assign(1,14,1,1,0).fill(imglist[p1].get_stats(),false);
- _cimg_mp_const_scalar(list_stats(p1,arg2));
- }
- _cimg_mp_scalar2(mp_list_stats,arg1,arg2);
- }
- }
- if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='#' && ss4<se) { // whd#ind
- arg1 = compile(ss4,se,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(arg1);
- if (!imglist) _cimg_mp_return(0);
- p1 = (unsigned int)(_cimg_mp_is_const_scalar(arg1)?cimg::mod((int)mem[arg1],imglist.width()):~0U);
- if (p1!=~0U) _cimg_mp_const_scalar(imglist[p1]._width*imglist[p1]._height*imglist[p1]._depth);
- _cimg_mp_scalar1(mp_list_whd,arg1);
- }
- if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='#' && ss5<se) { // whds#ind
- arg1 = compile(ss5,se,depth1,0,block_flags);
- _cimg_mp_check_notnan_index(arg1);
- if (!imglist) _cimg_mp_return(0);
- p1 = (unsigned int)(_cimg_mp_is_const_scalar(arg1)?cimg::mod((int)mem[arg1],imglist.width()):~0U);
- if (p1!=~0U)
- _cimg_mp_const_scalar(imglist[p1]._width*imglist[p1]._height*imglist[p1]._depth*imglist[p1]._spectrum);
- _cimg_mp_scalar1(mp_list_whds,arg1);
- }
- if (!std::strcmp(ss,"interpolation")) _cimg_mp_return(_cimg_mp_interpolation); // interpolation
- if (!std::strcmp(ss,"boundary")) _cimg_mp_return(_cimg_mp_boundary); // boundary
- variable_name.assign(ss,(unsigned int)(se + 1 - ss)).back() = 0;
- #ifdef cimg_mp_operator_dollar
- if (*ss=='$' && ss1<se) // External variable '$varname'.
- _cimg_mp_const_scalar(cimg_mp_operator_dollar(variable_name._data + 1));
- #endif
- // No known item found, assuming this is an already initialized variable.
- if (cimg::is_varname(variable_name)) { // Valid variable name
- get_variable_pos(variable_name,arg1,arg2);
- arg1 = arg2!=~0U?reserved_label[arg2]:arg1!=~0U?variable_pos[arg1]:~0U;
- if (arg1!=~0U) _cimg_mp_return(arg1);
- }
- // Reached an unknown item -> error.
- c1 = *se1;
- _cimg_mp_strerr;
- cimg::strellipsize(variable_name,64);
- if (is_sth)
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: Undefined variable '%s' in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,
- variable_name._data,s0);
- s1 = std::strchr(ss,'(');
- s_op = s1 && c1==')'?"function call":"item";
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: Unrecognized %s '%s' in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,
- s_op,variable_name._data,s0);
- }
- // Evaluation procedure.
- double operator()(const double x, const double y, const double z, const double c) {
- mem[_cimg_mp_slot_x] = x; mem[_cimg_mp_slot_y] = y; mem[_cimg_mp_slot_z] = z; mem[_cimg_mp_slot_c] = c;
- for (p_code = code; p_code<p_code_end; ++p_code) {
- opcode._data = p_code->_data;
- const ulongT target = opcode[1];
- mem[target] = _cimg_mp_defunc(*this);
- }
- return *result;
- }
- // Evaluation procedure (return output values in vector 'output').
- template<typename t>
- void operator()(const double x, const double y, const double z, const double c, t *const output) {
- mem[_cimg_mp_slot_x] = x; mem[_cimg_mp_slot_y] = y; mem[_cimg_mp_slot_z] = z; mem[_cimg_mp_slot_c] = c;
- for (p_code = code; p_code<p_code_end; ++p_code) {
- opcode._data = p_code->_data;
- const ulongT target = opcode[1];
- mem[target] = _cimg_mp_defunc(*this);
- }
- if (result_dim) {
- const double *ptrs = result + 1;
- t *ptrd = output;
- for (unsigned int k = 0; k<result_dim; ++k) *(ptrd++) = (t)*(ptrs++);
- } else *output = (t)*result;
- }
- // Evaluation procedure for begin_t() bloc.
- void begin_t() {
- if (!code_begin_t) return;
- mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
- p_code_end = code_begin_t.end();
- for (p_code = code_begin_t; p_code<p_code_end; ++p_code) {
- opcode._data = p_code->_data;
- const ulongT target = opcode[1];
- mem[target] = _cimg_mp_defunc(*this);
- }
- p_code_end = code.end();
- }
- // Evaluation procedure for end_t() bloc.
- void end_t() {
- if (!code_end_t) return;
- if (imgin) {
- mem[_cimg_mp_slot_x] = imgin._width - 1.;
- mem[_cimg_mp_slot_y] = imgin._height - 1.;
- mem[_cimg_mp_slot_z] = imgin._depth - 1.;
- mem[_cimg_mp_slot_c] = imgin._spectrum - 1.;
- } else mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
- p_code_end = code_end_t.end();
- for (p_code = code_end_t; p_code<p_code_end; ++p_code) {
- opcode._data = p_code->_data;
- const ulongT target = opcode[1];
- mem[target] = _cimg_mp_defunc(*this);
- }
- }
- // Evaluation procedure the end() bloc.
- void end() {
- if (!code_end) return;
- if (imgin) {
- mem[_cimg_mp_slot_x] = imgin._width - 1.;
- mem[_cimg_mp_slot_y] = imgin._height - 1.;
- mem[_cimg_mp_slot_z] = imgin._depth - 1.;
- mem[_cimg_mp_slot_c] = imgin._spectrum - 1.;
- } else mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
- p_code_end = code_end.end();
- for (p_code = code_end; p_code<p_code_end; ++p_code) {
- opcode._data = p_code->_data;
- const ulongT target = opcode[1];
- mem[target] = _cimg_mp_defunc(*this);
- }
- }
- // Merge inter-thread variables.
- // (argument 'mp' is the master instance).
- void merge(_cimg_math_parser& mp) {
- if (&mp==this) return;
- cimg_rofY(mp.memmerge,k) {
- const unsigned int
- pos = (unsigned int)mp.memmerge(0,k),
- siz = (unsigned int)mp.memmerge(1,k),
- iop = (unsigned int)mp.memmerge(2,k);
- if (!siz) switch (iop) { // Scalar value
- case 0 : mp.mem[pos] = mem[pos]; break; // Assignment
- case 1 : mp.mem[pos]+=mem[pos]; break; // Operator+
- case 2 : mp.mem[pos]-=mem[pos]; break; // Operator-
- case 3 : mp.mem[pos]*=mem[pos]; break; // Operator*
- case 4 : mp.mem[pos]/=mem[pos]; break; // Operator/
- case 5 : mp.mem[pos] = (double)((longT)mp.mem[pos] & (longT)mem[pos]); break; // Operator&
- case 6 : mp.mem[pos] = (double)((longT)mp.mem[pos] | (longT)mem[pos]); break; // Operator|
- case 7 : mp.mem[pos] = (double)((longT)mp.mem[pos] ^ (longT)mem[pos]); break; // Operator 'xor'
- case 8 : mp.mem[pos] = mp.mem[pos] && mem[pos]; break; // Operator&&
- case 9 : mp.mem[pos] = mp.mem[pos] || mem[pos]; break; // Operator||
- case 10 : mp.mem[pos] = std::min(mp.mem[pos],mem[pos]); break; // Operator 'min'
- case 11 : mp.mem[pos] = std::max(mp.mem[pos],mem[pos]); break; // Operator 'max'
- } else switch (iop) { // Vector value
- case 0 : // Assignment
- CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true) = CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
- break;
- case 1 : // Operator+
- CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)+=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
- break;
- case 2 : // Operator-
- CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)-=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
- break;
- case 3 : // Operator*
- CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)*=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
- break;
- case 4 : // Operator/
- CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)/=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
- break;
- case 5 : // Operator&
- CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)&=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
- break;
- case 6 : // Operator|
- CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)|=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
- break;
- case 7 : // Operator 'xor'
- CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)^=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
- break;
- case 8 : { // Operator&&
- CImg<doubleT>
- arg1(&mp.mem[pos + 1],siz,1,1,1,true),
- arg2(&mem[pos + 1],siz,1,1,1,true);
- cimg_foroff(arg1,off) arg1[off] = arg1[off] && arg2[off];
- } break;
- case 9 : { // Operator||
- CImg<doubleT>
- arg1(&mp.mem[pos + 1],siz,1,1,1,true),
- arg2(&mem[pos + 1],siz,1,1,1,true);
- cimg_foroff(arg1,off) arg1[off] = arg1[off] || arg2[off];
- } break;
- case 10 : // Operator 'min'
- CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true).min(CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true));
- break;
- case 11 : // Operator 'max'
- CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true).max(CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true));
- break;
- }
- }
- }
- // Return specified argument number as a string.
- static const char *s_argth(const unsigned int n_arg) {
- const char
- *_s_arg[] = { "", "First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Seventh", "Eighth","Ninth",
- "10th", "11th", "12th", "13th", "14th", "15th", "16th", "17th", "18th", "19th",
- "20th", "21st", "22nd", "23rd", "24th", "25th", "26th", "27th", "28th", "One of the" };
- return _s_arg[n_arg<sizeof(_s_arg)/sizeof(char*)?n_arg:sizeof(_s_arg)/sizeof(char*)-1];
- }
- // Return a string that defines the calling function + the user-defined function scope.
- CImg<charT> s_calling_function() const {
- CImg<charT> res;
- const unsigned int
- l1 = calling_function?(unsigned int)std::strlen(calling_function):0U,
- l2 = user_macro?(unsigned int)std::strlen(user_macro):0U;
- if (l2) {
- res.assign(l1 + l2 + 48);
- cimg_snprintf(res,res._width,"%s(): When substituting function '%s()'",calling_function,user_macro);
- } else {
- res.assign(l1 + l2 + 4);
- cimg_snprintf(res,res._width,"%s()",calling_function);
- }
- return res;
- }
- // Return type of a memory slot as a string.
- CImg<charT> s_type(const unsigned int arg) const {
- CImg<charT> res;
- if (_cimg_mp_is_vector(arg)) { // Vector
- CImg<charT>::string("vectorXXXXXXXXXXXXXXXX").move_to(res);
- cimg_snprintf(res._data + 6,res._width - 6,"%u",_cimg_mp_size(arg));
- } else if (_cimg_mp_is_const_scalar(arg)) CImg<charT>::string("const scalar").move_to(res); // Const scalar
- else CImg<charT>::string("scalar").move_to(res); // Scalar
- return res;
- }
- // Return reference state of a memory slot as a string.
- CImg<charT> s_ref(const unsigned int *const p_ref) const {
- CImg<charT> res;
- if (!p_ref || !*p_ref) return res.assign(1,1,1,1,0);
- res.assign(32);
- switch (p_ref[0]) {
- case 1 : // Reference to vector value as a scalar
- cimg_snprintf(res,res._width,", ref: ([%u])[%u]",
- p_ref[1],p_ref[2]);
- break;
- case 2 : // Reference to image value as a scalar (offset)
- if (p_ref[1]==~0U)
- cimg_snprintf(res,res._width,", ref: %c[%u]",
- p_ref[2]?'j':'i',p_ref[3]);
- else
- cimg_snprintf(res,res._width,", ref: %c[#%u,%u]",
- p_ref[2]?'j':'i',p_ref[1],p_ref[3]);
- break;
- case 3 : // Reference to image value as a scalar (coordinates)
- if (p_ref[1]==~0U)
- cimg_snprintf(res,res._width,", ref: %c(%u,%u,%u,%u)",
- p_ref[2]?'j':'i',p_ref[3],p_ref[4],p_ref[5],p_ref[6]);
- else
- cimg_snprintf(res,res._width,", ref: %c(#%u,%u,%u,%u,%u)",
- p_ref[2]?'j':'i',p_ref[1],p_ref[3],p_ref[4],p_ref[5],p_ref[6]);
- break;
- case 4 : // Reference to image value as a vector (offset)
- if (p_ref[1]==~0U)
- cimg_snprintf(res,res._width,", ref: %c[%u]",
- p_ref[2]?'J':'I',p_ref[3]);
- else
- cimg_snprintf(res,res._width,", ref: %c[#%u,%u]",
- p_ref[2]?'J':'I',p_ref[1],p_ref[3]);
- break;
- case 5 : // Reference to image value as a vector (coordinates)
- if (p_ref[1]==~0U)
- cimg_snprintf(res,res._width,", ref: %c(%u,%u,%u)",
- p_ref[2]?'J':'I',p_ref[3],p_ref[4],p_ref[5]);
- else
- cimg_snprintf(res,res._width,", ref: %c(#%u,%u,%u,%u)",
- p_ref[2]?'J':'I',p_ref[1],p_ref[3],p_ref[4],p_ref[5]);
- break;
- }
- return res;
- }
- // Count parentheses/brackets level of each character of the expression.
- CImg<uintT> get_level(CImg<charT>& _expr) const {
- bool is_escaped = false, next_is_escaped = false;
- unsigned int mode = 0, next_mode = 0; // { 0=normal | 1=char-string | 2=vector-string
- CImg<uintT> res(_expr._width - 1);
- unsigned int *pd = res._data;
- int _level = 0;
- for (const char *ps = _expr._data; *ps && _level>=0; ++ps) {
- if (!is_escaped && !next_is_escaped && *ps=='\\') next_is_escaped = true;
- if (!is_escaped && *ps=='\'') { // Non-escaped character
- if (!mode && ps>_expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string
- else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string
- else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string
- }
- *(pd++) = (unsigned int)(mode>=1 || is_escaped?_level + (mode==1):
- *ps=='(' || *ps=='['?_level++:
- *ps==')' || *ps==']'?--_level:
- _level);
- mode = next_mode;
- is_escaped = next_is_escaped;
- next_is_escaped = false;
- }
- if (mode) {
- cimg::strellipsize(_expr,64);
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: Unterminated string literal, in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,
- _expr._data);
- }
- if (_level) {
- cimg::strellipsize(_expr,64);
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: Unbalanced parentheses/brackets, in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,
- _expr._data);
- }
- return res;
- }
- // Find and return index of current image 'imgin' within image list 'imglist'.
- unsigned int get_mem_img_index() {
- if (mem_img_index==~0U) {
- if (&imgout>imglist.data() && &imgout<imglist.end())
- mem_img_index = const_scalar((double)(&imgout - imglist.data()));
- else {
- unsigned int pos = ~0U;
- cimglist_for(imglist,l)
- if (imgout._data==imglist[l]._data && imgout.is_sameXYZC(imglist[l])) { pos = l; break; }
- if (pos!=~0U) mem_img_index = const_scalar((double)pos);
- }
- }
- return mem_img_index;
- }
- // Return indices for accessing math parser variables.
- void get_variable_pos(const char *variable_name, unsigned int &pos, unsigned int &rpos) {
- char c1, c2, c3, c4;
- pos = rpos = ~0U;
- if (!variable_name || !*variable_name) return;
- unsigned int rp = variable_name[1]?~0U:*variable_name; // One-char variable
- if (variable_name[1] && !variable_name[2]) { // Two-chars variable
- c1 = variable_name[0];
- c2 = variable_name[1];
- if (c1=='w' && c2=='h') rp = 0; // wh
- else if (c1=='p' && c2=='i') rp = 3; // pi
- else if (c1=='i') {
- if (c2>='0' && c2<='9') rp = 21 + c2 - '0'; // i0...i9
- else if (c2=='m') rp = 4; // im
- else if (c2=='M') rp = 5; // iM
- else if (c2=='a') rp = 6; // ia
- else if (c2=='v') rp = 7; // iv
- else if (c2=='d') rp = 8; // id
- else if (c2=='s') rp = 9; // is
- else if (c2=='p') rp = 10; // ip
- else if (c2=='c') rp = 11; // ic
- else if (c2=='n') rp = 12; // in
- } else if (c2=='m') {
- if (c1=='x') rp = 13; // xm
- else if (c1=='y') rp = 14; // ym
- else if (c1=='z') rp = 15; // zm
- else if (c1=='c') rp = 16; // cm
- } else if (c2=='M') {
- if (c1=='x') rp = 17; // xM
- else if (c1=='y') rp = 18; // yM
- else if (c1=='z') rp = 19; // zM
- else if (c1=='c') rp = 20; // cM
- }
- } else if (variable_name[1] && variable_name[2] && !variable_name[3]) { // Three-chars variable
- c1 = variable_name[0];
- c2 = variable_name[1];
- c3 = variable_name[2];
- if (c1=='w' && c2=='h' && c3=='d') rp = 1; // whd
- } else if (variable_name[1] && variable_name[2] && variable_name[3] &&
- !variable_name[4]) { // Four-chars variable
- c1 = variable_name[0];
- c2 = variable_name[1];
- c3 = variable_name[2];
- c4 = variable_name[3];
- if (c1=='w' && c2=='h' && c3=='d' && c4=='s') rp = 2; // whds
- } else if (!std::strcmp(variable_name,"interpolation")) rp = 31; // interpolation
- else if (!std::strcmp(variable_name,"boundary")) rp = 32; // boundary
- if (rp!=~0U) { rpos = rp; return; } // One of the reserved labels
- // Multi-char variable name : check for existing variable with same name
- cimglist_for(variable_def,i)
- if (!std::strcmp(variable_name,variable_def[i])) { pos = i; break; }
- }
- // Return true if all values of a vector are computation values.
- bool is_comp_vector(const unsigned int arg) const {
- unsigned int siz = _cimg_mp_size(arg);
- if (siz>128) return false;
- const int *ptr = memtype.data(arg + 1);
- bool is_tmp = true;
- while (siz-->0) if (*(ptr++)) { is_tmp = false; break; }
- return is_tmp;
- }
- // Check if a memory slot is a positive integer constant scalar value.
- // 'mode' can be:
- // { 0=constant | 1=integer constant | 2=positive integer constant | 3=strictly-positive integer constant }
- void check_const_scalar(const unsigned int arg, const unsigned int n_arg,
- const unsigned int mode,
- char *const ss, char *const se, const char saved_char) {
- _cimg_mp_check_type(arg,n_arg,1,0);
- if (!_cimg_mp_is_const_scalar(arg)) {
- const char *const s_arg = s_argth(n_arg);
- char *s0; _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s%s %s%s (of type '%s') is not a constant, "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
- s_arg,*s_arg?" argument":" Argument",s_type(arg)._data,s0);
- }
- const double val = mem[arg];
- if (!((!mode || (double)(int)mem[arg]==mem[arg]) &&
- (mode<2 || mem[arg]>=(mode==3)))) {
- const char *const s_arg = s_argth(n_arg);
- char *s0; _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s%s %s%s (of type '%s' and value %g) is not a%s constant, "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
- s_arg,*s_arg?" argument":" Argument",s_type(arg)._data,val,
- !mode?"":mode==1?"n integer":
- mode==2?" positive integer":" strictly positive integer",s0);
- }
- }
- // Check if an image index is a constant value.
- void check_const_index(const unsigned int arg,
- char *const ss, char *const se, const char saved_char) {
- if (arg!=~0U && !_cimg_mp_is_const_scalar(arg)) {
- char *s0; _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s%s Specified image index is not a constant, "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",s0);
- }
- }
- // Check that specified constant is not nan.
- void check_notnan_index(const unsigned int arg,
- char *const ss, char *const se, const char saved_char) {
- if (arg!=~0U &&
- (arg==_cimg_mp_slot_nan || (_cimg_mp_is_const_scalar(arg) && cimg::type<double>::is_nan(mem[arg])))) {
- char *s0; _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s%s Specified index is NaN.",
- pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"");
- }
- }
- // Check a matrix is square.
- void check_matrix_square(const unsigned int arg, const unsigned int n_arg,
- char *const ss, char *const se, const char saved_char) {
- _cimg_mp_check_type(arg,n_arg,2,0);
- const unsigned int
- siz = _cimg_mp_size(arg),
- n = (unsigned int)cimg::round(std::sqrt((float)siz));
- if (n*n!=siz) {
- const char *s_arg;
- if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand":"Right-hand";
- else s_arg = !n_arg?"":n_arg==1?"First":n_arg==2?"Second":n_arg==3?"Third":"One";
- char *s0; _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s%s %s%s (of type '%s') "
- "cannot be considered as a square matrix, in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
- s_arg,*s_op=='F'?(*s_arg?" argument":" Argument"):(*s_arg?" operand":" Operand"),
- s_type(arg)._data,s0);
- }
- }
- // Check type compatibility for one argument.
- // Bits of 'mode' tells what types are allowed:
- // { 1 = scalar | 2 = vectorN }.
- // If 'N' is not zero, it also restricts the vectors to be of size N only.
- void check_type(const unsigned int arg, const unsigned int n_arg,
- const unsigned int mode, const unsigned int N,
- char *const ss, char *const se, const char saved_char) {
- const bool
- is_scalar = _cimg_mp_is_scalar(arg),
- is_vector = _cimg_mp_is_vector(arg) && (!N || _cimg_mp_size(arg)==N);
- bool cond = false;
- if (mode&1) cond|=is_scalar;
- if (mode&2) cond|=is_vector;
- if (!cond) {
- const char *s_arg;
- if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand":"Right-hand";
- else s_arg = s_argth(n_arg);
- CImg<charT> sb_type(32);
- if (mode==1) cimg_snprintf(sb_type,sb_type._width,"'scalar'");
- else if (mode==2) {
- if (N) cimg_snprintf(sb_type,sb_type._width,"'vector%u'",N);
- else cimg_snprintf(sb_type,sb_type._width,"'vector'");
- } else {
- if (N) cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector%u'",N);
- else cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector'");
- }
- char *s0; _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s%s %s%s has invalid type '%s' (should be %s), "
- "in expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
- s_arg,*s_op=='F'?(*s_arg?" argument":" Argument"):(*s_arg?" operand":" Operand"),
- s_type(arg)._data,sb_type._data,s0);
- }
- }
- // Check that imglist are not empty.
- void check_list(char *const ss, char *const se, const char saved_char) {
- if (!imglist) {
- char *s0; _cimg_mp_strerr;
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>::%s: %s%s Image list cannot be empty, for expression '%s'.",
- pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",s0);
- }
- }
- static void mp_check_list(_cimg_math_parser& mp, const char *const funcname) {
- if (!mp.imglist)
- throw CImgArgumentException("[" cimg_appname "_math_parser] "
- "CImg<%s>: Function '%s()': Images list cannot be empty.",
- pixel_type(),funcname);
- }
- // Insert constant value in memory.
- unsigned int const_scalar(const double val) {
- // Search for built-in constant.
- if (cimg::type<double>::is_nan(val)) return _cimg_mp_slot_nan;
- if (val==(double)(int)val) {
- if (val>=0 && val<=10) return (unsigned int)val;
- if (val<0 && val>=-5) return (unsigned int)(10 - val);
- }
- if (val==0.5) return 16;
- // Search for constant already requested before (in const cache).
- unsigned int ind = ~0U;
- if (constcache_size<1024) {
- if (!constcache_size) {
- constcache_vals.assign(16,1,1,1,0);
- constcache_inds.assign(16,1,1,1,0);
- *constcache_vals = val;
- constcache_size = 1;
- ind = 0;
- } else { // Dichotomic search
- const double val_beg = *constcache_vals, val_end = constcache_vals[constcache_size - 1];
- if (val_beg>=val) ind = 0;
- else if (val_end==val) ind = constcache_size - 1;
- else if (val_end<val) ind = constcache_size;
- else {
- unsigned int i0 = 1, i1 = constcache_size - 2;
- while (i0<=i1) {
- const unsigned int mid = (i0 + i1)/2;
- if (constcache_vals[mid]==val) { i0 = mid; break; }
- else if (constcache_vals[mid]<val) i0 = mid + 1;
- else i1 = mid - 1;
- }
- ind = i0;
- }
- if (ind>=constcache_size || constcache_vals[ind]!=val) {
- ++constcache_size;
- if (constcache_size>constcache_vals._width) {
- constcache_vals.resize(-200,1,1,1,0);
- constcache_inds.resize(-200,1,1,1,0);
- }
- const int l = constcache_size - (int)ind - 1;
- if (l>0) {
- std::memmove(&constcache_vals[ind + 1],&constcache_vals[ind],l*sizeof(double));
- std::memmove(&constcache_inds[ind + 1],&constcache_inds[ind],l*sizeof(unsigned int));
- }
- constcache_vals[ind] = val;
- constcache_inds[ind] = 0;
- }
- }
- if (constcache_inds[ind]) return constcache_inds[ind];
- }
- // Insert new constant in memory if necessary.
- if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(-200,1,1,1,0); }
- const unsigned int pos = mempos++;
- mem[pos] = val;
- memtype[pos] = 1; // Set constant property
- if (ind!=~0U) constcache_inds[ind] = pos;
- return pos;
- }
- // Insert new scalar in memory.
- unsigned int scalar() {
- if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(mem._width,1,1,1,0); }
- return mempos++;
- }
- // Insert new vector of specified size in memory.
- unsigned int vector(const unsigned int siz) {
- if (mempos + siz>=mem._width) {
- mem.resize(2*mem._width + siz,1,1,1,0);
- memtype.resize(mem._width,1,1,1,0);
- }
- const unsigned int pos = mempos++;
- mem[pos] = cimg::type<double>::nan();
- memtype[pos] = siz + 1;
- mempos+=siz;
- return pos;
- }
- // Insert new initialized vector.
- unsigned int vector(const unsigned int siz, const double value) {
- const unsigned int pos = vector(siz);
- double *ptr = &mem[pos] + 1;
- for (unsigned int i = 0; i<siz; ++i) *(ptr++) = value;
- return pos;
- }
- // Insert new copy of specified vector in memory.
- unsigned int vector_copy(const unsigned int arg) {
- const unsigned int
- siz = _cimg_mp_size(arg),
- pos = vector(siz);
- CImg<ulongT>::vector((ulongT)mp_vector_copy,pos,arg,siz).move_to(code);
- return pos;
- }
- // Set reserved status to all values of a vector.
- void set_reserved_vector(const unsigned int arg) {
- unsigned int siz = _cimg_mp_size(arg);
- int *ptr = memtype.data(arg + 1);
- while (siz-->0) *(ptr++) = -1;
- }
- unsigned int scalar0(const mp_func op) {
- const unsigned int pos = scalar();
- CImg<ulongT>::vector((ulongT)op,pos).move_to(code);
- return_new_comp = true;
- return pos;
- }
- unsigned int scalar1(const mp_func op, const unsigned int arg1) {
- const unsigned int pos =
- arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1) && op!=mp_copy?arg1:
- ((return_new_comp = true), scalar());
- CImg<ulongT>::vector((ulongT)op,pos,arg1).move_to(code);
- return pos;
- }
- unsigned int scalar2(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
- const unsigned int pos =
- arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
- arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
- ((return_new_comp = true), scalar());
- CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2).move_to(code);
- return pos;
- }
- unsigned int scalar3(const mp_func op,
- const unsigned int arg1, const unsigned int arg2, const unsigned int arg3) {
- const unsigned int pos =
- arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
- arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
- arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
- ((return_new_comp = true), scalar());
- CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3).move_to(code);
- return pos;
- }
- unsigned int scalar4(const mp_func op,
- const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
- const unsigned int arg4) {
- const unsigned int pos =
- arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
- arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
- arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
- arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
- ((return_new_comp = true), scalar());
- CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4).move_to(code);
- return pos;
- }
- unsigned int scalar5(const mp_func op,
- const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
- const unsigned int arg4, const unsigned int arg5) {
- const unsigned int pos =
- arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
- arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
- arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
- arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
- arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:
- ((return_new_comp = true), scalar());
- CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5).move_to(code);
- return pos;
- }
- unsigned int scalar6(const mp_func op,
- const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
- const unsigned int arg4, const unsigned int arg5, const unsigned int arg6) {
- const unsigned int pos =
- arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
- arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
- arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
- arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
- arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:
- arg6!=~0U && arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6:
- ((return_new_comp = true), scalar());
- CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6).move_to(code);
- return pos;
- }
- unsigned int scalar7(const mp_func op,
- const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
- const unsigned int arg4, const unsigned int arg5, const unsigned int arg6,
- const unsigned int arg7) {
- const unsigned int pos =
- arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
- arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
- arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
- arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
- arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:
- arg6!=~0U && arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6:
- arg7!=~0U && arg7>_cimg_mp_slot_c && _cimg_mp_is_comp(arg7)?arg7:
- ((return_new_comp = true), scalar());
- CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6,arg7).move_to(code);
- return pos;
- }
- void self_vector_s(const unsigned int pos, const mp_func op, const unsigned int arg1) {
- const unsigned int siz = _cimg_mp_size(pos);
- if (siz>24) CImg<ulongT>::vector((ulongT)mp_self_map_vector_s,pos,siz,(ulongT)op,arg1).move_to(code);
- else {
- code.insert(siz);
- for (unsigned int k = 1; k<=siz; ++k)
- CImg<ulongT>::vector((ulongT)op,pos + k,arg1).move_to(code[code._width - 1 - siz + k]);
- }
- }
- void self_vector_v(const unsigned int pos, const mp_func op, const unsigned int arg1) {
- const unsigned int siz = _cimg_mp_size(pos);
- if (siz>24) CImg<ulongT>::vector((ulongT)mp_self_map_vector_v,pos,siz,(ulongT)op,arg1).move_to(code);
- else {
- code.insert(siz);
- for (unsigned int k = 1; k<=siz; ++k)
- CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]);
- }
- }
- unsigned int vector1_v(const mp_func op, const unsigned int arg1) {
- const unsigned int
- siz = _cimg_mp_size(arg1),
- pos = is_comp_vector(arg1)?arg1:
- ((return_new_comp = true), vector(siz));
- if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_v,pos,1,siz,(ulongT)op,arg1).move_to(code);
- else {
- code.insert(siz);
- for (unsigned int k = 1; k<=siz; ++k)
- CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]);
- }
- return pos;
- }
- unsigned int vector2_vv(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
- const unsigned int
- siz = _cimg_mp_size(arg1),
- pos = is_comp_vector(arg1)?arg1:is_comp_vector(arg2)?arg2:
- ((return_new_comp = true), vector(siz));
- if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_vv,pos,2,siz,(ulongT)op,arg1,arg2).move_to(code);
- else {
- code.insert(siz);
- for (unsigned int k = 1; k<=siz; ++k)
- CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2 + k).move_to(code[code._width - 1 - siz + k]);
- }
- return pos;
- }
- unsigned int vector2_vs(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
- const unsigned int
- siz = _cimg_mp_size(arg1),
- pos = is_comp_vector(arg1)?arg1:
- ((return_new_comp = true), vector(siz));
- if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_v,pos,2,siz,(ulongT)op,arg1,arg2).move_to(code);
- else {
- code.insert(siz);
- for (unsigned int k = 1; k<=siz; ++k)
- CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2).move_to(code[code._width - 1 - siz + k]);
- }
- return pos;
- }
- unsigned int vector2_sv(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
- const unsigned int
- siz = _cimg_mp_size(arg2),
- pos = is_comp_vector(arg2)?arg2:
- ((return_new_comp = true), vector(siz));
- if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_sv,pos,2,siz,(ulongT)op,arg1,arg2).move_to(code);
- else {
- code.insert(siz);
- for (unsigned int k = 1; k<=siz; ++k)
- CImg<ulongT>::vector((ulongT)op,pos + k,arg1,arg2 + k).move_to(code[code._width - 1 - siz + k]);
- }
- return pos;
- }
- unsigned int vector3_vss(const mp_func op, const unsigned int arg1, const unsigned int arg2,
- const unsigned int arg3) {
- const unsigned int
- siz = _cimg_mp_size(arg1),
- pos = is_comp_vector(arg1)?arg1:
- ((return_new_comp = true), vector(siz));
- if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_v,pos,3,siz,(ulongT)op,arg1,arg2,arg3).move_to(code);
- else {
- code.insert(siz);
- for (unsigned int k = 1; k<=siz; ++k)
- CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2,arg3).move_to(code[code._width - 1 - siz + k]);
- }
- return pos;
- }
- unsigned int vector4_vvss(const mp_func op, const unsigned int arg1, const unsigned int arg2,
- const unsigned int arg3, const unsigned int arg4) {
- const unsigned int
- siz = _cimg_mp_size(arg1),
- pos = is_comp_vector(arg1)?arg1:is_comp_vector(arg2)?arg2:
- ((return_new_comp = true), vector(siz));
- if (siz>24)
- CImg<ulongT>::vector((ulongT)mp_vector_map_vv,pos,4,siz,(ulongT)op,arg1,arg2,arg3,arg4).move_to(code);
- else {
- code.insert(siz);
- for (unsigned int k = 1; k<=siz; ++k)
- CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2 + k,arg3,arg4).
- move_to(code[code._width - 1 - siz + k]);
- }
- return pos;
- }
- unsigned int vector4_vsss(const mp_func op, const unsigned int arg1, const unsigned int arg2,
- const unsigned int arg3, const unsigned int arg4) {
- const unsigned int
- siz = _cimg_mp_size(arg1),
- pos = is_comp_vector(arg1)?arg1:
- ((return_new_comp = true), vector(siz));
- if (siz>24)
- CImg<ulongT>::vector((ulongT)mp_vector_map_v,pos,4,siz,(ulongT)op,arg1,arg2,arg3,arg4).move_to(code);
- else {
- code.insert(siz);
- for (unsigned int k = 1; k<=siz; ++k)
- CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2,arg3,arg4).
- move_to(code[code._width - 1 - siz + k]);
- }
- return pos;
- }
- unsigned int vector4_svss(const mp_func op, const unsigned int arg1, const unsigned int arg2,
- const unsigned int arg3, const unsigned int arg4) {
- const unsigned int
- siz = _cimg_mp_size(arg2),
- pos = is_comp_vector(arg2)?arg2:
- ((return_new_comp = true), vector(siz));
- if (siz>24)
- CImg<ulongT>::vector((ulongT)mp_vector_map_sv,pos,4,siz,(ulongT)op,arg1,arg2,arg3,arg4).move_to(code);
- else {
- code.insert(siz);
- for (unsigned int k = 1; k<=siz; ++k)
- CImg<ulongT>::vector((ulongT)op,pos + k,arg1,arg2 + k,arg3,arg4).
- move_to(code[code._width - 1 - siz + k]);
- }
- return pos;
- }
- // Evaluation functions, known by the parser.
- // Defining these functions 'static' ensures that sizeof(mp_func)==sizeof(ulongT),
- // so we can store pointers to them directly in the opcode vectors.
- #ifdef _mp_arg
- #undef _mp_arg
- #endif
- #define _mp_arg(x) mp.mem[mp.opcode[x]]
- #ifdef cimg_mp_func_abort
- static double mp_abort(_cimg_math_parser& mp) {
- cimg::unused(mp);
- cimg_mp_func_abort();
- return cimg::type<double>::nan();
- }
- #endif
- static double mp_abs(_cimg_math_parser& mp) {
- return cimg::abs(_mp_arg(2));
- }
- static double mp_add(_cimg_math_parser& mp) {
- return _mp_arg(2) + _mp_arg(3);
- }
- static double mp_acos(_cimg_math_parser& mp) {
- return std::acos(_mp_arg(2));
- }
- static double mp_acosh(_cimg_math_parser& mp) {
- return cimg::acosh(_mp_arg(2));
- }
- static double mp_asinh(_cimg_math_parser& mp) {
- return cimg::asinh(_mp_arg(2));
- }
- static double mp_atanh(_cimg_math_parser& mp) {
- return cimg::atanh(_mp_arg(2));
- }
- static double mp_arg(_cimg_math_parser& mp) {
- const int _ind = (int)_mp_arg(4);
- const unsigned int
- nb_args = (unsigned int)mp.opcode[2] - 4,
- ind = _ind<0?_ind + nb_args:(unsigned int)_ind,
- siz = (unsigned int)mp.opcode[3];
- if (siz>0) {
- if (ind>=nb_args) std::memset(&_mp_arg(1) + 1,0,siz*sizeof(double));
- else std::memcpy(&_mp_arg(1) + 1,&_mp_arg(ind + 4) + 1,siz*sizeof(double));
- return cimg::type<double>::nan();
- }
- if (ind>=nb_args) return 0;
- return _mp_arg(ind + 4);
- }
- static double mp_arg0(_cimg_math_parser& mp) {
- const int _ind = (int)_mp_arg(4);
- const unsigned int
- nb_args = (unsigned int)mp.opcode[2] - 4,
- ind = _ind<0?_ind + nb_args:_ind + 1U,
- siz = (unsigned int)mp.opcode[3];
- if (siz>0) {
- if (ind>=nb_args) std::memset(&_mp_arg(1) + 1,0,siz*sizeof(double));
- else std::memcpy(&_mp_arg(1) + 1,&_mp_arg(ind + 4) + 1,siz*sizeof(double));
- return cimg::type<double>::nan();
- }
- if (ind>=nb_args) return 0;
- return _mp_arg(ind + 4);
- }
- static double mp_argkth(_cimg_math_parser& mp) {
- const unsigned int i_end = (unsigned int)mp.opcode[2];
- CImg<double> values;
- if (i_end==5) values.assign(&_mp_arg(3),(unsigned int)mp.opcode[4],1,1,1,true); // Only a single argument
- else {
- unsigned int siz = 0;
- for (unsigned int i = 4; i<i_end; i+=2) siz+=(unsigned int)mp.opcode[i];
- values.assign(siz);
- double *ptr = values;
- for (unsigned int i = 3; i<i_end; i+=2) {
- const unsigned int len = (unsigned int)mp.opcode[i + 1];
- if (len>1) std::memcpy(ptr,&_mp_arg(i),len*sizeof(double));
- else *ptr = _mp_arg(i);
- ptr+=len;
- }
- }
- longT ind = (longT)cimg::round(_mp_arg(3));
- ++values._data; --values._width; // Skip first value
- if (ind<0) ind+=values.width() + 1;
- ind = cimg::cut(ind,(longT)1,(longT)values.width());
- const double kth = values.kth_smallest((ulongT)(ind - 1));
- --values._data; ++values._width;
- for (unsigned int argkth = 1; argkth<values._width; ++argkth)
- if (values[argkth]==kth) return argkth;
- return cimg::type<double>::nan();
- }
- static double mp_argmin(_cimg_math_parser& mp) {
- const unsigned int i_end = (unsigned int)mp.opcode[2];
- double val, valmin = cimg::type<double>::inf();
- unsigned int siz = 0, argmin = 0;
- for (unsigned int i = 3; i<i_end; i+=2) {
- const unsigned int len = (unsigned int)mp.opcode[i + 1];
- if (len>1) {
- const double *ptr = &_mp_arg(i);
- for (unsigned int k = 0; k<len; ++k) { val = *(ptr++); if (val<valmin) { valmin = val; argmin = siz + k; } }
- } else { val = _mp_arg(i); if (val<valmin) { valmin = val; argmin = siz; } }
- siz+=len;
- }
- return (double)argmin;
- }
- static double mp_argminabs(_cimg_math_parser& mp) {
- const unsigned int i_end = (unsigned int)mp.opcode[2];
- double val, abs_val, abs_valminabs = cimg::type<double>::inf();
- unsigned int siz = 0, argminabs = 0;
- for (unsigned int i = 3; i<i_end; i+=2) {
- const unsigned int len = (unsigned int)mp.opcode[i + 1];
- if (len>1) {
- const double *ptr = &_mp_arg(i);
- for (unsigned int k = 0; k<len; ++k) {
- val = *(ptr++);
- abs_val = cimg::abs(val);
- if (abs_val<abs_valminabs) { abs_valminabs = abs_val; argminabs = siz + k; }
- }
- } else {
- val = _mp_arg(i);
- abs_val = cimg::abs(val);
- if (abs_val<abs_valminabs) { abs_valminabs = abs_val; argminabs = siz; }
- }
- siz+=len;
- }
- return (double)argminabs;
- }
- static double mp_argmax(_cimg_math_parser& mp) {
- const unsigned int i_end = (unsigned int)mp.opcode[2];
- double val, valmax = -cimg::type<double>::inf();
- unsigned int siz = 0, argmax = 0;
- for (unsigned int i = 3; i<i_end; i+=2) {
- const unsigned int len = (unsigned int)mp.opcode[i + 1];
- if (len>1) {
- const double *ptr = &_mp_arg(i);
- for (unsigned int k = 0; k<len; ++k) { val = *(ptr++); if (val>valmax) { valmax = val; argmax = siz + k; } }
- } else { val = _mp_arg(i); if (val>valmax) { valmax = val; argmax = siz; } }
- siz+=len;
- }
- return (double)argmax;
- }
- static double mp_argmaxabs(_cimg_math_parser& mp) {
- const unsigned int i_end = (unsigned int)mp.opcode[2];
- double val, abs_val, abs_valmaxabs = 0;
- unsigned int siz = 0, argmaxabs = 0;
- for (unsigned int i = 3; i<i_end; i+=2) {
- const unsigned int len = (unsigned int)mp.opcode[i + 1];
- if (len>1) {
- const double *ptr = &_mp_arg(i);
- for (unsigned int k = 0; k<len; ++k) {
- val = *(ptr++);
- abs_val = cimg::abs(val);
- if (abs_val>abs_valmaxabs) { abs_valmaxabs = abs_val; argmaxabs = siz + k; }
- }
- } else {
- val = _mp_arg(i);
- abs_val = cimg::abs(val);
- if (abs_val>abs_valmaxabs) { abs_valmaxabs = abs_val; argmaxabs = siz; }
- }
- siz+=len;
- }
- return (double)argmaxabs;
- }
- static double mp_asin(_cimg_math_parser& mp) {
- return std::asin(_mp_arg(2));
- }
- static double mp_atan(_cimg_math_parser& mp) {
- return std::atan(_mp_arg(2));
- }
- static double mp_atan2(_cimg_math_parser& mp) {
- return std::atan2(_mp_arg(2),_mp_arg(3));
- }
- static double mp_avg(_cimg_math_parser& mp) {
- const unsigned int i_end = (unsigned int)mp.opcode[2];
- unsigned int siz = 0;
- double sum = 0;
- for (unsigned int i = 3; i<i_end; i+=2) {
- const unsigned int len = (unsigned int)mp.opcode[i + 1];
- if (len>1) {
- const double *ptr = &_mp_arg(i);
- for (unsigned int k = 0; k<len; ++k) sum+=*(ptr++);
- } else sum+=_mp_arg(i);
- siz+=len;
- }
- return sum/siz;
- }
- static double mp_bitwise_and(_cimg_math_parser& mp) {
- return (double)((longT)_mp_arg(2) & (longT)_mp_arg(3));
- }
- static double mp_bitwise_left_shift(_cimg_math_parser& mp) {
- return (double)((longT)_mp_arg(2)<<(unsigned int)_mp_arg(3));
- }
- static double mp_bitwise_not(_cimg_math_parser& mp) {
- // Limit result to 32bits such that it can be entirely represented as a 'double'.
- return (double)~(unsigned int)_mp_arg(2);
- }
- static double mp_bitwise_or(_cimg_math_parser& mp) {
- return (double)((longT)_mp_arg(2) | (longT)_mp_arg(3));
- }
- static double mp_bitwise_right_shift(_cimg_math_parser& mp) {
- return (double)((longT)_mp_arg(2)>>(unsigned int)_mp_arg(3));
- }
- static double mp_bitwise_xor(_cimg_math_parser& mp) {
- return (double)((longT)_mp_arg(2) ^ (longT)_mp_arg(3));
- }
- static double mp_bool(_cimg_math_parser& mp) {
- return (double)(bool)_mp_arg(2);
- }
- static double mp_break(_cimg_math_parser& mp) {
- mp.break_type = 1;
- mp.p_code = mp.p_break - 1;
- return cimg::type<double>::nan();
- }
- static double mp_breakpoint(_cimg_math_parser& mp) {
- cimg_abort_init;
- cimg_abort_test;
- cimg::unused(mp);
- return cimg::type<double>::nan();
- }
- static double mp_c2o(_cimg_math_parser& mp) {
- mp_check_list(mp,"c2o");
- unsigned int ind = (unsigned int)mp.opcode[2];
- if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- const CImg<T> &img = ind==~0U?mp.imgin:mp.imglist[ind];
- const int
- x = (int)_mp_arg(3),
- y = (int)_mp_arg(4),
- z = (int)_mp_arg(5),
- c = (int)_mp_arg(6);
- return (double)img.offset(x,y,z,c);
- }
- static double mp_cbrt(_cimg_math_parser& mp) {
- return cimg::cbrt(_mp_arg(2));
- }
- static double mp_ceil(_cimg_math_parser& mp) {
- return std::ceil(_mp_arg(2));
- }
- static double mp_complex_abs(_cimg_math_parser& mp) {
- return cimg::hypot(_mp_arg(2),_mp_arg(3));
- }
- static double mp_complex_conj(_cimg_math_parser& mp) {
- const double real = _mp_arg(2), imag = _mp_arg(3);
- double *ptrd = &_mp_arg(1) + 1;
- ptrd[0] = real;
- ptrd[1] = -imag;
- return cimg::type<double>::nan();
- }
- static double mp_complex_div_sv(_cimg_math_parser& mp) {
- const double
- *ptr2 = &_mp_arg(3) + 1,
- r1 = _mp_arg(2),
- r2 = *(ptr2++), i2 = *ptr2;
- double *ptrd = &_mp_arg(1) + 1;
- const double denom = r2*r2 + i2*i2;
- *(ptrd++) = r1*r2/denom;
- *ptrd = -r1*i2/denom;
- return cimg::type<double>::nan();
- }
- static double mp_complex_div_vv(_cimg_math_parser& mp) {
- const double
- *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1,
- r1 = *(ptr1++), i1 = *ptr1,
- r2 = *(ptr2++), i2 = *ptr2;
- double *ptrd = &_mp_arg(1) + 1;
- const double denom = r2*r2 + i2*i2;
- *(ptrd++) = (r1*r2 + i1*i2)/denom;
- *ptrd = (r2*i1 - r1*i2)/denom;
- return cimg::type<double>::nan();
- }
- static double mp_complex_exp(_cimg_math_parser& mp) {
- const double real = _mp_arg(2), imag = _mp_arg(3), exp_real = std::exp(real);
- double *ptrd = &_mp_arg(1) + 1;
- ptrd[0] = exp_real*std::cos(imag);
- ptrd[1] = exp_real*std::sin(imag);
- return cimg::type<double>::nan();
- }
- static double mp_complex_log(_cimg_math_parser& mp) {
- const double real = _mp_arg(2), imag = _mp_arg(3);
- double *ptrd = &_mp_arg(1) + 1;
- ptrd[0] = 0.5*std::log(real*real + imag*imag);
- ptrd[1] = std::atan2(imag,real);
- return cimg::type<double>::nan();
- }
- static double mp_complex_mul(_cimg_math_parser& mp) {
- const double
- *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1,
- r1 = *(ptr1++), i1 = *ptr1,
- r2 = *(ptr2++), i2 = *ptr2;
- double *ptrd = &_mp_arg(1) + 1;
- *(ptrd++) = r1*r2 - i1*i2;
- *(ptrd++) = r1*i2 + r2*i1;
- return cimg::type<double>::nan();
- }
- static void _mp_complex_pow(const double r1, const double i1,
- const double r2, const double i2,
- double *ptrd) {
- double ro, io;
- if (cimg::abs(i2)<1e-15) { // Exponent is real
- if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) {
- if (cimg::abs(r2)<1e-15) { ro = 1; io = 0; }
- else ro = io = 0;
- } else {
- const double
- mod1_2 = r1*r1 + i1*i1,
- phi1 = std::atan2(i1,r1),
- modo = std::pow(mod1_2,0.5*r2),
- phio = r2*phi1;
- ro = modo*std::cos(phio);
- io = modo*std::sin(phio);
- }
- } else { // Exponent is complex
- if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) ro = io = 0;
- const double
- mod1_2 = r1*r1 + i1*i1,
- phi1 = std::atan2(i1,r1),
- modo = std::pow(mod1_2,0.5*r2)*std::exp(-i2*phi1),
- phio = r2*phi1 + 0.5*i2*std::log(mod1_2);
- ro = modo*std::cos(phio);
- io = modo*std::sin(phio);
- }
- *(ptrd++) = ro;
- *ptrd = io;
- }
- static double mp_complex_pow_ss(_cimg_math_parser& mp) {
- const double val1 = _mp_arg(2), val2 = _mp_arg(3);
- double *ptrd = &_mp_arg(1) + 1;
- _mp_complex_pow(val1,0,val2,0,ptrd);
- return cimg::type<double>::nan();
- }
- static double mp_complex_pow_sv(_cimg_math_parser& mp) {
- const double val1 = _mp_arg(2), *ptr2 = &_mp_arg(3) + 1;
- double *ptrd = &_mp_arg(1) + 1;
- _mp_complex_pow(val1,0,ptr2[0],ptr2[1],ptrd);
- return cimg::type<double>::nan();
- }
- static double mp_complex_pow_vs(_cimg_math_parser& mp) {
- const double *ptr1 = &_mp_arg(2) + 1, val2 = _mp_arg(3);
- double *ptrd = &_mp_arg(1) + 1;
- _mp_complex_pow(ptr1[0],ptr1[1],val2,0,ptrd);
- return cimg::type<double>::nan();
- }
- static double mp_complex_pow_vv(_cimg_math_parser& mp) {
- const double *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1;
- double *ptrd = &_mp_arg(1) + 1;
- _mp_complex_pow(ptr1[0],ptr1[1],ptr2[0],ptr2[1],ptrd);
- return cimg::type<double>::nan();
- }
- static double mp_complex_cos(_cimg_math_parser& mp) {
- const double real = _mp_arg(2), imag = _mp_arg(3);
- double *ptrd = &_mp_arg(1) + 1;
- ptrd[0] = std::cos(real)*std::cosh(imag);
- ptrd[1] = -std::sin(real)*std::sinh(imag);
- return cimg::type<double>::nan();
- }
- static double mp_complex_sin(_cimg_math_parser& mp) {
- const double real = _mp_arg(2), imag = _mp_arg(3);
- double *ptrd = &_mp_arg(1) + 1;
- ptrd[0] = std::sin(real)*std::cosh(imag);
- ptrd[1] = std::cos(real)*std::sinh(imag);
- return cimg::type<double>::nan();
- }
- static double mp_complex_sqrt(_cimg_math_parser& mp) {
- const double
- real = _mp_arg(2), imag = _mp_arg(3),
- r = std::sqrt(cimg::hypot(real,imag)),
- theta = std::atan2(imag,real)/2;
- double *ptrd = &_mp_arg(1) + 1;
- ptrd[0] = r*std::cos(theta);
- ptrd[1] = r*std::sin(theta);
- return cimg::type<double>::nan();
- }
- static double mp_complex_tan(_cimg_math_parser& mp) {
- const double real = _mp_arg(2), imag = _mp_arg(3), denom = std::cos(2*real) + std::cosh(2*imag);
- double *ptrd = &_mp_arg(1) + 1;
- ptrd[0] = std::sin(2*real)/denom;
- ptrd[1] = std::sinh(2*imag)/denom;
- return cimg::type<double>::nan();
- }
- static double mp_complex_cosh(_cimg_math_parser& mp) {
- const double real = _mp_arg(2), imag = _mp_arg(3);
- double *ptrd = &_mp_arg(1) + 1;
- ptrd[0] = std::cosh(real)*std::cos(imag);
- ptrd[1] = std::sinh(real)*std::sin(imag);
- return cimg::type<double>::nan();
- }
- static double mp_complex_sinh(_cimg_math_parser& mp) {
- const double real = _mp_arg(2), imag = _mp_arg(3);
- double *ptrd = &_mp_arg(1) + 1;
- ptrd[0] = std::sinh(real)*std::cos(imag);
- ptrd[1] = std::cosh(real)*std::sin(imag);
- return cimg::type<double>::nan();
- }
- static double mp_complex_tanh(_cimg_math_parser& mp) {
- const double real = _mp_arg(2), imag = _mp_arg(3), denom = std::cosh(2*real) + std::cos(2*imag);
- double *ptrd = &_mp_arg(1) + 1;
- ptrd[0] = std::sinh(2*real)/denom;
- ptrd[1] = std::sin(2*imag)/denom;
- return cimg::type<double>::nan();
- }
- static double mp_continue(_cimg_math_parser& mp) {
- mp.break_type = 2;
- mp.p_code = mp.p_break - 1;
- return cimg::type<double>::nan();
- }
- static double mp_convolve(_cimg_math_parser &mp) {
- return _mp_correlate(mp,true);
- }
- static double mp_copy(_cimg_math_parser& mp) {
- return _mp_arg(2);
- }
- static double mp_correlate(_cimg_math_parser &mp) {
- return _mp_correlate(mp,false);
- }
- static double _mp_correlate(_cimg_math_parser &mp, bool is_convolve) {
- double *ptrd = &_mp_arg(1) + 1;
- const double *const ptrA = &_mp_arg(2) + 1, *const ptrM = &_mp_arg(7) + 1;
- const unsigned int
- wA = (unsigned int)mp.opcode[3],
- hA = (unsigned int)mp.opcode[4],
- dA = (unsigned int)mp.opcode[5],
- sA = (unsigned int)mp.opcode[6],
- wM = (unsigned int)mp.opcode[8],
- hM = (unsigned int)mp.opcode[9],
- dM = (unsigned int)mp.opcode[10],
- sM = (unsigned int)mp.opcode[11],
- boundary_conditions = (unsigned int)_mp_arg(12),
- channel_mode = (unsigned int)mp.opcode[14];
- const bool
- is_normalized = (bool)_mp_arg(13),
- interpolation_type = (bool)_mp_arg(30);
- const int
- xcenter = mp.opcode[15]!=~0U?(int)_mp_arg(15):(int)(~0U>>1),
- ycenter = mp.opcode[16]!=~0U?(int)_mp_arg(16):(int)(~0U>>1),
- zcenter = mp.opcode[17]!=~0U?(int)_mp_arg(17):(int)(~0U>>1),
- xstart = (int)mp.opcode[18],
- ystart = (int)mp.opcode[19],
- zstart = (int)mp.opcode[20],
- xend = (int)mp.opcode[21],
- yend = (int)mp.opcode[22],
- zend = (int)mp.opcode[23];
- const float
- xstride = (float)_mp_arg(24),
- ystride = (float)_mp_arg(25),
- zstride = (float)_mp_arg(26),
- xdilation = (float)_mp_arg(27),
- ydilation = (float)_mp_arg(28),
- zdilation = (float)_mp_arg(29);
- CImg<doubleT> res;
- if (is_convolve) res = CImg<doubleT>(ptrA,wA,hA,dA,sA,true).
- get_convolve(CImg<doubleT>(ptrM,wM,hM,dM,sM,true),
- boundary_conditions,is_normalized,channel_mode,
- xcenter,ycenter,zcenter,
- xstart,ystart,zstart,
- xend,yend,zend,
- xstride,ystride,zstride,
- xdilation,ydilation,zdilation,
- interpolation_type);
- else res = CImg<doubleT>(ptrA,wA,hA,dA,sA,true).
- get_correlate(CImg<doubleT>(ptrM,wM,hM,dM,sM,true),
- boundary_conditions,is_normalized,channel_mode,
- xcenter,ycenter,zcenter,
- xstart,ystart,zstart,
- xend,yend,zend,
- xstride,ystride,zstride,
- xdilation,ydilation,zdilation,
- interpolation_type);
- CImg<doubleT>(ptrd,res._width,res._height,res._depth,res._spectrum,true) = res;
- return cimg::type<double>::nan();
- }
- static double mp_cos(_cimg_math_parser& mp) {
- return std::cos(_mp_arg(2));
- }
- static double mp_cosh(_cimg_math_parser& mp) {
- return std::cosh(_mp_arg(2));
- }
- static double mp_cov(_cimg_math_parser& mp) {
- const unsigned int
- _siz = (unsigned int)mp.opcode[4],
- siz = std::max(_siz,1U),
- off = _siz?1:0,
- sizm1 = siz>1?siz - 1:1;
- const CImg<doubleT>
- A(&_mp_arg(2) + off,1,siz,1,1,true),
- B(&_mp_arg(3) + off,1,siz,1,1,true);
- const double
- avgA = (unsigned int)mp.opcode[5]==~0U?A.mean():_mp_arg(5),
- avgB = (unsigned int)mp.opcode[6]==~0U?B.mean():_mp_arg(6);
- double res = 0;
- cimg_forY(A,k) res+=(A[k] - avgA)*(B[k] - avgB);
- return res/sizm1;
- }
- static double mp_critical(_cimg_math_parser& mp) {
- const ulongT g_target = mp.opcode[1];
- cimg_pragma_openmp(critical(mp_critical))
- {
- for (const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[2];
- mp.p_code<p_end; ++mp.p_code) { // Evaluate body
- mp.opcode._data = mp.p_code->_data;
- const ulongT target = mp.opcode[1];
- mp.mem[target] = _cimg_mp_defunc(mp);
- }
- }
- --mp.p_code;
- return mp.mem[g_target];
- }
- static double mp_image_crop(_cimg_math_parser& mp) {
- double *ptrd = &_mp_arg(1) + 1;
- const int x = (int)_mp_arg(3), y = (int)_mp_arg(4), z = (int)_mp_arg(5), c = (int)_mp_arg(6);
- const unsigned int
- dx = (unsigned int)mp.opcode[7],
- dy = (unsigned int)mp.opcode[8],
- dz = (unsigned int)mp.opcode[9],
- dc = (unsigned int)mp.opcode[10];
- const unsigned int boundary_conditions = (unsigned int)_mp_arg(11);
- unsigned int ind = (unsigned int)mp.opcode[2];
- if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- const CImg<T> &img = ind==~0U?mp.imgin:mp.imglist[ind];
- if (!img) std::memset(ptrd,0,dx*dy*dz*dc*sizeof(double));
- else CImg<doubleT>(ptrd,dx,dy,dz,dc,true) = img.get_crop(x,y,z,c,
- x + dx - 1,y + dy - 1,
- z + dz - 1,c + dc - 1,
- boundary_conditions);
- return cimg::type<double>::nan();
- }
- static double mp_cross(_cimg_math_parser& mp) {
- CImg<doubleT>
- vout(&_mp_arg(1) + 1,1,3,1,1,true),
- v1(&_mp_arg(2) + 1,1,3,1,1,true),
- v2(&_mp_arg(3) + 1,1,3,1,1,true);
- (vout = v1).cross(v2);
- return cimg::type<double>::nan();
- }
- static double mp_cut(_cimg_math_parser& mp) {
- double val = _mp_arg(2), cmin = _mp_arg(3), cmax = _mp_arg(4);
- return val<cmin?cmin:val>cmax?cmax:val;
- }
- static double mp_da_back_or_pop(_cimg_math_parser& mp) {
- const bool is_pop_heap = mp.opcode[4]==2, is_pop = (bool)mp.opcode[4];
- const char *const s_op = is_pop_heap?"da_pop_heap":is_pop?"da_pop":"da_back";
- mp_check_list(mp,s_op);
- const unsigned int
- dim = (unsigned int)mp.opcode[2],
- ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.imglist.width());
- double *const ptrd = &_mp_arg(1) + (dim>1?1:0);
- CImg<T> &img = mp.imglist[ind];
- int siz = img?(int)cimg::float2uint((float)img[img._height - 1]):0;
- if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1))
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': "
- "Specified image #%u of size (%d,%d,%d,%d) cannot be used as dynamic array%s.",
- mp.imgout.pixel_type(),s_op,ind,
- img.width(),img.height(),img.depth(),img.spectrum(),
- img._width==1 && img._depth==1?"":" (contains invalid element counter)");
- if (!siz)
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': "
- "Specified dynamic array #%u contains no elements.",
- mp.imgout.pixel_type(),s_op,ind);
- const int siz1 = siz - 1;
- if (is_pop_heap) { // Heapify-down
- if (dim==1) cimg::swap(img[0],img[siz1]);
- else {
- T *ptr0 = img.data(), *ptr1 = img.data(0,siz1);
- cimg_forC(img,c) { cimg::swap(*ptr0,*ptr1); ptr0+=img._height; ptr1+=img._height; }
- }
- int index = 0;
- while (true) {
- const int child_left = 2*index + 1, child_right = child_left + 1;
- int smallest = index;
- if (child_left<siz1 && img[child_left]<img[smallest]) smallest = child_left;
- if (child_right<siz1 && img[child_right]<img[smallest]) smallest = child_right;
- if (smallest!=index) {
- if (dim==1) cimg::swap(img[index],img[smallest]);
- else {
- T *ptr0 = img.data(0,index), *ptr1 = img.data(0,smallest);
- cimg_forC(img,c) { cimg::swap(*ptr0,*ptr1); ptr0+=img._height; ptr1+=img._height; }
- }
- index = smallest;
- } else break;
- }
- }
- double ret = cimg::type<double>::nan();
- if (dim==1) ret = img[siz1]; // Scalar element
- else {
- const T *ptrs = img.data(0,siz1);
- cimg_forC(img,c) { ptrd[c] = *ptrs; ptrs+=img._height; } // Vector element
- }
- if (is_pop) { // Remove element from array
- --siz;
- if (img.height()>32 && siz<img.height()/8) // Reduce size of dynamic array
- img.resize(1,std::max(2*siz + 1,32),1,-100,0);
- img[img._height - 1] = (T)cimg::uint2float(siz);
- }
- return ret;
- }
- static double mp_da_freeze(_cimg_math_parser& mp) {
- const char *const s_op = "da_freeze";
- mp_check_list(mp,s_op);
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- CImg<T> &img = mp.imglist[ind];
- int siz = img?(int)cimg::float2uint((float)img[img._height - 1]):0;
- if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1))
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': "
- "Specified image #%u of size (%d,%d,%d,%d) cannot be used as dynamic array%s.",
- mp.imgout.pixel_type(),s_op,ind,
- img.width(),img.height(),img.depth(),img.spectrum(),
- img._width==1 && img._depth==1?"":" (contains invalid element counter)");
- if (siz) img.resize(1,siz,1,-100,0,0); else img.assign();
- return cimg::type<double>::nan();
- }
- static double mp_da_insert_or_push(_cimg_math_parser& mp) {
- const bool is_push_heap = mp.opcode[3]==~0U - 1, is_push = mp.opcode[3]>=~0U - 1;
- const char *const s_op = is_push_heap?"da_push_heap":is_push?"da_push":"da_insert";
- mp_check_list(mp,s_op);
- const unsigned int
- dim = (unsigned int)mp.opcode[4],
- _dim = std::max(1U,dim),
- nb_elts = (unsigned int)mp.opcode[5] - 6,
- ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- CImg<T> &img = mp.imglist[ind];
- const int
- siz = img?(int)cimg::float2uint((float)img[img._height - 1]):0,
- pos0 = is_push?siz:(int)_mp_arg(3),
- pos = pos0<0?pos0 + siz:pos0;
- if (img && _dim!=img._spectrum)
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': "
- "Element to insert has invalid size %u (should be %u).",
- mp.imgout.pixel_type(),s_op,_dim,img._spectrum);
- if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1))
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': "
- "Specified image #%u of size (%d,%d,%d,%d) cannot be used as dynamic array%s.",
- mp.imgout.pixel_type(),s_op,ind,
- img.width(),img.height(),img.depth(),img.spectrum(),
- img._width==1 && img._depth==1?"":" (contains invalid element counter)");
- if (pos<0 || pos>siz)
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': "
- "Invalid position %d (not in range -%d...%d).",
- mp.imgout.pixel_type(),s_op,pos0,siz,siz);
- if (siz + nb_elts + 1>=img._height) // Increase size of dynamic array, if necessary
- img.resize(1,2*siz + nb_elts + 1,1,_dim,0);
- if (pos!=siz) // Move existing data in dynamic array
- cimg_forC(img,c) std::memmove(img.data(0,pos + nb_elts,0,c),img.data(0,pos,0,c),(siz - pos)*sizeof(T));
- if (!dim) // Scalar or vector1() elements
- for (unsigned int k = 0; k<nb_elts; ++k) {
- int index = pos + k;
- img[index] = (T)_mp_arg(6 + k);
- if (is_push_heap) while (index>0) { // Heapify-up
- const int index_parent = (index - 1)/2;
- if (img[index]<img[index_parent]) {
- cimg::swap(img[index],img[index_parent]);
- index = index_parent; }
- else break;
- }
- }
- else // vectorN() elements, with N>1
- for (unsigned int k = 0; k<nb_elts; ++k) {
- int index = pos + k;
- const double *const ptrs = &_mp_arg(6 + k) + 1;
- T *ptrd = img.data(0,index);
- cimg_forC(img,c) { *ptrd = ptrs[c]; ptrd+=img._height; }
- if (is_push_heap) while (index>0) { // Heapify-up
- const int index_parent = (index - 1)/2;
- if (img[index]<img[index_parent]) {
- T *ptr0 = img.data(0,index), *ptr1 = img.data(0,index_parent);
- cimg_forC(img,c) { cimg::swap(*ptr0,*ptr1); ptr0+=img._height; ptr1+=img._height; }
- index = index_parent;
- }
- else break;
- }
- }
- img[img._height - 1] = (T)cimg::uint2float(siz + nb_elts);
- return cimg::type<double>::nan();
- }
- static double mp_da_remove(_cimg_math_parser& mp) {
- mp_check_list(mp,"da_remove");
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- CImg<T> &img = mp.imglist[ind];
- int siz = img?(int)cimg::float2uint((float)img[img._height - 1]):0;
- if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1))
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'da_remove()': "
- "Specified image #%u of size (%d,%d,%d,%d) cannot be used as dynamic array%s.",
- mp.imgout.pixel_type(),ind,
- img.width(),img.height(),img.depth(),img.spectrum(),
- img._width==1 && img._depth==1?"":" (contains invalid element counter)");
- if (!siz)
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'da_remove()': "
- "Dynamic array is empty.",
- mp.imgout.pixel_type());
- int
- start0 = mp.opcode[3]==~0U?siz - 1:_mp_arg(3),
- end0 = mp.opcode[4]==~0U?start0:_mp_arg(4),
- start = start0<0?start0 + siz:start0,
- end = end0<0?end0 + siz:end0;
- if (start<0 || start>=siz || end<0 || end>=siz || start>end)
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'da_remove()': "
- "Invalid starting (%d) and ending (%d) positions "
- "(not ordered, in range -%d...%d).",
- mp.imgout.pixel_type(),start0,end0,siz,siz - 1);
- if (end<siz - 1) // Move remaining data in dynamic array
- cimg_forC(img,c) std::memmove(img.data(0,start,0,c),img.data(0,end + 1,0,c),(siz - 1 - end)*sizeof(T));
- siz-=end - start + 1;
- if (img.height()>32 && siz<img.height()/8) // Reduce size of dynamic array
- img.resize(1,std::max(2*siz + 1,32),1,-100,0);
- img[img._height - 1] = (T)cimg::uint2float(siz);
- return cimg::type<double>::nan();
- }
- static double mp_da_size(_cimg_math_parser& mp) {
- mp_check_list(mp,"da_size");
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- CImg<T> &img = mp.imglist[ind];
- const int siz = img?(int)cimg::float2uint((float)img[img._height - 1]):0;
- if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1))
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'da_size()': "
- "Specified image #%u of size (%d,%d,%d,%d) cannot be used as dynamic array%s.",
- mp.imgout.pixel_type(),ind,
- img.width(),img.height(),img.depth(),img.spectrum(),
- img._width==1 && img._depth==1?"":" (contains invalid element counter)");
- return siz;
- }
- static double mp_date(_cimg_math_parser& mp) {
- const unsigned int
- siz_out = (unsigned int)mp.opcode[2],
- siz_arg1 = (unsigned int)mp.opcode[4],
- siz_arg2 = (unsigned int)mp.opcode[6];
- double *ptr_out = &_mp_arg(1) + (siz_out?1:0);
- const double
- *ptr_arg1 = siz_arg1==~0U?0:&_mp_arg(3) + (siz_arg1?1:0),
- *ptr_arg2 = siz_arg2==~0U?0:&_mp_arg(5) + 1;
- if (!ptr_arg2) { // No filename specified
- if (!siz_arg1) return cimg::date((unsigned int)*ptr_arg1);
- if (siz_arg1==~0U) for (unsigned int k = 0; k<siz_out; ++k) ptr_out[k] = k;
- else for (unsigned int k = 0; k<siz_out; ++k) ptr_out[k] = ptr_arg1[k];
- cimg::date(ptr_out,siz_out);
- return cimg::type<double>::nan();
- }
- // Filename specified.
- CImg<charT> ss(siz_arg2 + 1);
- cimg_forX(ss,i) ss[i] = (char)ptr_arg2[i];
- ss.back() = 0;
- if (!siz_arg1) return cimg::fdate(ss,(unsigned int)*ptr_arg1);
- for (unsigned int k = 0; k<siz_out; ++k) ptr_out[k] = ptr_arg1[k];
- cimg::fdate(ss,ptr_out,siz_out);
- return cimg::type<double>::nan();
- }
- static double mp_debug(_cimg_math_parser& mp) {
- CImg<charT> expr(mp.opcode[2] - 4);
- {
- const ulongT *ptrs = mp.opcode._data + 4;
- cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++);
- }
- cimg::strellipsize(expr);
- const ulongT g_target = mp.opcode[1];
- #if cimg_use_openmp==0
- const unsigned int n_thread = 0;
- #else
- const unsigned int n_thread = omp_get_thread_num();
- #endif
- cimg_pragma_openmp(critical(mp_debug))
- {
- std::fprintf(cimg::output(),
- "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c"
- "Start debugging '%s', code length: %u -> mem[%u] (memsize: %u)",
- (void*)&mp,n_thread,mp.debug_indent,' ',
- expr._data,(unsigned int)mp.opcode[3],(unsigned int)g_target,mp.mem._width);
- std::fflush(cimg::output());
- mp.debug_indent+=3;
- }
- const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[3];
- CImg<ulongT> _op;
- for ( ; mp.p_code<p_end; ++mp.p_code) {
- const CImg<ulongT> &op = *mp.p_code;
- mp.opcode._data = op._data;
- _op.assign(1,op._height - 1);
- const ulongT *ptrs = op._data + 1;
- for (ulongT *ptrd = _op._data, *const ptrde = _op._data + _op._height; ptrd<ptrde; ++ptrd)
- *ptrd = *(ptrs++);
- const ulongT target = mp.opcode[1];
- mp.mem[target] = _cimg_mp_defunc(mp);
- cimg_pragma_openmp(critical(mp_debug))
- {
- std::fprintf(cimg::output(),
- "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c"
- "Opcode %p = [ %p,%s ] -> mem[%u] = %.17g",
- (void*)&mp,n_thread,mp.debug_indent,' ',
- (void*)mp.opcode._data,(void*)*mp.opcode,_op.value_string().data(),
- (unsigned int)target,mp.mem[target]);
- std::fflush(cimg::output());
- }
- }
- cimg_pragma_openmp(critical(mp_debug))
- {
- mp.debug_indent-=3;
- std::fprintf(cimg::output(),
- "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c"
- "End debugging '%s' -> mem[%u] = %.17g (memsize: %u)",
- (void*)&mp,n_thread,mp.debug_indent,' ',
- expr._data,(unsigned int)g_target,mp.mem[g_target],mp.mem._width);
- std::fflush(cimg::output());
- }
- --mp.p_code;
- return mp.mem[g_target];
- }
- static double mp_decrement(_cimg_math_parser& mp) {
- return _mp_arg(2) - 1;
- }
- static double mp_deg2rad(_cimg_math_parser& mp) {
- return _mp_arg(2)*cimg::PI/180;
- }
- static double mp_det(_cimg_math_parser& mp) {
- const double *ptrs = &_mp_arg(2) + 1;
- const unsigned int k = (unsigned int)mp.opcode[3];
- return CImg<doubleT>(ptrs,k,k,1,1,true).det();
- }
- static double mp_diag(_cimg_math_parser& mp) {
- const unsigned int i_end = (unsigned int)mp.opcode[2], siz = mp.opcode[2] - 3;
- double *ptrd = &_mp_arg(1) + 1;
- std::memset(ptrd,0,siz*siz*sizeof(double));
- for (unsigned int i = 3; i<i_end; ++i) { *(ptrd++) = _mp_arg(i); ptrd+=siz; }
- return cimg::type<double>::nan();
- }
- static double mp_display_memory(_cimg_math_parser& mp) {
- cimg::unused(mp);
- std::fputc('\n',cimg::output());
- CImg<charT> title(128);
- cimg_snprintf(title,title._width,"%s (%u)","[" cimg_appname "_math_parser] Memory snapshot",mp.mem._width);
- mp.mem.display(title);
- return cimg::type<double>::nan();
- }
- static double mp_display(_cimg_math_parser& mp) {
- const unsigned int
- _siz = (unsigned int)mp.opcode[3],
- siz = _siz?_siz:1;
- const double *const ptr = &_mp_arg(1) + (_siz?1:0);
- const int
- w = (int)_mp_arg(4),
- h = (int)_mp_arg(5),
- d = (int)_mp_arg(6),
- s = (int)_mp_arg(7);
- CImg<doubleT> img;
- if (w>0 && h>0 && d>0 && s>0) {
- if ((unsigned int)w*h*d*s<=siz) img.assign(ptr,w,h,d,s,true);
- else img.assign(ptr,siz).resize(w,h,d,s,-1);
- } else img.assign(ptr,1,siz,1,1,true);
- CImg<charT> expr(mp.opcode[2] - 8);
- const ulongT *ptrs = mp.opcode._data + 8;
- cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++);
- ((CImg<charT>::string("[" cimg_appname "_math_parser] ",false,true),expr)>'x').move_to(expr);
- cimg::strellipsize(expr);
- std::fputc('\n',cimg::output());
- img.display(expr._data);
- return cimg::type<double>::nan();
- }
- static double mp_div(_cimg_math_parser& mp) {
- return _mp_arg(2)/_mp_arg(3);
- }
- static double mp_dot(_cimg_math_parser& mp) {
- const unsigned int siz = (unsigned int)mp.opcode[4];
- return CImg<doubleT>(&_mp_arg(2) + 1,1,siz,1,1,true).
- dot(CImg<doubleT>(&_mp_arg(3) + 1,1,siz,1,1,true));
- }
- static double mp_do(_cimg_math_parser& mp) {
- const ulongT
- mem_body = mp.opcode[1],
- mem_cond = mp.opcode[2];
- const CImg<ulongT>
- *const p_body = ++mp.p_code,
- *const p_cond = p_body + mp.opcode[3],
- *const p_end = p_cond + mp.opcode[4];
- const unsigned int vsiz = (unsigned int)mp.opcode[5];
- if (mp.opcode[6]) { // Set default value for result and condition if necessary
- if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
- else mp.mem[mem_body] = cimg::type<double>::nan();
- }
- if (mp.opcode[7]) mp.mem[mem_cond] = 0;
- const unsigned int _break_type = mp.break_type;
- mp.break_type = 0;
- do {
- for (mp.p_code = p_body; mp.p_code<p_cond; ++mp.p_code) { // Evaluate body
- mp.opcode._data = mp.p_code->_data;
- const ulongT target = mp.opcode[1];
- mp.mem[target] = _cimg_mp_defunc(mp);
- }
- if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
- for (mp.p_code = p_cond; mp.p_code<p_end; ++mp.p_code) { // Evaluate condition
- mp.opcode._data = mp.p_code->_data;
- const ulongT target = mp.opcode[1];
- mp.mem[target] = _cimg_mp_defunc(mp);
- }
- if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
- } while (mp.mem[mem_cond]);
- mp.break_type = _break_type;
- mp.p_code = p_end - 1;
- return mp.mem[mem_body];
- }
- static double mp_echo(_cimg_math_parser& mp) {
- const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2;
- if (!nb_args) { std::fputc('\n',cimg::output()); return cimg::type<double>::nan(); } // No arguments
- CImgList<charT> _str;
- CImg<charT> it;
- for (unsigned int n = 0; n<nb_args; ++n) {
- const unsigned int siz = (unsigned int)mp.opcode[4 + 2*n];
- if (siz) { // Vector argument -> string
- const double *ptr = &_mp_arg(3 + 2*n) + 1;
- unsigned int l = 0;
- while (l<siz && ptr[l]) ++l;
- CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str);
- } else { // Scalar argument -> number
- it.assign(24);
- cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n));
- CImg<charT>::string(it,false,true).move_to(_str);
- }
- }
- CImg(1,1,1,1,0).move_to(_str);
- const CImg<charT> str = _str>'x';
- std::fprintf(cimg::output(),"\n%s",str._data);
- return cimg::type<double>::nan();
- }
- static double mp_ellipse(_cimg_math_parser& mp) {
- mp_check_list(mp,"ellipse");
- const unsigned int i_end = (unsigned int)mp.opcode[2];
- unsigned int ind = (unsigned int)mp.opcode[3];
- if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.imglist.width());
- CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
- CImg<T> color(img._spectrum,1,1,1,0);
- bool is_invalid_arguments = false, is_outlined = false;
- float r1 = 0, r2 = 0, angle = 0, opacity = 1;
- unsigned int i = 4, pattern = ~0U;
- int x0 = 0, y0 = 0;
- if (i>=i_end) is_invalid_arguments = true;
- else {
- x0 = (int)cimg::round(_mp_arg(i++));
- if (i>=i_end) is_invalid_arguments = true;
- else {
- y0 = (int)cimg::round(_mp_arg(i++));
- if (i>=i_end) is_invalid_arguments = true;
- else {
- r1 = (float)_mp_arg(i++);
- if (i>=i_end) r2 = r1;
- else {
- r2 = (float)_mp_arg(i++);
- if (i<i_end) {
- angle = (float)(_mp_arg(i++)*180/cimg::PI);
- if (i<i_end) {
- opacity = (float)_mp_arg(i++);
- if (r1<0 && r2<0) {
- pattern = (unsigned int)_mp_arg(i++);
- is_outlined = true;
- r1 = -r1; r2 = -r2;
- }
- if (i<i_end) {
- cimg_forX(color,k) if (i<i_end) color[k] = (T)_mp_arg(i++);
- else { color.resize(k,1,1,1,-1); break; }
- color.resize(img._spectrum,1,1,1,0,2);
- }
- }
- }
- }
- }
- }
- }
- if (!is_invalid_arguments) {
- if (is_outlined) img.draw_ellipse(x0,y0,r1,r2,angle,color._data,opacity,pattern);
- else img.draw_ellipse(x0,y0,r1,r2,angle,color._data,opacity);
- } else {
- CImg<doubleT> args(i_end - 4);
- cimg_forX(args,k) args[k] = _mp_arg(4 + k);
- if (ind==~0U)
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'ellipse()': "
- "Invalid arguments '%s'. ",
- mp.imgin.pixel_type(),args.value_string()._data);
- else
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'ellipse()': "
- "Invalid arguments '#%u%s%s'. ",
- mp.imgin.pixel_type(),ind,args._width?",":"",args.value_string()._data);
- }
- return cimg::type<double>::nan();
- }
- static double mp_eq(_cimg_math_parser& mp) {
- return (double)(_mp_arg(2)==_mp_arg(3));
- }
- #if cimg_use_cpp11==1
- static double mp_erf(_cimg_math_parser& mp) {
- return std::erf(_mp_arg(2));
- }
- #endif
- static double mp_erfinv(_cimg_math_parser& mp) {
- return cimg::erfinv(_mp_arg(2));
- }
- static double mp_exp(_cimg_math_parser& mp) {
- return std::exp(_mp_arg(2));
- }
- static double mp_expr(_cimg_math_parser& mp) {
- const unsigned int
- sizs = (unsigned int)mp.opcode[3],
- w = (unsigned int)mp.opcode[4],
- h = (unsigned int)mp.opcode[5],
- d = (unsigned int)mp.opcode[6],
- s = (unsigned int)mp.opcode[7],
- sizd = w*h*d*s;
- const double *ptrs = &_mp_arg(2) + 1;
- double *ptrd = &_mp_arg(1);
- CImg<charT> ss(sizs + 1);
- cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptrs[i];
- ss.back() = 0;
- if (!sizd) return CImg<T>(w,h,d,s,0).eval(ss,0,0,0,0,&mp.imglist); // Scalar result
- CImg<doubleT>(++ptrd,w,h,d,s,true) = CImg<T>(w,h,d,s,0).fill(ss,true,true,&mp.imglist);
- return cimg::type<double>::nan();
- }
- static double mp_eye(_cimg_math_parser& mp) {
- double *ptrd = &_mp_arg(1) + 1;
- const unsigned int k = (unsigned int)mp.opcode[2];
- CImg<doubleT>(ptrd,k,k,1,1,true).identity_matrix();
- return cimg::type<double>::nan();
- }
- static double mp_f2ui(_cimg_math_parser& mp) {
- return (double)cimg::float2uint((float)_mp_arg(2));
- }
- static double mp_factorial(_cimg_math_parser& mp) {
- return cimg::factorial((int)_mp_arg(2));
- }
- static double mp_fibonacci(_cimg_math_parser& mp) {
- return cimg::fibonacci((int)_mp_arg(2));
- }
- static double mp_fill(_cimg_math_parser& mp) {
- unsigned int siz = (unsigned int)mp.opcode[2];
- double
- *ptrd = &_mp_arg(1),
- *const ptrc = mp.opcode[3]!=~0U?&_mp_arg(3):0,
- *const ptrs = &_mp_arg(4);
- if (siz) ++ptrd; else ++siz; // Fill vector-valued slot
- const CImg<ulongT>
- *const p_body = ++mp.p_code,
- *const p_end = p_body + mp.opcode[5];
- const unsigned int _break_type = mp.break_type;
- mp.break_type = 0;
- unsigned int it = 0;
- if (ptrc) { // Version with loop variable (3 arguments)
- while (it<siz) {
- *ptrc = (double)it;
- for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
- mp.opcode._data = mp.p_code->_data;
- const ulongT target = mp.opcode[1];
- mp.mem[target] = _cimg_mp_defunc(mp);
- }
- if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
- else ptrd[it] = *ptrs;
- ++it;
- }
- *ptrc = (double)it;
- } else // Version without loop variable (2 arguments)
- while (it<siz) {
- for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
- mp.opcode._data = mp.p_code->_data;
- const ulongT target = mp.opcode[1];
- mp.mem[target] = _cimg_mp_defunc(mp);
- }
- if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
- else ptrd[it] = *ptrs;
- ++it;
- }
- mp.break_type = _break_type;
- mp.p_code = p_end - 1;
- return *ptrd;
- }
- static double mp_find(_cimg_math_parser& mp) {
- const int _step = (int)_mp_arg(6), step = _step?_step:-1;
- const ulongT siz = (ulongT)mp.opcode[3];
- longT ind = (longT)(mp.opcode[5]!=_cimg_mp_slot_nan?_mp_arg(5):step>0?0:siz - 1);
- if (ind<0 || ind>=(longT)siz) return -1.;
- const double
- *const ptrb = &_mp_arg(2) + 1,
- *const ptre = ptrb + siz,
- val = _mp_arg(4),
- *ptr = ptrb + ind;
- // Forward search
- if (step>0) {
- while (ptr<ptre && *ptr!=val) ptr+=step;
- return ptr>=ptre?-1.:(double)(ptr - ptrb);
- }
- // Backward search.
- while (ptr>=ptrb && *ptr!=val) ptr+=step;
- return ptr<ptrb?-1.:(double)(ptr - ptrb);
- }
- static double mp_find_seq(_cimg_math_parser& mp) {
- const int _step = (int)_mp_arg(7), step = _step?_step:-1;
- const ulongT
- siz1 = (ulongT)mp.opcode[3],
- siz2 = (ulongT)mp.opcode[5];
- longT ind = (longT)(mp.opcode[6]!=_cimg_mp_slot_nan?_mp_arg(6):step>0?0:siz1 - 1);
- if (ind<0 || ind>=(longT)siz1) return -1.;
- const double
- *const ptr1b = &_mp_arg(2) + 1,
- *const ptr1e = ptr1b + siz1,
- *const ptr2b = &_mp_arg(4) + 1,
- *const ptr2e = ptr2b + siz2,
- *ptr1 = ptr1b + ind,
- *p1 = 0,
- *p2 = 0;
- // Forward search.
- if (step>0) {
- do {
- while (ptr1<ptr1e && *ptr1!=*ptr2b) ptr1+=step;
- if (ptr1>=ptr1e) return -1.;
- p1 = ptr1 + 1;
- p2 = ptr2b + 1;
- while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
- } while (p2<ptr2e && (ptr1+=step)<ptr1e);
- return p2<ptr2e?-1.:(double)(ptr1 - ptr1b);
- }
- // Backward search.
- do {
- while (ptr1>=ptr1b && *ptr1!=*ptr2b) ptr1+=step;
- if (ptr1<ptr1b) return -1.;
- p1 = ptr1 + 1;
- p2 = ptr2b + 1;
- while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
- } while (p2<ptr2e && (ptr1+=step)>=ptr1b);
- return p2<ptr2e?-1.:(double)(ptr1 - ptr1b);
- }
- static double mp_floor(_cimg_math_parser& mp) {
- return std::floor(_mp_arg(2));
- }
- static double mp_for(_cimg_math_parser& mp) {
- const ulongT
- mem_body = mp.opcode[1],
- mem_cond = mp.opcode[3];
- const CImg<ulongT>
- *const p_init = ++mp.p_code,
- *const p_cond = p_init + mp.opcode[4],
- *const p_body = p_cond + mp.opcode[5],
- *const p_post = p_body + mp.opcode[6],
- *const p_end = p_post + mp.opcode[7];
- const unsigned int vsiz = (unsigned int)mp.opcode[2];
- bool is_cond = false;
- if (mp.opcode[8]) { // Set default value for result and condition if necessary
- if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
- else mp.mem[mem_body] = cimg::type<double>::nan();
- }
- if (mp.opcode[9]) mp.mem[mem_cond] = 0;
- const unsigned int _break_type = mp.break_type;
- mp.break_type = 0;
- for (mp.p_code = p_init; mp.p_code<p_cond; ++mp.p_code) { // Evaluate init
- mp.opcode._data = mp.p_code->_data;
- const ulongT target = mp.opcode[1];
- mp.mem[target] = _cimg_mp_defunc(mp);
- }
- if (!mp.break_type) do {
- for (mp.p_code = p_cond; mp.p_code<p_body; ++mp.p_code) { // Evaluate condition
- mp.opcode._data = mp.p_code->_data;
- const ulongT target = mp.opcode[1];
- mp.mem[target] = _cimg_mp_defunc(mp);
- }
- if (mp.break_type==1) break;
- is_cond = (bool)mp.mem[mem_cond];
- if (is_cond && !mp.break_type) {
- for (mp.p_code = p_body; mp.p_code<p_post; ++mp.p_code) { // Evaluate body
- mp.opcode._data = mp.p_code->_data;
- const ulongT target = mp.opcode[1];
- mp.mem[target] = _cimg_mp_defunc(mp);
- }
- if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
- for (mp.p_code = p_post; mp.p_code<p_end; ++mp.p_code) { // Evaluate post-code
- mp.opcode._data = mp.p_code->_data;
- const ulongT target = mp.opcode[1];
- mp.mem[target] = _cimg_mp_defunc(mp);
- }
- if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
- }
- } while (is_cond);
- mp.break_type = _break_type;
- mp.p_code = p_end - 1;
- return mp.mem[mem_body];
- }
- static double mp_fsize(_cimg_math_parser& mp) {
- const double *ptrs = &_mp_arg(2) + 1;
- const ulongT siz = (ulongT)mp.opcode[3];
- CImg<charT> ss(siz + 1);
- cimg_forX(ss,i) ss[i] = (char)ptrs[i];
- ss.back() = 0;
- return (double)cimg::fsize(ss);
- }
- static double mp_g(_cimg_math_parser& mp) {
- cimg::unused(mp);
- return cimg::grand(&mp.rng);
- }
- #if cimg_use_cpp11==1
- static double mp_gamma(_cimg_math_parser& mp) {
- return std::tgamma(_mp_arg(2));
- }
- #endif
- static double mp_gauss(_cimg_math_parser& mp) {
- const double x = _mp_arg(2), s = _mp_arg(3);
- return std::exp(-x*x/(2*s*s))/(_mp_arg(4)?std::sqrt(2*s*s*cimg::PI):1);
- }
- #ifdef cimg_mp_func_get
- static double mp_get(_cimg_math_parser& mp) {
- const double *ptrs = &_mp_arg(2) + 1;
- double *ptrd = &_mp_arg(1);
- const unsigned int
- sizs = (unsigned int)mp.opcode[3],
- sizd = (unsigned int)mp.opcode[4];
- const bool to_string = (bool)mp.opcode[5];
- CImg<charT> ss(sizs + 1);
- cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptrs[i];
- ss.back() = 0;
- if (sizd) cimg_mp_func_get(ptrd + 1,sizd,to_string,ss._data);
- else cimg_mp_func_get(ptrd,0,to_string,ss._data);
- return cimg::type<double>::nan();
- }
- #endif
- static double mp_gcd(_cimg_math_parser& mp) {
- return cimg::gcd((long)_mp_arg(2),(long)_mp_arg(3));
- }
- #ifdef cimg_mp_func_name
- static double mp_name(_cimg_math_parser& mp) {
- double *const ptr = &_mp_arg(1) + 1;
- const unsigned int siz = (unsigned int)mp.opcode[3];
- unsigned int ind = (unsigned int)mp.opcode[2];
- if (ind==~0U) std::memset(ptr,0,siz*sizeof(double));
- else {
- ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- cimg_mp_func_name(ind,ptr,siz);
- }
- return cimg::type<double>::nan();
- }
- #endif
- static double mp_gt(_cimg_math_parser& mp) {
- return (double)(_mp_arg(2)>_mp_arg(3));
- }
- static double mp_gte(_cimg_math_parser& mp) {
- return (double)(_mp_arg(2)>=_mp_arg(3));
- }
- static double mp_i(_cimg_math_parser& mp) {
- return (double)mp.imgin((int)mp.mem[_cimg_mp_slot_x],(int)mp.mem[_cimg_mp_slot_y],
- (int)mp.mem[_cimg_mp_slot_z],(int)mp.mem[_cimg_mp_slot_c]);
- }
- static double mp_if(_cimg_math_parser& mp) {
- const bool is_cond = (bool)_mp_arg(2);
- const ulongT
- mem_left = mp.opcode[3],
- mem_right = mp.opcode[4];
- const CImg<ulongT>
- *const p_right = ++mp.p_code + mp.opcode[5],
- *const p_end = p_right + mp.opcode[6];
- const unsigned int vtarget = (unsigned int)mp.opcode[1], vsiz = (unsigned int)mp.opcode[7];
- if (is_cond) for ( ; mp.p_code<p_right; ++mp.p_code) {
- mp.opcode._data = mp.p_code->_data;
- const ulongT target = mp.opcode[1];
- mp.mem[target] = _cimg_mp_defunc(mp);
- }
- else for (mp.p_code = p_right; mp.p_code<p_end; ++mp.p_code) {
- mp.opcode._data = mp.p_code->_data;
- const ulongT target = mp.opcode[1];
- mp.mem[target] = _cimg_mp_defunc(mp);
- }
- if (mp.p_code==mp.p_break) --mp.p_code;
- else mp.p_code = p_end - 1;
- if (vsiz) std::memcpy(&mp.mem[vtarget] + 1,&mp.mem[is_cond?mem_left:mem_right] + 1,sizeof(double)*vsiz);
- return mp.mem[is_cond?mem_left:mem_right];
- }
- static double mp_image_d(_cimg_math_parser& mp) {
- unsigned int ind = (unsigned int)mp.opcode[2];
- if (ind!=~0U) {
- if (!mp.imglist.width()) return cimg::type<double>::nan();
- ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- }
- const CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
- return (double)img.depth();
- }
- static double mp_image_display(_cimg_math_parser& mp) {
- mp_check_list(mp,"display");
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- cimg::mutex(6);
- CImg<T> &img = mp.imglist[ind];
- CImg<charT> title(256);
- std::fputc('\n',cimg::output());
- cimg_snprintf(title,title._width,"[ Image #%u ]",ind);
- img.display(title);
- cimg::mutex(6,0);
- return cimg::type<double>::nan();
- }
- static double mp_image_draw(_cimg_math_parser& mp) {
- const int x = (int)_mp_arg(4), y = (int)_mp_arg(5), z = (int)_mp_arg(6), c = (int)_mp_arg(7);
- unsigned int ind = (unsigned int)mp.opcode[3];
- if (ind!=~0U) {
- if (!mp.imglist.width()) return cimg::type<double>::nan();
- ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.imglist.width());
- }
- CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
- unsigned int
- dx = (unsigned int)mp.opcode[8],
- dy = (unsigned int)mp.opcode[9],
- dz = (unsigned int)mp.opcode[10],
- dc = (unsigned int)mp.opcode[11];
- dx = dx==~0U?img._width:(unsigned int)_mp_arg(8);
- dy = dy==~0U?img._height:(unsigned int)_mp_arg(9);
- dz = dz==~0U?img._depth:(unsigned int)_mp_arg(10);
- dc = dc==~0U?img._spectrum:(unsigned int)_mp_arg(11);
- const ulongT sizS = mp.opcode[2];
- if (sizS<(ulongT)dx*dy*dz*dc)
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
- "Sprite vector (%lu values) and its specified geometry (%u,%u,%u,%u) "
- "(%lu values) do not match.",
- mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc);
- const CImg<doubleT> S(&_mp_arg(1) + 1,dx,dy,dz,dc,true);
- const float opacity = (float)_mp_arg(12);
- if (img._data) {
- if (mp.opcode[13]!=~0U) { // Opacity mask specified
- const ulongT sizM = mp.opcode[14];
- if (sizM<(ulongT)dx*dy*dz)
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
- "Mask vector (%lu values) and specified sprite geometry (%u,%u,%u,%u) "
- "(%lu values) do not match.",
- mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc);
- const CImg<doubleT> M(&_mp_arg(13) + 1,dx,dy,dz,(unsigned int)(sizM/(dx*dy*dz)),true);
- img.draw_image(x,y,z,c,S,M,opacity,(float)_mp_arg(15));
- } else img.draw_image(x,y,z,c,S,opacity);
- }
- return cimg::type<double>::nan();
- }
- static double mp_image_h(_cimg_math_parser& mp) {
- unsigned int ind = (unsigned int)mp.opcode[2];
- if (ind!=~0U) {
- if (!mp.imglist.width()) return cimg::type<double>::nan();
- ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- }
- const CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
- return (double)img.height();
- }
- static double mp_image_median(_cimg_math_parser& mp) {
- unsigned int ind = (unsigned int)mp.opcode[2];
- if (ind!=~0U) {
- if (!mp.imglist.width()) return cimg::type<double>::nan();
- ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- }
- const CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
- return (double)img.median();
- }
- static double mp_image_norm(_cimg_math_parser& mp) {
- unsigned int ind = (unsigned int)mp.opcode[2];
- if (ind!=~0U) {
- if (!mp.imglist.width()) return cimg::type<double>::nan();
- ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- }
- const CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
- return (double)img.magnitude(2);
- }
- static double mp_image_print(_cimg_math_parser& mp) {
- mp_check_list(mp,"print");
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- cimg::mutex(6);
- CImg<T> &img = mp.imglist[ind];
- CImg<charT> title(256);
- std::fputc('\n',cimg::output());
- cimg_snprintf(title,title._width,"[ Image #%u ]",ind);
- img.print(title);
- cimg::mutex(6,0);
- return cimg::type<double>::nan();
- }
- static double mp_image_resize(_cimg_math_parser& mp) {
- mp_check_list(mp,"resize");
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- cimg::mutex(6);
- CImg<T> &img = mp.imglist[ind];
- const double
- _w = mp.opcode[3]==~0U?-100:_mp_arg(3),
- _h = mp.opcode[4]==~0U?-100:_mp_arg(4),
- _d = mp.opcode[5]==~0U?-100:_mp_arg(5),
- _s = mp.opcode[6]==~0U?-100:_mp_arg(6);
- const unsigned int
- w = (unsigned int)(_w>=0?_w:-_w*img.width()/100),
- h = (unsigned int)(_h>=0?_h:-_h*img.height()/100),
- d = (unsigned int)(_d>=0?_d:-_d*img.depth()/100),
- s = (unsigned int)(_s>=0?_s:-_s*img.spectrum()/100),
- interp = (int)_mp_arg(7);
- if (mp.is_fill && img._data==mp.imgout._data) {
- cimg::mutex(6,0);
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'resize()': "
- "Cannot both fill and resize image (%u,%u,%u,%u) "
- "to new dimensions (%u,%u,%u,%u).",
- img.pixel_type(),img._width,img._height,img._depth,img._spectrum,w,h,d,s);
- }
- const unsigned int
- boundary = (int)_mp_arg(8);
- const float
- cx = (float)_mp_arg(9),
- cy = (float)_mp_arg(10),
- cz = (float)_mp_arg(11),
- cc = (float)_mp_arg(12);
- img.resize(w,h,d,s,interp,boundary,cx,cy,cz,cc);
- cimg::mutex(6,0);
- return cimg::type<double>::nan();
- }
- static double mp_image_s(_cimg_math_parser& mp) {
- unsigned int ind = (unsigned int)mp.opcode[2];
- if (ind!=~0U) {
- if (!mp.imglist.width()) return cimg::type<double>::nan();
- ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- }
- const CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
- return (double)img.spectrum();
- }
- static double mp_image_sort(_cimg_math_parser& mp) {
- mp_check_list(mp,"sort");
- const bool is_increasing = (bool)_mp_arg(3);
- const unsigned int
- ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()),
- axis = (unsigned int)_mp_arg(4);
- cimg::mutex(6);
- CImg<T> &img = mp.imglist[ind];
- img.sort(is_increasing,
- axis==0 || axis=='x'?'x':
- axis==1 || axis=='y'?'y':
- axis==2 || axis=='z'?'z':
- axis==3 || axis=='c'?'c':0);
- cimg::mutex(6,0);
- return cimg::type<double>::nan();
- }
- static double mp_image_stats(_cimg_math_parser& mp) {
- double *ptrd = &_mp_arg(1) + 1;
- unsigned int ind = (unsigned int)mp.opcode[2];
- if (ind==~0U)
- CImg<doubleT>(ptrd,14,1,1,1,true) = mp.imgout.get_stats();
- else {
- if (!mp.imglist.width()) return cimg::type<double>::nan();
- ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- CImg<doubleT>(ptrd,14,1,1,1,true) = mp.imglist[ind].get_stats();
- }
- return cimg::type<double>::nan();
- }
- static double mp_image_w(_cimg_math_parser& mp) {
- unsigned int ind = (unsigned int)mp.opcode[2];
- if (ind!=~0U) {
- if (!mp.imglist.width()) return cimg::type<double>::nan();
- ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- }
- const CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
- return (double)img.width();
- }
- static double mp_image_wh(_cimg_math_parser& mp) {
- unsigned int ind = (unsigned int)mp.opcode[2];
- if (ind!=~0U) {
- if (!mp.imglist.width()) return cimg::type<double>::nan();
- ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- }
- const CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
- return (double)img.width()*img.height();
- }
- static double mp_image_whd(_cimg_math_parser& mp) {
- unsigned int ind = (unsigned int)mp.opcode[2];
- if (ind!=~0U) {
- if (!mp.imglist.width()) return cimg::type<double>::nan();
- ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- }
- const CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
- return (double)img.width()*img.height()*img.depth();
- }
- static double mp_image_whds(_cimg_math_parser& mp) {
- unsigned int ind = (unsigned int)mp.opcode[2];
- if (ind!=~0U) {
- if (!mp.imglist.width()) return cimg::type<double>::nan();
- ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- }
- const CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
- return (double)img.width()*img.height()*img.depth()*img.spectrum();
- }
- static double mp_increment(_cimg_math_parser& mp) {
- return _mp_arg(2) + 1;
- }
- static double mp_inrange(_cimg_math_parser& mp) {
- const unsigned int sizd = (unsigned int)mp.opcode[2];
- const bool
- include_m = (bool)_mp_arg(9),
- include_M = (bool)_mp_arg(10);
- if (!sizd) { // Scalar result
- const double val = _mp_arg(3);
- const double m = _mp_arg(5), M = _mp_arg(7);
- if (M>=m) return (double)((include_m?(val>=m):(val>m)) && (include_M?(val<=M):(val<M)));
- else return (double)((include_M?(val>=M):(val>M)) && (include_m?(val<=m):(val<m)));
- }
- // Vector result
- const unsigned int
- siz1 = (unsigned int)mp.opcode[4],
- siz2 = (unsigned int)mp.opcode[6],
- siz3 = (unsigned int)mp.opcode[8],
- off1 = siz1?1:0,
- off2 = siz2?1:0,
- off3 = siz3?1:0;
- double *ptrd = &_mp_arg(1) + 1;
- const double
- *ptr1 = &_mp_arg(3) + off1,
- *ptr2 = &_mp_arg(5) + off2,
- *ptr3 = &_mp_arg(7) + off3;
- for (unsigned int k = 0; k<sizd; ++k) {
- const double val = *ptr1;
- const double m = *ptr2, M = *ptr3;
- if (M>=m)
- ptrd[k] = (double)((include_m?(val>=m):(val>m)) && (include_M?(val<=M):(val<M)));
- else
- ptrd[k] = (double)((include_M?(val>=M):(val>M)) && (include_m?(val<=m):(val<m)));
- ptr1+=off1;
- ptr2+=off2;
- ptr3+=off3;
- }
- return cimg::type<double>::nan();
- }
- static double mp_int(_cimg_math_parser& mp) {
- return (double)(longT)_mp_arg(2);
- }
- static double mp_ioff(_cimg_math_parser& mp) {
- const unsigned int
- boundary_conditions = (unsigned int)_mp_arg(3);
- const CImg<T> &img = mp.imgin;
- const longT
- off = (longT)_mp_arg(2),
- whds = (longT)img.size();
- if (off>=0 && off<whds) return (double)img[off];
- if (img._data) switch (boundary_conditions) {
- case 3 : { // Mirror
- const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
- return (double)img[moff<whds?moff:whds2 - moff - 1];
- }
- case 2 : // Periodic
- return (double)img[cimg::mod(off,whds)];
- case 1 : // Neumann
- return (double)img[off<0?0:whds - 1];
- default : // Dirichlet
- return 0;
- }
- return 0;
- }
- static double mp_isbool(_cimg_math_parser& mp) {
- const double val = _mp_arg(2);
- return (double)(val==0. || val==1.);
- }
- static double mp_isdir(_cimg_math_parser& mp) {
- const unsigned int siz = (unsigned int)mp.opcode[3];
- const double *const ptrs = &_mp_arg(2) + (siz?1:0);
- if (!siz) { char str[2] = {}; *str = *ptrs; return (double)cimg::is_directory(str); }
- CImg<charT> ss(siz + 1);
- cimg_forX(ss,i) ss[i] = (char)ptrs[i];
- ss.back() = 0;
- return (double)cimg::is_directory(ss);
- }
- static double mp_isin(_cimg_math_parser& mp) {
- const unsigned int
- i_end = (unsigned int)mp.opcode[2],
- siz_ref = (unsigned int)mp.opcode[4];
- bool res = false;
- if (siz_ref) { // Reference value is a vector
- const CImg<doubleT> ref(&_mp_arg(3) + 1,siz_ref,1,1,1,true);
- for (unsigned int i = 5; i<i_end; i+=2) {
- const unsigned int siz = (unsigned int)mp.opcode[i + 1];
- if (siz==siz_ref && CImg<doubleT>(&_mp_arg(i) + 1,siz,1,1,1,true)==ref) { res = true; break; }
- }
- } else { // Reference value is a scalar
- const double ref = _mp_arg(3);
- for (unsigned i = 5; i<i_end; i+=2) {
- const unsigned int siz = (unsigned int)mp.opcode[i + 1];
- if (!siz && _mp_arg(i)==ref) { res = true; break; }
- }
- }
- return res?1.:0.;
- }
- static double mp_isinf(_cimg_math_parser& mp) {
- return (double)cimg::type<double>::is_inf(_mp_arg(2));
- }
- static double mp_isint(_cimg_math_parser& mp) {
- return (double)((double)(longT)_mp_arg(2)==_mp_arg(2));
- }
- static double mp_isfile(_cimg_math_parser& mp) {
- const unsigned int siz = (unsigned int)mp.opcode[3];
- const double *const ptrs = &_mp_arg(2) + (siz?1:0);
- if (!siz) { char str[2] = {}; *str = *ptrs; return (double)cimg::is_file(str); }
- CImg<charT> ss(siz + 1);
- cimg_forX(ss,i) ss[i] = (char)ptrs[i];
- ss.back() = 0;
- return (double)cimg::is_file(ss);
- }
- static double mp_isnan(_cimg_math_parser& mp) {
- return (double)cimg::type<double>::is_nan(_mp_arg(2));
- }
- static double mp_isvarname(_cimg_math_parser& mp) {
- const unsigned int siz = (unsigned int)mp.opcode[3];
- const double *ptrs = &_mp_arg(2) + (siz?1:0);
- if (!siz) {
- const char c = (char)*ptrs;
- return (c>='a' && c<='z') || (c>='A' && c<='Z') || c=='_';
- }
- if (*ptrs>='0' && *ptrs<='9') return 0;
- for (unsigned int k = 0; k<siz; ++k) if (!cimg::is_varchar((char)ptrs[k])) return 0;
- return 1;
- }
- static double mp_ixyzc(_cimg_math_parser& mp) {
- const unsigned int
- interpolation = (unsigned int)_mp_arg(6),
- boundary_conditions = (unsigned int)_mp_arg(7);
- const CImg<T> &img = mp.imgin;
- const double
- x = _mp_arg(2), y = _mp_arg(3),
- z = _mp_arg(4), c = _mp_arg(5);
- switch (interpolation) {
- case 2 : // Cubic interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float
- w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
- mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
- mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
- return (double)img._cubic_atXYZ(mx<img.width()?mx:w2 - mx - 1,
- my<img.height()?my:h2 - my - 1,
- mz<img.depth()?mz:d2 - mz - 1,
- (int)(mc<img.spectrum()?mc:s2 - mc - 1));
- }
- case 2 : // Periodic
- return (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,
- (int)cimg::mod(c,(double)img._spectrum));
- case 1 : // Neumann
- return (double)img._cubic_atXYZ((float)x,(float)y,(float)z,
- (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
- default : // Dirichlet
- if (c<0 || c>=img._spectrum) return (T)0;
- return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
- }
- case 1 : // Linear interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float
- w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
- mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
- mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
- return (double)img._linear_atXYZ(mx<img.width()?mx:w2 - mx - 1,
- my<img.height()?my:h2 - my - 1,
- mz<img.depth()?mz:d2 - mz - 1,
- (int)(mc<img.spectrum()?mc:s2 - mc - 1));
- }
- case 2 : // Periodic
- return (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,
- (int)cimg::mod(c,(double)img._spectrum));
- case 1 : // Neumann
- return (double)img._linear_atXYZ((float)x,(float)y,(float)z,
- (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
- default : // Dirichlet
- if (c<0 || c>=img._spectrum) return (T)0;
- return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
- }
- default : // Nearest neighbor interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const int
- w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
- mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
- mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
- return (double)img(mx<img.width()?mx:w2 - mx - 1,
- my<img.height()?my:h2 - my - 1,
- mz<img.depth()?mz:d2 - mz - 1,
- mc<img.spectrum()?mc:s2 - mc - 1);
- }
- case 2 : // Periodic
- return (double)img((int)cimg::mod(x,(double)img._width),
- (int)cimg::mod(y,(double)img._height),
- (int)cimg::mod(z,(double)img._depth),
- (int)cimg::mod(c,(double)img._spectrum));
- case 1 : // Neumann
- return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
- default : // Dirichlet
- return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
- }
- }
- }
- static double mp_joff(_cimg_math_parser& mp) {
- const unsigned int
- boundary_conditions = (unsigned int)_mp_arg(3);
- const int
- ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
- oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
- const CImg<T> &img = mp.imgin;
- const longT
- off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
- whds = (longT)img.size();
- if (off>=0 && off<whds) return (double)img[off];
- if (img._data) switch (boundary_conditions) {
- case 3 : { // Mirror
- const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
- return (double)img[moff<whds?moff:whds2 - moff - 1];
- }
- case 2 : // Periodic
- return (double)img[cimg::mod(off,whds)];
- case 1 : // Neumann
- return (double)img[off<0?0:whds - 1];
- default : // Dirichlet
- return 0;
- }
- return 0;
- }
- static double mp_jxyzc(_cimg_math_parser& mp) {
- const unsigned int
- interpolation = (unsigned int)_mp_arg(6),
- boundary_conditions = (unsigned int)_mp_arg(7);
- const CImg<T> &img = mp.imgin;
- const double
- ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
- oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c],
- x = ox + _mp_arg(2), y = oy + _mp_arg(3),
- z = oz + _mp_arg(4), c = oc + _mp_arg(5);
- switch (interpolation) {
- case 2 : // Cubic interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float
- w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
- mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
- mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
- return (double)img._cubic_atXYZ(mx<img.width()?mx:w2 - mx - 1,
- my<img.height()?my:h2 - my - 1,
- mz<img.depth()?mz:d2 - mz - 1,
- (int)(mc<img.spectrum()?mc:s2 - mc - 1));
- }
- case 2 : // Periodic
- return (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,
- (int)cimg::mod(c,(double)img._spectrum));
- case 1 : // Neumann
- return (double)img._cubic_atXYZ((float)x,(float)y,(float)z,
- (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
- default : // Dirichlet
- if (c<0 || c>=img._spectrum) return (T)0;
- return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
- }
- case 1 : // Linear interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float
- w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
- mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
- mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
- return (double)img._linear_atXYZ(mx<img.width()?mx:w2 - mx - 1,
- my<img.height()?my:h2 - my - 1,
- mz<img.depth()?mz:d2 - mz - 1,
- (int)(mc<img.spectrum()?mc:s2 - mc - 1));
- }
- case 2 : // Periodic
- return (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,
- (int)cimg::mod(c,(double)img._spectrum));
- case 1 : // Neumann
- return (double)img._linear_atXYZ((float)x,(float)y,(float)z,
- (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
- default : // Dirichlet
- if (c<0 || c>=img._spectrum) return (T)0;
- return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
- }
- default : // Nearest neighbor interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const int
- w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
- mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
- mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
- return (double)img(mx<img.width()?mx:w2 - mx - 1,
- my<img.height()?my:h2 - my - 1,
- mz<img.depth()?mz:d2 - mz - 1,
- mc<img.spectrum()?mc:s2 - mc - 1);
- }
- case 2 : // Periodic
- return (double)img((int)cimg::mod(x,(double)img._width),
- (int)cimg::mod(y,(double)img._height),
- (int)cimg::mod(z,(double)img._depth),
- (int)cimg::mod(c,(double)img._spectrum));
- case 1 : // Neumann
- return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
- default : // Dirichlet
- return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
- }
- }
- }
- static double mp_kth(_cimg_math_parser& mp) {
- const unsigned int i_end = (unsigned int)mp.opcode[2];
- CImg<double> values;
- if (i_end==5) values.assign(&_mp_arg(3),(unsigned int)mp.opcode[4],1,1,1,true); // Only a single argument
- else {
- unsigned int siz = 0;
- for (unsigned int i = 4; i<i_end; i+=2) siz+=(unsigned int)mp.opcode[i];
- values.assign(siz);
- double *ptr = values;
- for (unsigned int i = 3; i<i_end; i+=2) {
- const unsigned int len = (unsigned int)mp.opcode[i + 1];
- if (len>1) std::memcpy(ptr,&_mp_arg(i),len*sizeof(double));
- else *ptr = _mp_arg(i);
- ptr+=len;
- }
- }
- longT ind = (longT)values[0];
- ++values._data; --values._width; // Skip first value
- if (ind<0) ind+=values.width() + 1;
- ind = cimg::cut(ind,(longT)1,(longT)values.width());
- const double &kth = values.kth_smallest((ulongT)(ind - 1));
- --values._data; ++values._width;
- return kth;
- }
- static double mp_lerp(_cimg_math_parser& mp) {
- const double t = _mp_arg(4);
- return _mp_arg(2)*(1-t) + _mp_arg(3)*t;
- }
- static double mp_linear_add(_cimg_math_parser& mp) {
- return _mp_arg(2)*_mp_arg(3) + _mp_arg(4);
- }
- static double mp_linear_sub_left(_cimg_math_parser& mp) {
- return _mp_arg(2)*_mp_arg(3) - _mp_arg(4);
- }
- static double mp_linear_sub_right(_cimg_math_parser& mp) {
- return _mp_arg(4) - _mp_arg(2)*_mp_arg(3);
- }
- static double mp_list_depth(_cimg_math_parser& mp) {
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- return (double)mp.imglist[ind]._depth;
- }
- static double mp_list_find(_cimg_math_parser& mp) {
- const unsigned int
- indi = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- const CImg<T> &img = mp.imglist[indi];
- const int _step = (int)_mp_arg(5), step = _step?_step:-1;
- const ulongT siz = (ulongT)img.size();
- longT ind = (longT)(mp.opcode[4]!=_cimg_mp_slot_nan?_mp_arg(4):step>0?0:siz - 1);
- if (ind<0 || ind>=(longT)siz) return -1.;
- const T
- *const ptrb = img.data(),
- *const ptre = img.end(),
- *ptr = ptrb + ind;
- const double val = _mp_arg(3);
- // Forward search
- if (step>0) {
- while (ptr<ptre && (double)*ptr!=val) ptr+=step;
- return ptr>=ptre?-1.:(double)(ptr - ptrb);
- }
- // Backward search.
- while (ptr>=ptrb && (double)*ptr!=val) ptr+=step;
- return ptr<ptrb?-1.:(double)(ptr - ptrb);
- }
- static double mp_list_find_seq(_cimg_math_parser& mp) {
- const unsigned int
- indi = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- const CImg<T> &img = mp.imglist[indi];
- const int _step = (int)_mp_arg(6), step = _step?_step:-1;
- const ulongT
- siz1 = (ulongT)img.size(),
- siz2 = (ulongT)mp.opcode[4];
- longT ind = (longT)(mp.opcode[5]!=_cimg_mp_slot_nan?_mp_arg(5):step>0?0:siz1 - 1);
- if (ind<0 || ind>=(longT)siz1) return -1.;
- const T
- *const ptr1b = img.data(),
- *const ptr1e = ptr1b + siz1,
- *ptr1 = ptr1b + ind,
- *p1 = 0;
- const double
- *const ptr2b = &_mp_arg(3) + 1,
- *const ptr2e = ptr2b + siz2,
- *p2 = 0;
- // Forward search.
- if (step>0) {
- do {
- while (ptr1<ptr1e && *ptr1!=*ptr2b) ptr1+=step;
- if (ptr1>=ptr1e) return -1.;
- p1 = ptr1 + 1;
- p2 = ptr2b + 1;
- while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
- } while (p2<ptr2e && (ptr1+=step)<ptr1e);
- return p2<ptr2e?-1.:(double)(ptr1 - ptr1b);
- }
- // Backward search.
- do {
- while (ptr1>=ptr1b && *ptr1!=*ptr2b) ptr1+=step;
- if (ptr1<ptr1b) return -1.;
- p1 = ptr1 + 1;
- p2 = ptr2b + 1;
- while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
- } while (p2<ptr2e && (ptr1+=step)>=ptr1b);
- return p2<ptr2e?-1.:(double)(ptr1 - ptr1b);
- }
- static double mp_list_height(_cimg_math_parser& mp) {
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- return (double)mp.imglist[ind]._height;
- }
- static double mp_list_ioff(_cimg_math_parser& mp) {
- const unsigned int
- ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()),
- boundary_conditions = (unsigned int)_mp_arg(4);
- const CImg<T> &img = mp.imglist[ind];
- const longT
- off = (longT)_mp_arg(3),
- whds = (longT)img.size();
- if (off>=0 && off<whds) return (double)img[off];
- if (img._data) switch (boundary_conditions) {
- case 3 : { // Mirror
- const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
- return (double)img[moff<whds?moff:whds2 - moff - 1];
- }
- case 2 : // Periodic
- return (double)img[cimg::mod(off,whds)];
- case 1 : // Neumann
- return (double)img[off<0?0:whds - 1];
- default : // Dirichlet
- return 0;
- }
- return 0;
- }
- static double mp_list_is_shared(_cimg_math_parser& mp) {
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- return (double)mp.imglist[ind]._is_shared;
- }
- static double mp_list_ixyzc(_cimg_math_parser& mp) {
- const unsigned int
- ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()),
- interpolation = (unsigned int)_mp_arg(7),
- boundary_conditions = (unsigned int)_mp_arg(8);
- const CImg<T> &img = mp.imglist[ind];
- const double
- x = _mp_arg(3), y = _mp_arg(4),
- z = _mp_arg(5), c = _mp_arg(6);
- switch (interpolation) {
- case 2 : // Cubic interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float
- w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
- mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
- mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
- return (double)img._cubic_atXYZ(mx<img.width()?mx:w2 - mx - 1,
- my<img.height()?my:h2 - my - 1,
- mz<img.depth()?mz:d2 - mz - 1,
- (int)(mc<img.spectrum()?mc:s2 - mc - 1));
- }
- case 2 : // Periodic
- return (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,
- (int)cimg::mod(c,(double)img._spectrum));
- case 1 : // Neumann
- return (double)img._cubic_atXYZ((float)x,(float)y,(float)z,
- (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
- default : // Dirichlet
- if (c<0 || c>=img._spectrum) return (T)0;
- return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
- }
- case 1 : // Linear interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float
- w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
- mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
- mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
- return (double)img._linear_atXYZ(mx<img.width()?mx:w2 - mx - 1,
- my<img.height()?my:h2 - my - 1,
- mz<img.depth()?mz:d2 - mz - 1,
- (int)(mc<img.spectrum()?mc:s2 - mc - 1));
- }
- case 2 : // Periodic
- return (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,
- (int)cimg::mod(c,(double)img._spectrum));
- case 1 : // Neumann
- return (double)img._linear_atXYZ((float)x,(float)y,(float)z,
- (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
- default : // Dirichlet
- if (c<0 || c>=img._spectrum) return (T)0;
- return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
- }
- default : // Nearest neighbor interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const int
- w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
- mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
- mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
- return (double)img(mx<img.width()?mx:w2 - mx - 1,
- my<img.height()?my:h2 - my - 1,
- mz<img.depth()?mz:d2 - mz - 1,
- mc<img.spectrum()?mc:s2 - mc - 1);
- }
- case 2 : // Periodic
- return (double)img((int)cimg::mod(x,(double)img._width),
- (int)cimg::mod(y,(double)img._height),
- (int)cimg::mod(z,(double)img._depth),
- (int)cimg::mod(c,(double)img._spectrum));
- case 1 : // Neumann
- return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
- default : // Dirichlet
- return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
- }
- }
- }
- static double mp_list_joff(_cimg_math_parser& mp) {
- const unsigned int
- ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()),
- boundary_conditions = (unsigned int)_mp_arg(4);
- const int
- ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
- oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
- const CImg<T> &img = mp.imglist[ind];
- const longT
- off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
- whds = (longT)img.size();
- if (off>=0 && off<whds) return (double)img[off];
- if (img._data) switch (boundary_conditions) {
- case 3 : { // Mirror
- const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
- return (double)img[moff<whds?moff:whds2 - moff - 1];
- }
- case 2 : // Periodic
- return (double)img[cimg::mod(off,whds)];
- case 1 : // Neumann
- return (double)img[off<0?0:whds - 1];
- default : // Dirichlet
- return 0;
- }
- return 0;
- }
- static double mp_list_jxyzc(_cimg_math_parser& mp) {
- const unsigned int
- ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()),
- interpolation = (unsigned int)_mp_arg(7),
- boundary_conditions = (unsigned int)_mp_arg(8);
- const CImg<T> &img = mp.imglist[ind];
- const double
- ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
- oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c],
- x = ox + _mp_arg(3), y = oy + _mp_arg(4),
- z = oz + _mp_arg(5), c = oc + _mp_arg(6);
- switch (interpolation) {
- case 2 : // Cubic interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float
- w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
- mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
- mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
- return (double)img._cubic_atXYZ(mx<img.width()?mx:w2 - mx - 1,
- my<img.height()?my:h2 - my - 1,
- mz<img.depth()?mz:d2 - mz - 1,
- (int)(mc<img.spectrum()?mc:s2 - mc - 1));
- }
- case 2 : // Periodic
- return (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,
- (int)cimg::mod(c,(double)img._spectrum));
- case 1 : // Neumann
- return (double)img._cubic_atXYZ((float)x,(float)y,(float)z,
- (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
- default : // Dirichlet
- if (c<0 || c>=img._spectrum) return (T)0;
- return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
- }
- case 1 : // Linear interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float
- w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
- mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
- mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
- return (double)img._linear_atXYZ(mx<img.width()?mx:w2 - mx - 1,
- my<img.height()?my:h2 - my - 1,
- mz<img.depth()?mz:d2 - mz - 1,
- (int)(mc<img.spectrum()?mc:s2 - mc - 1));
- }
- case 2 : // Periodic
- return (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,
- (int)cimg::mod(c,(double)img._spectrum));
- case 1 : // Neumann
- return (double)img._linear_atXYZ((float)x,(float)y,(float)z,
- (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
- default : // Dirichlet
- if (c<0 || c>=img._spectrum) return (T)0;
- return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
- }
- default : // Nearest neighbor interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const int
- w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
- mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
- mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
- return (double)img(mx<img.width()?mx:w2 - mx - 1,
- my<img.height()?my:h2 - my - 1,
- mz<img.depth()?mz:d2 - mz - 1,
- mc<img.spectrum()?mc:s2 - mc - 1);
- }
- case 2 : // Periodic
- return (double)img((int)cimg::mod(x,(double)img._width),
- (int)cimg::mod(y,(double)img._height),
- (int)cimg::mod(z,(double)img._depth),
- (int)cimg::mod(c,(double)img._spectrum));
- case 1 : // Neumann
- return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
- default : // Dirichlet
- return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
- }
- }
- }
- static double mp_list_l(_cimg_math_parser& mp) {
- return (double)mp.imglist.width();
- }
- static double mp_list_median(_cimg_math_parser& mp) {
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- if (!mp.list_median) mp.list_median.assign(mp.imglist._width);
- if (!mp.list_median[ind]) CImg<doubleT>::vector(mp.imglist[ind].median()).move_to(mp.list_median[ind]);
- return *mp.list_median[ind];
- }
- static double mp_list_norm(_cimg_math_parser& mp) {
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- if (!mp.list_norm) mp.list_norm.assign(mp.imglist._width);
- if (!mp.list_norm[ind]) CImg<doubleT>::vector(mp.imglist[ind].magnitude(2)).move_to(mp.list_norm[ind]);
- return *mp.list_norm[ind];
- }
- static double mp_list_id(_cimg_math_parser& mp) {
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- bool get_stats = false;
- cimg::mutex(13);
- if (!mp.list_stats || mp.list_stats.size()!=mp.imglist._width) mp.list_stats.assign(mp.imglist._width);
- if (!mp.list_stats[ind]) get_stats = true;
- cimg::mutex(13,0);
- if (get_stats) {
- CImg<Tdouble> st = mp.imglist[ind].get_stats();
- cimg::mutex(13);
- st.move_to(mp.list_stats[ind]);
- cimg::mutex(13,0);
- }
- return std::sqrt(mp.list_stats(ind,3));
- }
- static double mp_list_set_ioff(_cimg_math_parser& mp) {
- if (!mp.imglist.width()) return cimg::type<double>::nan();
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- CImg<T> &img = mp.imglist[ind];
- const longT
- off = (longT)_mp_arg(3),
- whds = (longT)img.size();
- const double val = _mp_arg(1);
- if (off>=0 && off<whds) img[off] = (T)val;
- return val;
- }
- static double mp_list_set_ixyzc(_cimg_math_parser& mp) {
- if (!mp.imglist.width()) return cimg::type<double>::nan();
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- CImg<T> &img = mp.imglist[ind];
- const int
- x = (int)_mp_arg(3), y = (int)_mp_arg(4),
- z = (int)_mp_arg(5), c = (int)_mp_arg(6);
- const double val = _mp_arg(1);
- if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
- z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
- img(x,y,z,c) = (T)val;
- return val;
- }
- static double mp_list_set_joff(_cimg_math_parser& mp) {
- if (!mp.imglist.width()) return cimg::type<double>::nan();
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- CImg<T> &img = mp.imglist[ind];
- const int
- ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
- oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
- const longT
- off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
- whds = (longT)img.size();
- const double val = _mp_arg(1);
- if (off>=0 && off<whds) img[off] = (T)val;
- return val;
- }
- static double mp_list_set_jxyzc(_cimg_math_parser& mp) {
- if (!mp.imglist.width()) return cimg::type<double>::nan();
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- CImg<T> &img = mp.imglist[ind];
- const double
- ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
- oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c];
- const int
- x = (int)(ox + _mp_arg(3)), y = (int)(oy + _mp_arg(4)),
- z = (int)(oz + _mp_arg(5)), c = (int)(oc + _mp_arg(6));
- const double val = _mp_arg(1);
- if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
- z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
- img(x,y,z,c) = (T)val;
- return val;
- }
- static double mp_list_set_Ioff_s(_cimg_math_parser& mp) {
- if (!mp.imglist.width()) return cimg::type<double>::nan();
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- CImg<T> &img = mp.imglist[ind];
- const longT
- off = (longT)_mp_arg(3),
- whd = (longT)img.width()*img.height()*img.depth();
- const T val = (T)_mp_arg(1);
- if (off>=0 && off<whd) {
- T *ptrd = &img[off];
- cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
- }
- return _mp_arg(1);
- }
- static double mp_list_set_Ioff_v(_cimg_math_parser& mp) {
- if (!mp.imglist.width()) return cimg::type<double>::nan();
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- CImg<T> &img = mp.imglist[ind];
- const longT
- off = (longT)_mp_arg(3),
- whd = (longT)img.width()*img.height()*img.depth();
- const double *ptrs = &_mp_arg(1) + 1;
- if (off>=0 && off<whd) {
- const unsigned int vsiz = (unsigned int)mp.opcode[4];
- T *ptrd = &img[off];
- cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
- }
- return cimg::type<double>::nan();
- }
- static double mp_list_set_Ixyz_s(_cimg_math_parser& mp) {
- if (!mp.imglist.width()) return cimg::type<double>::nan();
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- CImg<T> &img = mp.imglist[ind];
- const int
- x = (int)_mp_arg(3),
- y = (int)_mp_arg(4),
- z = (int)_mp_arg(5);
- const T val = (T)_mp_arg(1);
- if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
- T *ptrd = &img(x,y,z);
- const ulongT whd = (ulongT)img._width*img._height*img._depth;
- cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
- }
- return _mp_arg(1);
- }
- static double mp_list_set_Ixyz_v(_cimg_math_parser& mp) {
- if (!mp.imglist.width()) return cimg::type<double>::nan();
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- CImg<T> &img = mp.imglist[ind];
- const int
- x = (int)_mp_arg(3),
- y = (int)_mp_arg(4),
- z = (int)_mp_arg(5);
- const double *ptrs = &_mp_arg(1) + 1;
- if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
- const unsigned int vsiz = (unsigned int)mp.opcode[6];
- T *ptrd = &img(x,y,z);
- const ulongT whd = (ulongT)img._width*img._height*img._depth;
- cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
- }
- return cimg::type<double>::nan();
- }
- static double mp_list_set_Joff_s(_cimg_math_parser& mp) {
- if (!mp.imglist.width()) return cimg::type<double>::nan();
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- CImg<T> &img = mp.imglist[ind];
- const int
- ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
- oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
- const longT
- off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
- whd = (longT)img.width()*img.height()*img.depth();
- const T val = (T)_mp_arg(1);
- if (off>=0 && off<whd) {
- T *ptrd = &img[off];
- cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
- }
- return _mp_arg(1);
- }
- static double mp_list_set_Joff_v(_cimg_math_parser& mp) {
- if (!mp.imglist.width()) return cimg::type<double>::nan();
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- CImg<T> &img = mp.imglist[ind];
- const int
- ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
- oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
- const longT
- off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
- whd = (longT)img.width()*img.height()*img.depth();
- const double *ptrs = &_mp_arg(1) + 1;
- if (off>=0 && off<whd) {
- const unsigned int vsiz = (unsigned int)mp.opcode[4];
- T *ptrd = &img[off];
- cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
- }
- return cimg::type<double>::nan();
- }
- static double mp_list_set_Jxyz_s(_cimg_math_parser& mp) {
- if (!mp.imglist.width()) return cimg::type<double>::nan();
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- CImg<T> &img = mp.imglist[ind];
- const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
- const int
- x = (int)(ox + _mp_arg(3)),
- y = (int)(oy + _mp_arg(4)),
- z = (int)(oz + _mp_arg(5));
- const T val = (T)_mp_arg(1);
- if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
- T *ptrd = &img(x,y,z);
- const ulongT whd = (ulongT)img._width*img._height*img._depth;
- cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
- }
- return _mp_arg(1);
- }
- static double mp_list_set_Jxyz_v(_cimg_math_parser& mp) {
- if (!mp.imglist.width()) return cimg::type<double>::nan();
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- CImg<T> &img = mp.imglist[ind];
- const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
- const int
- x = (int)(ox + _mp_arg(3)),
- y = (int)(oy + _mp_arg(4)),
- z = (int)(oz + _mp_arg(5));
- const double *ptrs = &_mp_arg(1) + 1;
- if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
- const unsigned int vsiz = (unsigned int)mp.opcode[6];
- T *ptrd = &img(x,y,z);
- const ulongT whd = (ulongT)img._width*img._height*img._depth;
- cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
- }
- return cimg::type<double>::nan();
- }
- static double mp_list_spectrum(_cimg_math_parser& mp) {
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- return (double)mp.imglist[ind]._spectrum;
- }
- static double mp_list_stats(_cimg_math_parser& mp) {
- const unsigned int
- ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()),
- k = (unsigned int)mp.opcode[3];
- bool get_stats = false;
- cimg::mutex(13);
- if (!mp.list_stats || mp.list_stats.size()!=mp.imglist._width) mp.list_stats.assign(mp.imglist._width);
- if (!mp.list_stats[ind]) get_stats = true;
- cimg::mutex(13,0);
- if (get_stats) {
- CImg<Tdouble> st = mp.imglist[ind].get_stats();
- cimg::mutex(13);
- st.move_to(mp.list_stats[ind]);
- cimg::mutex(13,0);
- }
- return mp.list_stats(ind,k);
- }
- static double mp_list_wh(_cimg_math_parser& mp) {
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- return (double)mp.imglist[ind]._width*mp.imglist[ind]._height;
- }
- static double mp_list_whd(_cimg_math_parser& mp) {
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- return (double)mp.imglist[ind]._width*mp.imglist[ind]._height*mp.imglist[ind]._depth;
- }
- static double mp_list_whds(_cimg_math_parser& mp) {
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- return (double)mp.imglist[ind]._width*mp.imglist[ind]._height*mp.imglist[ind]._depth*mp.imglist[ind]._spectrum;
- }
- static double mp_list_width(_cimg_math_parser& mp) {
- const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- return (double)mp.imglist[ind]._width;
- }
- static double mp_list_Ioff(_cimg_math_parser& mp) {
- double *ptrd = &_mp_arg(1) + 1;
- const unsigned int
- ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()),
- boundary_conditions = (unsigned int)_mp_arg(4),
- vsiz = (unsigned int)mp.opcode[5];
- const CImg<T> &img = mp.imglist[ind];
- const longT
- off = (longT)_mp_arg(3),
- whd = (longT)img.width()*img.height()*img.depth();
- const T *ptrs;
- if (off>=0 && off<whd) {
- ptrs = &img[off];
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
- return cimg::type<double>::nan();
- }
- if (img._data) switch (boundary_conditions) {
- case 3 : { // Mirror
- const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
- ptrs = &img[moff<whd?moff:whd2 - moff - 1];
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
- return cimg::type<double>::nan();
- }
- case 2 : // Periodic
- ptrs = &img[cimg::mod(off,whd)];
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
- return cimg::type<double>::nan();
- case 1 : // Neumann
- ptrs = off<0?&img[0]:&img[whd - 1];
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
- return cimg::type<double>::nan();
- default : // Dirichlet
- std::memset(ptrd,0,vsiz*sizeof(double));
- return cimg::type<double>::nan();
- }
- std::memset(ptrd,0,vsiz*sizeof(double));
- return cimg::type<double>::nan();
- }
- static double mp_list_Ixyz(_cimg_math_parser& mp) {
- double *ptrd = &_mp_arg(1) + 1;
- const unsigned int
- ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()),
- interpolation = (unsigned int)_mp_arg(6),
- boundary_conditions = (unsigned int)_mp_arg(7),
- vsiz = (unsigned int)mp.opcode[8];
- const CImg<T> &img = mp.imglist[ind];
- const double x = _mp_arg(3), y = _mp_arg(4), z = _mp_arg(5);
- const ulongT whd = (ulongT)img._width*img._height*img._depth;
- const T *ptrs;
- switch (interpolation) {
- case 2 : // Cubic interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float
- w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
- mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
- cx = mx<img.width()?mx:w2 - mx - 1,
- cy = my<img.height()?my:h2 - my - 1,
- cz = mz<img.depth()?mz:d2 - mz - 1;
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ(cx,cy,cz,c);
- } break;
- case 2 : // Periodic
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,c);
- break;
- case 1 : // Neumann
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ((float)x,(float)y,(float)z,c);
- break;
- default : // Dirichlet
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.cubic_atXYZ((float)x,(float)y,(float)z,c,(T)0);
- } break;
- case 1 : // Linear interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float
- w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
- mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
- cx = mx<img.width()?mx:w2 - mx - 1,
- cy = my<img.height()?my:h2 - my - 1,
- cz = mz<img.depth()?mz:d2 - mz - 1;
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
- } break;
- case 2 : // Periodic
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,c);
- break;
- case 1 : // Neumann
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
- break;
- default : // Dirichlet
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
- } break;
- default : // Nearest neighbor interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const int
- w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
- mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
- cx = mx<img.width()?mx:w2 - mx - 1,
- cy = my<img.height()?my:h2 - my - 1,
- cz = mz<img.depth()?mz:d2 - mz - 1;
- ptrs = &img(cx,cy,cz);
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
- } break;
- case 2 : { // Periodic
- const int
- cx = (int)cimg::mod(x,(double)img._width),
- cy = (int)cimg::mod(y,(double)img._height),
- cz = (int)cimg::mod(z,(double)img._depth);
- ptrs = &img(cx,cy,cz);
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
- } break;
- case 1 : { // Neumann
- ptrs = &img._atXYZ((int)x,(int)y,(int)z);
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
- } break;
- default : // Dirichlet
- if (img.containsXYZC((int)x,(int)y,(int)z)) {
- ptrs = &img((int)x,(int)y,(int)z);
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
- } else std::memset(ptrd,0,vsiz*sizeof(double));
- }
- }
- return cimg::type<double>::nan();
- }
- static double mp_list_Joff(_cimg_math_parser& mp) {
- double *ptrd = &_mp_arg(1) + 1;
- const unsigned int
- ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()),
- boundary_conditions = (unsigned int)_mp_arg(4),
- vsiz = (unsigned int)mp.opcode[5];
- const int
- ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], oz = (int)mp.mem[_cimg_mp_slot_z];
- const CImg<T> &img = mp.imglist[ind];
- const longT
- off = img.offset(ox,oy,oz) + (longT)_mp_arg(3),
- whd = (longT)img.width()*img.height()*img.depth();
- const T *ptrs;
- if (off>=0 && off<whd) {
- ptrs = &img[off];
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
- return cimg::type<double>::nan();
- }
- if (img._data) switch (boundary_conditions) {
- case 3 : { // Mirror
- const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
- ptrs = &img[moff<whd?moff:whd2 - moff - 1];
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
- return cimg::type<double>::nan();
- }
- case 2 : // Periodic
- ptrs = &img[cimg::mod(off,whd)];
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
- return cimg::type<double>::nan();
- case 1 : // Neumann
- ptrs = off<0?&img[0]:&img[whd - 1];
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
- return cimg::type<double>::nan();
- default : // Dirichlet
- std::memset(ptrd,0,vsiz*sizeof(double));
- return cimg::type<double>::nan();
- }
- std::memset(ptrd,0,vsiz*sizeof(double));
- return cimg::type<double>::nan();
- }
- static double mp_list_Jxyz(_cimg_math_parser& mp) {
- double *ptrd = &_mp_arg(1) + 1;
- const unsigned int
- ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()),
- interpolation = (unsigned int)_mp_arg(6),
- boundary_conditions = (unsigned int)_mp_arg(7),
- vsiz = (unsigned int)mp.opcode[8];
- const CImg<T> &img = mp.imglist[ind];
- const double
- ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z],
- x = ox + _mp_arg(3), y = oy + _mp_arg(4), z = oz + _mp_arg(5);
- const ulongT whd = (ulongT)img._width*img._height*img._depth;
- const T *ptrs;
- switch (interpolation) {
- case 2 : // Cubic interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float
- w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
- mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
- cx = mx<img.width()?mx:w2 - mx - 1,
- cy = my<img.height()?my:h2 - my - 1,
- cz = mz<img.depth()?mz:d2 - mz - 1;
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ(cx,cy,cz,c);
- } break;
- case 2 : // Periodic
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,c);
- break;
- case 1 : // Neumann
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ((float)x,(float)y,(float)z,c);
- break;
- default : // Dirichlet
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.cubic_atXYZ((float)x,(float)y,(float)z,c,(T)0);
- } break;
- case 1 : // Linear interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float
- w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
- mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
- cx = mx<img.width()?mx:w2 - mx - 1,
- cy = my<img.height()?my:h2 - my - 1,
- cz = mz<img.depth()?mz:d2 - mz - 1;
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
- } break;
- case 2 : // Periodic
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,c);
- break;
- case 1 : // Neumann
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
- break;
- default : // Dirichlet
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
- } break;
- case 0 : // Nearest neighbor interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const int
- w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
- mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
- cx = mx<img.width()?mx:w2 - mx - 1,
- cy = my<img.height()?my:h2 - my - 1,
- cz = mz<img.depth()?mz:d2 - mz - 1;
- ptrs = &img(cx,cy,cz);
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
- } break;
- case 2 : { // Periodic
- const int
- cx = (int)cimg::mod(x,(double)img._width),
- cy = (int)cimg::mod(y,(double)img._height),
- cz = (int)cimg::mod(z,(double)img._depth);
- ptrs = &img(cx,cy,cz);
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
- } break;
- case 1 : { // Neumann
- ptrs = &img._atXYZ((int)x,(int)y,(int)z);
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
- } break;
- default : // Dirichlet
- if (img.containsXYZC((int)x,(int)y,(int)z)) {
- ptrs = &img((int)x,(int)y,(int)z);
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
- } else std::memset(ptrd,0,vsiz*sizeof(double));
- }
- }
- return cimg::type<double>::nan();
- }
- static double mp_log(_cimg_math_parser& mp) {
- return std::log(_mp_arg(2));
- }
- static double mp_log10(_cimg_math_parser& mp) {
- return std::log10(_mp_arg(2));
- }
- static double mp_log2(_cimg_math_parser& mp) {
- return cimg::log2(_mp_arg(2));
- }
- static double mp_logical_and(_cimg_math_parser& mp) {
- const bool val_left = (bool)_mp_arg(2);
- const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[4];
- if (!val_left) { mp.p_code = p_end - 1; return 0; }
- const ulongT mem_right = mp.opcode[3];
- for ( ; mp.p_code<p_end; ++mp.p_code) {
- mp.opcode._data = mp.p_code->_data;
- const ulongT target = mp.opcode[1];
- mp.mem[target] = _cimg_mp_defunc(mp);
- }
- --mp.p_code;
- return (double)(bool)mp.mem[mem_right];
- }
- static double mp_logical_not(_cimg_math_parser& mp) {
- return (double)!_mp_arg(2);
- }
- static double mp_logical_or(_cimg_math_parser& mp) {
- const bool val_left = (bool)_mp_arg(2);
- const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[4];
- if (val_left) { mp.p_code = p_end - 1; return 1; }
- const ulongT mem_right = mp.opcode[3];
- for ( ; mp.p_code<p_end; ++mp.p_code) {
- mp.opcode._data = mp.p_code->_data;
- const ulongT target = mp.opcode[1];
- mp.mem[target] = _cimg_mp_defunc(mp);
- }
- --mp.p_code;
- return (double)(bool)mp.mem[mem_right];
- }
- static double mp_lowercase(_cimg_math_parser& mp) {
- return cimg::lowercase(_mp_arg(2));
- }
- static double mp_lt(_cimg_math_parser& mp) {
- return (double)(_mp_arg(2)<_mp_arg(3));
- }
- static double mp_lte(_cimg_math_parser& mp) {
- return (double)(_mp_arg(2)<=_mp_arg(3));
- }
- static double mp_map(_cimg_math_parser& mp) {
- double *ptrd = &_mp_arg(1) + 1;
- const double
- *ptrX = &_mp_arg(2) + 1,
- *ptrP = &_mp_arg(3) + 1;
- const unsigned int
- sizX = (unsigned int)mp.opcode[4],
- sizP = (unsigned int)mp.opcode[5],
- nb_channelsX = (unsigned int)mp.opcode[6],
- nb_channelsP = (unsigned int)mp.opcode[7],
- boundary_conditions = (unsigned int)_mp_arg(8);
- CImg<doubleT>(ptrd,sizX/nb_channelsX,1,1,nb_channelsX*nb_channelsP,true) =
- CImg<doubleT>(ptrX,sizX/nb_channelsX,1,1,nb_channelsX,true).
- get_map(CImg<doubleT>(ptrP,sizP/nb_channelsP,1,1,nb_channelsP,true),boundary_conditions);
- return cimg::type<double>::nan();
- }
- static double mp_matrix_eig(_cimg_math_parser& mp) {
- double *ptrd = &_mp_arg(1) + 1;
- const double *ptr1 = &_mp_arg(2) + 1;
- const unsigned int k = (unsigned int)mp.opcode[3];
- CImg<doubleT> val, vec;
- CImg<doubleT>(ptr1,k,k,1,1,true).symmetric_eigen(val,vec);
- CImg<doubleT>(ptrd,1,k,1,1,true) = val;
- CImg<doubleT>(ptrd + k,k,k,1,1,true) = vec.get_transpose();
- return cimg::type<double>::nan();
- }
- static double mp_matrix_invert(_cimg_math_parser& mp) {
- double *const ptrd = &_mp_arg(1) + 1;
- const double *const ptr1 = &_mp_arg(2) + 1;
- const unsigned int
- w = (unsigned int)mp.opcode[3],
- h = (unsigned int)mp.opcode[4];
- const bool use_LU = (bool)_mp_arg(5);
- const float lambda = (float)_mp_arg(6);
- CImg<doubleT>(ptrd,h,w,1,1,true) = CImg<doubleT>(ptr1,w,h,1,1,true).get_invert(use_LU,lambda);
- return cimg::type<double>::nan();
- }
- static double mp_matrix_mul(_cimg_math_parser& mp) {
- double *ptrd = &_mp_arg(1) + 1;
- const double
- *ptr1 = &_mp_arg(2) + 1,
- *ptr2 = &_mp_arg(3) + 1;
- const unsigned int
- k = (unsigned int)mp.opcode[4],
- l = (unsigned int)mp.opcode[5],
- m = (unsigned int)mp.opcode[6];
- CImg<doubleT>(ptrd,m,k,1,1,true) = CImg<doubleT>(ptr1,l,k,1,1,true)*CImg<doubleT>(ptr2,m,l,1,1,true);
- return cimg::type<double>::nan();
- }
- static double mp_matrix_svd(_cimg_math_parser& mp) {
- double *ptrd = &_mp_arg(1) + 1;
- const double *ptr1 = &_mp_arg(2) + 1;
- const unsigned int
- k = (unsigned int)mp.opcode[3],
- l = (unsigned int)mp.opcode[4];
- CImg<doubleT> U, S, V;
- CImg<doubleT>(ptr1,k,l,1,1,true).SVD(U,S,V);
- CImg<doubleT>(ptrd,k,l,1,1,true) = U;
- CImg<doubleT>(ptrd + k*l,1,k,1,1,true) = S;
- CImg<doubleT>(ptrd + k*l + k,k,k,1,1,true) = V;
- return cimg::type<double>::nan();
- }
- static double mp_max(_cimg_math_parser& mp) {
- const unsigned int i_end = (unsigned int)mp.opcode[2];
- double val, valmax = -cimg::type<double>::inf();
- for (unsigned int i = 3; i<i_end; i+=2) {
- const unsigned int len = (unsigned int)mp.opcode[i + 1];
- if (len>1) {
- const double *ptr = &_mp_arg(i);
- for (unsigned int k = 0; k<len; ++k) { val = *(ptr++); if (val>valmax) valmax = val; }
- } else { val = _mp_arg(i); if (val>valmax) valmax = val; }
- }
- return valmax;
- }
- static double mp_maxabs(_cimg_math_parser& mp) {
- const unsigned int i_end = (unsigned int)mp.opcode[2];
- double val, abs_val, valmaxabs = 0, abs_valmaxabs = 0;
- for (unsigned int i = 3; i<i_end; i+=2) {
- const unsigned int len = (unsigned int)mp.opcode[i + 1];
- if (len>1) {
- const double *ptr = &_mp_arg(i);
- for (unsigned int k = 0; k<len; ++k) {
- val = *(ptr++);
- abs_val = cimg::abs(val);
- if (abs_val>abs_valmaxabs) { valmaxabs = val; abs_valmaxabs = abs_val; }
- }
- } else {
- val = _mp_arg(i);
- abs_val = cimg::abs(val);
- if (abs_val>abs_valmaxabs) { valmaxabs = val; abs_valmaxabs = abs_val; }
- }
- }
- return valmaxabs;
- }
- static double* _mp_memcopy_double(_cimg_math_parser& mp, const unsigned int ind, const ulongT *const p_ref,
- const longT siz, const long inc) {
- const longT
- off = *p_ref?p_ref[1] + (longT)mp.mem[(longT)p_ref[2]] + 1:ind,
- eoff = off + (siz - 1)*inc;
- if (off<0 || eoff>=mp.mem.width())
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': "
- "Out-of-bounds variable pointer "
- "(length: %ld, increment: %ld, offset start: %ld, "
- "offset end: %ld, offset max: %u).",
- mp.imgin.pixel_type(),siz,inc,off,eoff,mp.mem._width - 1);
- return &mp.mem[off];
- }
- static float* _mp_memcopy_float(_cimg_math_parser& mp, const ulongT *const p_ref,
- const longT siz, const long inc, const bool is_out) {
- const unsigned ind = (unsigned int)p_ref[1];
- const CImg<T> &img = is_out?
- (ind==~0U?mp.imgout:mp.imglist[cimg::mod((int)mp.mem[ind],mp.imglist.width())]):
- (ind==~0U?mp.imgin:mp.imglist[cimg::mod((int)mp.mem[ind],mp.imglist.width())]);
- const bool is_relative = (bool)p_ref[2];
- int ox, oy, oz, oc;
- longT off = 0;
- if (is_relative) {
- ox = (int)mp.mem[_cimg_mp_slot_x];
- oy = (int)mp.mem[_cimg_mp_slot_y];
- oz = (int)mp.mem[_cimg_mp_slot_z];
- oc = (int)mp.mem[_cimg_mp_slot_c];
- off = img.offset(ox,oy,oz,oc);
- }
- if ((*p_ref)%2) {
- const int
- x = (int)mp.mem[p_ref[3]],
- y = (int)mp.mem[p_ref[4]],
- z = (int)mp.mem[p_ref[5]],
- c = *p_ref==5?0:(int)mp.mem[p_ref[6]];
- off+=img.offset(x,y,z,c);
- } else off+=(longT)mp.mem[p_ref[3]];
- const longT eoff = off + (siz - 1)*inc;
- if (off<0 || eoff>=(longT)img.size())
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': "
- "Out-of-bounds image pointer "
- "(length: %ld, increment: %ld, offset start: %ld, "
- "offset end: %ld, offset max: %lu).",
- mp.imgin.pixel_type(),siz,inc,off,eoff,img.size() - 1);
- return (float*)&img[off];
- }
- static double mp_memcopy(_cimg_math_parser& mp) {
- longT siz = (longT)_mp_arg(4);
- const longT inc_d = (longT)_mp_arg(5), inc_s = (longT)_mp_arg(6);
- const float
- _opacity = (float)_mp_arg(7),
- opacity = (float)cimg::abs(_opacity),
- omopacity = 1 - std::max(_opacity,0.f);
- if (siz>0) {
- const bool
- is_doubled = mp.opcode[8]<=1,
- is_doubles = mp.opcode[15]<=1;
- if (is_doubled && is_doubles) { // (double*) <- (double*)
- double *ptrd = _mp_memcopy_double(mp,(unsigned int)mp.opcode[2],&mp.opcode[8],siz,inc_d);
- const double *ptrs = _mp_memcopy_double(mp,(unsigned int)mp.opcode[3],&mp.opcode[15],siz,inc_s);
- if (inc_d==1 && inc_s==1 && _opacity>=1) {
- if (ptrs + siz - 1<ptrd || ptrs>ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(double));
- else std::memmove(ptrd,ptrs,siz*sizeof(double));
- } else {
- if (ptrs + (siz - 1)*inc_s<ptrd || ptrs>ptrd + (siz - 1)*inc_d) {
- if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; }
- else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; }
- } else { // Overlapping buffers
- CImg<doubleT> buf((unsigned int)siz);
- cimg_for(buf,ptr,double) { *ptr = *ptrs; ptrs+=inc_s; }
- ptrs = buf;
- if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; }
- else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**(ptrs++); ptrd+=inc_d; }
- }
- }
- } else if (is_doubled && !is_doubles) { // (double*) <- (float*)
- double *ptrd = _mp_memcopy_double(mp,(unsigned int)mp.opcode[2],&mp.opcode[8],siz,inc_d);
- const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[15],siz,inc_s,false);
- if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; }
- else while (siz-->0) { *ptrd = omopacity**ptrd + _opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; }
- } else if (!is_doubled && is_doubles) { // (float*) <- (double*)
- float *ptrd = _mp_memcopy_float(mp,&mp.opcode[8],siz,inc_d,true);
- const double *ptrs = _mp_memcopy_double(mp,(unsigned int)mp.opcode[3],&mp.opcode[15],siz,inc_s);
- if (_opacity>=1) while (siz-->0) { *ptrd = (float)*ptrs; ptrd+=inc_d; ptrs+=inc_s; }
- else while (siz-->0) { *ptrd = (float)(omopacity**ptrd + opacity**ptrs); ptrd+=inc_d; ptrs+=inc_s; }
- } else { // (float*) <- (float*)
- float *ptrd = _mp_memcopy_float(mp,&mp.opcode[8],siz,inc_d,true);
- const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[15],siz,inc_s,false);
- if (inc_d==1 && inc_s==1 && _opacity>=1) {
- if (ptrs + siz - 1<ptrd || ptrs>ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(float));
- else std::memmove(ptrd,ptrs,siz*sizeof(float));
- } else {
- if (ptrs + (siz - 1)*inc_s<ptrd || ptrs>ptrd + (siz - 1)*inc_d) {
- if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; }
- else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; }
- } else { // Overlapping buffers
- CImg<floatT> buf((unsigned int)siz);
- cimg_for(buf,ptr,float) { *ptr = *ptrs; ptrs+=inc_s; }
- ptrs = buf;
- if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; }
- else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**(ptrs++); ptrd+=inc_d; }
- }
- }
- }
- }
- return _mp_arg(1);
- }
- static double mp_min(_cimg_math_parser& mp) {
- const unsigned int i_end = (unsigned int)mp.opcode[2];
- double val, valmin = cimg::type<double>::inf();
- for (unsigned int i = 3; i<i_end; i+=2) {
- const unsigned int len = (unsigned int)mp.opcode[i + 1];
- if (len>1) {
- const double *ptr = &_mp_arg(i);
- for (unsigned int k = 0; k<len; ++k) { val = *(ptr++); if (val<valmin) valmin = val; }
- } else { val = _mp_arg(i); if (val<valmin) valmin = val; }
- }
- return valmin;
- }
- static double mp_minabs(_cimg_math_parser& mp) {
- const unsigned int i_end = (unsigned int)mp.opcode[2];
- double val, abs_val, valminabs = cimg::type<double>::inf(), abs_valminabs = cimg::type<double>::inf();
- for (unsigned int i = 3; i<i_end; i+=2) {
- const unsigned int len = (unsigned int)mp.opcode[i + 1];
- if (len>1) {
- const double *ptr = &_mp_arg(i);
- for (unsigned int k = 0; k<len; ++k) {
- val = *(ptr++);
- abs_val = cimg::abs(val);
- if (abs_val<abs_valminabs) { valminabs = val; abs_valminabs = abs_val; }
- }
- } else {
- val = _mp_arg(i);
- abs_val = cimg::abs(val);
- if (abs_val<abs_valminabs) { valminabs = val; abs_valminabs = abs_val; }
- }
- }
- return valminabs;
- }
- static double mp_minus(_cimg_math_parser& mp) {
- return -_mp_arg(2);
- }
- static double mp_med(_cimg_math_parser& mp) {
- const unsigned int i_end = (unsigned int)mp.opcode[2];
- CImg<double> values;
- if (i_end==5) { // Only a single argument
- if ((unsigned)mp.opcode[4]==1) return _mp_arg(3); // Real value
- else values.assign(&_mp_arg(3),(unsigned int)mp.opcode[4],1,1,1,true); // Vector value
- } else {
- unsigned int siz = 0;
- for (unsigned int i = 4; i<i_end; i+=2) siz+=(unsigned int)mp.opcode[i];
- values.assign(siz);
- double *ptr = values;
- for (unsigned int i = 3; i<i_end; i+=2) {
- const unsigned int len = (unsigned int)mp.opcode[i + 1];
- if (len>1) std::memcpy(ptr,&_mp_arg(i),len*sizeof(double));
- else *ptr = _mp_arg(i);
- ptr+=len;
- }
- }
- return values.median();
- }
- static double mp_modulo(_cimg_math_parser& mp) {
- return cimg::mod(_mp_arg(2),_mp_arg(3));
- }
- static double mp_mproj(_cimg_math_parser& mp) {
- double *ptrd = &_mp_arg(1) + 1;
- const double
- *ptrS = &_mp_arg(2) + 1,
- *ptrD = &_mp_arg(5) + 1;
- const unsigned int
- wS = (unsigned int)mp.opcode[3],
- hS = (unsigned int)mp.opcode[4],
- wD = (unsigned int)mp.opcode[6];
- const int
- method = std::max(0,(int)_mp_arg(7)),
- max_iter = std::max(0,(int)_mp_arg(8));
- const double
- max_residual = std::max(0.,_mp_arg(9));
- CImg<doubleT>(ptrd,wS,wD,1,1,true) = CImg<doubleT>(ptrS,wS,hS,1,1,false).
- project_matrix(CImg<doubleT>(ptrD,wD,hS,1,1,true),method,max_iter,max_residual);
- return cimg::type<double>::nan();
- }
- static double mp_mse(_cimg_math_parser& mp) {
- const unsigned int
- _siz = (unsigned int)mp.opcode[4],
- siz = std::max(_siz,1U),
- off = _siz?1:0;
- return CImg<doubleT>(&_mp_arg(2) + off,1,siz,1,1,true).
- MSE(CImg<doubleT>(&_mp_arg(3) + off,1,siz,1,1,true));
- }
- static double mp_mul(_cimg_math_parser& mp) {
- return _mp_arg(2)*_mp_arg(3);
- }
- static double mp_mul2(_cimg_math_parser& mp) {
- return _mp_arg(2)*_mp_arg(3)*_mp_arg(4);
- }
- static double mp_neq(_cimg_math_parser& mp) {
- return (double)(_mp_arg(2)!=_mp_arg(3));
- }
- static double mp_o2c(_cimg_math_parser& mp) {
- mp_check_list(mp,"o2c");
- unsigned int ind = (unsigned int)mp.opcode[2];
- if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
- const CImg<T> &img = ind==~0U?mp.imgin:mp.imglist[ind];
- longT offset = (longT)_mp_arg(3);
- double *ptrd = &_mp_arg(1) + 1;
- if (!img)
- ptrd[0] = ptrd[1] = ptrd[2] = ptrd[3] = cimg::type<double>::nan();
- else {
- *(ptrd++) = (double)(offset%img.width());
- offset/=img.width();
- *(ptrd++) = (double)(offset%img.height());
- offset/=img.height();
- *(ptrd++) = (double)(offset%img.depth());
- offset/=img.depth();
- *ptrd = (double)(offset%img.spectrum());
- }
- return cimg::type<double>::nan();
- }
- static double mp_permutations(_cimg_math_parser& mp) {
- return cimg::permutations((int)_mp_arg(2),(int)_mp_arg(3),(bool)_mp_arg(4));
- }
- static double mp_polygon(_cimg_math_parser& mp) {
- const unsigned int i_end = (unsigned int)mp.opcode[2];
- unsigned int ind = (unsigned int)mp.opcode[3];
- if (ind!=~0U) {
- if (!mp.imglist.width()) return cimg::type<double>::nan();
- ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.imglist.width());
- }
- CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
- bool is_invalid_arguments = i_end<=4, is_outlined = false;
- if (!is_invalid_arguments) {
- int nbv = (int)_mp_arg(4);
- if (!nbv) is_invalid_arguments = true;
- else {
- if (nbv<0) { nbv = -nbv; is_outlined = true; }
- CImg<intT> points(nbv,2,1,1,0);
- CImg<T> color(img._spectrum,1,1,1,0);
- float opacity = 1;
- unsigned int i = 5, pattern=~0U;
- cimg_foroff(points,k) if (i<i_end) points(k/2,k%2) = (int)cimg::round(_mp_arg(i++));
- else { is_invalid_arguments = true; break; }
- if (!is_invalid_arguments) {
- if (i<i_end) opacity = (float)_mp_arg(i++);
- if (is_outlined && i<i_end) pattern = (unsigned int)_mp_arg(i++);
- cimg_forX(color,k) if (i<i_end) color[k] = (T)_mp_arg(i++);
- else { color.resize(k,1,1,1,-1); break; }
- color.resize(img._spectrum,1,1,1,0,2);
- if (is_outlined) img.draw_polygon(points,color._data,opacity,pattern);
- else img.draw_polygon(points,color._data,opacity);
- }
- }
- }
- if (is_invalid_arguments) {
- CImg<doubleT> args(i_end - 4);
- cimg_forX(args,k) args[k] = _mp_arg(4 + k);
- if (ind==~0U)
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'polygon()': "
- "Invalid arguments '%s'. ",
- mp.imgin.pixel_type(),args.value_string()._data);
- else
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'polygon()': "
- "Invalid arguments '#%u%s%s'. ",
- mp.imgin.pixel_type(),ind,args._width?",":"",args.value_string()._data);
- }
- return cimg::type<double>::nan();
- }
- static double mp_pow(_cimg_math_parser& mp) {
- const double v = _mp_arg(2), p = _mp_arg(3);
- return std::pow(v,p);
- }
- static double mp_pow0_25(_cimg_math_parser& mp) {
- const double val = _mp_arg(2);
- return std::sqrt(std::sqrt(val));
- }
- static double mp_pow3(_cimg_math_parser& mp) {
- const double val = _mp_arg(2);
- return val*val*val;
- }
- static double mp_pow4(_cimg_math_parser& mp) {
- const double val = _mp_arg(2);
- return val*val*val*val;
- }
- static double mp_print(_cimg_math_parser& mp) {
- const double val = _mp_arg(1);
- const bool print_char = (bool)mp.opcode[3];
- cimg_pragma_openmp(critical(mp_print))
- {
- CImg<charT> _expr(mp.opcode[2] - 4);
- const ulongT *ptrs = mp.opcode._data + 4;
- cimg_for(_expr,ptrd,char) *ptrd = (char)*(ptrs++);
- cimg::strellipsize(_expr);
- cimg::mutex(6);
- if (print_char)
- std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %.17g = '%c'",
- _expr._data,val,(int)val);
- else
- std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %.17g",
- _expr._data,val);
- std::fflush(cimg::output());
- cimg::mutex(6,0);
- }
- return val;
- }
- static double mp_prod(_cimg_math_parser& mp) {
- const unsigned int i_end = (unsigned int)mp.opcode[2];
- double prod = 1;
- for (unsigned int i = 3; i<i_end; i+=2) {
- const unsigned int len = (unsigned int)mp.opcode[i + 1];
- if (len>1) {
- const double *ptr = &_mp_arg(i);
- for (unsigned int k = 0; k<len; ++k) prod*=*(ptr++);
- } else prod*=_mp_arg(i);
- }
- return prod;
- }
- static double mp_rad2deg(_cimg_math_parser& mp) {
- return _mp_arg(2)*180/cimg::PI;
- }
- static double mp_repeat(_cimg_math_parser& mp) {
- const double nb_it = _mp_arg(2), nb_itm1 = nb_it - 1;
- double
- *const ptrc = mp.opcode[3]!=~0U?&_mp_arg(3):0,
- *const ptrs = &_mp_arg(1);
- const CImg<ulongT>
- *const p_body = ++mp.p_code,
- *const p_end = p_body + mp.opcode[4];
- if (nb_it>=1) {
- const unsigned int _break_type = mp.break_type;
- mp.break_type = 0;
- double it = 0;
- if (ptrc) { // Version with loop variable (3 arguments)
- while (it<=nb_itm1) {
- *ptrc = it;
- for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
- mp.opcode._data = mp.p_code->_data;
- const ulongT target = mp.opcode[1];
- mp.mem[target] = _cimg_mp_defunc(mp);
- }
- if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
- ++it;
- }
- *ptrc = it;
- } else // Version without loop variable (2 arguments)
- while (it<=nb_itm1) {
- for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
- mp.opcode._data = mp.p_code->_data;
- const ulongT target = mp.opcode[1];
- mp.mem[target] = _cimg_mp_defunc(mp);
- }
- if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
- ++it;
- }
- mp.break_type = _break_type;
- }
- mp.p_code = p_end - 1;
- return *ptrs;
- }
- static double mp_rol(_cimg_math_parser& mp) {
- return cimg::rol(_mp_arg(2),(unsigned int)_mp_arg(3));
- }
- static double mp_ror(_cimg_math_parser& mp) {
- return cimg::ror(_mp_arg(2),(unsigned int)_mp_arg(3));
- }
- static double mp_rot2d(_cimg_math_parser& mp) {
- double *ptrd = &_mp_arg(1) + 1;
- const float
- theta = (float)_mp_arg(2),
- ca = std::cos(theta),
- sa = std::sin(theta);
- *(ptrd++) = ca;
- *(ptrd++) = -sa;
- *(ptrd++) = sa;
- *ptrd = ca;
- return cimg::type<double>::nan();
- }
- static double mp_rot3d(_cimg_math_parser& mp) {
- double *ptrd = &_mp_arg(1) + 1;
- const float
- x = (float)_mp_arg(2),
- y = (float)_mp_arg(3),
- z = (float)_mp_arg(4),
- theta = (float)_mp_arg(5);
- CImg<doubleT>(ptrd,3,3,1,1,true) = CImg<doubleT>::rotation_matrix(x,y,z,theta*180/cimg::PI);
- return cimg::type<double>::nan();
- }
- static double mp_round(_cimg_math_parser& mp) {
- return cimg::round(_mp_arg(2),_mp_arg(3),(int)_mp_arg(4));
- }
- #ifdef cimg_mp_func_run
- static double mp_run(_cimg_math_parser& mp) {
- const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2;
- CImgList<charT> _str;
- CImg<charT> it;
- for (unsigned int n = 0; n<nb_args; ++n) {
- const unsigned int siz = (unsigned int)mp.opcode[4 + 2*n];
- if (siz) { // Vector argument -> string
- const double *ptr = &_mp_arg(3 + 2*n) + 1;
- unsigned int l = 0;
- while (l<siz && ptr[l]) ++l;
- CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str);
- } else { // Scalar argument -> number
- it.assign(24);
- cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n));
- CImg<charT>::string(it,false,true).move_to(_str);
- }
- }
- CImg(1,1,1,1,0).move_to(_str);
- CImg<charT> str = _str>'x';
- #if cimg_use_openmp==0
- const unsigned int n_thread = 0;
- #else
- const unsigned int n_thread = omp_get_thread_num();
- #endif
- cimg_mp_func_run(str._data,n_thread && mp.is_noncritical_run);
- return cimg::type<double>::nan();
- }
- #endif
- static double mp_self_add(_cimg_math_parser& mp) {
- return _mp_arg(1)+=_mp_arg(2);
- }
- static double mp_self_bitwise_and(_cimg_math_parser& mp) {
- double &val = _mp_arg(1);
- return val = (double)((longT)val & (longT)_mp_arg(2));
- }
- static double mp_self_bitwise_left_shift(_cimg_math_parser& mp) {
- double &val = _mp_arg(1);
- return val = (double)((longT)val<<(unsigned int)_mp_arg(2));
- }
- static double mp_self_bitwise_or(_cimg_math_parser& mp) {
- double &val = _mp_arg(1);
- return val = (double)((longT)val | (longT)_mp_arg(2));
- }
- static double mp_self_bitwise_right_shift(_cimg_math_parser& mp) {
- double &val = _mp_arg(1);
- return val = (double)((longT)val>>(unsigned int)_mp_arg(2));
- }
- static double mp_self_decrement(_cimg_math_parser& mp) {
- return --_mp_arg(1);
- }
- static double mp_self_increment(_cimg_math_parser& mp) {
- return ++_mp_arg(1);
- }
- static double mp_self_map_vector_s(_cimg_math_parser& mp) { // Vector += scalar
- unsigned int
- ptrd = (unsigned int)mp.opcode[1] + 1,
- siz = (unsigned int)mp.opcode[2];
- mp_func op = (mp_func)mp.opcode[3];
- CImg<ulongT> l_opcode(1,3);
- l_opcode[2] = mp.opcode[4]; // Scalar argument
- l_opcode.swap(mp.opcode);
- ulongT &target = mp.opcode[1];
- while (siz-->0) { target = ptrd++; (*op)(mp); }
- l_opcode.swap(mp.opcode);
- return cimg::type<double>::nan();
- }
- static double mp_self_map_vector_v(_cimg_math_parser& mp) { // Vector += vector
- unsigned int
- ptrd = (unsigned int)mp.opcode[1] + 1,
- siz = (unsigned int)mp.opcode[2],
- ptrs = (unsigned int)mp.opcode[4] + 1;
- mp_func op = (mp_func)mp.opcode[3];
- CImg<ulongT> l_opcode(1,4);
- l_opcode.swap(mp.opcode);
- ulongT &target = mp.opcode[1], &argument = mp.opcode[2];
- while (siz-->0) { target = ptrd++; argument = ptrs++; (*op)(mp); }
- l_opcode.swap(mp.opcode);
- return cimg::type<double>::nan();
- }
- static double mp_self_mul(_cimg_math_parser& mp) {
- return _mp_arg(1)*=_mp_arg(2);
- }
- static double mp_self_div(_cimg_math_parser& mp) {
- return _mp_arg(1)/=_mp_arg(2);
- }
- static double mp_self_modulo(_cimg_math_parser& mp) {
- double &val = _mp_arg(1);
- return val = cimg::mod(val,_mp_arg(2));
- }
- static double mp_self_pow(_cimg_math_parser& mp) {
- double &val = _mp_arg(1);
- return val = std::pow(val,_mp_arg(2));
- }
- static double mp_self_sub(_cimg_math_parser& mp) {
- return _mp_arg(1)-=_mp_arg(2);
- }
- #ifdef cimg_mp_func_set
- static double mp_set(_cimg_math_parser& mp) {
- const double *ptrs = &_mp_arg(1);
- double *ptrd = &_mp_arg(3) + 1;
- const unsigned int
- sizs = (unsigned int)mp.opcode[2],
- sizd = (unsigned int)mp.opcode[4];
- CImg<charT> sd(sizd + 1);
- cimg_for_inX(sd,0,sd.width() - 1,i) sd[i] = (char)ptrd[i];
- sd.back() = 0;
- if (sizs) cimg_mp_func_set(ptrs + 1,sizs,sd._data);
- else cimg_mp_func_set(ptrs,0,sd._data);
- return *ptrs;
- }
- #endif
- static double mp_set_ioff(_cimg_math_parser& mp) {
- CImg<T> &img = mp.imgout;
- const longT
- off = (longT)_mp_arg(2),
- whds = (longT)img.size();
- const double val = _mp_arg(1);
- if (off>=0 && off<whds) img[off] = (T)val;
- return val;
- }
- static double mp_set_ixyzc(_cimg_math_parser& mp) {
- CImg<T> &img = mp.imgout;
- const int
- x = (int)_mp_arg(2), y = (int)_mp_arg(3),
- z = (int)_mp_arg(4), c = (int)_mp_arg(5);
- const double val = _mp_arg(1);
- if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
- z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
- img(x,y,z,c) = (T)val;
- return val;
- }
- static double mp_set_joff(_cimg_math_parser& mp) {
- CImg<T> &img = mp.imgout;
- const int
- ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
- oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
- const longT
- off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
- whds = (longT)img.size();
- const double val = _mp_arg(1);
- if (off>=0 && off<whds) img[off] = (T)val;
- return val;
- }
- static double mp_set_jxyzc(_cimg_math_parser& mp) {
- CImg<T> &img = mp.imgout;
- const double
- ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
- oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c];
- const int
- x = (int)(ox + _mp_arg(2)), y = (int)(oy + _mp_arg(3)),
- z = (int)(oz + _mp_arg(4)), c = (int)(oc + _mp_arg(5));
- const double val = _mp_arg(1);
- if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
- z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
- img(x,y,z,c) = (T)val;
- return val;
- }
- static double mp_set_Ioff_s(_cimg_math_parser& mp) {
- CImg<T> &img = mp.imgout;
- const longT
- off = (longT)_mp_arg(2),
- whd = (longT)img.width()*img.height()*img.depth();
- const T val = (T)_mp_arg(1);
- if (off>=0 && off<whd) {
- T *ptrd = &img[off];
- cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
- }
- return _mp_arg(1);
- }
- static double mp_set_Ioff_v(_cimg_math_parser& mp) {
- CImg<T> &img = mp.imgout;
- const longT
- off = (longT)_mp_arg(2),
- whd = (longT)img.width()*img.height()*img.depth();
- const double *ptrs = &_mp_arg(1) + 1;
- if (off>=0 && off<whd) {
- const unsigned int vsiz = (unsigned int)mp.opcode[3];
- T *ptrd = &img[off];
- cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
- }
- return cimg::type<double>::nan();
- }
- static double mp_set_Ixyz_s(_cimg_math_parser& mp) {
- CImg<T> &img = mp.imgout;
- const int
- x = (int)_mp_arg(2),
- y = (int)_mp_arg(3),
- z = (int)_mp_arg(4);
- const T val = (T)_mp_arg(1);
- if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
- T *ptrd = &img(x,y,z);
- const ulongT whd = (ulongT)img._width*img._height*img._depth;
- cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
- }
- return _mp_arg(1);
- }
- static double mp_set_Ixyz_v(_cimg_math_parser& mp) {
- CImg<T> &img = mp.imgout;
- const int
- x = (int)_mp_arg(2),
- y = (int)_mp_arg(3),
- z = (int)_mp_arg(4);
- const double *ptrs = &_mp_arg(1) + 1;
- if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
- const unsigned int vsiz = (unsigned int)mp.opcode[5];
- T *ptrd = &img(x,y,z);
- const ulongT whd = (ulongT)img._width*img._height*img._depth;
- cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
- }
- return cimg::type<double>::nan();
- }
- static double mp_set_Joff_s(_cimg_math_parser& mp) {
- CImg<T> &img = mp.imgout;
- const int
- ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
- oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
- const longT
- off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
- whd = (longT)img.width()*img.height()*img.depth();
- const T val = (T)_mp_arg(1);
- if (off>=0 && off<whd) {
- T *ptrd = &img[off];
- cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
- }
- return _mp_arg(1);
- }
- static double mp_set_Joff_v(_cimg_math_parser& mp) {
- CImg<T> &img = mp.imgout;
- const int
- ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
- oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
- const longT
- off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
- whd = (longT)img.width()*img.height()*img.depth();
- const double *ptrs = &_mp_arg(1) + 1;
- if (off>=0 && off<whd) {
- const unsigned int vsiz = (unsigned int)mp.opcode[3];
- T *ptrd = &img[off];
- cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
- }
- return cimg::type<double>::nan();
- }
- static double mp_set_Jxyz_s(_cimg_math_parser& mp) {
- CImg<T> &img = mp.imgout;
- const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
- const int
- x = (int)(ox + _mp_arg(2)),
- y = (int)(oy + _mp_arg(3)),
- z = (int)(oz + _mp_arg(4));
- const T val = (T)_mp_arg(1);
- if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
- T *ptrd = &img(x,y,z);
- const ulongT whd = (ulongT)img._width*img._height*img._depth;
- cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
- }
- return _mp_arg(1);
- }
- static double mp_set_Jxyz_v(_cimg_math_parser& mp) {
- CImg<T> &img = mp.imgout;
- const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
- const int
- x = (int)(ox + _mp_arg(2)),
- y = (int)(oy + _mp_arg(3)),
- z = (int)(oz + _mp_arg(4));
- const double *ptrs = &_mp_arg(1) + 1;
- if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
- const unsigned int vsiz = (unsigned int)mp.opcode[5];
- T *ptrd = &img(x,y,z);
- const ulongT whd = (ulongT)img._width*img._height*img._depth;
- cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
- }
- return cimg::type<double>::nan();
- }
- static double mp_shift(_cimg_math_parser& mp) {
- double *const ptrd = &_mp_arg(1) + 1;
- const double *const ptrs = &_mp_arg(2) + 1;
- const unsigned int siz = (unsigned int)mp.opcode[3];
- const int
- shift = (int)_mp_arg(4),
- boundary_conditions = (int)_mp_arg(5);
- CImg<doubleT>(ptrd,siz,1,1,1,true) = CImg<doubleT>(ptrs,siz,1,1,1,true).shift(shift,0,0,0,boundary_conditions);
- return cimg::type<double>::nan();
- }
- static double mp_sign(_cimg_math_parser& mp) {
- return cimg::sign(_mp_arg(2));
- }
- static double mp_sin(_cimg_math_parser& mp) {
- return std::sin(_mp_arg(2));
- }
- static double mp_sinc(_cimg_math_parser& mp) {
- return cimg::sinc(_mp_arg(2));
- }
- static double mp_sinh(_cimg_math_parser& mp) {
- return std::sinh(_mp_arg(2));
- }
- static double mp_solve(_cimg_math_parser& mp) {
- double *ptrd = &_mp_arg(1) + 1;
- const double
- *ptr1 = &_mp_arg(2) + 1,
- *ptr2 = &_mp_arg(3) + 1;
- const unsigned int
- k = (unsigned int)mp.opcode[4],
- l = (unsigned int)mp.opcode[5],
- m = (unsigned int)mp.opcode[6];
- const bool use_LU = (bool)_mp_arg(7);
- CImg<doubleT>(ptrd,m,k,1,1,true) = CImg<doubleT>(ptr2,m,l,1,1,false).
- solve(CImg<doubleT>(ptr1,k,l,1,1,true),use_LU);
- return cimg::type<double>::nan();
- }
- static double mp_sort(_cimg_math_parser& mp) {
- double *const ptrd = &_mp_arg(1) + 1;
- const double *const ptrs = &_mp_arg(2) + 1;
- const bool is_increasing = (bool)_mp_arg(4);
- const unsigned int
- siz = (unsigned int)mp.opcode[3],
- nb_elts = mp.opcode[5]==~0U?siz:(unsigned int)_mp_arg(5),
- siz_elt = (unsigned int)_mp_arg(6);
- const ulongT sn = siz_elt*nb_elts;
- if (sn>siz || siz_elt<1)
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'sort()': "
- "Arguments 'nb_elts=%g' and 'siz_elt=%g' are invalid "
- "for sorting a vector of size %u.",
- mp.imgin.pixel_type(),_mp_arg(5),_mp_arg(6),siz);
- CImg<doubleT>(ptrd,siz_elt,nb_elts,1,1,true) = CImg<doubleT>(ptrs,siz_elt,nb_elts,1,1,true).
- get_sort(is_increasing,siz_elt>1?'y':0);
- if (sn<siz) CImg<doubleT>(ptrd + sn,siz - sn,1,1,1,true) = CImg<doubleT>(ptrs + sn,siz - sn,1,1,1,true);
- return cimg::type<double>::nan();
- }
- static double mp_sqr(_cimg_math_parser& mp) {
- return cimg::sqr(_mp_arg(2));
- }
- static double mp_sqrt(_cimg_math_parser& mp) {
- return std::sqrt(_mp_arg(2));
- }
- static double mp_srand(_cimg_math_parser& mp) {
- mp.rng = (cimg_uint64)_mp_arg(2);
- return cimg::type<double>::nan();
- }
- static double mp_srand0(_cimg_math_parser& mp) {
- cimg::srand(&mp.rng);
- #if cimg_use_openmp!=0
- mp.rng+=omp_get_thread_num();
- #endif
- return cimg::type<double>::nan();
- }
- static double mp_std(_cimg_math_parser& mp) {
- return std::sqrt(mp_var(mp));
- }
- static double mp_string_init(_cimg_math_parser& mp) {
- const unsigned char *ptrs = (unsigned char*)&mp.opcode[3];
- unsigned int
- ptrd = (unsigned int)mp.opcode[1] + 1,
- siz = (unsigned int)mp.opcode[2];
- while (siz-->0) mp.mem[ptrd++] = (double)*(ptrs++);
- return cimg::type<double>::nan();
- }
- #ifdef cimg_mp_func_store
- static double mp_store(_cimg_math_parser& mp) {
- const double
- *ptr1 = &_mp_arg(2),
- *ptr2 = &_mp_arg(4) + 1;
- const unsigned int
- siz1 = (unsigned int)mp.opcode[3],
- siz2 = (unsigned int)mp.opcode[5];
- const int
- w = (int)_mp_arg(6),
- h = (int)_mp_arg(7),
- d = (int)_mp_arg(8),
- s = (int)_mp_arg(9);
- const bool is_compressed = (bool)_mp_arg(10);
- if (w<0 || h<0 || d<0 || s<0)
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'store()': "
- "Specified image dimensions (%d,%d,%d,%d) are invalid.",
- pixel_type(),w,h,d,s);
- CImg<charT> ss(siz2 + 1);
- cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptr2[i];
- ss.back() = 0;
- if (siz1) cimg_mp_func_store(ptr1 + 1,siz1,
- (unsigned int)w,(unsigned int)h,(unsigned int)d,(unsigned int)s,
- is_compressed,ss._data);
- else cimg_mp_func_store(ptr1,1,(unsigned int)w,(unsigned int)h,(unsigned int)d,(unsigned int)s,
- is_compressed,ss._data);
- return cimg::type<double>::nan();
- }
- #endif
- static double mp_s2v(_cimg_math_parser& mp) {
- const double *ptrs = &_mp_arg(2);
- const ulongT siz = (ulongT)mp.opcode[3];
- longT ind = (longT)_mp_arg(4);
- const bool is_strict = (bool)_mp_arg(5);
- double val = cimg::type<double>::nan();
- if (ind<0 || ind>=(longT)siz) return val;
- if (!siz) return *ptrs>='0' && *ptrs<='9'?*ptrs - '0':val;
- CImg<charT> ss(siz + 1 - ind);
- ptrs+=1 + ind;
- cimg_forX(ss,i) ss[i] = (char)ptrs[i];
- ss.back() = 0;
- const char *s = ss._data;
- while (*s && *s<=32) ++s;
- const bool is_negative = *s=='-';
- if (is_negative || *s=='+') ++s;
- int err = 0;
- char sep;
- if (*s=='0' && (s[1]=='x' || s[1]=='X') && s[2]>32) { // Hexadecimal number
- unsigned int ival;
- err = cimg_sscanf(s + 2,"%x%c",&ival,&sep);
- if (err>0) val = (double)ival;
- } else if (*s>32) { // Decimal number
- err = cimg_sscanf(s,"%lf%c",&val,&sep);
- #if cimg_OS==2
- // Check for +/-NaN and +/-inf as Microsoft's sscanf() version is not able
- // to read those particular values.
- if (!err && (*s=='i' || *s=='I' || *s=='n' || *s=='N')) {
- if (!cimg::strncasecmp(s,"inf",3)) { val = cimg::type<double>::inf(); err = 1 + (s[3]!=0); }
- else if (!cimg::strncasecmp(s,"nan",3)) { val = cimg::type<double>::nan(); err = 1 + (s[3]!=0); }
- }
- #endif
- }
- if (err<=0 || (is_strict && err!=1)) return cimg::type<double>::nan();
- if (is_negative) val = -val;
- return val;
- }
- static double mp_string(_cimg_math_parser& mp) {
- double *const ptrd = &_mp_arg(1) + 1;
- const unsigned int nb_args = (unsigned int)(mp.opcode[3] - 3)/2;
- CImgList<charT> _str;
- CImg<charT> it;
- for (unsigned int n = 0; n<nb_args; ++n) {
- const unsigned int siz = (unsigned int)mp.opcode[5 + 2*n];
- if (siz) { // Vector argument -> string
- const double *ptr = &_mp_arg(4 + 2*n) + 1;
- unsigned int l = 0;
- while (l<siz && ptr[l]) ++l;
- CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str);
- } else { // Scalar argument -> number
- it.assign(24);
- cimg_snprintf(it,it._width,"%.17g",_mp_arg(4 + 2*n));
- CImg<charT>::string(it,false,true).move_to(_str);
- }
- }
- const CImg<charT> str = _str>'x';
- const unsigned int sizd = std::min(str._width,(unsigned int)mp.opcode[2]);
- std::memset(ptrd,0,mp.opcode[2]*sizeof(double));
- for (unsigned int k = 0; k<sizd; ++k) ptrd[k] = (double)str[k];
- return cimg::type<double>::nan();
- }
- static double mp_sub(_cimg_math_parser& mp) {
- return _mp_arg(2) - _mp_arg(3);
- }
- static double mp_sum(_cimg_math_parser& mp) {
- const unsigned int i_end = (unsigned int)mp.opcode[2];
- double sum = 0;
- for (unsigned int i = 3; i<i_end; i+=2) {
- const unsigned int len = (unsigned int)mp.opcode[i + 1];
- if (len>1) {
- const double *ptr = &_mp_arg(i);
- for (unsigned int k = 0; k<len; ++k) sum+=*(ptr++);
- } else sum+=_mp_arg(i);
- }
- return sum;
- }
- static double mp_swap(_cimg_math_parser& mp) {
- const unsigned int siz = (unsigned int)mp.opcode[3];
- if (!siz) { // Scalar
- double &arg1 = _mp_arg(1), &arg2 = _mp_arg(2);
- cimg::swap(arg1,arg2);
- } else { // Vector
- double *const ptr1 = &_mp_arg(1) + 1, *const ptr2 = &_mp_arg(2) + 1;
- for (unsigned int k = 0; k<siz; ++k) cimg::swap(ptr1[k],ptr2[k]);
- }
- return _mp_arg(1);
- }
- static double mp_tan(_cimg_math_parser& mp) {
- return std::tan(_mp_arg(2));
- }
- static double mp_tanh(_cimg_math_parser& mp) {
- return std::tanh(_mp_arg(2));
- }
- static double mp_trace(_cimg_math_parser& mp) {
- const double *ptrs = &_mp_arg(2) + 1;
- const unsigned int k = (unsigned int)mp.opcode[3];
- return CImg<doubleT>(ptrs,k,k,1,1,true).trace();
- }
- static double mp_transpose(_cimg_math_parser& mp) {
- double *ptrd = &_mp_arg(1) + 1;
- const double *ptrs = &_mp_arg(2) + 1;
- const unsigned int
- k = (unsigned int)mp.opcode[3],
- l = (unsigned int)mp.opcode[4];
- CImg<doubleT>(ptrd,l,k,1,1,true) = CImg<doubleT>(ptrs,k,l,1,1,true).get_transpose();
- return cimg::type<double>::nan();
- }
- static double mp_u(_cimg_math_parser& mp) {
- return cimg::rand(_mp_arg(2),_mp_arg(3),&mp.rng);
- }
- static double mp_u_ext(_cimg_math_parser& mp) { // Extended version with extremum control
- const double eps = 1e-5;
- const bool
- include_min = (bool)_mp_arg(4),
- include_max = (bool)_mp_arg(5);
- double
- m = _mp_arg(2),
- M = _mp_arg(3);
- if (m>M) cimg::swap(m,M);
- if (!include_min) m = m>0?m*(1 + eps):m<0?m*(1 - eps):eps;
- if (!include_max) M = M>0?M*(1 - eps):M<0?M*(1 + eps):-eps;
- return cimg::rand(m,M,&mp.rng);
- }
- static double mp_ui2f(_cimg_math_parser& mp) {
- return (double)cimg::uint2float((unsigned int)_mp_arg(2));
- }
- static double mp_uppercase(_cimg_math_parser& mp) {
- return cimg::uppercase(_mp_arg(2));
- }
- static double mp_var(_cimg_math_parser& mp) {
- const unsigned int i_end = (unsigned int)mp.opcode[2];
- unsigned int siz = 0;
- double val, S = 0, S2 = 0;
- for (unsigned int i = 3; i<i_end; i+=2) {
- const unsigned int len = (unsigned int)mp.opcode[i + 1];
- if (len>1) {
- const double *ptr = &_mp_arg(i);
- for (unsigned int k = 0; k<len; ++k) { val = *(ptr++); S+=val; S2+=val*val; }
- } else { val = _mp_arg(i); S+=val; S2+=val*val; }
- siz+=len;
- }
- return (S2 - S*S/siz)/(siz - 1);
- }
- static double mp_vector_copy(_cimg_math_parser& mp) {
- std::memcpy(&_mp_arg(1) + 1,&_mp_arg(2) + 1,sizeof(double)*mp.opcode[3]);
- return cimg::type<double>::nan();
- }
- static double mp_vector_crop(_cimg_math_parser& mp) {
- double *ptrd = &_mp_arg(1) + 1;
- const double *ptrs = &_mp_arg(2) + 1;
- const longT
- length = (longT)mp.opcode[3],
- start = (longT)_mp_arg(4),
- sublength = (longT)mp.opcode[5],
- step = (longT)_mp_arg(6);
- if (start<0 || start + step*(sublength-1)>=length)
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Value accessor '[]': "
- "Out-of-bounds sub-vector request "
- "(length: %ld, start: %ld, sub-length: %ld, step: %ld).",
- mp.imgin.pixel_type(),length,start,sublength,step);
- ptrs+=start;
- if (step==1) std::memcpy(ptrd,ptrs,sublength*sizeof(double));
- else for (longT k = 0; k<sublength; ++k) { *(ptrd++) = *ptrs; ptrs+=step; }
- return cimg::type<double>::nan();
- }
- static double mp_vector_crop_ext(_cimg_math_parser& mp) {
- double *const ptrd = &_mp_arg(1) + 1;
- const double *const ptrs = &_mp_arg(2) + 1;
- const unsigned int
- w = (unsigned int)mp.opcode[3],
- h = (unsigned int)mp.opcode[4],
- d = (unsigned int)mp.opcode[5],
- s = (unsigned int)mp.opcode[6],
- dx = (unsigned int)mp.opcode[11],
- dy = (unsigned int)mp.opcode[12],
- dz = (unsigned int)mp.opcode[13],
- dc = (unsigned int)mp.opcode[14],
- boundary_conditions = (int)_mp_arg(15);
- const int x = (int)_mp_arg(7), y = (int)_mp_arg(8), z = (int)_mp_arg(9), c = (int)_mp_arg(10);
- CImg<doubleT>(ptrd,dx,dy,dz,dc,true) = CImg<doubleT>(ptrs,w,h,d,s,true).
- get_crop(x,y,z,c,x + dx - 1,y + dy - 1,z + dz - 1,c + dc - 1,boundary_conditions);
- return cimg::type<double>::nan();
- }
- static double mp_vector_draw(_cimg_math_parser& mp) {
- double *const ptrd = &_mp_arg(1) + 1;
- const double *const ptrs = &_mp_arg(7) + 1;
- const unsigned int
- sizD = (unsigned int)mp.opcode[2],
- sizS = (unsigned int)mp.opcode[8];
- const int
- w = (int)_mp_arg(3), h = (int)_mp_arg(4), d = (int)_mp_arg(5), s = (int)_mp_arg(6),
- x = (int)_mp_arg(9), y = (int)_mp_arg(10), z = (int)_mp_arg(11), c = (int)_mp_arg(12);
- int dx = (int)mp.opcode[13], dy = (int)mp.opcode[14], dz = (int)mp.opcode[15], dc = (int)mp.opcode[16];
- dx = (unsigned int)dx==~0U?w:(int)_mp_arg(13);
- dy = (unsigned int)dy==~0U?h:(int)_mp_arg(14);
- dz = (unsigned int)dz==~0U?d:(int)_mp_arg(15);
- dc = (unsigned int)dc==~0U?s:(int)_mp_arg(16);
- if (w<=0 || h<=0 || d<=0 || s<=0)
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
- "Invalid specified target vector geometry (%d,%d,%d,%d).",
- mp.imgin.pixel_type(),w,h,d,s);
- if (sizD<(ulongT)w*h*d*s)
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
- "Target vector (%lu values) and its specified target geometry (%d,%d,%d,%d) "
- "(%lu values) do not match.",
- mp.imgin.pixel_type(),sizD,w,h,d,s,(ulongT)w*h*d*s);
- if (dx<=0 || dy<=0 || dz<=0 || dc<=0)
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
- "Invalid specified sprite geometry (%d,%d,%d,%d).",
- mp.imgin.pixel_type(),dx,dy,dz,dc);
- if (sizS<(ulongT)dx*dy*dz*dc)
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
- "Sprite vector (%lu values) and its specified sprite geometry (%d,%d,%d,%d) "
- "(%lu values) do not match.",
- mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc);
- CImg<doubleT> D(ptrd,w,h,d,s,true);
- const CImg<doubleT> S(ptrs,dx,dy,dz,dc,true);
- const float opacity = (float)_mp_arg(17);
- if (mp.opcode[18]!=~0U) { // Opacity mask specified
- const ulongT sizM = mp.opcode[19];
- if (sizM<(ulongT)dx*dy*dz)
- throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
- "Mask vector (%lu values) and specified sprite geometry (%u,%u,%u,%u) "
- "(%lu values) do not match.",
- mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc);
- const CImg<doubleT> M(&_mp_arg(18) + 1,dx,dy,dz,(unsigned int)(sizM/(dx*dy*dz)),true);
- D.draw_image(x,y,z,c,S,M,opacity,(float)_mp_arg(20));
- } else D.draw_image(x,y,z,c,S,opacity);
- return cimg::type<double>::nan();
- }
- static double mp_vector_init(_cimg_math_parser& mp) {
- unsigned int
- ptrs = 4U,
- ptrd = (unsigned int)mp.opcode[1] + 1,
- siz = (unsigned int)mp.opcode[3];
- switch (mp.opcode[2] - 4) {
- case 0 : std::memset(mp.mem._data + ptrd,0,siz*sizeof(double)); break; // 0 values given
- case 1 : { const double val = _mp_arg(ptrs); while (siz-->0) mp.mem[ptrd++] = val; } break;
- default : while (siz-->0) { mp.mem[ptrd++] = _mp_arg(ptrs++); if (ptrs>=mp.opcode[2]) ptrs = 4U; }
- }
- return cimg::type<double>::nan();
- }
- static double mp_vector_eq(_cimg_math_parser& mp) {
- const double
- *ptr1 = &_mp_arg(2) + 1,
- *ptr2 = &_mp_arg(4) + 1;
- unsigned int p1 = (unsigned int)mp.opcode[3], p2 = (unsigned int)mp.opcode[5], n;
- const int N = (int)_mp_arg(6);
- const bool case_sensitive = (bool)_mp_arg(7);
- bool still_equal = true;
- double value;
- if (!N) return true;
- // Compare all values.
- if (N<0) {
- if (p1>0 && p2>0) { // Vector == vector
- if (p1!=p2) return false;
- if (case_sensitive)
- while (still_equal && p1--) still_equal = *(ptr1++)==*(ptr2++);
- else
- while (still_equal && p1--)
- still_equal = cimg::lowercase(*(ptr1++))==cimg::lowercase(*(ptr2++));
- return still_equal;
- } else if (p1>0 && !p2) { // Vector == scalar
- value = _mp_arg(4);
- if (!case_sensitive) value = cimg::lowercase(value);
- while (still_equal && p1--) still_equal = *(ptr1++)==value;
- return still_equal;
- } else if (!p1 && p2>0) { // Scalar == vector
- value = _mp_arg(2);
- if (!case_sensitive) value = cimg::lowercase(value);
- while (still_equal && p2--) still_equal = *(ptr2++)==value;
- return still_equal;
- } else { // Scalar == scalar
- if (case_sensitive) return _mp_arg(2)==_mp_arg(4);
- else return cimg::lowercase(_mp_arg(2))==cimg::lowercase(_mp_arg(4));
- }
- }
- // Compare only first N values.
- if (p1>0 && p2>0) { // Vector == vector
- n = cimg::min((unsigned int)N,p1,p2);
- if (case_sensitive)
- while (still_equal && n--) still_equal = *(ptr1++)==(*ptr2++);
- else
- while (still_equal && n--) still_equal = cimg::lowercase(*(ptr1++))==cimg::lowercase(*(ptr2++));
- return still_equal;
- } else if (p1>0 && !p2) { // Vector == scalar
- n = std::min((unsigned int)N,p1);
- value = _mp_arg(4);
- if (!case_sensitive) value = cimg::lowercase(value);
- while (still_equal && n--) still_equal = *(ptr1++)==value;
- return still_equal;
- } else if (!p1 && p2>0) { // Scalar == vector
- n = std::min((unsigned int)N,p2);
- value = _mp_arg(2);
- if (!case_sensitive) value = cimg::lowercase(value);
- while (still_equal && n--) still_equal = *(ptr2++)==value;
- return still_equal;
- } // Scalar == scalar
- if (case_sensitive) return _mp_arg(2)==_mp_arg(4);
- return cimg::lowercase(_mp_arg(2))==cimg::lowercase(_mp_arg(4));
- }
- static double mp_vector_lerp(_cimg_math_parser& mp) {
- unsigned int siz = (unsigned int)mp.opcode[2];
- double *ptrd = &_mp_arg(1) + 1;
- const double
- *ptrs1 = &_mp_arg(3) + 1,
- *ptrs2 = &_mp_arg(4) + 1,
- t = _mp_arg(5);
- for (unsigned int k = 0; k<siz; ++k) ptrd[k] = ptrs1[k]*(1-t) + ptrs2[k]*t;
- return cimg::type<double>::nan();
- }
- static double mp_vector_off(_cimg_math_parser& mp) {
- const unsigned int
- ptr = (unsigned int)mp.opcode[2] + 1,
- siz = (unsigned int)mp.opcode[3];
- const int off = (int)_mp_arg(4);
- return off>=0 && off<(int)siz?mp.mem[ptr + off]:cimg::type<double>::nan();
- }
- static double mp_vector_map_sv(_cimg_math_parser& mp) { // Operator(scalar,vector,[...])
- unsigned int
- nb_args = (unsigned int)mp.opcode[2],
- siz_vector = (unsigned int)mp.opcode[3],
- ptrs = (unsigned int)mp.opcode[6] + 1;
- double *ptrd = &_mp_arg(1) + 1;
- mp_func op = (mp_func)mp.opcode[4];
- CImg<ulongT> l_opcode(mp.opcode._data + 3,nb_args + 2);
- l_opcode[0] = mp.opcode[1];
- l_opcode.swap(mp.opcode);
- ulongT &argument2 = mp.opcode[3];
- while (siz_vector-->0) { argument2 = ptrs++; *(ptrd++) = (*op)(mp); }
- l_opcode.swap(mp.opcode);
- return cimg::type<double>::nan();
- }
- static double mp_vector_map_v(_cimg_math_parser& mp) { // Operator(vector,[...])
- unsigned int
- nb_args = (unsigned int)mp.opcode[2],
- siz_vector = (unsigned int)mp.opcode[3],
- ptrs = (unsigned int)mp.opcode[5] + 1;
- double *ptrd = &_mp_arg(1) + 1;
- mp_func op = (mp_func)mp.opcode[4];
- CImg<ulongT> l_opcode(mp.opcode._data + 3,nb_args + 2);
- l_opcode[0] = l_opcode[1];
- l_opcode.swap(mp.opcode);
- ulongT &argument = mp.opcode[2];
- while (siz_vector-->0) { argument = ptrs++; *(ptrd++) = (*op)(mp); }
- l_opcode.swap(mp.opcode);
- return cimg::type<double>::nan();
- }
- static double mp_vector_map_vv(_cimg_math_parser& mp) { // Operator(vector,vector,[...])
- unsigned int
- nb_args = (unsigned int)mp.opcode[2],
- siz_vector = (unsigned int)mp.opcode[3],
- ptrs1 = (unsigned int)mp.opcode[5] + 1,
- ptrs2 = (unsigned int)mp.opcode[6] + 1;
- double *ptrd = &_mp_arg(1) + 1;
- mp_func op = (mp_func)mp.opcode[4];
- CImg<ulongT> l_opcode(mp.opcode._data + 3,nb_args + 2);
- l_opcode[0] = l_opcode[1];
- l_opcode.swap(mp.opcode);
- ulongT &argument1 = mp.opcode[2], &argument2 = mp.opcode[3];
- while (siz_vector-->0) { argument1 = ptrs1++; argument2 = ptrs2++; *(ptrd++) = (*op)(mp); }
- l_opcode.swap(mp.opcode);
- return cimg::type<double>::nan();
- }
- static double mp_vector_neq(_cimg_math_parser& mp) {
- return !mp_vector_eq(mp);
- }
- static double _mp_vector_norm0(_cimg_math_parser& mp) {
- const unsigned int siz = (unsigned int)mp.opcode[2];
- double res = 0;
- for (unsigned int i = siz - 1; i>3; --i) res+=(double)(_mp_arg(i)?1:0);
- return res;
- }
- static double _mp_vector_norm1(_cimg_math_parser& mp) {
- const unsigned int siz = (unsigned int)mp.opcode[2];
- double res = 0;
- for (unsigned int i = siz - 1; i>3; --i) res+=(double)cimg::abs(_mp_arg(i));
- return res;
- }
- static double _mp_vector_norm2(_cimg_math_parser& mp) {
- const unsigned int siz = (unsigned int)mp.opcode[2];
- double res = 0;
- for (unsigned int i = siz - 1; i>3; --i) res+=(double)cimg::sqr(_mp_arg(i));
- return (double)std::sqrt(res);
- }
- static double _mp_vector_norminf(_cimg_math_parser& mp) {
- const unsigned int siz = (unsigned int)mp.opcode[2];
- double res = 0;
- for (unsigned int i = siz - 1; i>3; --i) {
- const double val = (double)cimg::abs(_mp_arg(i));
- if (val>res) res = val;
- }
- return res;
- }
- static double _mp_vector_normp(_cimg_math_parser& mp) {
- const unsigned int siz = (unsigned int)mp.opcode[2];
- const double p = _mp_arg(3);
- double res = 0;
- for (unsigned int i = siz - 1; i>3; --i) res+=(double)std::pow(cimg::abs(_mp_arg(i)),p);
- res = (double)std::pow(res,1.0/p);
- return res;
- }
- static double mp_vector_normp(_cimg_math_parser& mp) {
- const unsigned int siz = (unsigned int)mp.opcode[3];
- const double p = _mp_arg(4);
- if (siz>0) { // Vector-valued argument
- const double *ptrs = &_mp_arg(2) + 1;
- double res = 0;
- if (p==2) { // L2
- for (unsigned int i = 0; i<siz; ++i) res+=(double)cimg::sqr(*(ptrs++));
- res = (double)std::sqrt(res);
- } else if (p==1) // L1
- for (unsigned int i = 0; i<siz; ++i) res+=(double)cimg::abs(*(ptrs++));
- else if (!p) // L0
- for (unsigned int i = 0; i<siz; ++i) res+=(double)(*(ptrs++)?1:0);
- else if (cimg::type<float>::is_inf(p)) { // L-inf
- for (unsigned int i = 0; i<siz; ++i) {
- const double val = (double)cimg::abs(*(ptrs++));
- if (val>res) res = val;
- }
- } else { // L-p
- for (unsigned int i = 0; i<siz; ++i) res+=(double)std::pow(cimg::abs(*(ptrs++)),p);
- res = (double)std::pow(res,1.0/p);
- }
- return res>0?res:0;
- }
- // Scalar-valued argument.
- const double val = _mp_arg(2);
- return p?cimg::abs(val):(val!=0);
- }
- static double _mp_vector_hypot(_cimg_math_parser& mp) {
- switch ((unsigned int)mp.opcode[2]) {
- case 5 : return cimg::abs(_mp_arg(4));
- case 6 : return cimg::hypot(_mp_arg(4),_mp_arg(5));
- case 7 : return cimg::hypot(_mp_arg(4),_mp_arg(5),_mp_arg(6));
- };
- return _mp_vector_norm2(mp);
- }
- static double mp_vector_print(_cimg_math_parser& mp) {
- const bool print_string = (bool)mp.opcode[4];
- cimg_pragma_openmp(critical(mp_vector_print))
- {
- CImg<charT> _expr(mp.opcode[2] - 5);
- const ulongT *ptrs = mp.opcode._data + 5;
- cimg_for(_expr,ptrd,char) *ptrd = (char)*(ptrs++);
- cimg::strellipsize(_expr);
- unsigned int
- ptr = (unsigned int)mp.opcode[1] + 1,
- siz0 = (unsigned int)mp.opcode[3],
- siz = siz0;
- cimg::mutex(6);
- std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = [ ",_expr._data);
- unsigned int count = 0;
- while (siz-->0) {
- if (count>=64 && siz>=64) {
- std::fprintf(cimg::output(),"...,");
- ptr = (unsigned int)mp.opcode[1] + 1 + siz0 - 64;
- siz = 64;
- } else std::fprintf(cimg::output(),"%.17g%s",mp.mem[ptr++],siz?",":"");
- ++count;
- }
- if (print_string) {
- CImg<charT> str(siz0 + 1);
- ptr = (unsigned int)mp.opcode[1] + 1;
- for (unsigned int k = 0; k<siz0; ++k) str[k] = (char)mp.mem[ptr++];
- str[siz0] = 0;
- cimg::strellipsize(str,1024,false);
- std::fprintf(cimg::output()," ] = '%s' (size: %u)",str._data,siz0);
- } else std::fprintf(cimg::output()," ] (size: %u)",siz0);
- std::fflush(cimg::output());
- cimg::mutex(6,0);
- }
- return cimg::type<double>::nan();
- }
- static double mp_vector_resize(_cimg_math_parser& mp) {
- double *const ptrd = &_mp_arg(1) + 1;
- const unsigned int p1 = (unsigned int)mp.opcode[2], p2 = (unsigned int)mp.opcode[4];
- const int
- interpolation = (int)_mp_arg(5),
- boundary_conditions = (int)_mp_arg(6);
- if (p2) { // Resize vector
- const double *const ptrs = &_mp_arg(3) + 1;
- CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(ptrs,p2,1,1,1,true).
- get_resize(p1,1,1,1,interpolation,boundary_conditions);
- } else { // Resize scalar
- const double value = _mp_arg(3);
- CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(1,1,1,1,value).resize(p1,1,1,1,interpolation,
- boundary_conditions);
- }
- return cimg::type<double>::nan();
- }
- static double mp_vector_resize_ext(_cimg_math_parser& mp) {
- double *const ptrd = &_mp_arg(1) + 1;
- const unsigned int
- siz = (unsigned int)mp.opcode[2],
- ow = (unsigned int)mp.opcode[4],
- oh = (unsigned int)mp.opcode[5],
- od = (unsigned int)mp.opcode[6],
- os = (unsigned int)mp.opcode[7],
- nw = (unsigned int)mp.opcode[8],
- nh = (unsigned int)mp.opcode[9],
- nd = (unsigned int)mp.opcode[10],
- ns = (unsigned int)mp.opcode[11];
- const int
- interpolation = (int)_mp_arg(12),
- boundary_conditions = (int)_mp_arg(13);
- const float
- ax = (float)_mp_arg(14),
- ay = (float)_mp_arg(15),
- az = (float)_mp_arg(16),
- ac = (float)_mp_arg(17);
- if (siz) { // Resize vector
- const double *const ptrs = &_mp_arg(3) + 1;
- CImg<doubleT>(ptrd,nw,nh,nd,ns,true) = CImg<doubleT>(ptrs,ow,oh,od,os,true).
- get_resize(nw,nh,nd,ns,interpolation,boundary_conditions,ax,ay,az,ac);
- } else { // Resize scalar
- const double value = _mp_arg(3);
- CImg<doubleT>(ptrd,nw,nh,nd,ns,true) = CImg<doubleT>(1,1,1,1,value).
- resize(nw,nh,nd,ns,interpolation,boundary_conditions,ax,ay,az,ac);
- }
- return cimg::type<double>::nan();
- }
- static double mp_vector_reverse(_cimg_math_parser& mp) {
- double *const ptrd = &_mp_arg(1) + 1;
- const double *const ptrs = &_mp_arg(2) + 1;
- const unsigned int p1 = (unsigned int)mp.opcode[3];
- CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(ptrs,p1,1,1,1,true).get_mirror('x');
- return cimg::type<double>::nan();
- }
- static double mp_vector_set_off(_cimg_math_parser& mp) {
- const unsigned int
- ptr = (unsigned int)mp.opcode[2] + 1,
- siz = (unsigned int)mp.opcode[3];
- const int off = (int)_mp_arg(4);
- if (off>=0 && off<(int)siz) mp.mem[ptr + off] = _mp_arg(1);
- return _mp_arg(1);
- }
- static double mp_vector_unitnorm(_cimg_math_parser& mp) {
- const unsigned int siz = (unsigned int)mp.opcode[3];
- const double p = _mp_arg(4);
- if (siz>0) { // Vector-valued argument
- double *const ptrd = &_mp_arg(1) + 1;
- const double *const ptrs = &_mp_arg(2) + 1;
- if (ptrd!=ptrs) std::memcpy(ptrd,ptrs,siz*sizeof(double));
- CImg<doubleT> vec(ptrd,siz,1,1,1,true);
- const double mag = vec.magnitude(p);
- if (mag>0) vec/=mag;
- return cimg::type<double>::nan();
- }
- // Scalar-valued argument.
- const double val = _mp_arg(2);
- return val?(_mp_arg(2)?1:val):0;
- }
- #define _cimg_mp_vfunc(func) \
- const longT sizd = (longT)mp.opcode[2];\
- const unsigned int nbargs = (unsigned int)(mp.opcode[3] - 4)/2; \
- double *const ptrd = &_mp_arg(1) + (sizd?1:0); \
- cimg_pragma_openmp(parallel cimg_openmp_if_size(sizd,256)) \
- { CImg<doubleT> vec(nbargs); double res; \
- cimg_pragma_openmp(for) for (longT k = sizd?sizd - 1:0; k>=0; --k) { \
- cimg_forX(vec,n) vec[n] = *(&_mp_arg(4 + 2*n) + (k+1)*(mp.opcode[4 + 2*n + 1]?1:0)); \
- func; ptrd[k] = res; \
- }} \
- return sizd?cimg::type<double>::nan():*ptrd;
- static double _mp_vargkth(CImg<doubleT>& vec) {
- const double val = (+vec).get_shared_points(1,vec.width() - 1).
- kth_smallest((ulongT)cimg::cut((longT)*vec - 1,(longT)0,(longT)vec.width() - 2));
- cimg_for_inX(vec,1,vec.width()-1,ind) if (vec[ind]==val) return ind - 1.;
- return 1.;
- }
- static double mp_vargkth(_cimg_math_parser& mp) {
- _cimg_mp_vfunc(res = _mp_vargkth(vec));
- }
- static double mp_vargmax(_cimg_math_parser& mp) {
- _cimg_mp_vfunc(res = (double)(&vec.max() - vec.data()));
- }
- static double mp_vargmaxabs(_cimg_math_parser& mp) {
- _cimg_mp_vfunc(res = (double)(&vec.maxabs() - vec.data()));
- }
- static double mp_vargmin(_cimg_math_parser& mp) {
- _cimg_mp_vfunc(res = (double)(&vec.min() - vec.data()));
- }
- static double mp_vargminabs(_cimg_math_parser& mp) {
- _cimg_mp_vfunc(res = (double)(&vec.minabs() - vec.data()));
- }
- static double mp_vavg(_cimg_math_parser& mp) {
- _cimg_mp_vfunc(res = vec.mean());
- }
- static double mp_vkth(_cimg_math_parser& mp) {
- _cimg_mp_vfunc(res = vec.get_shared_points(1,vec.width() - 1).
- kth_smallest((ulongT)cimg::cut((longT)*vec - 1,(longT)0,(longT)vec.width() - 2)));
- }
- static double mp_vmax(_cimg_math_parser& mp) {
- _cimg_mp_vfunc(res = vec.max());
- }
- static double mp_vmaxabs(_cimg_math_parser& mp) {
- _cimg_mp_vfunc(res = vec.maxabs());
- }
- static double mp_vmedian(_cimg_math_parser& mp) {
- _cimg_mp_vfunc(res = vec.median());
- }
- static double mp_vmin(_cimg_math_parser& mp) {
- _cimg_mp_vfunc(res = vec.min());
- }
- static double mp_vminabs(_cimg_math_parser& mp) {
- _cimg_mp_vfunc(res = vec.minabs());
- }
- static double mp_vprod(_cimg_math_parser& mp) {
- _cimg_mp_vfunc(res = vec.product());
- }
- static double mp_vstd(_cimg_math_parser& mp) {
- _cimg_mp_vfunc(res = std::sqrt(vec.get_stats()[3]));
- }
- static double mp_vsum(_cimg_math_parser& mp) {
- _cimg_mp_vfunc(res = vec.sum());
- }
- static double mp_vvar(_cimg_math_parser& mp) {
- _cimg_mp_vfunc(res = vec.get_stats()[3]);
- }
- static double mp_v2s(_cimg_math_parser& mp) {
- double *ptrd = &_mp_arg(1) + 1;
- const unsigned int
- sizd = (unsigned int)mp.opcode[2],
- sizs = (unsigned int)mp.opcode[4];
- std::memset(ptrd,0,sizd*sizeof(double));
- const int nb_digits = (int)_mp_arg(5);
- CImg<charT> format(8);
- switch (nb_digits) {
- case -1 : std::strcpy(format,"%g"); break;
- case 0 : std::strcpy(format,"%.17g"); break;
- default :
- if (nb_digits>=-1) cimg_snprintf(format,format._width,"%%.%dg",nb_digits);
- else cimg_snprintf(format,format._width,"%%.%dld",-nb_digits);
- }
- CImg<charT> str;
- if (sizs) { // Vector expression
- const double *ptrs = &_mp_arg(3) + 1;
- if (nb_digits>=-1) CImg<doubleT>(ptrs,sizs,1,1,1,true).value_string(',',sizd + 1,format).move_to(str);
- else CImg<longT>(ptrs,sizs,1,1,1).value_string(',',sizd + 1,format).move_to(str);
- } else { // Scalar expression
- str.assign(sizd + 1);
- if (nb_digits>=-1) cimg_snprintf(str,sizd + 1,format,_mp_arg(3));
- else cimg_snprintf(str,sizd + 1,format,(long)_mp_arg(3));
- }
- const unsigned int l = std::min(sizd,(unsigned int)std::strlen(str) + 1);
- CImg<doubleT>(ptrd,l,1,1,1,true) = str.get_shared_points(0,l - 1);
- return cimg::type<double>::nan();
- }
- static double mp_while(_cimg_math_parser& mp) {
- const ulongT
- mem_body = mp.opcode[1],
- mem_cond = mp.opcode[2];
- const CImg<ulongT>
- *const p_cond = ++mp.p_code,
- *const p_body = p_cond + mp.opcode[3],
- *const p_end = p_body + mp.opcode[4];
- const unsigned int vsiz = (unsigned int)mp.opcode[5];
- bool is_cond = false;
- if (mp.opcode[6]) { // Set default value for result and condition if necessary
- if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
- else mp.mem[mem_body] = cimg::type<double>::nan();
- }
- if (mp.opcode[7]) mp.mem[mem_cond] = 0;
- const unsigned int _break_type = mp.break_type;
- mp.break_type = 0;
- do {
- for (mp.p_code = p_cond; mp.p_code<p_body; ++mp.p_code) { // Evaluate condition
- mp.opcode._data = mp.p_code->_data;
- const ulongT target = mp.opcode[1];
- mp.mem[target] = _cimg_mp_defunc(mp);
- }
- if (mp.break_type==1) break;
- is_cond = (bool)mp.mem[mem_cond];
- if (is_cond && !mp.break_type) // Evaluate body
- for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
- mp.opcode._data = mp.p_code->_data;
- const ulongT target = mp.opcode[1];
- mp.mem[target] = _cimg_mp_defunc(mp);
- }
- if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
- } while (is_cond);
- mp.break_type = _break_type;
- mp.p_code = p_end - 1;
- return mp.mem[mem_body];
- }
- static double mp_Ioff(_cimg_math_parser& mp) {
- double *ptrd = &_mp_arg(1) + 1;
- const unsigned int
- boundary_conditions = (unsigned int)_mp_arg(3),
- vsiz = (unsigned int)mp.opcode[4];
- const CImg<T> &img = mp.imgin;
- const longT
- off = (longT)_mp_arg(2),
- whd = (longT)img.width()*img.height()*img.depth();
- const T *ptrs;
- if (off>=0 && off<whd) {
- ptrs = &img[off];
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
- return cimg::type<double>::nan();
- }
- if (img._data) switch (boundary_conditions) {
- case 3 : { // Mirror
- const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
- ptrs = &img[moff<whd?moff:whd2 - moff - 1];
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
- return cimg::type<double>::nan();
- }
- case 2 : // Periodic
- ptrs = &img[cimg::mod(off,whd)];
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
- return cimg::type<double>::nan();
- case 1 : // Neumann
- ptrs = off<0?&img[0]:&img[whd - 1];
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
- return cimg::type<double>::nan();
- default : // Dirichlet
- std::memset(ptrd,0,vsiz*sizeof(double));
- return cimg::type<double>::nan();
- }
- std::memset(ptrd,0,vsiz*sizeof(double));
- return cimg::type<double>::nan();
- }
- static double mp_Ixyz(_cimg_math_parser& mp) {
- double *ptrd = &_mp_arg(1) + 1;
- const unsigned int
- interpolation = (unsigned int)_mp_arg(5),
- boundary_conditions = (unsigned int)_mp_arg(6),
- vsiz = (unsigned int)mp.opcode[7];
- const CImg<T> &img = mp.imgin;
- const double x = _mp_arg(2), y = _mp_arg(3), z = _mp_arg(4);
- const ulongT whd = (ulongT)img._width*img._height*img._depth;
- const T *ptrs;
- switch (interpolation) {
- case 2 : // Cubic interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float
- w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
- mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
- cx = mx<img.width()?mx:w2 - mx - 1,
- cy = my<img.height()?my:h2 - my - 1,
- cz = mz<img.depth()?mz:d2 - mz - 1;
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ(cx,cy,cz,c);
- } break;
- case 2 : // Periodic
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,c);
- break;
- case 1 : // Neumann
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ((float)x,(float)y,(float)z,c);
- break;
- default : // Dirichlet
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.cubic_atXYZ((float)x,(float)y,(float)z,c,(T)0);
- } break;
- case 1 : // Linear interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float
- w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
- mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
- cx = mx<img.width()?mx:w2 - mx - 1,
- cy = my<img.height()?my:h2 - my - 1,
- cz = mz<img.depth()?mz:d2 - mz - 1;
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
- } break;
- case 2 : // Periodic
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,c);
- break;
- case 1 : // Neumann
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
- break;
- default : // Dirichlet
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
- } break;
- default : // Nearest neighbor interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const int
- w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
- mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
- cx = mx<img.width()?mx:w2 - mx - 1,
- cy = my<img.height()?my:h2 - my - 1,
- cz = mz<img.depth()?mz:d2 - mz - 1;
- ptrs = &img(cx,cy,cz);
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
- } break;
- case 2 : { // Periodic
- const int
- cx = (int)cimg::mod(x,(double)img._width),
- cy = (int)cimg::mod(y,(double)img._height),
- cz = (int)cimg::mod(z,(double)img._depth);
- ptrs = &img(cx,cy,cz);
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
- } break;
- case 1 : { // Neumann
- ptrs = &img._atXYZ((int)x,(int)y,(int)z);
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
- } break;
- default : // Dirichlet
- if (img.containsXYZC((int)x,(int)y,(int)z)) {
- ptrs = &img((int)x,(int)y,(int)z);
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
- } else std::memset(ptrd,0,vsiz*sizeof(double));
- }
- }
- return cimg::type<double>::nan();
- }
- static double mp_Joff(_cimg_math_parser& mp) {
- double *ptrd = &_mp_arg(1) + 1;
- const unsigned int
- boundary_conditions = (unsigned int)_mp_arg(3),
- vsiz = (unsigned int)mp.opcode[4];
- const CImg<T> &img = mp.imgin;
- const int
- ox = (int)mp.mem[_cimg_mp_slot_x],
- oy = (int)mp.mem[_cimg_mp_slot_y],
- oz = (int)mp.mem[_cimg_mp_slot_z];
- const longT
- off = img.offset(ox,oy,oz) + (longT)_mp_arg(2),
- whd = (longT)img.width()*img.height()*img.depth();
- const T *ptrs;
- if (off>=0 && off<whd) {
- ptrs = &img[off];
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
- return cimg::type<double>::nan();
- }
- if (img._data) switch (boundary_conditions) {
- case 3 : { // Mirror
- const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
- ptrs = &img[moff<whd?moff:whd2 - moff - 1];
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
- return cimg::type<double>::nan();
- }
- case 2 : // Periodic
- ptrs = &img[cimg::mod(off,whd)];
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
- return cimg::type<double>::nan();
- case 1 : // Neumann
- ptrs = off<0?&img[0]:&img[whd - 1];
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
- return cimg::type<double>::nan();
- default : // Dirichlet
- std::memset(ptrd,0,vsiz*sizeof(double));
- return cimg::type<double>::nan();
- }
- std::memset(ptrd,0,vsiz*sizeof(double));
- return cimg::type<double>::nan();
- }
- static double mp_Jxyz(_cimg_math_parser& mp) {
- double *ptrd = &_mp_arg(1) + 1;
- const unsigned int
- interpolation = (unsigned int)_mp_arg(5),
- boundary_conditions = (unsigned int)_mp_arg(6),
- vsiz = (unsigned int)mp.opcode[7];
- const CImg<T> &img = mp.imgin;
- const double
- ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z],
- x = ox + _mp_arg(2), y = oy + _mp_arg(3), z = oz + _mp_arg(4);
- const ulongT whd = (ulongT)img._width*img._height*img._depth;
- const T *ptrs;
- switch (interpolation) {
- case 2 : // Cubic interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float
- w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
- mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
- cx = mx<img.width()?mx:w2 - mx - 1,
- cy = my<img.height()?my:h2 - my - 1,
- cz = mz<img.depth()?mz:d2 - mz - 1;
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ(cx,cy,cz,c);
- } break;
- case 2 : // Periodic
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,c);
- break;
- case 1 : // Neumann
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ((float)x,(float)y,(float)z,c);
- break;
- default : // Dirichlet
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.cubic_atXYZ((float)x,(float)y,(float)z,c,(T)0);
- } break;
- case 1 : // Linear interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float
- w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
- mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
- cx = mx<img.width()?mx:w2 - mx - 1,
- cy = my<img.height()?my:h2 - my - 1,
- cz = mz<img.depth()?mz:d2 - mz - 1;
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
- } break;
- case 2 : // Periodic
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,c);
- break;
- case 1 : // Neumann
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
- break;
- default : // Dirichlet
- cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
- } break;
- default : // Nearest neighbor interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const int
- w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
- mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
- cx = mx<img.width()?mx:w2 - mx - 1,
- cy = my<img.height()?my:h2 - my - 1,
- cz = mz<img.depth()?mz:d2 - mz - 1;
- ptrs = &img(cx,cy,cz);
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
- } break;
- case 2 : { // Periodic
- const int
- cx = (int)cimg::mod(x,(double)img._width),
- cy = (int)cimg::mod(y,(double)img._height),
- cz = (int)cimg::mod(z,(double)img._depth);
- ptrs = &img(cx,cy,cz);
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
- } break;
- case 1 : { // Neumann
- ptrs = &img._atXYZ((int)x,(int)y,(int)z);
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
- } break;
- default : // Dirichlet
- if (img.containsXYZC((int)x,(int)y,(int)z)) {
- ptrs = &img((int)x,(int)y,(int)z);
- cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
- } else std::memset(ptrd,0,vsiz*sizeof(double));
- }
- }
- return cimg::type<double>::nan();
- }
- #undef _mp_arg
- }; // struct _cimg_math_parser {}
- #define _cimg_create_pointwise_functions(name,func,min_size) \
- CImg<T>& name() { \
- if (is_empty()) return *this; \
- cimg_openmp_for(*this,func((typename cimg::superset<T,float>::type)*ptr),min_size); \
- return *this; \
- } \
- CImg<Tfloat> get_##name() const { \
- return CImg<Tfloat>(*this,false).name(); \
- }
- //! Compute the square value of each pixel value.
- /**
- Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its square value \f$I_{(x,y,z,c)}^2\f$.
- \note
- - The \inplace of this method statically casts the computed values to the pixel type \c T.
- - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
- \par Example
- \code
- const CImg<float> img("reference.jpg");
- (img,img.get_sqr().normalize(0,255)).display();
- \endcode
- \image html ref_sqr.jpg
- **/
- _cimg_create_pointwise_functions(sqr,cimg::sqr,524288)
- //! Compute the square root of each pixel value.
- /**
- Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its square root \f$\sqrt{I_{(x,y,z,c)}}\f$.
- \note
- - The \inplace of this method statically casts the computed values to the pixel type \c T.
- - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
- \par Example
- \code
- const CImg<float> img("reference.jpg");
- (img,img.get_sqrt().normalize(0,255)).display();
- \endcode
- \image html ref_sqrt.jpg
- **/
- _cimg_create_pointwise_functions(sqrt,std::sqrt,8192)
- //! Compute the exponential of each pixel value.
- /**
- Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its exponential \f$e^{I_{(x,y,z,c)}}\f$.
- \note
- - The \inplace of this method statically casts the computed values to the pixel type \c T.
- - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
- **/
- _cimg_create_pointwise_functions(exp,std::exp,4096)
- //! Compute the error function of each pixel value.
- /**
- Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its error function.
- \note
- - The \inplace of this method statically casts the computed values to the pixel type \c T.
- - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
- **/
- #if cimg_use_cpp11==1
- _cimg_create_pointwise_functions(erf,std::erf,4096)
- #endif
- //! Compute the logarithm of each pixel value.
- /**
- Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its logarithm
- \f$\mathrm{log}_{e}(I_{(x,y,z,c)})\f$.
- \note
- - The \inplace of this method statically casts the computed values to the pixel type \c T.
- - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
- **/
- _cimg_create_pointwise_functions(log,std::log,262144)
- //! Compute the base-2 logarithm of each pixel value.
- /**
- Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-2 logarithm
- \f$\mathrm{log}_{2}(I_{(x,y,z,c)})\f$.
- \note
- - The \inplace of this method statically casts the computed values to the pixel type \c T.
- - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
- **/
- _cimg_create_pointwise_functions(log2,cimg::log2,4096)
- //! Compute the base-10 logarithm of each pixel value.
- /**
- Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-10 logarithm
- \f$\mathrm{log}_{10}(I_{(x,y,z,c)})\f$.
- \note
- - The \inplace of this method statically casts the computed values to the pixel type \c T.
- - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
- **/
- _cimg_create_pointwise_functions(log10,std::log10,4096)
- //! Compute the absolute value of each pixel value.
- /**
- Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its absolute value \f$|I_{(x,y,z,c)}|\f$.
- \note
- - The \inplace of this method statically casts the computed values to the pixel type \c T.
- - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
- **/
- _cimg_create_pointwise_functions(abs,cimg::abs,524288)
- //! Compute the sign of each pixel value.
- /**
- Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sign
- \f$\mathrm{sign}(I_{(x,y,z,c)})\f$.
- \note
- - The sign is set to:
- - \c 1 if pixel value is strictly positive.
- - \c -1 if pixel value is strictly negative.
- - \c 0 if pixel value is equal to \c 0.
- - The \inplace of this method statically casts the computed values to the pixel type \c T.
- - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
- **/
- _cimg_create_pointwise_functions(sign,cimg::sign,32768)
- //! Compute the cosine of each pixel value.
- /**
- Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its cosine \f$\cos(I_{(x,y,z,c)})\f$.
- \note
- - Pixel values are regarded as being in \e radian.
- - The \inplace of this method statically casts the computed values to the pixel type \c T.
- - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
- **/
- _cimg_create_pointwise_functions(cos,std::cos,8192)
- //! Compute the sine of each pixel value.
- /**
- Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sine \f$\sin(I_{(x,y,z,c)})\f$.
- \note
- - Pixel values are regarded as being in \e radian.
- - The \inplace of this method statically casts the computed values to the pixel type \c T.
- - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
- **/
- _cimg_create_pointwise_functions(sin,std::sin,8192)
- //! Compute the sinc of each pixel value.
- /**
- Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sinc
- \f$\mathrm{sinc}(I_{(x,y,z,c)})\f$.
- \note
- - Pixel values are regarded as being exin \e radian.
- - The \inplace of this method statically casts the computed values to the pixel type \c T.
- - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
- **/
- _cimg_create_pointwise_functions(sinc,cimg::sinc,2048)
- //! Compute the tangent of each pixel value.
- /**
- Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its tangent \f$\tan(I_{(x,y,z,c)})\f$.
- \note
- - Pixel values are regarded as being exin \e radian.
- - The \inplace of this method statically casts the computed values to the pixel type \c T.
- - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
- **/
- _cimg_create_pointwise_functions(tan,std::tan,2048)
- //! Compute the hyperbolic cosine of each pixel value.
- /**
- Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic cosine
- \f$\mathrm{cosh}(I_{(x,y,z,c)})\f$.
- \note
- - The \inplace of this method statically casts the computed values to the pixel type \c T.
- - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
- **/
- _cimg_create_pointwise_functions(cosh,std::cosh,2048)
- //! Compute the hyperbolic sine of each pixel value.
- /**
- Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic sine
- \f$\mathrm{sinh}(I_{(x,y,z,c)})\f$.
- \note
- - The \inplace of this method statically casts the computed values to the pixel type \c T.
- - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
- **/
- _cimg_create_pointwise_functions(sinh,std::sinh,2048)
- //! Compute the hyperbolic tangent of each pixel value.
- /**
- Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic tangent
- \f$\mathrm{tanh}(I_{(x,y,z,c)})\f$.
- \note
- - The \inplace of this method statically casts the computed values to the pixel type \c T.
- - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
- **/
- _cimg_create_pointwise_functions(tanh,std::tanh,2048)
- //! Compute the arccosine of each pixel value.
- /**
- Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosine
- \f$\mathrm{acos}(I_{(x,y,z,c)})\f$.
- \note
- - The \inplace of this method statically casts the computed values to the pixel type \c T.
- - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
- **/
- _cimg_create_pointwise_functions(acos,std::acos,8192)
- //! Compute the arcsine of each pixel value.
- /**
- Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arcsine
- \f$\mathrm{asin}(I_{(x,y,z,c)})\f$.
- \note
- - The \inplace of this method statically casts the computed values to the pixel type \c T.
- - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
- **/
- _cimg_create_pointwise_functions(asin,std::asin,8192)
- //! Compute the arctangent of each pixel value.
- /**
- Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent
- \f$\mathrm{atan}(I_{(x,y,z,c)})\f$.
- \note
- - The \inplace of this method statically casts the computed values to the pixel type \c T.
- - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
- **/
- _cimg_create_pointwise_functions(atan,std::atan,8192)
- //! Compute the arctangent2 of each pixel value.
- /**
- Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent2
- \f$\mathrm{atan2}(I_{(x,y,z,c)})\f$.
- \param img Image whose pixel values specify the second argument of the \c atan2() function.
- \note
- - The \inplace of this method statically casts the computed values to the pixel type \c T.
- - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
- \par Example
- \code
- const CImg<float>
- img_x(100,100,1,1,"x-w/2",false), // Define an horizontal centered gradient, from '-width/2' to 'width/2'
- img_y(100,100,1,1,"y-h/2",false), // Define a vertical centered gradient, from '-height/2' to 'height/2'
- img_atan2 = img_y.get_atan2(img_x); // Compute atan2(y,x) for each pixel value
- (img_x,img_y,img_atan2).display();
- \endcode
- **/
- template<typename t>
- CImg<T>& atan2(const CImg<t>& img) {
- const ulongT siz = size(), isiz = img.size();
- if (siz && isiz) {
- if (is_overlapped(img)) return atan2(+img);
- T *ptrd = _data, *const ptre = _data + siz;
- if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
- for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
- *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++));
- for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++));
- }
- return *this;
- }
- //! Compute the arctangent2 of each pixel value \newinstance.
- template<typename t>
- CImg<Tfloat> get_atan2(const CImg<t>& img) const {
- return CImg<Tfloat>(*this,false).atan2(img);
- }
- //! Compute the hyperbolic arccosine of each pixel value.
- /**
- Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosineh
- \f$\mathrm{acosh}(I_{(x,y,z,c)})\f$.
- \note
- - The \inplace of this method statically casts the computed values to the pixel type \c T.
- - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
- **/
- _cimg_create_pointwise_functions(acosh,cimg::acosh,8192)
- //! Compute the hyperbolic arcsine of each pixel value.
- /**
- Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic arcsine
- \f$\mathrm{asinh}(I_{(x,y,z,c)})\f$.
- \note
- - The \inplace of this method statically casts the computed values to the pixel type \c T.
- - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
- **/
- _cimg_create_pointwise_functions(asinh,cimg::asinh,8192)
- //! Compute the hyperbolic arctangent of each pixel value.
- /**
- Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic arctangent
- \f$\mathrm{atanh}(I_{(x,y,z,c)})\f$.
- \note
- - The \inplace of this method statically casts the computed values to the pixel type \c T.
- - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
- **/
- _cimg_create_pointwise_functions(atanh,cimg::atanh,8192)
- //! In-place pointwise multiplication.
- /**
- Compute the pointwise multiplication between the image instance and the specified input image \c img.
- \param img Input image, as the second operand of the multiplication.
- \note
- - Similar to operator+=(const CImg<t>&), except that it performs a pointwise multiplication
- instead of an addition.
- - It does \e not perform a \e matrix multiplication. For this purpose, use operator*=(const CImg<t>&) instead.
- \par Example
- \code
- CImg<float>
- img("reference.jpg"),
- shade(img.width,img.height(),1,1,"-(x-w/2)^2-(y-h/2)^2",false);
- shade.normalize(0,1);
- (img,shade,img.get_mul(shade)).display();
- \endcode
- **/
- template<typename t>
- CImg<T>& mul(const CImg<t>& img) {
- const ulongT siz = size(), isiz = img.size();
- if (siz && isiz) {
- if (is_overlapped(img)) return mul(+img);
- T *ptrd = _data, *const ptre = _data + siz;
- if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
- for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
- *ptrd = (T)(*ptrd * *(ptrs++));
- for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd * *(ptrs++));
- }
- return *this;
- }
- //! In-place pointwise multiplication \newinstance.
- template<typename t>
- CImg<_cimg_Tt> get_mul(const CImg<t>& img) const {
- return CImg<_cimg_Tt>(*this,false).mul(img);
- }
- //! In-place pointwise division.
- /**
- Similar to mul(const CImg<t>&), except that it performs a pointwise division instead of a multiplication.
- **/
- template<typename t>
- CImg<T>& div(const CImg<t>& img) {
- const ulongT siz = size(), isiz = img.size();
- if (siz && isiz) {
- if (is_overlapped(img)) return div(+img);
- T *ptrd = _data, *const ptre = _data + siz;
- if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
- for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
- *ptrd = (T)(*ptrd / *(ptrs++));
- for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd / *(ptrs++));
- }
- return *this;
- }
- //! In-place pointwise division \newinstance.
- template<typename t>
- CImg<_cimg_Tt> get_div(const CImg<t>& img) const {
- return CImg<_cimg_Tt>(*this,false).div(img);
- }
- //! Raise each pixel value to a specified power.
- /**
- Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its power \f$I_{(x,y,z,c)}^p\f$.
- \param p Exponent value.
- \note
- - The \inplace of this method statically casts the computed values to the pixel type \c T.
- - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
- \par Example
- \code
- const CImg<float>
- img0("reference.jpg"), // Load reference color image
- img1 = (img0/255).pow(1.8)*=255, // Compute gamma correction, with gamma = 1.8
- img2 = (img0/255).pow(0.5)*=255; // Compute gamma correction, with gamma = 0.5
- (img0,img1,img2).display();
- \endcode
- **/
- CImg<T>& pow(const double p) {
- if (is_empty()) return *this;
- if (p==-4) { cimg_openmp_for(*this,1/(Tfloat)cimg::pow4(*ptr),32768); return *this; }
- if (p==-3) { cimg_openmp_for(*this,1/(Tfloat)cimg::pow3(*ptr),32768); return *this; }
- if (p==-2) { cimg_openmp_for(*this,1/(Tfloat)cimg::sqr(*ptr),32768); return *this; }
- if (p==-1) { cimg_openmp_for(*this,1/(Tfloat)*ptr,32768); return *this; }
- if (p==-0.5) { cimg_openmp_for(*this,1/std::sqrt((Tfloat)*ptr),8192); return *this; }
- if (p==0) return fill((T)1);
- if (p==0.5) return sqrt();
- if (p==1) return *this;
- if (p==2) return sqr();
- if (p==3) { cimg_openmp_for(*this,cimg::pow3(*ptr),262144); return *this; }
- if (p==4) { cimg_openmp_for(*this,cimg::pow4(*ptr),131072); return *this; }
- cimg_openmp_for(*this,std::pow((Tfloat)*ptr,(Tfloat)p),1024);
- return *this;
- }
- //! Raise each pixel value to a specified power \newinstance.
- CImg<Tfloat> get_pow(const double p) const {
- return CImg<Tfloat>(*this,false).pow(p);
- }
- //! Raise each pixel value to a power, specified from an expression.
- /**
- Similar to operator+=(const char*), except it performs a pointwise exponentiation instead of an addition.
- **/
- CImg<T>& pow(const char *const expression) {
- return pow((+*this)._fill(expression,true,3,0,"pow",this,0));
- }
- //! Raise each pixel value to a power, specified from an expression \newinstance.
- CImg<Tfloat> get_pow(const char *const expression) const {
- return CImg<Tfloat>(*this,false).pow(expression);
- }
- //! Raise each pixel value to a power, pointwisely specified from another image.
- /**
- Similar to operator+=(const CImg<t>& img), except that it performs an exponentiation instead of an addition.
- **/
- template<typename t>
- CImg<T>& pow(const CImg<t>& img) {
- const ulongT siz = size(), isiz = img.size();
- if (siz && isiz) {
- if (is_overlapped(img)) return pow(+img);
- T *ptrd = _data, *const ptre = _data + siz;
- if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
- for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
- *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
- for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
- }
- return *this;
- }
- //! Raise each pixel value to a power, pointwisely specified from another image \newinstance.
- template<typename t>
- CImg<Tfloat> get_pow(const CImg<t>& img) const {
- return CImg<Tfloat>(*this,false).pow(img);
- }
- //! Compute the bitwise left rotation of each pixel value.
- /**
- Similar to operator<<=(unsigned int), except that it performs a left rotation instead of a left shift.
- **/
- CImg<T>& rol(const unsigned int n=1) {
- if (is_empty()) return *this;
- cimg_openmp_for(*this,cimg::rol(*ptr,n),32768);
- return *this;
- }
- //! Compute the bitwise left rotation of each pixel value \newinstance.
- CImg<T> get_rol(const unsigned int n=1) const {
- return (+*this).rol(n);
- }
- //! Compute the bitwise left rotation of each pixel value.
- /**
- Similar to operator<<=(const char*), except that it performs a left rotation instead of a left shift.
- **/
- CImg<T>& rol(const char *const expression) {
- return rol((+*this)._fill(expression,true,3,0,"rol",this,0));
- }
- //! Compute the bitwise left rotation of each pixel value \newinstance.
- CImg<T> get_rol(const char *const expression) const {
- return (+*this).rol(expression);
- }
- //! Compute the bitwise left rotation of each pixel value.
- /**
- Similar to operator<<=(const CImg<t>&), except that it performs a left rotation instead of a left shift.
- **/
- template<typename t>
- CImg<T>& rol(const CImg<t>& img) {
- const ulongT siz = size(), isiz = img.size();
- if (siz && isiz) {
- if (is_overlapped(img)) return rol(+img);
- T *ptrd = _data, *const ptre = _data + siz;
- if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
- for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
- *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
- for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
- }
- return *this;
- }
- //! Compute the bitwise left rotation of each pixel value \newinstance.
- template<typename t>
- CImg<T> get_rol(const CImg<t>& img) const {
- return (+*this).rol(img);
- }
- //! Compute the bitwise right rotation of each pixel value.
- /**
- Similar to operator>>=(unsigned int), except that it performs a right rotation instead of a right shift.
- **/
- CImg<T>& ror(const unsigned int n=1) {
- if (is_empty()) return *this;
- cimg_openmp_for(*this,cimg::ror(*ptr,n),32768);
- return *this;
- }
- //! Compute the bitwise right rotation of each pixel value \newinstance.
- CImg<T> get_ror(const unsigned int n=1) const {
- return (+*this).ror(n);
- }
- //! Compute the bitwise right rotation of each pixel value.
- /**
- Similar to operator>>=(const char*), except that it performs a right rotation instead of a right shift.
- **/
- CImg<T>& ror(const char *const expression) {
- return ror((+*this)._fill(expression,true,3,0,"ror",this,0));
- }
- //! Compute the bitwise right rotation of each pixel value \newinstance.
- CImg<T> get_ror(const char *const expression) const {
- return (+*this).ror(expression);
- }
- //! Compute the bitwise right rotation of each pixel value.
- /**
- Similar to operator>>=(const CImg<t>&), except that it performs a right rotation instead of a right shift.
- **/
- template<typename t>
- CImg<T>& ror(const CImg<t>& img) {
- const ulongT siz = size(), isiz = img.size();
- if (siz && isiz) {
- if (is_overlapped(img)) return ror(+img);
- T *ptrd = _data, *const ptre = _data + siz;
- if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
- for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
- *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
- for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
- }
- return *this;
- }
- //! Compute the bitwise right rotation of each pixel value \newinstance.
- template<typename t>
- CImg<T> get_ror(const CImg<t>& img) const {
- return (+*this).ror(img);
- }
- //! Pointwise min operator between instance image and a value.
- /**
- \param val Value used as the reference argument of the min operator.
- \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
- \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{val})\f$.
- **/
- CImg<T>& min(const T& value) {
- if (is_empty()) return *this;
- cimg_openmp_for(*this,std::min(*ptr,value),65536);
- return *this;
- }
- //! Pointwise min operator between instance image and a value \newinstance.
- CImg<T> get_min(const T& value) const {
- return (+*this).min(value);
- }
- //! Pointwise min operator between two images.
- /**
- \param img Image used as the reference argument of the min operator.
- \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
- \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
- **/
- template<typename t>
- CImg<T>& min(const CImg<t>& img) {
- const ulongT siz = size(), isiz = img.size();
- if (siz && isiz) {
- if (is_overlapped(img)) return min(+img);
- T *ptrd = _data, *const ptre = _data + siz;
- if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
- for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
- *ptrd = std::min((T)*(ptrs++),*ptrd);
- for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = std::min((T)*(ptrs++),*ptrd);
- }
- return *this;
- }
- //! Pointwise min operator between two images \newinstance.
- template<typename t>
- CImg<_cimg_Tt> get_min(const CImg<t>& img) const {
- return CImg<_cimg_Tt>(*this,false).min(img);
- }
- //! Pointwise min operator between an image and an expression.
- /**
- \param expression Math formula as a C-string.
- \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
- \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
- **/
- CImg<T>& min(const char *const expression) {
- return min((+*this)._fill(expression,true,3,0,"min",this,0));
- }
- //! Pointwise min operator between an image and an expression \newinstance.
- CImg<Tfloat> get_min(const char *const expression) const {
- return CImg<Tfloat>(*this,false).min(expression);
- }
- //! Pointwise max operator between instance image and a value.
- /**
- \param val Value used as the reference argument of the max operator.
- \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
- \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{val})\f$.
- **/
- CImg<T>& max(const T& value) {
- if (is_empty()) return *this;
- cimg_openmp_for(*this,std::max(*ptr,value),65536);
- return *this;
- }
- //! Pointwise max operator between instance image and a value \newinstance.
- CImg<T> get_max(const T& value) const {
- return (+*this).max(value);
- }
- //! Pointwise max operator between two images.
- /**
- \param img Image used as the reference argument of the max operator.
- \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
- \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
- **/
- template<typename t>
- CImg<T>& max(const CImg<t>& img) {
- const ulongT siz = size(), isiz = img.size();
- if (siz && isiz) {
- if (is_overlapped(img)) return max(+img);
- T *ptrd = _data, *const ptre = _data + siz;
- if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
- for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
- *ptrd = std::max((T)*(ptrs++),*ptrd);
- for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = std::max((T)*(ptrs++),*ptrd);
- }
- return *this;
- }
- //! Pointwise max operator between two images \newinstance.
- template<typename t>
- CImg<_cimg_Tt> get_max(const CImg<t>& img) const {
- return CImg<_cimg_Tt>(*this,false).max(img);
- }
- //! Pointwise max operator between an image and an expression.
- /**
- \param expression Math formula as a C-string.
- \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
- \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
- **/
- CImg<T>& max(const char *const expression) {
- return max((+*this)._fill(expression,true,3,0,"max",this,0));
- }
- //! Pointwise max operator between an image and an expression \newinstance.
- CImg<Tfloat> get_max(const char *const expression) const {
- return CImg<Tfloat>(*this,false).max(expression);
- }
- //! Pointwise minabs operator between instance image and a value.
- /**
- \param val Value used as the reference argument of the minabs operator.
- \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
- \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{val})\f$.
- **/
- CImg<T>& minabs(const T& value) {
- if (is_empty()) return *this;
- const T absvalue = cimg::abs(value);
- cimg_openmp_for(*this,cimg::minabs(*ptr,value,absvalue),65536);
- return *this;
- }
- //! Pointwise minabs operator between instance image and a value \newinstance.
- CImg<T> get_minabs(const T& value) const {
- return (+*this).minabs(value);
- }
- //! Pointwise minabs operator between two images.
- /**
- \param img Image used as the reference argument of the minabs operator.
- \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
- \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
- **/
- template<typename t>
- CImg<T>& minabs(const CImg<t>& img) {
- const ulongT siz = size(), isiz = img.size();
- if (siz && isiz) {
- if (is_overlapped(img)) return minabs(+img);
- T *ptrd = _data, *const ptre = _data + siz;
- if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
- for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
- *ptrd = cimg::minabs((T)*(ptrs++),*ptrd);
- for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::minabs((T)*(ptrs++),*ptrd);
- }
- return *this;
- }
- //! Pointwise minabs operator between two images \newinstance.
- template<typename t>
- CImg<_cimg_Tt> get_minabs(const CImg<t>& img) const {
- return CImg<_cimg_Tt>(*this,false).minabs(img);
- }
- //! Pointwise minabs operator between an image and an expression.
- /**
- \param expression Math formula as a C-string.
- \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
- \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
- **/
- CImg<T>& minabs(const char *const expression) {
- return minabs((+*this)._fill(expression,true,3,0,"minabs",this,0));
- }
- //! Pointwise minabs operator between an image and an expression \newinstance.
- CImg<Tfloat> get_minabs(const char *const expression) const {
- return CImg<Tfloat>(*this,false).minabs(expression);
- }
- //! Pointwise maxabs operator between instance image and a value.
- /**
- \param val Value used as the reference argument of the maxabs operator.
- \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
- \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{val})\f$.
- **/
- CImg<T>& maxabs(const T& value) {
- if (is_empty()) return *this;
- const T absvalue = cimg::abs(value);
- cimg_openmp_for(*this,cimg::maxabs(*ptr,value,absvalue),65536);
- return *this;
- }
- //! Pointwise maxabs operator between instance image and a value \newinstance.
- CImg<T> get_maxabs(const T& value) const {
- return (+*this).maxabs(value);
- }
- //! Pointwise maxabs operator between two images.
- /**
- \param img Image used as the reference argument of the maxabs operator.
- \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
- \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
- **/
- template<typename t>
- CImg<T>& maxabs(const CImg<t>& img) {
- const ulongT siz = size(), isiz = img.size();
- if (siz && isiz) {
- if (is_overlapped(img)) return maxabs(+img);
- T *ptrd = _data, *const ptre = _data + siz;
- if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
- for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
- *ptrd = cimg::maxabs((T)*(ptrs++),*ptrd);
- for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::maxabs((T)*(ptrs++),*ptrd);
- }
- return *this;
- }
- //! Pointwise maxabs operator between two images \newinstance.
- template<typename t>
- CImg<_cimg_Tt> get_maxabs(const CImg<t>& img) const {
- return CImg<_cimg_Tt>(*this,false).maxabs(img);
- }
- //! Pointwise maxabs operator between an image and an expression.
- /**
- \param expression Math formula as a C-string.
- \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
- \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
- **/
- CImg<T>& maxabs(const char *const expression) {
- return maxabs((+*this)._fill(expression,true,3,0,"maxabs",this,0));
- }
- //! Pointwise maxabs operator between an image and an expression \newinstance.
- CImg<Tfloat> get_maxabs(const char *const expression) const {
- return CImg<Tfloat>(*this,false).maxabs(expression);
- }
- //! Return a reference to the minimum pixel value.
- /**
- **/
- T& min() {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "min(): Empty instance.",
- cimg_instance);
- T *ptr_min = _data;
- T min_value = *ptr_min;
- cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
- return *ptr_min;
- }
- //! Return a reference to the minimum pixel value \const.
- const T& min() const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "min(): Empty instance.",
- cimg_instance);
- const T *ptr_min = _data;
- T min_value = *ptr_min;
- cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
- return *ptr_min;
- }
- //! Return a reference to the minimum pixel value in absolute value.
- /**
- **/
- T& minabs() {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "minabs(): Empty instance.",
- cimg_instance);
- T *ptr_minabs = _data;
- T minabs_value = *ptr_minabs;
- cimg_for(*this,ptrs,T) {
- const T ma = cimg::abs(*ptrs);
- if (ma<minabs_value) { minabs_value = ma; ptr_minabs = ptrs; }
- }
- return *ptr_minabs;
- }
- //! Return a reference to the minimum pixel value in absolute value \const.
- const T& minabs() const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "minabs(): Empty instance.",
- cimg_instance);
- const T *ptr_minabs = _data;
- T minabs_value = *ptr_minabs;
- cimg_for(*this,ptrs,T) {
- const T ma = cimg::abs(*ptrs);
- if (ma<minabs_value) { minabs_value = ma; ptr_minabs = ptrs; }
- }
- return *ptr_minabs;
- }
- //! Return a reference to the maximum pixel value.
- /**
- **/
- T& max() {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "max(): Empty instance.",
- cimg_instance);
- T *ptr_max = _data;
- T max_value = *ptr_max;
- cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
- return *ptr_max;
- }
- //! Return a reference to the maximum pixel value \const.
- const T& max() const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "max(): Empty instance.",
- cimg_instance);
- const T *ptr_max = _data;
- T max_value = *ptr_max;
- cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
- return *ptr_max;
- }
- //! Return a reference to the maximum pixel value in absolute value.
- /**
- **/
- T& maxabs() {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "maxabs(): Empty instance.",
- cimg_instance);
- T *ptr_maxabs = _data;
- T maxabs_value = *ptr_maxabs;
- cimg_for(*this,ptrs,T) {
- const T ma = cimg::abs(*ptrs);
- if (ma>maxabs_value) { maxabs_value = ma; ptr_maxabs = ptrs; }
- }
- return *ptr_maxabs;
- }
- //! Return a reference to the maximum pixel value in absolute value \const.
- const T& maxabs() const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "maxabs(): Empty instance.",
- cimg_instance);
- const T *ptr_maxabs = _data;
- T maxabs_value = *ptr_maxabs;
- cimg_for(*this,ptrs,T) {
- const T ma = cimg::abs(*ptrs);
- if (ma>maxabs_value) { maxabs_value = ma; ptr_maxabs = ptrs; }
- }
- return *ptr_maxabs;
- }
- //! Return a reference to the minimum pixel value as well as the maximum pixel value.
- /**
- \param[out] max_val Maximum pixel value.
- **/
- template<typename t>
- T& min_max(t& max_val) {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "min_max(): Empty instance.",
- cimg_instance);
- T *ptr_min = _data;
- T min_value = *ptr_min, max_value = min_value;
- cimg_for(*this,ptrs,T) {
- const T val = *ptrs;
- if (val<min_value) { min_value = val; ptr_min = ptrs; }
- if (val>max_value) max_value = val;
- }
- max_val = (t)max_value;
- return *ptr_min;
- }
- //! Return a reference to the minimum pixel value as well as the maximum pixel value \const.
- template<typename t>
- const T& min_max(t& max_val) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "min_max(): Empty instance.",
- cimg_instance);
- const T *ptr_min = _data;
- T min_value = *ptr_min, max_value = min_value;
- cimg_for(*this,ptrs,T) {
- const T val = *ptrs;
- if (val<min_value) { min_value = val; ptr_min = ptrs; }
- if (val>max_value) max_value = val;
- }
- max_val = (t)max_value;
- return *ptr_min;
- }
- //! Return a reference to the maximum pixel value as well as the minimum pixel value.
- /**
- \param[out] min_val Minimum pixel value.
- **/
- template<typename t>
- T& max_min(t& min_val) {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "max_min(): Empty instance.",
- cimg_instance);
- T *ptr_max = _data;
- T max_value = *ptr_max, min_value = max_value;
- cimg_for(*this,ptrs,T) {
- const T val = *ptrs;
- if (val>max_value) { max_value = val; ptr_max = ptrs; }
- if (val<min_value) min_value = val;
- }
- min_val = (t)min_value;
- return *ptr_max;
- }
- //! Return a reference to the maximum pixel value as well as the minimum pixel value \const.
- template<typename t>
- const T& max_min(t& min_val) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "max_min(): Empty instance.",
- cimg_instance);
- const T *ptr_max = _data;
- T max_value = *ptr_max, min_value = max_value;
- cimg_for(*this,ptrs,T) {
- const T val = *ptrs;
- if (val>max_value) { max_value = val; ptr_max = ptrs; }
- if (val<min_value) min_value = val;
- }
- min_val = (t)min_value;
- return *ptr_max;
- }
- //! Return the kth smallest pixel value.
- /**
- \param k Rank of the smallest element searched.
- **/
- T kth_smallest(const ulongT k) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "kth_smallest(): Empty instance.",
- cimg_instance);
- if (k>=size()) return max();
- CImg<T> arr(*this,false);
- ulongT l = 0, ir = size() - 1;
- for ( ; ; ) {
- if (ir<=l + 1) {
- if (ir==l + 1 && arr[ir]<arr[l]) cimg::swap(arr[l],arr[ir]);
- return arr[k];
- } else {
- const ulongT mid = (l + ir)>>1;
- cimg::swap(arr[mid],arr[l + 1]);
- if (arr[l]>arr[ir]) cimg::swap(arr[l],arr[ir]);
- if (arr[l + 1]>arr[ir]) cimg::swap(arr[l + 1],arr[ir]);
- if (arr[l]>arr[l + 1]) cimg::swap(arr[l],arr[l + 1]);
- ulongT i = l + 1, j = ir;
- const T pivot = arr[l + 1];
- for ( ; ; ) {
- do ++i; while (arr[i]<pivot);
- do --j; while (arr[j]>pivot);
- if (j<i) break;
- cimg::swap(arr[i],arr[j]);
- }
- arr[l + 1] = arr[j];
- arr[j] = pivot;
- if (j>=k) ir = j - 1;
- if (j<=k) l = i;
- }
- }
- }
- //! Return the median pixel value.
- /**
- **/
- T median() const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "median(): Empty instance.",
- cimg_instance);
- const ulongT s = size();
- switch (s) {
- case 1 : return _data[0];
- case 2 : return cimg::median(_data[0],_data[1]);
- case 3 : return cimg::median(_data[0],_data[1],_data[2]);
- case 5 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4]);
- case 7 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6]);
- case 9 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6],_data[7],_data[8]);
- case 13 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6],_data[7],_data[8],
- _data[9],_data[10],_data[11],_data[12]);
- }
- const T res = kth_smallest(s>>1);
- return (s%2)?res:(T)((res + kth_smallest((s>>1) - 1))/2);
- }
- //! Return the product of all the pixel values.
- /**
- **/
- double product() const {
- if (is_empty()) return 0;
- double res = 1;
- cimg_for(*this,ptrs,T) res*=(double)*ptrs;
- return res;
- }
- //! Return the sum of all the pixel values.
- /**
- **/
- double sum() const {
- double res = 0;
- cimg_for(*this,ptrs,T) res+=(double)*ptrs;
- return res;
- }
- //! Return the average pixel value.
- /**
- **/
- double mean() const {
- double res = 0;
- cimg_for(*this,ptrs,T) res+=(double)*ptrs;
- return res/size();
- }
- //! Return the variance of the pixel values.
- /**
- \param variance_method Method used to estimate the variance. Can be:
- - \c 0: Second moment, computed as
- \f$1/N \sum\limits_{k=1}^{N} (x_k - \bar x)^2 =
- 1/N \left( \sum\limits_{k=1}^N x_k^2 - \left( \sum\limits_{k=1}^N x_k \right)^2 / N \right)\f$
- with \f$ \bar x = 1/N \sum\limits_{k=1}^N x_k \f$.
- - \c 1: Best unbiased estimator, computed as \f$\frac{1}{N - 1} \sum\limits_{k=1}^{N} (x_k - \bar x)^2 \f$.
- - \c 2: Least median of squares.
- - \c 3: Least trimmed of squares.
- **/
- double variance(const unsigned int variance_method=1) const {
- double foo;
- return variance_mean(variance_method,foo);
- }
- //! Return the variance as well as the average of the pixel values.
- /**
- \param variance_method Method used to estimate the variance (see variance(const unsigned int) const).
- \param[out] mean Average pixel value.
- **/
- template<typename t>
- double variance_mean(const unsigned int variance_method, t& mean) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "variance_mean(): Empty instance.",
- cimg_instance);
- double variance = 0, average = 0;
- const ulongT siz = size();
- switch (variance_method) {
- case 0 : { // Least mean square (standard definition)
- double S = 0, S2 = 0;
- cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; }
- variance = (S2 - S*S/siz)/siz;
- average = S;
- } break;
- case 1 : { // Least mean square (robust definition)
- double S = 0, S2 = 0;
- cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; }
- variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
- average = S;
- } break;
- case 2 : { // Least Median of Squares (MAD)
- CImg<Tfloat> buf(*this,false);
- buf.sort();
- const ulongT siz2 = siz>>1;
- const double med_i = (double)buf[siz2];
- cimg_for(buf,ptrs,Tfloat) {
- const double val = (double)*ptrs; *ptrs = (Tfloat)cimg::abs(val - med_i); average+=val;
- }
- buf.sort();
- const double sig = (double)(1.4828*buf[siz2]);
- variance = sig*sig;
- } break;
- default : { // Least trimmed of Squares
- CImg<Tfloat> buf(*this,false);
- const ulongT siz2 = siz>>1;
- cimg_for(buf,ptrs,Tfloat) {
- const double val = (double)*ptrs; (*ptrs)=(Tfloat)((*ptrs)*val); average+=val;
- }
- buf.sort();
- double a = 0;
- const Tfloat *ptrs = buf._data;
- for (ulongT j = 0; j<siz2; ++j) a+=(double)*(ptrs++);
- const double sig = (double)(2.6477*std::sqrt(a/siz2));
- variance = sig*sig;
- }
- }
- mean = (t)(average/siz);
- return variance>0?variance:0;
- }
- //! Return estimated variance of the noise.
- /**
- \param variance_method Method used to compute the variance (see variance(const unsigned int) const).
- \note Because of structures such as edges in images it is
- recommended to use a robust variance estimation. The variance of the
- noise is estimated by computing the variance of the Laplacian \f$(\Delta
- I)^2 \f$ scaled by a factor \f$c\f$ insuring \f$ c E[(\Delta I)^2]=
- \sigma^2\f$ where \f$\sigma\f$ is the noise variance.
- **/
- double variance_noise(const unsigned int variance_method=2) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "variance_noise(): Empty instance.",
- cimg_instance);
- const ulongT siz = size();
- if (!siz || !_data) return 0;
- if (variance_method>1) { // Compute a scaled version of the Laplacian
- CImg<Tdouble> tmp(*this,false);
- if (_depth==1) {
- const double cste = 1./std::sqrt(20.); // Depends on how the Laplacian is computed
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*262144 &&
- _spectrum>=2))
- cimg_forC(*this,c) {
- CImg_3x3(I,T);
- cimg_for3x3(*this,x,y,0,c,I,T) {
- tmp(x,y,c) = cste*((double)Inc + (double)Ipc + (double)Icn +
- (double)Icp - 4*(double)Icc);
- }
- }
- } else {
- const double cste = 1./std::sqrt(42.); // Depends on how the Laplacian is computed
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*262144 &&
- _spectrum>=2))
- cimg_forC(*this,c) {
- CImg_3x3x3(I,T);
- cimg_for3x3x3(*this,x,y,z,c,I,T) {
- tmp(x,y,z,c) = cste*(
- (double)Incc + (double)Ipcc + (double)Icnc + (double)Icpc +
- (double)Iccn + (double)Iccp - 6*(double)Iccc);
- }
- }
- }
- return tmp.variance(variance_method);
- }
- // Version that doesn't need intermediate images.
- double variance = 0, S = 0, S2 = 0;
- if (_depth==1) {
- const double cste = 1./std::sqrt(20.);
- CImg_3x3(I,T);
- cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) {
- const double val = cste*((double)Inc + (double)Ipc +
- (double)Icn + (double)Icp - 4*(double)Icc);
- S+=val; S2+=val*val;
- }
- } else {
- const double cste = 1./std::sqrt(42.);
- CImg_3x3x3(I,T);
- cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,T) {
- const double val = cste *
- ((double)Incc + (double)Ipcc + (double)Icnc +
- (double)Icpc +
- (double)Iccn + (double)Iccp - 6*(double)Iccc);
- S+=val; S2+=val*val;
- }
- }
- if (variance_method) variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
- else variance = (S2 - S*S/siz)/siz;
- return variance>0?variance:0;
- }
- //! Compute the MSE (Mean-Squared Error) between two images.
- /**
- \param img Image used as the second argument of the MSE operator.
- **/
- template<typename t>
- double MSE(const CImg<t>& img) const {
- if (img.size()!=size())
- throw CImgArgumentException(_cimg_instance
- "MSE(): Instance and specified image (%u,%u,%u,%u,%p) have different dimensions.",
- cimg_instance,
- img._width,img._height,img._depth,img._spectrum,img._data);
- double vMSE = 0;
- const t* ptr2 = img._data;
- cimg_for(*this,ptr1,T) {
- const double diff = (double)*ptr1 - (double)*(ptr2++);
- vMSE+=diff*diff;
- }
- const ulongT siz = img.size();
- if (siz) vMSE/=siz;
- return vMSE;
- }
- //! Compute the PSNR (Peak Signal-to-Noise Ratio) between two images.
- /**
- \param img Image used as the second argument of the PSNR operator.
- \param max_value Maximum theoretical value of the signal.
- **/
- template<typename t>
- double PSNR(const CImg<t>& img, const double max_value=255) const {
- const double vMSE = (double)std::sqrt(MSE(img));
- return (vMSE!=0)?(double)(20*std::log10(max_value/vMSE)):(double)(cimg::type<double>::max());
- }
- //! Evaluate math formula.
- /**
- \param expression Math formula, as a C-string.
- \param x Value of the pre-defined variable \c x.
- \param y Value of the pre-defined variable \c y.
- \param z Value of the pre-defined variable \c z.
- \param c Value of the pre-defined variable \c c.
- \param list_images A list of images attached to the specified math formula.
- **/
- double eval(const char *const expression,
- const double x=0, const double y=0, const double z=0, const double c=0,
- CImgList<T> *const list_images=0) {
- return _eval(this,expression,x,y,z,c,list_images);
- }
- //! Evaluate math formula \const.
- double eval(const char *const expression,
- const double x=0, const double y=0, const double z=0, const double c=0,
- CImgList<T> *const list_images=0) const {
- return _eval(0,expression,x,y,z,c,list_images);
- }
- // Fast function to pre-evaluate common expressions.
- // (return 'true' in case of success, and set value of 'res').
- template<typename t>
- bool __eval(const char *const expression, t &res) const {
- #define __eval_op(op) if (__eval_get(++ptr,val2) && !*ptr) { res = (t)(op); return true; } else return false;
- double val1, val2;
- if (!expression || !*expression || *expression==';' || *expression=='[') return false;
- if (!expression[1]) switch (*expression) {
- case 'w' : res = (t)_width; return true;
- case 'h' : res = (t)_height; return true;
- case 'd' : res = (t)_depth; return true;
- case 's' : res = (t)_spectrum; return true;
- case 'r' : res = (t)_is_shared; return true;
- default : if (*expression>='0' && *expression<='9') { res = (t)(*expression - '0'); return true; }
- }
- if (*expression=='w' && expression[1]=='h') {
- if (!expression[2]) { res = (t)(_width*_height); return true; }
- if (expression[2]=='d') {
- if (!expression[3]) { res = (t)(_width*_height*_depth); return true; }
- if (expression[3]=='s' && !expression[4]) { res = (t)(_width*_height*_depth*_spectrum); return true; }
- }
- if (expression[2]=='s' && !expression[3]) { res = (t)(_width*_height*_spectrum); return true; }
- }
- const char *ptr = expression;
- while (*ptr && cimg::is_blank(*ptr)) ++ptr;
- if (*ptr=='\'' && *(++ptr)) { // Detect 'stringA' op 'stringB' (op='==' or '!=')
- const char *ptr2 = std::strchr(ptr,'\'');
- if (ptr2) {
- const char *ptr3 = ptr2 + 1;
- while (*ptr3 && cimg::is_blank(*ptr3)) ++ptr3;
- const char *ptr4 = ptr3;
- if ((*ptr3=='!' || *ptr3=='=') && *(++ptr4)=='=') {
- ++ptr4;
- while (*ptr4 && cimg::is_blank(*ptr4)) ++ptr4;
- if (*ptr4=='\'' && *(++ptr4)) {
- const char *const ptr5 = std::strchr(ptr4,'\'');
- if (ptr5) {
- const char *ptr6 = ptr5 + 1;
- while (*ptr6 && cimg::is_blank(*ptr6)) ++ptr6;
- if (!*ptr6) {
- CImg<charT> str1(ptr,ptr2 - ptr,1,1,1,true), str2(ptr4,ptr5 - ptr4,1,1,1,true);
- if (*ptr3=='!') res = (t)!(str1==str2); else res = (t)(str1==str2);
- return true;
- }
- }
- }
- }
- }
- return false;
- }
- if (__eval_get(ptr,val1)) { // Detect 'value1' op 'value2'
- switch (*ptr) {
- case 0 : res = (t)val1; return true;
- case '+' : __eval_op(val1 + val2);
- case '-' : __eval_op(val1 - val2);
- case '*' : __eval_op(val1 * val2);
- case '/' : __eval_op(val1 / val2);
- case '%' : __eval_op(cimg::mod(val1,val2));
- case '&' : if (ptr[1]=='&') { ++ptr; __eval_op(val1 && val2); } else { __eval_op((long)val1 & (long)val2); }
- case '|' : if (ptr[1]=='|') { ++ptr; __eval_op(val1 || val2); } else { __eval_op((long)val1 | (long)val2); }
- case '>' : if (ptr[1]=='=') { ++ptr; __eval_op(val1>=val2); } else { __eval_op(val1>val2); }
- case '<' : if (ptr[1]=='=') { ++ptr; __eval_op(val1<=val2); } else { __eval_op(val1<val2); }
- case ';' : __eval_op(val2);
- case '^' : __eval_op(std::pow(val1,val2));
- case '=' : if (*++ptr=='=') { __eval_op(val1==val2); } else return false;
- case '!' : if (*++ptr=='=') { __eval_op(val1!=val2); } else return false;
- }
- }
- return false;
- }
- // Return 'true' is a single 'value' or '!value' has been succesfully read ('value' being a double or { w,h,d,s }).
- bool __eval_get(const char* &ptr, double &value) const {
- int n = 0;
- while (*ptr && cimg::is_blank(*ptr)) ++ptr;
- bool is_not = false; // Detect preceding '!' operator
- if (*ptr=='!') { is_not = true; ++ptr; while (*ptr && cimg::is_blank(*ptr)) ++ptr; }
- if ((*ptr=='w' || *ptr=='h' || *ptr=='d' || *ptr=='s' || *ptr=='r') || std::sscanf(ptr,"%lf %n",&value,&n)==1) {
- if (!n) {
- switch (*ptr) {
- case 'w': value = (double)_width; break;
- case 'h': value = (double)_height; break;
- case 'd': value = (double)_depth; break;
- case 's': value = (double)_spectrum; break;
- case 'r': value = (double)_is_shared; break;
- }
- ++ptr; while (*ptr && cimg::is_blank(*ptr)) ++ptr;
- } else ptr+=n;
- if (is_not) value = (double)!value;
- return true;
- }
- return false;
- }
- double _eval(CImg<T> *const img_output, const char *const expression,
- const double x, const double y, const double z, const double c,
- CImgList<T> *const list_images) const {
- if (!expression || !*expression) return 0;
- double _val = 0;
- if (__eval(expression,_val)) return _val;
- _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='+' ||
- *expression=='*' || *expression==':'),"eval",
- *this,img_output,list_images,false);
- mp.begin_t();
- const double val = mp(x,y,z,c);
- mp.end_t();
- mp.end();
- return val;
- }
- //! Evaluate math formula.
- /**
- \param[out] output Contains values of output vector returned by the evaluated expression
- (or is empty if the returned type is scalar).
- \param expression Math formula, as a C-string.
- \param x Value of the pre-defined variable \c x.
- \param y Value of the pre-defined variable \c y.
- \param z Value of the pre-defined variable \c z.
- \param c Value of the pre-defined variable \c c.
- \param list_images A list of input images attached to the specified math formula.
- **/
- template<typename t>
- void eval(CImg<t> &output, const char *const expression,
- const double x=0, const double y=0, const double z=0, const double c=0,
- CImgList<T> *const list_images=0) {
- _eval(output,this,expression,x,y,z,c,list_images);
- }
- //! Evaluate math formula \const.
- template<typename t>
- void eval(CImg<t>& output, const char *const expression,
- const double x=0, const double y=0, const double z=0, const double c=0,
- CImgList<T> *const list_images=0) const {
- _eval(output,0,expression,x,y,z,c,list_images);
- }
- template<typename t>
- void _eval(CImg<t>& output, CImg<T> *const img_output, const char *const expression,
- const double x, const double y, const double z, const double c,
- CImgList<T> *const list_images) const {
- if (!expression || !*expression) { output.assign(1); *output = 0; return; }
- double _val = 0;
- if (__eval(expression,_val)) { output.assign(1); *output = _val; return; }
- _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='+' ||
- *expression=='*' || *expression==':'),"eval",
- *this,img_output,list_images,false);
- output.assign(1,std::max(1U,mp.result_dim));
- mp.begin_t();
- mp(x,y,z,c,output._data);
- mp.end_t();
- mp.end();
- }
- //! Evaluate math formula on a set of variables.
- /**
- \param expression Math formula, as a C-string.
- \param xyzc Set of values (x,y,z,c) used for the evaluation.
- \param list_images A list of input images attached to the specified math formula.
- **/
- template<typename t>
- CImg<doubleT> eval(const char *const expression, const CImg<t>& xyzc,
- CImgList<T> *const list_images=0) {
- return _eval(this,expression,xyzc,list_images);
- }
- //! Evaluate math formula on a set of variables \const.
- template<typename t>
- CImg<doubleT> eval(const char *const expression, const CImg<t>& xyzc,
- CImgList<T> *const list_images=0) const {
- return _eval(0,expression,xyzc,list_images);
- }
- template<typename t>
- CImg<doubleT> _eval(CImg<T> *const output, const char *const expression, const CImg<t>& xyzc,
- CImgList<T> *const list_images=0) const {
- CImg<doubleT> res(1,xyzc.size()/4);
- if (!expression || !*expression) return res.fill(0);
- _cimg_math_parser mp(expression,"eval",*this,output,list_images,false);
- #if cimg_use_openmp!=0
- cimg_pragma_openmp(parallel if (res._height>=512))
- {
- _cimg_math_parser
- *const _mp = omp_get_thread_num()?new _cimg_math_parser(mp):&mp,
- &lmp = *_mp;
- cimg_pragma_openmp(barrier)
- lmp.begin_t();
- cimg_pragma_openmp(for)
- for (int i = 0; i<res.height(); ++i) {
- const unsigned int i4 = 4*i;
- const double
- x = (double)xyzc[i4], y = (double)xyzc[i4 + 1],
- z = (double)xyzc[i4 + 2], c = (double)xyzc[i4 + 3];
- res[i] = lmp(x,y,z,c);
- }
- lmp.end_t();
- cimg_pragma_openmp(barrier) cimg_pragma_openmp(critical) { lmp.merge(mp); }
- if (&lmp!=&mp) delete &lmp;
- }
- #else
- mp.begin_t();
- const t *ps = xyzc._data;
- cimg_for(res,pd,double) {
- const double x = (double)*(ps++), y = (double)*(ps++), z = (double)*(ps++), c = (double)*(ps++);
- *pd = mp(x,y,z,c);
- }
- mp.end_t();
- #endif
- mp.end();
- return res;
- }
- //! Compute statistics vector from the pixel values.
- /**
- \param variance_method Method used to compute the variance (see variance(const unsigned int) const).
- \return Statistics vector as
- <tt>[min, max, mean, variance, xmin, ymin, zmin, cmin, xmax, ymax, zmax, cmax, sum, product]</tt>.
- **/
- CImg<Tdouble> get_stats(const unsigned int variance_method=1) const {
- if (is_empty()) return CImg<doubleT>();
- const ulongT siz = size();
- const longT off_end = (longT)siz;
- double S = 0, S2 = 0, P = 1;
- longT offm = 0, offM = 0;
- T m = *_data, M = m;
- cimg_pragma_openmp(parallel reduction(+:S,S2) reduction(*:P) cimg_openmp_if_size(siz,131072)) {
- longT loffm = 0, loffM = 0;
- T lm = *_data, lM = lm;
- cimg_pragma_openmp(for)
- for (longT off = 0; off<off_end; ++off) {
- const T val = _data[off];
- const double _val = (double)val;
- if (val<lm) { lm = val; loffm = off; }
- if (val>lM) { lM = val; loffM = off; }
- S+=_val;
- S2+=_val*_val;
- P*=_val;
- }
- cimg_pragma_openmp(critical(get_stats)) {
- if (lm<m || (lm==m && loffm<offm)) { m = lm; offm = loffm; }
- if (lM>M || (lM==M && loffM<offM)) { M = lM; offM = loffM; }
- }
- }
- const double
- mean_value = S/siz,
- _variance_value = variance_method==0?(S2 - S*S/siz)/siz:
- (variance_method==1?(siz>1?(S2 - S*S/siz)/(siz - 1):0):
- variance(variance_method)),
- variance_value = _variance_value>0?_variance_value:0;
- int
- xm = 0, ym = 0, zm = 0, cm = 0,
- xM = 0, yM = 0, zM = 0, cM = 0;
- contains(_data[offm],xm,ym,zm,cm);
- contains(_data[offM],xM,yM,zM,cM);
- return CImg<Tdouble>(1,14).fill((double)m,(double)M,mean_value,variance_value,
- (double)xm,(double)ym,(double)zm,(double)cm,
- (double)xM,(double)yM,(double)zM,(double)cM,
- S,P);
- }
- //! Compute statistics vector from the pixel values \inplace.
- CImg<T>& stats(const unsigned int variance_method=1) {
- return get_stats(variance_method).move_to(*this);
- }
- //@}
- //-------------------------------------
- //
- //! \name Vector / Matrix Operations
- //@{
- //-------------------------------------
- //! Compute norm of the image, viewed as a matrix.
- /**
- \param magnitude_type Can be:
- - \c 0: L0-norm
- - \c 1: L1-norm
- - \c 2: L2-norm
- - \c p>2 : Lp-norm
- - \c ~0U: Linf-norm
- **/
- double magnitude(const float magnitude_type=2) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "magnitude(): Empty instance.",
- cimg_instance);
- const ulongT siz = size();
- double res = 0;
- if (magnitude_type==2) { // L2
- cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192))
- for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::sqr(_data[off]);
- res = (double)std::sqrt(res);
- } else if (magnitude_type==1) { // L1
- cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192))
- for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::abs(_data[off]);
- } else if (!magnitude_type) { // L0
- cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192))
- for (longT off = 0; off<(longT)siz; ++off) res+=(double)(_data[off]?1:0);
- } else if (cimg::type<float>::is_inf(magnitude_type)) { // L-inf
- cimg_for(*this,ptrs,T) { const double val = (double)cimg::abs(*ptrs); if (val>res) res = val; }
- } else { // L-p
- cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192))
- for (longT off = 0; off<(longT)siz; ++off)
- res+=(double)std::pow((double)cimg::abs(_data[off]),(double)magnitude_type);
- res = (double)std::pow(res,1.0/magnitude_type);
- }
- return res;
- }
- //! Compute the trace of the image, viewed as a matrix.
- /**
- **/
- double trace() const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "trace(): Empty instance.",
- cimg_instance);
- double res = 0;
- cimg_forX(*this,k) res+=(double)(*this)(k,k);
- return res;
- }
- //! Compute the determinant of the image, viewed as a matrix.
- /**
- **/
- double det() const {
- if (is_empty() || _width!=_height || _depth!=1 || _spectrum!=1)
- throw CImgInstanceException(_cimg_instance
- "det(): Instance is not a square matrix.",
- cimg_instance);
- switch (_width) {
- case 1 : return (double)((*this)(0,0));
- case 2 : return (double)((*this)(0,0))*(double)((*this)(1,1)) - (double)((*this)(0,1))*(double)((*this)(1,0));
- case 3 : {
- const double
- a = (double)_data[0], d = (double)_data[1], g = (double)_data[2],
- b = (double)_data[3], e = (double)_data[4], h = (double)_data[5],
- c = (double)_data[6], f = (double)_data[7], i = (double)_data[8];
- return i*a*e - a*h*f - i*b*d + b*g*f + c*d*h - c*g*e;
- }
- default : {
- CImg<Tfloat> lu(*this,false);
- CImg<uintT> indx;
- bool d;
- lu._LU(indx,d);
- double res = d?(double)1:(double)-1;
- cimg_forX(lu,i) res*=lu(i,i);
- return res;
- }
- }
- }
- //! Compute the dot product between instance and argument, viewed as matrices.
- /**
- \param img Image used as a second argument of the dot product.
- **/
- template<typename t>
- double dot(const CImg<t>& img) const {
- const ulongT nb = std::min(size(),img.size());
- double res = 0;
- cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(nb,8192))
- for (longT off = 0; off<(longT)nb; ++off) res+=(double)_data[off]*(double)img[off];
- return res;
- }
- //! Get vector-valued pixel located at specified position.
- /**
- \param x X-coordinate of the pixel value.
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- **/
- CImg<T> get_vector_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
- CImg<T> res;
- if (res._height!=_spectrum) res.assign(1,_spectrum);
- const ulongT whd = (ulongT)_width*_height*_depth;
- const T *ptrs = data(x,y,z);
- T *ptrd = res._data;
- cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
- return res;
- }
- //! Get (square) matrix-valued pixel located at specified position.
- /**
- \param x X-coordinate of the pixel value.
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- \note - The spectrum() of the image must be a square.
- **/
- CImg<T> get_matrix_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const {
- const int n = (int)cimg::round(std::sqrt((double)_spectrum));
- const T *ptrs = data(x,y,z,0);
- const ulongT whd = (ulongT)_width*_height*_depth;
- CImg<T> res(n,n);
- T *ptrd = res._data;
- cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
- return res;
- }
- //! Get tensor-valued pixel located at specified position.
- /**
- \param x X-coordinate of the pixel value.
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- **/
- CImg<T> get_tensor_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
- const T *ptrs = data(x,y,z,0);
- const ulongT whd = (ulongT)_width*_height*_depth;
- if (_spectrum==6)
- return tensor(*ptrs,*(ptrs + whd),*(ptrs + 2*whd),*(ptrs + 3*whd),*(ptrs + 4*whd),*(ptrs + 5*whd));
- if (_spectrum==3)
- return tensor(*ptrs,*(ptrs + whd),*(ptrs + 2*whd));
- return tensor(*ptrs);
- }
- //! Set vector-valued pixel at specified position.
- /**
- \param vec Vector to put on the instance image.
- \param x X-coordinate of the pixel value.
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- **/
- template<typename t>
- CImg<T>& set_vector_at(const CImg<t>& vec, const unsigned int x, const unsigned int y=0, const unsigned int z=0) {
- if (x<_width && y<_height && z<_depth) {
- const t *ptrs = vec._data;
- const ulongT whd = (ulongT)_width*_height*_depth;
- T *ptrd = data(x,y,z);
- for (unsigned int k = std::min((unsigned int)vec.size(),_spectrum); k; --k) {
- *ptrd = (T)*(ptrs++); ptrd+=whd;
- }
- }
- return *this;
- }
- //! Set (square) matrix-valued pixel at specified position.
- /**
- \param mat Matrix to put on the instance image.
- \param x X-coordinate of the pixel value.
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- **/
- template<typename t>
- CImg<T>& set_matrix_at(const CImg<t>& mat, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
- return set_vector_at(mat,x,y,z);
- }
- //! Set tensor-valued pixel at specified position.
- /**
- \param ten Tensor to put on the instance image.
- \param x X-coordinate of the pixel value.
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- **/
- template<typename t>
- CImg<T>& set_tensor_at(const CImg<t>& ten, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
- T *ptrd = data(x,y,z,0);
- const ulongT siz = (ulongT)_width*_height*_depth;
- if (ten._height==2) {
- *ptrd = (T)ten[0]; ptrd+=siz;
- *ptrd = (T)ten[1]; ptrd+=siz;
- *ptrd = (T)ten[3];
- }
- else {
- *ptrd = (T)ten[0]; ptrd+=siz;
- *ptrd = (T)ten[1]; ptrd+=siz;
- *ptrd = (T)ten[2]; ptrd+=siz;
- *ptrd = (T)ten[4]; ptrd+=siz;
- *ptrd = (T)ten[5]; ptrd+=siz;
- *ptrd = (T)ten[8];
- }
- return *this;
- }
- //! Resize image to become a diagonal matrix.
- /**
- \note Transform the image as a diagonal matrix so that each of its initial value becomes a diagonal coefficient.
- **/
- CImg<T>& diagonal() {
- return get_diagonal().move_to(*this);
- }
- //! Resize image to become a diagonal matrix \newinstance.
- CImg<T> get_diagonal() const {
- if (is_empty()) return *this;
- const unsigned int siz = (unsigned int)size();
- CImg<T> res(siz,siz,1,1,0);
- cimg_foroff(*this,off) res((unsigned int)off,(unsigned int)off) = (*this)[off];
- return res;
- }
- //! Replace the image by an identity matrix.
- /**
- \note If the instance image is not square, it is resized to a square matrix using its maximum
- dimension as a reference.
- **/
- CImg<T>& identity_matrix() {
- return identity_matrix(std::max(_width,_height)).move_to(*this);
- }
- //! Replace the image by an identity matrix \newinstance.
- CImg<T> get_identity_matrix() const {
- return identity_matrix(std::max(_width,_height));
- }
- //! Fill image with a linear sequence of values.
- /**
- \param a0 Starting value of the sequence.
- \param a1 Ending value of the sequence.
- **/
- CImg<T>& sequence(const T& a0, const T& a1) {
- if (is_empty()) return *this;
- const ulongT siz = size() - 1;
- T* ptr = _data;
- if (siz) {
- const double delta = (double)a1 - (double)a0;
- cimg_foroff(*this,l) *(ptr++) = (T)(a0 + delta*l/siz);
- } else *ptr = a0;
- return *this;
- }
- //! Fill image with a linear sequence of values \newinstance.
- CImg<T> get_sequence(const T& a0, const T& a1) const {
- return (+*this).sequence(a0,a1);
- }
- //! Transpose the image, viewed as a matrix.
- /**
- \note Equivalent to \code permute_axes("yxzc"); \endcode.
- **/
- CImg<T>& transpose() {
- if (_width==1) { _width = _height; _height = 1; return *this; }
- if (_height==1) { _height = _width; _width = 1; return *this; }
- if (_width==_height) {
- cimg_forYZC(*this,y,z,c) for (int x = y; x<width(); ++x) cimg::swap((*this)(x,y,z,c),(*this)(y,x,z,c));
- return *this;
- }
- return get_transpose().move_to(*this);
- }
- //! Transpose the image, viewed as a matrix \newinstance.
- CImg<T> get_transpose() const {
- return get_permute_axes("yxzc");
- }
- //! Compute the cross product between two \c 1x3 images, viewed as 3D vectors.
- /**
- \param img Image used as the second argument of the cross product.
- \note The first argument of the cross product is \c *this.
- **/
- template<typename t>
- CImg<T>& cross(const CImg<t>& img) {
- if (_width!=1 || _height<3 || img._width!=1 || img._height<3)
- throw CImgInstanceException(_cimg_instance
- "cross(): Instance and/or specified image (%u,%u,%u,%u,%p) are not 3D vectors.",
- cimg_instance,
- img._width,img._height,img._depth,img._spectrum,img._data);
- const T x = (*this)[0], y = (*this)[1], z = (*this)[2];
- (*this)[0] = (T)(y*img[2] - z*img[1]);
- (*this)[1] = (T)(z*img[0] - x*img[2]);
- (*this)[2] = (T)(x*img[1] - y*img[0]);
- return *this;
- }
- //! Compute the cross product between two \c 1x3 images, viewed as 3D vectors \newinstance.
- template<typename t>
- CImg<_cimg_Tt> get_cross(const CImg<t>& img) const {
- return CImg<_cimg_Tt>(*this).cross(img);
- }
- //! Invert the instance image, viewed as a matrix.
- /**
- If the instance matrix is not square, the Moore-Penrose pseudo-inverse is computed instead.
- \param use_LU Choose the inverting algorithm. Can be:
- - \c true: LU solver (faster but sometimes less precise).
- - \c false: SVD solver (more precise but slower).
- \param lambda is used only in the Moore-Penrose pseudoinverse for estimating A^t.(A^t.A + lambda.Id)^-1.
- **/
- CImg<T>& invert(const bool use_LU=false, const float lambda=0) {
- if (_depth!=1 || _spectrum!=1)
- throw CImgInstanceException(_cimg_instance
- "invert(): Instance is not a matrix.",
- cimg_instance);
- if (lambda<0)
- throw CImgArgumentException(_cimg_instance
- "invert(): Specified lambda (%g) should be >=0.",
- cimg_instance);
- if (_width!=_height) return get_invert(use_LU,lambda).move_to(*this); // Non-square matrix: Pseudoinverse
- // Square matrix.
- const double dete = _width>3?-1.:det();
- if (dete!=0. && _width==2) {
- const double
- a = _data[0], c = _data[1],
- b = _data[2], d = _data[3];
- _data[0] = (T)(d/dete); _data[1] = (T)(-c/dete);
- _data[2] = (T)(-b/dete); _data[3] = (T)(a/dete);
- } else if (dete!=0. && _width==3) {
- const double
- a = _data[0], d = _data[1], g = _data[2],
- b = _data[3], e = _data[4], h = _data[5],
- c = _data[6], f = _data[7], i = _data[8];
- _data[0] = (T)((i*e - f*h)/dete), _data[1] = (T)((g*f - i*d)/dete), _data[2] = (T)((d*h - g*e)/dete);
- _data[3] = (T)((h*c - i*b)/dete), _data[4] = (T)((i*a - c*g)/dete), _data[5] = (T)((g*b - a*h)/dete);
- _data[6] = (T)((b*f - e*c)/dete), _data[7] = (T)((d*c - a*f)/dete), _data[8] = (T)((a*e - d*b)/dete);
- } else {
- #ifdef cimg_use_lapack
- int INFO = (int)use_LU, N = _width, LWORK = 4*N, *const IPIV = new int[N];
- Tfloat
- *const lapA = new Tfloat[N*N],
- *const WORK = new Tfloat[LWORK];
- cimg_forXY(*this,k,l) lapA[k*N + l] = (Tfloat)((*this)(k,l));
- cimg::getrf(N,lapA,IPIV,INFO);
- if (INFO)
- cimg::warn(_cimg_instance
- "invert(): LAPACK function dgetrf_() returned error code %d.",
- cimg_instance,
- INFO);
- else {
- cimg::getri(N,lapA,IPIV,WORK,LWORK,INFO);
- if (INFO)
- cimg::warn(_cimg_instance
- "invert(): LAPACK function dgetri_() returned error code %d.",
- cimg_instance,
- INFO);
- }
- if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)(lapA[k*N + l]); else fill(0);
- delete[] IPIV; delete[] lapA; delete[] WORK;
- #else
- if (use_LU) { // LU solver
- CImg<Tfloat> A(*this,false), indx;
- bool d;
- A._LU(indx,d);
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,16*16))
- cimg_forX(*this,j) {
- CImg<Tfloat> col(1,_width,1,1,0);
- col(j) = 1;
- col._solve(A,indx);
- cimg_forX(*this,i) (*this)(j,i) = (T)col(i);
- }
- } else _get_invert_svd(false).move_to(*this); // SVD solver
- #endif
- }
- return *this;
- }
- //! Invert the instance image, viewed as a matrix \newinstance.
- CImg<Tfloat> get_invert(const bool use_LU=false, const float lambda=0) const {
- if (_depth!=1 || _spectrum!=1)
- throw CImgInstanceException(_cimg_instance
- "invert(): Instance is not a matrix.",
- cimg_instance);
- if (lambda<0)
- throw CImgArgumentException(_cimg_instance
- "invert(): Specified lambda (%g) should be >=0.",
- cimg_instance);
- if (_width==_height) return CImg<Tfloat>(*this,false).invert(use_LU,lambda); // Square matrix
- // Non-square matrix: Pseudoinverse
- if (use_LU) {
- if (_width<_height) { // under-solved system -> (A^t.A)^-1.A^t
- CImg<Tfloat> AtA(width(),width());
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,128*128))
- cimg_forY(AtA,i)
- for (int j = 0; j<=i; ++j) {
- double res = 0;
- cimg_forY(*this,k) res+=(*this)(i,k)*(*this)(j,k);
- AtA(j,i) = AtA(i,j) = (Tfloat)res;
- }
- if (lambda!=0) cimg_forY(AtA,i) AtA(i,i)+=lambda;
- AtA.invert(true);
- return AtA*get_transpose();
- } else { // over-resolved linear system -> A^t.(A.A^t)^-1
- CImg<Tfloat> AAt(height(),height());
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,128*128))
- cimg_forY(AAt,i)
- for (int j = 0; j<=i; ++j) {
- double res = 0;
- cimg_forX(*this,k) res+=(*this)(k,i)*(*this)(k,j);
- AAt(j,i) = AAt(i,j) = (Tfloat)res;
- }
- if (lambda!=0) cimg_forY(AAt,i) AAt(i,i)+=lambda;
- AAt.invert(true);
- return get_transpose()*AAt;
- }
- }
- return _get_invert_svd(lambda);
- }
- // SVD solver, both used for inverse and pseudoinverse.
- CImg<Tfloat> _get_invert_svd(const float lambda) const {
- CImg<Tfloat> U, S, V;
- SVD(U,S,V,false);
- const Tfloat epsilon = (sizeof(Tfloat)<=4?5.96e-8f:1.11e-16f)*std::max(_width,_height)*S.max();
- cimg_forX(V,x) {
- const Tfloat s = S(x), invs = lambda?1/(lambda + s):s>epsilon?1/s:0;
- cimg_forY(V,y) V(x,y)*=invs;
- }
- return V*U.transpose();
- }
- //! Solve a system of linear equations.
- /**
- \param A Matrix of the linear system.
- \param use_LU In case of non square system (least-square solution),
- choose between SVD (\c false) or LU (\c true) solver.
- LU solver is faster for large matrices, but numerically less stable.
- \note Solve \c AX = B where \c B=*this.
- **/
- template<typename t>
- CImg<T>& solve(const CImg<t>& A, const bool use_LU=false) {
- if (_depth!=1 || _spectrum!=1 || _height!=A._height || A._depth!=1 || A._spectrum!=1)
- throw CImgArgumentException(_cimg_instance
- "solve(): Instance and specified matrix (%u,%u,%u,%u,%p) have "
- "incompatible dimensions.",
- cimg_instance,
- A._width,A._height,A._depth,A._spectrum,A._data);
- typedef _cimg_Ttfloat Ttfloat;
- if (A.size()==1) return (*this)/=A[0];
- if (A._width==2 && A._height==2 && _height==2) { // 2x2 linear system
- const double a = (double)A[0], b = (double)A[1], c = (double)A[2], d = (double)A[3],
- fa = std::fabs(a), fb = std::fabs(b), fc = std::fabs(c), fd = std::fabs(d),
- det = a*d - b*c, fM = cimg::max(fa,fb,fc,fd);
- if (fM==fa)
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256))
- cimg_forX(*this,k) {
- const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), y = (a*v - c*u)/det;
- (*this)(k,0) = (T)((u - b*y)/a); (*this)(k,1) = (T)y;
- } else if (fM==fc)
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256))
- cimg_forX(*this,k) {
- const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), y = (a*v - c*u)/det;
- (*this)(k,0) = (T)((v - d*y)/c); (*this)(k,1) = (T)y;
- } else if (fM==fb)
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256))
- cimg_forX(*this,k) {
- const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), x = (d*u - b*v)/det;
- (*this)(k,0) = (T)x; (*this)(k,1) = (T)((u - a*x)/b);
- } else
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256))
- cimg_forX(*this,k) {
- const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), x = (d*u - b*v)/det;
- (*this)(k,0) = (T)x; (*this)(k,1) = (T)((v - c*x)/d);
- }
- return *this;
- }
- if (A._width==A._height) { // Square linear system
- #ifdef cimg_use_lapack
- char TRANS = 'N';
- int INFO, N = _height, LWORK = 4*N, *const IPIV = new int[N];
- Ttfloat
- *const lapA = new Ttfloat[N*N],
- *const lapB = new Ttfloat[N],
- *const WORK = new Ttfloat[LWORK];
- cimg_forXY(A,k,l) lapA[k*N + l] = (Ttfloat)(A(k,l));
- cimg_forX(*this,i) {
- cimg_forY(*this,j) lapB[j] = (Ttfloat)((*this)(i,j));
- cimg::getrf(N,lapA,IPIV,INFO);
- if (INFO)
- cimg::warn(_cimg_instance
- "solve(): LAPACK library function dgetrf_() returned error code %d.",
- cimg_instance,
- INFO);
- else {
- cimg::getrs(TRANS,N,lapA,IPIV,lapB,INFO);
- if (INFO)
- cimg::warn(_cimg_instance
- "solve(): LAPACK library function dgetrs_() returned error code %d.",
- cimg_instance,
- INFO);
- }
- if (!INFO) cimg_forY(*this,j) (*this)(i,j) = (T)(lapB[j]); else cimg_forY(*this,j) (*this)(i,j) = (T)0;
- }
- delete[] IPIV; delete[] lapA; delete[] lapB; delete[] WORK;
- #else
- CImg<Ttfloat> lu(A,false);
- CImg<Ttfloat> indx;
- bool d;
- lu._LU(indx,d);
- CImg<T> res(_width,A._width);
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,16))
- cimg_forX(*this,i) res.draw_image(i,get_column(i)._solve(lu,indx));
- res.move_to(*this);
- #endif
- } else { // Least-square solution for non-square systems
- #ifdef cimg_use_lapack
- char TRANS = 'N';
- int INFO, N = A._width, M = A._height, LWORK = -1, LDA = M, LDB = M, NRHS = _width;
- Ttfloat WORK_QUERY;
- Ttfloat
- * const lapA = new Ttfloat[M*N],
- * const lapB = new Ttfloat[M*NRHS];
- cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, &WORK_QUERY, LWORK, INFO);
- LWORK = (int) WORK_QUERY;
- Ttfloat *const WORK = new Ttfloat[LWORK];
- cimg_forXY(A,k,l) lapA[k*M + l] = (Ttfloat)(A(k,l));
- cimg_forXY(*this,k,l) lapB[k*M + l] = (Ttfloat)((*this)(k,l));
- cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, WORK, LWORK, INFO);
- if (INFO != 0)
- cimg::warn(_cimg_instance
- "solve(): LAPACK library function sgels() returned error code %d.",
- cimg_instance,
- INFO);
- assign(NRHS, N);
- if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)lapB[k*M + l];
- else (A.get_invert(use_LU)*(*this)).move_to(*this);
- delete[] lapA; delete[] lapB; delete[] WORK;
- #else
- (A.get_invert(use_LU)*(*this)).move_to(*this);
- #endif
- }
- return *this;
- }
- //! Solve a system of linear equations \newinstance.
- template<typename t>
- CImg<_cimg_Ttfloat> get_solve(const CImg<t>& A, const bool use_LU=false) const {
- typedef _cimg_Ttfloat Ttfloat;
- return CImg<Ttfloat>(*this,false).solve(A,use_LU);
- }
- template<typename t, typename ti>
- CImg<T>& _solve(const CImg<t>& A, const CImg<ti>& indx) {
- typedef _cimg_Ttfloat Ttfloat;
- const int N = height();
- int ii = -1;
- Ttfloat sum;
- for (int i = 0; i<N; ++i) {
- const int ip = (int)indx[i];
- sum = (*this)(ip);
- (*this)(ip) = (*this)(i);
- if (ii>=0) for (int j = ii; j<=i - 1; ++j) sum-=A(j,i)*(*this)(j);
- else if (sum!=0) ii = i;
- (*this)(i) = (T)sum;
- }
- for (int i = N - 1; i>=0; --i) {
- sum = (*this)(i);
- for (int j = i + 1; j<N; ++j) sum-=A(j,i)*(*this)(j);
- (*this)(i) = (T)(sum/A(i,i));
- }
- return *this;
- }
- //! Solve a tridiagonal system of linear equations.
- /**
- \param A Coefficients of the tridiagonal system.
- A is a tridiagonal matrix A = [ b0,c0,0,...; a1,b1,c1,0,... ; ... ; ...,0,aN,bN ],
- stored as a 3 columns matrix
- \note Solve AX=B where \c B=*this, using the Thomas algorithm.
- **/
- template<typename t>
- CImg<T>& solve_tridiagonal(const CImg<t>& A) {
- const unsigned int siz = (unsigned int)size();
- if (A._width!=3 || A._height!=siz)
- throw CImgArgumentException(_cimg_instance
- "solve_tridiagonal(): Instance and tridiagonal matrix "
- "(%u,%u,%u,%u,%p) have incompatible dimensions.",
- cimg_instance,
- A._width,A._height,A._depth,A._spectrum,A._data);
- typedef _cimg_Ttfloat Ttfloat;
- const Ttfloat epsilon = 1e-4f;
- CImg<Ttfloat> B = A.get_column(1), V(*this,false);
- for (int i = 1; i<(int)siz; ++i) {
- const Ttfloat m = A(0,i)/(B[i - 1]?B[i - 1]:epsilon);
- B[i] -= m*A(2,i - 1);
- V[i] -= m*V[i - 1];
- }
- (*this)[siz - 1] = (T)(V[siz - 1]/(B[siz - 1]?B[siz - 1]:epsilon));
- for (int i = (int)siz - 2; i>=0; --i) (*this)[i] = (T)((V[i] - A(2,i)*(*this)[i + 1])/(B[i]?B[i]:epsilon));
- return *this;
- }
- //! Solve a tridiagonal system of linear equations \newinstance.
- template<typename t>
- CImg<_cimg_Ttfloat> get_solve_tridiagonal(const CImg<t>& A) const {
- return CImg<_cimg_Ttfloat>(*this,false).solve_tridiagonal(A);
- }
- //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix.
- /**
- \param[out] val Vector of the estimated eigenvalues, in decreasing order.
- \param[out] vec Matrix of the estimated eigenvectors, sorted by columns.
- **/
- template<typename t>
- const CImg<T>& eigen(CImg<t>& val, CImg<t> &vec) const {
- if (is_empty()) { val.assign(); vec.assign(); }
- else {
- if (_width!=_height || _depth>1 || _spectrum>1)
- throw CImgInstanceException(_cimg_instance
- "eigen(): Instance is not a square matrix.",
- cimg_instance);
- if (val.size()<(ulongT)_width) val.assign(1,_width);
- if (vec.size()<(ulongT)_width*_width) vec.assign(_width,_width);
- switch (_width) {
- case 1 : { val[0] = (t)(*this)[0]; vec[0] = (t)1; } break;
- case 2 : {
- const double a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3], e = a + d;
- double f = e*e - 4*(a*d - b*c);
- if (f<0) cimg::warn(_cimg_instance
- "eigen(): Complex eigenvalues found.",
- cimg_instance);
- f = std::sqrt(f);
- const double
- l1 = 0.5*(e - f),
- l2 = 0.5*(e + f),
- b2 = b*b,
- norm1 = std::sqrt(cimg::sqr(l2 - a) + b2),
- norm2 = std::sqrt(cimg::sqr(l1 - a) + b2);
- val[0] = (t)l2;
- val[1] = (t)l1;
- if (norm1>0) { vec(0,0) = (t)(b/norm1); vec(0,1) = (t)((l2 - a)/norm1); } else { vec(0,0) = 1; vec(0,1) = 0; }
- if (norm2>0) { vec(1,0) = (t)(b/norm2); vec(1,1) = (t)((l1 - a)/norm2); } else { vec(1,0) = 1; vec(1,1) = 0; }
- } break;
- default :
- throw CImgInstanceException(_cimg_instance
- "eigen(): Eigenvalues computation of general matrices is limited "
- "to 2x2 matrices.",
- cimg_instance);
- }
- }
- return *this;
- }
- //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix.
- /**
- \return A list of two images <tt>[val; vec]</tt>, whose meaning is similar as in eigen(CImg<t>&,CImg<t>&) const.
- **/
- CImgList<Tfloat> get_eigen() const {
- CImgList<Tfloat> res(2);
- eigen(res[0],res[1]);
- return res;
- }
- //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix.
- /**
- \param[out] val Vector of the estimated eigenvalues, in decreasing order.
- \param[out] vec Matrix of the estimated eigenvectors, sorted by columns.
- **/
- template<typename t>
- const CImg<T>& symmetric_eigen(CImg<t>& val, CImg<t>& vec) const {
- if (is_empty()) { val.assign(); vec.assign(); return *this; }
- if (_width!=_height || _depth>1 || _spectrum>1)
- throw CImgInstanceException(_cimg_instance
- "eigen(): Instance is not a square matrix.",
- cimg_instance);
- val.assign(1,_width);
- vec.assign(_width,_width);
- if (_width==1) { val[0] = cimg::abs((*this)[0]); vec[0] = 1; return *this; }
- if (_width==2) {
- const double
- a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3],
- e = a + d, f = std::sqrt(std::max(e*e - 4*(a*d - b*c),0.0)),
- l1 = 0.5*(e - f), l2 = 0.5*(e + f),
- n = std::sqrt(cimg::sqr(l2 - a) + b*b);
- val[0] = (t)l2;
- val[1] = (t)l1;
- if (n>0) { vec[0] = (t)(b/n); vec[2] = (t)((l2 - a)/n); } else { vec[0] = 1; vec[2] = 0; }
- vec[1] = -vec[2];
- vec[3] = vec[0];
- return *this;
- }
- #ifdef cimg_use_lapack
- char JOB = 'V', UPLO = 'U';
- int N = _width, LWORK = 4*N, INFO;
- Tfloat
- *const lapA = new Tfloat[N*N],
- *const lapW = new Tfloat[N],
- *const WORK = new Tfloat[LWORK];
- cimg_forXY(*this,k,l) lapA[k*N + l] = (Tfloat)((*this)(k,l));
- cimg::syev(JOB,UPLO,N,lapA,lapW,WORK,LWORK,INFO);
- if (INFO)
- cimg::warn(_cimg_instance
- "symmetric_eigen(): LAPACK library function dsyev_() returned error code %d.",
- cimg_instance,
- INFO);
- if (!INFO) {
- cimg_forY(val,i) val(i) = (T)lapW[N - 1 -i];
- cimg_forXY(vec,k,l) vec(k,l) = (T)(lapA[(N - 1 - k)*N + l]);
- } else { val.fill(0); vec.fill(0); }
- delete[] lapA; delete[] lapW; delete[] WORK;
- #else
- CImg<t> V(_width,_width);
- Tfloat M = 0, m = (Tfloat)min_max(M), maxabs = cimg::max((Tfloat)1,cimg::abs(m),cimg::abs(M));
- (CImg<Tfloat>(*this,false)/=maxabs).SVD(vec,val,V,false);
- if (maxabs!=1) val*=maxabs;
- bool is_ambiguous = false;
- float eig = 0;
- cimg_forY(val,p) { // Check for ambiguous cases
- if (val[p]>eig) eig = (float)val[p];
- t scal = 0;
- cimg_forY(vec,y) scal+=vec(p,y)*V(p,y);
- if (cimg::abs(scal)<0.9f) is_ambiguous = true;
- if (scal<0) val[p] = -val[p];
- }
- if (is_ambiguous) {
- ++(eig*=2);
- SVD(vec,val,V,false,40,eig);
- val-=eig;
- }
- CImg<intT> permutations; // Sort eigenvalues in decreasing order
- CImg<t> tmp(_width);
- val.sort(permutations,false);
- cimg_forY(vec,k) {
- cimg_forY(permutations,y) tmp(y) = vec(permutations(y),k);
- std::memcpy(vec.data(0,k),tmp._data,sizeof(t)*_width);
- }
- #endif
- return *this;
- }
- //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix.
- /**
- \return A list of two images <tt>[val; vec]</tt>, whose meaning are similar as in
- symmetric_eigen(CImg<t>&,CImg<t>&) const.
- **/
- CImgList<Tfloat> get_symmetric_eigen() const {
- CImgList<Tfloat> res(2);
- symmetric_eigen(res[0],res[1]);
- return res;
- }
- //! Sort pixel values and get sorting permutations.
- /**
- \param[out] permutations Permutation map used for the sorting.
- \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way.
- **/
- template<typename t>
- CImg<T>& sort(CImg<t>& permutations, const bool is_increasing=true) {
- permutations.assign(_width,_height,_depth,_spectrum);
- if (is_empty()) return *this;
- cimg_foroff(permutations,off) permutations[off] = (t)off;
- return _quicksort(0,size() - 1,permutations,is_increasing,true);
- }
- //! Sort pixel values and get sorting permutations \newinstance.
- template<typename t>
- CImg<T> get_sort(CImg<t>& permutations, const bool is_increasing=true) const {
- return (+*this).sort(permutations,is_increasing);
- }
- //! Sort pixel values.
- /**
- \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way.
- \param axis Tells if the value sorting must be done along a specific axis. Can be:
- - \c 0: All pixel values are sorted, independently on their initial position.
- - \c 'x': Image columns are sorted, according to the first value in each column.
- - \c 'y': Image rows are sorted, according to the first value in each row.
- - \c 'z': Image slices are sorted, according to the first value in each slice.
- - \c 'c': Image channels are sorted, according to the first value in each channel.
- **/
- CImg<T>& sort(const bool is_increasing=true, const char axis=0) {
- if (is_empty()) return *this;
- CImg<uintT> perm;
- switch (cimg::lowercase(axis)) {
- case 0 :
- _quicksort(0,size() - 1,perm,is_increasing,false);
- break;
- case 'x' : {
- perm.assign(_width);
- get_crop(0,0,0,0,_width - 1,0,0,0).sort(perm,is_increasing);
- CImg<T> img(*this,false);
- cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(perm[x],y,z,c);
- } break;
- case 'y' : {
- perm.assign(_height);
- get_crop(0,0,0,0,0,_height - 1,0,0).sort(perm,is_increasing);
- CImg<T> img(*this,false);
- cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,perm[y],z,c);
- } break;
- case 'z' : {
- perm.assign(_depth);
- get_crop(0,0,0,0,0,0,_depth - 1,0).sort(perm,is_increasing);
- CImg<T> img(*this,false);
- cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,perm[z],c);
- } break;
- case 'c' : {
- perm.assign(_spectrum);
- get_crop(0,0,0,0,0,0,0,_spectrum - 1).sort(perm,is_increasing);
- CImg<T> img(*this,false);
- cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,z,perm[c]);
- } break;
- default :
- throw CImgArgumentException(_cimg_instance
- "sort(): Invalid specified axis '%c' "
- "(should be { x | y | z | c }).",
- cimg_instance,axis);
- }
- return *this;
- }
- //! Sort pixel values \newinstance.
- CImg<T> get_sort(const bool is_increasing=true, const char axis=0) const {
- return (+*this).sort(is_increasing,axis);
- }
- template<typename t>
- CImg<T>& _quicksort(const long indm, const long indM, CImg<t>& permutations,
- const bool is_increasing, const bool is_permutations) {
- if (indm<indM) {
- const long mid = (indm + indM)/2;
- if (is_increasing) {
- if ((*this)[indm]>(*this)[mid]) {
- cimg::swap((*this)[indm],(*this)[mid]);
- if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
- }
- if ((*this)[mid]>(*this)[indM]) {
- cimg::swap((*this)[indM],(*this)[mid]);
- if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
- }
- if ((*this)[indm]>(*this)[mid]) {
- cimg::swap((*this)[indm],(*this)[mid]);
- if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
- }
- } else {
- if ((*this)[indm]<(*this)[mid]) {
- cimg::swap((*this)[indm],(*this)[mid]);
- if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
- }
- if ((*this)[mid]<(*this)[indM]) {
- cimg::swap((*this)[indM],(*this)[mid]);
- if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
- }
- if ((*this)[indm]<(*this)[mid]) {
- cimg::swap((*this)[indm],(*this)[mid]);
- if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
- }
- }
- if (indM - indm>=3) {
- const T pivot = (*this)[mid];
- long i = indm, j = indM;
- if (is_increasing) {
- do {
- while ((*this)[i]<pivot) ++i;
- while ((*this)[j]>pivot) --j;
- if (i<=j) {
- if (is_permutations) cimg::swap(permutations[i],permutations[j]);
- cimg::swap((*this)[i++],(*this)[j--]);
- }
- } while (i<=j);
- } else {
- do {
- while ((*this)[i]>pivot) ++i;
- while ((*this)[j]<pivot) --j;
- if (i<=j) {
- if (is_permutations) cimg::swap(permutations[i],permutations[j]);
- cimg::swap((*this)[i++],(*this)[j--]);
- }
- } while (i<=j);
- }
- if (indm<j) _quicksort(indm,j,permutations,is_increasing,is_permutations);
- if (i<indM) _quicksort(i,indM,permutations,is_increasing,is_permutations);
- }
- }
- return *this;
- }
- //! Compute the SVD of the instance image, viewed as a general matrix.
- /**
- Compute the SVD decomposition \c *this=U*S*V' where \c U and \c V are orthogonal matrices
- and \c S is a diagonal matrix. \c V' denotes the matrix transpose of \c V.
- \param[out] U First matrix of the SVD product.
- \param[out] S Coefficients of the second (diagonal) matrix of the SVD product.
- These coefficients are stored as a vector.
- \param[out] V Third matrix of the SVD product.
- \param sorting Tells if the diagonal coefficients are sorted (in decreasing order).
- \param max_iteration Maximum number of iterations considered for the algorithm convergence.
- \param lambda Epsilon used for the algorithm convergence.
- \note The instance matrix can be computed from \c U,\c S and \c V by
- \code
- const CImg<> A; // Input matrix (assumed to contain some values)
- CImg<> U,S,V;
- A.SVD(U,S,V)
- \endcode
- **/
- template<typename t>
- const CImg<T>& SVD(CImg<t>& U, CImg<t>& S, CImg<t>& V, const bool sorting=true,
- const unsigned int max_iteration=40, const float lambda=0) const {
- typedef _cimg_Ttfloat Ttfloat;
- const Ttfloat epsilon = (Ttfloat)1e-25;
- if (is_empty()) { U.assign(); S.assign(); V.assign(); }
- else if (_depth!=1 || _spectrum!=1)
- throw CImgInstanceException(_cimg_instance
- "SVD(): Instance has invalid dimensions (depth or channels different from 1).",
- cimg_instance);
- else {
- U = *this;
- if (lambda!=0) {
- const unsigned int delta = std::min(U._width,U._height);
- for (unsigned int i = 0; i<delta; ++i) U(i,i) = (t)(U(i,i) + lambda);
- }
- if (S.size()<_width) S.assign(1,_width);
- if (V._width<_width || V._height<_height) V.assign(_width,_width);
- CImg<t> rv1(_width);
- Ttfloat anorm = 0, c, f, g = 0, h, s, scale = 0;
- int l = 0;
- cimg_forX(U,i) {
- l = i + 1;
- rv1[i] = scale*g;
- g = s = scale = 0;
- if (i<height()) {
- for (int k = i; k<height(); ++k) scale+=cimg::abs(U(i,k));
- if (scale) {
- for (int k = i; k<height(); ++k) {
- U(i,k)/=scale;
- s+=U(i,k)*U(i,k);
- }
- f = U(i,i);
- g = (Ttfloat)((f>=0?-1:1)*std::sqrt(s));
- h = f*g - s;
- U(i,i) = f - g;
- for (int j = l; j<width(); ++j) {
- s = 0;
- for (int k=i; k<height(); ++k) s+=U(i,k)*U(j,k);
- f = s/h;
- for (int k = i; k<height(); ++k) U(j,k)+=f*U(i,k);
- }
- for (int k = i; k<height(); ++k) U(i,k)*=scale;
- }
- }
- S[i] = scale*g;
- g = s = scale = 0;
- if (i<height() && i!=width() - 1) {
- for (int k = l; k<width(); ++k) scale+=cimg::abs(U(k,i));
- if (scale) {
- for (int k = l; k<width(); ++k) {
- U(k,i)/=scale;
- s+=U(k,i)*U(k,i);
- }
- f = U(l,i);
- g = (Ttfloat)((f>=0?-1:1)*std::sqrt(s));
- h = f*g - s;
- U(l,i) = f - g;
- for (int k = l; k<width(); ++k) rv1[k] = U(k,i)/h;
- for (int j = l; j<height(); ++j) {
- s = 0;
- for (int k = l; k<width(); ++k) s+=U(k,j)*U(k,i);
- for (int k = l; k<width(); ++k) U(k,j)+=s*rv1[k];
- }
- for (int k = l; k<width(); ++k) U(k,i)*=scale;
- }
- }
- anorm = (Ttfloat)std::max((float)anorm,(float)(cimg::abs(S[i]) + cimg::abs(rv1[i])));
- }
- for (int i = width() - 1; i>=0; --i) {
- if (i<width() - 1) {
- if (g) {
- for (int j = l; j<width(); ++j) V(i,j) =(U(j,i)/U(l,i))/g;
- for (int j = l; j<width(); ++j) {
- s = 0;
- for (int k = l; k<width(); ++k) s+=U(k,i)*V(j,k);
- for (int k = l; k<width(); ++k) V(j,k)+=s*V(i,k);
- }
- }
- for (int j = l; j<width(); ++j) V(j,i) = V(i,j) = (t)0.;
- }
- V(i,i) = (t)1;
- g = rv1[i];
- l = i;
- }
- for (int i = std::min(width(),height()) - 1; i>=0; --i) {
- l = i + 1;
- g = S[i];
- for (int j = l; j<width(); ++j) U(j,i) = 0;
- if (g) {
- g = 1/g;
- for (int j = l; j<width(); ++j) {
- s = 0;
- for (int k = l; k<height(); ++k) s+=U(i,k)*U(j,k);
- f = (s/U(i,i))*g;
- for (int k = i; k<height(); ++k) U(j,k)+=f*U(i,k);
- }
- for (int j = i; j<height(); ++j) U(i,j)*= g;
- } else for (int j = i; j<height(); ++j) U(i,j) = 0;
- ++U(i,i);
- }
- for (int k = width() - 1; k>=0; --k) {
- int nm = 0;
- for (unsigned int its = 0; its<max_iteration; ++its) {
- bool flag = true;
- for (l = k; l>=1; --l) {
- nm = l - 1;
- if ((cimg::abs(rv1[l]) + anorm)==anorm) { flag = false; break; }
- if ((cimg::abs(S[nm]) + anorm)==anorm) break;
- }
- if (flag) {
- c = 0;
- s = 1;
- for (int i = l; i<=k; ++i) {
- f = s*rv1[i];
- rv1[i] = c*rv1[i];
- if ((cimg::abs(f) + anorm)==anorm) break;
- g = S[i];
- h = cimg::hypot(f,g);
- S[i] = h;
- h = 1/h;
- c = g*h;
- s = -f*h;
- cimg_forY(U,j) {
- const t y = U(nm,j), z = U(i,j);
- U(nm,j) = y*c + z*s;
- U(i,j) = z*c - y*s;
- }
- }
- }
- const t z = S[k];
- if (l==k) { if (z<0) { S[k] = -z; cimg_forX(U,j) V(k,j) = -V(k,j); } break; }
- nm = k - 1;
- t x = S[l], y = S[nm];
- g = rv1[nm];
- h = rv1[k];
- f = ((y - z)*(y + z) + (g - h)*(g + h))/std::max(epsilon,(Ttfloat)2*h*y);
- g = cimg::hypot(f,(Ttfloat)1);
- f = ((x - z)*(x + z) + h*((y/(f + (f>=0?g:-g))) - h))/std::max(epsilon,(Ttfloat)x);
- c = s = 1;
- for (int j = l; j<=nm; ++j) {
- const int i = j + 1;
- g = rv1[i];
- h = s*g;
- g = c*g;
- t y1 = S[i], z1 = cimg::hypot(f,h);
- rv1[j] = z1;
- c = f/std::max(epsilon,(Ttfloat)z1);
- s = h/std::max(epsilon,(Ttfloat)z1);
- f = x*c + g*s;
- g = g*c - x*s;
- h = y1*s;
- y1*=c;
- cimg_forX(U,jj) {
- const t x2 = V(j,jj), z2 = V(i,jj);
- V(j,jj) = x2*c + z2*s;
- V(i,jj) = z2*c - x2*s;
- }
- z1 = cimg::hypot(f,h);
- S[j] = z1;
- if (z1) {
- z1 = 1/std::max(epsilon,(Ttfloat)z1);
- c = f*z1;
- s = h*z1;
- }
- f = c*g + s*y1;
- x = c*y1 - s*g;
- cimg_forY(U,jj) {
- const t y2 = U(j,jj), z2 = U(i,jj);
- U(j,jj) = y2*c + z2*s;
- U(i,jj) = z2*c - y2*s;
- }
- }
- rv1[l] = 0;
- rv1[k] = f;
- S[k] = x;
- }
- }
- if (sorting) {
- CImg<intT> permutations;
- CImg<t> tmp(_width);
- S.sort(permutations,false);
- cimg_forY(U,k) {
- cimg_forY(permutations,y) tmp(y) = U(permutations(y),k);
- std::memcpy(U.data(0,k),tmp._data,sizeof(t)*_width);
- }
- cimg_forY(V,k) {
- cimg_forY(permutations,y) tmp(y) = V(permutations(y),k);
- std::memcpy(V.data(0,k),tmp._data,sizeof(t)*_width);
- }
- }
- }
- return *this;
- }
- //! Compute the SVD of the instance image, viewed as a general matrix.
- /**
- \return A list of three images <tt>[U; S; V]</tt>, whose meaning is similar as in
- SVD(CImg<t>&,CImg<t>&,CImg<t>&,bool,unsigned int,float) const.
- **/
- CImgList<Tfloat> get_SVD(const bool sorting=true,
- const unsigned int max_iteration=40, const float lambda=0) const {
- CImgList<Tfloat> res(3);
- SVD(res[0],res[1],res[2],sorting,max_iteration,lambda);
- return res;
- }
- // [internal] Compute the LU decomposition of a permuted matrix.
- template<typename t>
- CImg<T>& _LU(CImg<t>& indx, bool& d) {
- const int N = width();
- int imax = 0;
- CImg<Tfloat> vv(N);
- indx.assign(N);
- d = true;
- bool return0 = false;
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=512))
- cimg_forX(*this,i) {
- Tfloat vmax = 0;
- cimg_forX(*this,j) {
- const Tfloat tmp = cimg::abs((*this)(j,i));
- if (tmp>vmax) vmax = tmp;
- }
- if (vmax==0) return0 = true; else vv[i] = 1/vmax;
- }
- if (return0) { indx.fill(0); return fill(0); }
- cimg_forX(*this,j) {
- for (int i = 0; i<j; ++i) {
- Tfloat sum = (*this)(j,i);
- for (int k = 0; k<i; ++k) sum-=(*this)(k,i)*(*this)(j,k);
- (*this)(j,i) = (T)sum;
- }
- Tfloat vmax = 0;
- for (int i = j; i<width(); ++i) {
- Tfloat sum = (*this)(j,i);
- for (int k = 0; k<j; ++k) sum-=(*this)(k,i)*(*this)(j,k);
- (*this)(j,i) = (T)sum;
- const Tfloat tmp = vv[i]*cimg::abs(sum);
- if (tmp>=vmax) { vmax = tmp; imax = i; }
- }
- if (j!=imax) {
- cimg_forX(*this,k) cimg::swap((*this)(k,imax),(*this)(k,j));
- d = !d;
- vv[imax] = vv[j];
- }
- indx[j] = (t)imax;
- if ((*this)(j,j)==0) (*this)(j,j) = (T)1e-20;
- if (j<N) {
- const Tfloat tmp = 1/(Tfloat)(*this)(j,j);
- for (int i = j + 1; i<N; ++i) (*this)(j,i) = (T)((*this)(j,i)*tmp);
- }
- }
- return *this;
- }
- //! Compute the projection of the instance matrix onto the specified dictionary.
- /**
- Find the best matching projection of selected matrix onto the span of an over-complete dictionary D,
- using the orthogonal projection or (opt. Orthogonal) Matching Pursuit algorithm.
- Instance image must a 2D-matrix in which each column represent a signal to project.
- \param dictionary A matrix in which each column is an element of the dictionary D.
- \param method Tell what projection method is applied. It can be:
- - 0 = orthogonal projection (default).
- - 1 = matching pursuit.
- - 2 = matching pursuit, with a single orthogonal projection step at the end.
- - >=3 = orthogonal matching pursuit where an orthogonal projection step is performed
- every 'method-2' iterations.
- \param max_iter Sets the max number of iterations processed for each signal.
- If set to '0' (default), 'max_iter' is set to the number of dictionary columns.
- (only meaningful for matching pursuit and its variants).
- \param max_residual Gives a stopping criterion on signal reconstruction accuracy.
- (only meaningful for matching pursuit and its variants).
- \return A matrix W whose columns correspond to the sparse weights of associated to each input matrix column.
- Thus, the matrix product D*W is an approximation of the input matrix.
- **/
- template<typename t>
- CImg<T>& project_matrix(const CImg<t>& dictionary, const unsigned int method=0,
- const unsigned int max_iter=0, const double max_residual=1e-6) {
- return get_project_matrix(dictionary,method,max_iter,max_residual).move_to(*this);
- }
- template<typename t>
- CImg<Tfloat> get_project_matrix(const CImg<t>& dictionary, const unsigned int method=0,
- const unsigned int max_iter=0, const double max_residual=1e-6) const {
- if (_depth!=1 || _spectrum!=1)
- throw CImgInstanceException(_cimg_instance
- "project_matrix(): Instance image is not a matrix.",
- cimg_instance);
- if (dictionary._height!=_height || dictionary._depth!=1 || dictionary._spectrum!=1)
- throw CImgArgumentException(_cimg_instance
- "project_matrix(): Specified dictionary (%u,%u,%u,%u) has an invalid size.",
- cimg_instance,
- dictionary._width,dictionary._height,dictionary._depth,dictionary._spectrum);
- if (!method) return get_solve(dictionary);
- CImg<Tfloat> W(_width,dictionary._width,1,1,0);
- // Compute dictionary norm and normalize it.
- CImg<Tfloat> D(dictionary,false), Dnorm(D._width);
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=2 && _width*_height>=32))
- cimg_forX(Dnorm,d) {
- Tfloat norm = 0;
- cimg_forY(D,y) norm+=cimg::sqr(D(d,y));
- Dnorm[d] = std::max((Tfloat)1e-8,std::sqrt(norm));
- }
- cimg_forXY(D,d,y) D(d,y)/=Dnorm[d];
- // Matching pursuit.
- const unsigned int proj_step = method<3?1:method - 2;
- bool is_orthoproj = false;
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=2 && _width*_height>=32))
- cimg_forX(*this,x) {
- CImg<Tfloat> S = get_column(x);
- const CImg<Tfloat> S0 = method<2?CImg<Tfloat>():S;
- Tfloat residual = S.magnitude(2)/S._height;
- const unsigned int nmax = max_iter?max_iter:D._width;
- for (unsigned int n = 0; n<nmax && residual>max_residual; ++n) {
- // Find best matching column in D.
- int dmax = 0;
- Tfloat absdotmax = 0, dotmax = 0;
- cimg_pragma_openmp(parallel for cimg_openmp_if(D._width>=2 && D._width*D._height>=32))
- cimg_forX(D,d) {
- Tfloat _dot = 0;
- cimg_forY(D,y) _dot+=S[y]*D(d,y);
- Tfloat absdot = cimg::abs(_dot);
- cimg_pragma_openmp(critical(get_project_matrix)) {
- if (absdot>absdotmax) {
- absdotmax = absdot;
- dotmax = _dot;
- dmax = d;
- }
- }
- }
- if (!n || method<3 || n%proj_step) {
- // Matching Pursuit: Subtract component to signal.
- W(x,dmax)+=dotmax;
- residual = 0;
- cimg_forY(S,y) {
- S[y]-=dotmax*D(dmax,y);
- residual+=cimg::sqr(S[y]);
- }
- residual = std::sqrt(residual)/S._height;
- is_orthoproj = false;
- } else {
- // Orthogonal Matching Pursuit: Orthogonal projection step.
- W(x,dmax) = 1; // Used as a marker only.
- unsigned int nbW = 0;
- cimg_forY(W,d) if (W(x,d)) ++nbW;
- CImg<Tfloat> sD(nbW,D._height);
- CImg<uintT> inds(nbW);
- int sd = 0;
- cimg_forY(W,d) if (W(x,d)) {
- cimg_forY(sD,y) sD(sd,y) = D(d,y);
- inds[sd++] = d;
- }
- S0.get_solve(sD).move_to(sD); // sD is now a one-column vector of weights
- // Recompute residual signal.
- S = S0;
- cimg_forY(sD,k) {
- const Tfloat weight = sD[k];
- const unsigned int ind = inds[k];
- W(x,ind) = weight;
- cimg_forY(S,y) S[y]-=weight*D(ind,y);
- }
- residual = S.magnitude(2)/S._height;
- is_orthoproj = true;
- }
- }
- // Perform last orthoprojection step if needed.
- if (method>=2 && !is_orthoproj) {
- unsigned int nbW = 0;
- cimg_forY(W,d) if (W(x,d)) ++nbW;
- if (nbW) { // Avoid degenerated case where 0 coefs are used
- CImg<Tfloat> sD(nbW,D._height);
- CImg<uintT> inds(nbW);
- int sd = 0;
- cimg_forY(W,d) if (W(x,d)) {
- cimg_forY(sD,y) sD(sd,y) = D(d,y);
- inds[sd++] = d;
- }
- S0.get_solve(sD).move_to(sD);
- cimg_forY(sD,k) W(x,inds[k]) = sD[k];
- }
- }
- }
- // Normalize resulting coefficients according to initial (non-normalized) dictionary.
- cimg_forXY(W,x,y) W(x,y)/=Dnorm[y];
- return W;
- }
- //! Compute minimal path in a graph, using the Dijkstra algorithm.
- /**
- \param distance An object having operator()(unsigned int i, unsigned int j) which returns distance
- between two nodes (i,j).
- \param nb_nodes Number of graph nodes.
- \param starting_node Index of the starting node.
- \param ending_node Index of the ending node (set to ~0U to ignore ending node).
- \param previous_node Array that gives the previous node index in the path to the starting node
- (optional parameter).
- \return Array of distances of each node to the starting node.
- **/
- template<typename tf, typename t>
- static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
- const unsigned int starting_node, const unsigned int ending_node,
- CImg<t>& previous_node) {
- if (starting_node>=nb_nodes)
- throw CImgArgumentException("CImg<%s>::dijkstra(): Specified index of starting node %u is higher "
- "than number of nodes %u.",
- pixel_type(),starting_node,nb_nodes);
- CImg<T> dist(1,nb_nodes,1,1,cimg::type<T>::max());
- dist(starting_node) = 0;
- previous_node.assign(1,nb_nodes,1,1,(t)-1);
- previous_node(starting_node) = (t)starting_node;
- CImg<uintT> Q(nb_nodes);
- cimg_forX(Q,u) Q(u) = (unsigned int)u;
- cimg::swap(Q(starting_node),Q(0));
- unsigned int sizeQ = nb_nodes;
- while (sizeQ) {
- // Update neighbors from minimal vertex
- const unsigned int umin = Q(0);
- if (umin==ending_node) sizeQ = 0;
- else {
- const T dmin = dist(umin);
- const T infty = cimg::type<T>::max();
- for (unsigned int q = 1; q<sizeQ; ++q) {
- const unsigned int v = Q(q);
- const T d = (T)distance(v,umin);
- if (d<infty) {
- const T alt = dmin + d;
- if (alt<dist(v)) {
- dist(v) = alt;
- previous_node(v) = (t)umin;
- const T distpos = dist(Q(q));
- for (unsigned int pos = q, par = 0; pos && distpos<dist(Q(par=(pos + 1)/2 - 1)); pos=par)
- cimg::swap(Q(pos),Q(par));
- }
- }
- }
- // Remove minimal vertex from queue
- Q(0) = Q(--sizeQ);
- const T distpos = dist(Q(0));
- for (unsigned int pos = 0, left = 0, right = 0;
- ((right=2*(pos + 1),(left=right - 1))<sizeQ && distpos>dist(Q(left))) ||
- (right<sizeQ && distpos>dist(Q(right)));) {
- if (right<sizeQ) {
- if (dist(Q(left))<dist(Q(right))) { cimg::swap(Q(pos),Q(left)); pos = left; }
- else { cimg::swap(Q(pos),Q(right)); pos = right; }
- } else { cimg::swap(Q(pos),Q(left)); pos = left; }
- }
- }
- }
- return dist;
- }
- //! Return minimal path in a graph, using the Dijkstra algorithm.
- template<typename tf, typename t>
- static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
- const unsigned int starting_node, const unsigned int ending_node=~0U) {
- CImg<uintT> foo;
- return dijkstra(distance,nb_nodes,starting_node,ending_node,foo);
- }
- //! Return minimal path in a graph, using the Dijkstra algorithm.
- /**
- \param starting_node Index of the starting node.
- \param ending_node Index of the ending node.
- \param previous_node Array that gives the previous node index in the path to the starting node
- (optional parameter).
- \return Array of distances of each node to the starting node.
- \note image instance corresponds to the adjacency matrix of the graph.
- **/
- template<typename t>
- CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node,
- CImg<t>& previous_node) {
- return get_dijkstra(starting_node,ending_node,previous_node).move_to(*this);
- }
- //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance.
- template<typename t>
- CImg<T> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node,
- CImg<t>& previous_node) const {
- if (_width!=_height || _depth!=1 || _spectrum!=1)
- throw CImgInstanceException(_cimg_instance
- "dijkstra(): Instance is not a graph adjacency matrix.",
- cimg_instance);
- return dijkstra(*this,_width,starting_node,ending_node,previous_node);
- }
- //! Return minimal path in a graph, using the Dijkstra algorithm.
- CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) {
- return get_dijkstra(starting_node,ending_node).move_to(*this);
- }
- //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance.
- CImg<Tfloat> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) const {
- CImg<uintT> foo;
- return get_dijkstra(starting_node,ending_node,foo);
- }
- //! Return an image containing the character codes of specified string.
- /**
- \param str input C-string to encode as an image.
- \param is_last_zero Tells if the ending \c '0' character appear in the resulting image.
- \param is_shared Return result that shares its buffer with \p str.
- **/
- static CImg<T> string(const char *const str, const bool is_last_zero=true, const bool is_shared=false) {
- if (!str) return CImg<T>();
- return CImg<T>(str,(unsigned int)std::strlen(str) + (is_last_zero?1:0),1,1,1,is_shared);
- }
- //! Return a \c 1x1 image containing specified value.
- /**
- \param a0 First vector value.
- **/
- static CImg<T> row_vector(const T& a0) {
- return vector(a0);
- }
- //! Return a \c 2x1 image containing specified values.
- /**
- \param a0 First vector value.
- \param a1 Second vector value.
- **/
- static CImg<T> row_vector(const T& a0, const T& a1) {
- CImg<T> r(2,1);
- r[0] = a0; r[1] = a1;
- return r;
- }
- //! Return a \c 3x1 image containing specified values.
- /**
- \param a0 First vector value.
- \param a1 Second vector value.
- \param a2 Third vector value.
- **/
- static CImg<T> row_vector(const T& a0, const T& a1, const T& a2) {
- CImg<T> r(3,1);
- r[0] = a0; r[1] = a1; r[2] = a2;
- return r;
- }
- //! Return a \c 4x1 image containing specified values.
- /**
- \param a0 First vector value.
- \param a1 Second vector value.
- \param a2 Third vector value.
- \param a3 Fourth vector value.
- **/
- static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3) {
- CImg<T> r(4,1);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3;
- return r;
- }
- //! Return a \c 5x1 image containing specified values.
- static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
- CImg<T> r(5,1);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4;
- return r;
- }
- //! Return a \c 6x1 image containing specified values.
- static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
- CImg<T> r(6,1);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5;
- return r;
- }
- //! Return a \c 7x1 image containing specified values.
- static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6) {
- CImg<T> r(7,1);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6;
- return r;
- }
- //! Return a \c 8x1 image containing specified values.
- static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6, const T& a7) {
- CImg<T> r(8,1);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7;
- return r;
- }
- //! Return a \c 9x1 image containing specified values.
- static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6, const T& a7,
- const T& a8) {
- CImg<T> r(9,1);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8;
- return r;
- }
- //! Return a \c 10x1 image containing specified values.
- static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6, const T& a7,
- const T& a8, const T& a9) {
- CImg<T> r(10,1);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
- return r;
- }
- //! Return a \c 11x1 image containing specified values.
- static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6, const T& a7,
- const T& a8, const T& a9, const T& a10) {
- CImg<T> r(11,1);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
- r[10] = a10;
- return r;
- }
- //! Return a \c 12x1 image containing specified values.
- static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6, const T& a7,
- const T& a8, const T& a9, const T& a10, const T& a11) {
- CImg<T> r(12,1);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
- r[10] = a10; r[11] = a11;
- return r;
- }
- //! Return a \c 13x1 image containing specified values.
- static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6, const T& a7,
- const T& a8, const T& a9, const T& a10, const T& a11,
- const T& a12) {
- CImg<T> r(13,1);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
- r[10] = a10; r[11] = a11; r[12] = a12;
- return r;
- }
- //! Return a \c 14x1 image containing specified values.
- static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6, const T& a7,
- const T& a8, const T& a9, const T& a10, const T& a11,
- const T& a12, const T& a13) {
- CImg<T> r(14,1);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
- r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13;
- return r;
- }
- //! Return a \c 15x1 image containing specified values.
- static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6, const T& a7,
- const T& a8, const T& a9, const T& a10, const T& a11,
- const T& a12, const T& a13, const T& a14) {
- CImg<T> r(15,1);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
- r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14;
- return r;
- }
- //! Return a \c 16x1 image containing specified values.
- static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6, const T& a7,
- const T& a8, const T& a9, const T& a10, const T& a11,
- const T& a12, const T& a13, const T& a14, const T& a15) {
- CImg<T> r(16,1);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
- r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15;
- return r;
- }
- //! Return a \c 1x1 image containing specified value.
- /**
- \param a0 First vector value.
- **/
- static CImg<T> vector(const T& a0) {
- CImg<T> r(1,1);
- r[0] = a0;
- return r;
- }
- //! Return a \c 1x2 image containing specified values.
- /**
- \param a0 First vector value.
- \param a1 Second vector value.
- **/
- static CImg<T> vector(const T& a0, const T& a1) {
- CImg<T> r(1,2);
- r[0] = a0; r[1] = a1;
- return r;
- }
- //! Return a \c 1x3 image containing specified values.
- /**
- \param a0 First vector value.
- \param a1 Second vector value.
- \param a2 Third vector value.
- **/
- static CImg<T> vector(const T& a0, const T& a1, const T& a2) {
- CImg<T> r(1,3);
- r[0] = a0; r[1] = a1; r[2] = a2;
- return r;
- }
- //! Return a \c 1x4 image containing specified values.
- /**
- \param a0 First vector value.
- \param a1 Second vector value.
- \param a2 Third vector value.
- \param a3 Fourth vector value.
- **/
- static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3) {
- CImg<T> r(1,4);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3;
- return r;
- }
- //! Return a \c 1x5 image containing specified values.
- static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
- CImg<T> r(1,5);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4;
- return r;
- }
- //! Return a \c 1x6 image containing specified values.
- static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
- CImg<T> r(1,6);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5;
- return r;
- }
- //! Return a \c 1x7 image containing specified values.
- static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6) {
- CImg<T> r(1,7);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6;
- return r;
- }
- //! Return a \c 1x8 image containing specified values.
- static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6, const T& a7) {
- CImg<T> r(1,8);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7;
- return r;
- }
- //! Return a \c 1x9 image containing specified values.
- static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6, const T& a7,
- const T& a8) {
- CImg<T> r(1,9);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8;
- return r;
- }
- //! Return a \c 1x10 image containing specified values.
- static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6, const T& a7,
- const T& a8, const T& a9) {
- CImg<T> r(1,10);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
- return r;
- }
- //! Return a \c 1x11 image containing specified values.
- static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6, const T& a7,
- const T& a8, const T& a9, const T& a10) {
- CImg<T> r(1,11);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
- r[10] = a10;
- return r;
- }
- //! Return a \c 1x12 image containing specified values.
- static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6, const T& a7,
- const T& a8, const T& a9, const T& a10, const T& a11) {
- CImg<T> r(1,12);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
- r[10] = a10; r[11] = a11;
- return r;
- }
- //! Return a \c 1x13 image containing specified values.
- static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6, const T& a7,
- const T& a8, const T& a9, const T& a10, const T& a11,
- const T& a12) {
- CImg<T> r(1,13);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
- r[10] = a10; r[11] = a11; r[12] = a12;
- return r;
- }
- //! Return a \c 1x14 image containing specified values.
- static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6, const T& a7,
- const T& a8, const T& a9, const T& a10, const T& a11,
- const T& a12, const T& a13) {
- CImg<T> r(1,14);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
- r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13;
- return r;
- }
- //! Return a \c 1x15 image containing specified values.
- static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6, const T& a7,
- const T& a8, const T& a9, const T& a10, const T& a11,
- const T& a12, const T& a13, const T& a14) {
- CImg<T> r(1,15);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
- r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14;
- return r;
- }
- //! Return a \c 1x16 image containing specified values.
- static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6, const T& a7,
- const T& a8, const T& a9, const T& a10, const T& a11,
- const T& a12, const T& a13, const T& a14, const T& a15) {
- CImg<T> r(1,16);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
- r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15;
- return r;
- }
- //! Return a \c 1x17 image containing specified values.
- static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6, const T& a7,
- const T& a8, const T& a9, const T& a10, const T& a11,
- const T& a12, const T& a13, const T& a14, const T& a15,
- const T& a16) {
- CImg<T> r(1,17);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
- r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15; r[16] = a16;
- return r;
- }
- //! Return a \c 1x18 image containing specified values.
- static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6, const T& a7,
- const T& a8, const T& a9, const T& a10, const T& a11,
- const T& a12, const T& a13, const T& a14, const T& a15,
- const T& a16, const T& a17) {
- CImg<T> r(1,18);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
- r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15; r[16] = a16; r[17] = a17;
- return r;
- }
- //! Return a \c 1x19 image containing specified values.
- static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6, const T& a7,
- const T& a8, const T& a9, const T& a10, const T& a11,
- const T& a12, const T& a13, const T& a14, const T& a15,
- const T& a16, const T& a17, const T& a18) {
- CImg<T> r(1,19);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
- r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15; r[16] = a16; r[17] = a17;
- r[18] = a18;
- return r;
- }
- //! Return a \c 1x20 image containing specified values.
- static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6, const T& a7,
- const T& a8, const T& a9, const T& a10, const T& a11,
- const T& a12, const T& a13, const T& a14, const T& a15,
- const T& a16, const T& a17, const T& a18, const T& a19) {
- CImg<T> r(1,20);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
- r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15; r[16] = a16; r[17] = a17;
- r[18] = a18; r[19] = a19;
- return r;
- }
- //! Return a \c 1x21 image containing specified values.
- static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6, const T& a7,
- const T& a8, const T& a9, const T& a10, const T& a11,
- const T& a12, const T& a13, const T& a14, const T& a15,
- const T& a16, const T& a17, const T& a18, const T& a19,
- const T& a20) {
- CImg<T> r(1,21);
- r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7; r[8] = a8; r[9] = a9;
- r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15; r[16] = a16; r[17] = a17;
- r[18] = a18; r[19] = a19; r[20] = a20;
- return r;
- }
- //! Return a 1x1 matrix containing specified coefficients.
- /**
- \param a0 First matrix value.
- \note Equivalent to vector(const T&).
- **/
- static CImg<T> matrix(const T& a0) {
- return vector(a0);
- }
- //! Return a 2x2 matrix containing specified coefficients.
- /**
- \param a0 First matrix value.
- \param a1 Second matrix value.
- \param a2 Third matrix value.
- \param a3 Fourth matrix value.
- **/
- static CImg<T> matrix(const T& a0, const T& a1,
- const T& a2, const T& a3) {
- CImg<T> r(2,2); T *ptr = r._data;
- *(ptr++) = a0; *(ptr++) = a1;
- *(ptr++) = a2; *(ptr++) = a3;
- return r;
- }
- //! Return a 3x3 matrix containing specified coefficients.
- /**
- \param a0 First matrix value.
- \param a1 Second matrix value.
- \param a2 Third matrix value.
- \param a3 Fourth matrix value.
- \param a4 Fifth matrix value.
- \param a5 Sixth matrix value.
- \param a6 Seventh matrix value.
- \param a7 Eighth matrix value.
- \param a8 Ninth matrix value.
- **/
- static CImg<T> matrix(const T& a0, const T& a1, const T& a2,
- const T& a3, const T& a4, const T& a5,
- const T& a6, const T& a7, const T& a8) {
- CImg<T> r(3,3); T *ptr = r._data;
- *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2;
- *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5;
- *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8;
- return r;
- }
- //! Return a 4x4 matrix containing specified coefficients.
- static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3,
- const T& a4, const T& a5, const T& a6, const T& a7,
- const T& a8, const T& a9, const T& a10, const T& a11,
- const T& a12, const T& a13, const T& a14, const T& a15) {
- CImg<T> r(4,4); T *ptr = r._data;
- *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
- *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
- *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
- *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15;
- return r;
- }
- //! Return a 5x5 matrix containing specified coefficients.
- static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4,
- const T& a5, const T& a6, const T& a7, const T& a8, const T& a9,
- const T& a10, const T& a11, const T& a12, const T& a13, const T& a14,
- const T& a15, const T& a16, const T& a17, const T& a18, const T& a19,
- const T& a20, const T& a21, const T& a22, const T& a23, const T& a24) {
- CImg<T> r(5,5); T *ptr = r._data;
- *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4;
- *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9;
- *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14;
- *(ptr++) = a15; *(ptr++) = a16; *(ptr++) = a17; *(ptr++) = a18; *(ptr++) = a19;
- *(ptr++) = a20; *(ptr++) = a21; *(ptr++) = a22; *(ptr++) = a23; *(ptr++) = a24;
- return r;
- }
- //! Return a 1x1 symmetric matrix containing specified coefficients.
- /**
- \param a0 First matrix value.
- \note Equivalent to vector(const T&).
- **/
- static CImg<T> tensor(const T& a0) {
- return matrix(a0);
- }
- //! Return a 2x2 symmetric matrix tensor containing specified coefficients.
- static CImg<T> tensor(const T& a0, const T& a1, const T& a2) {
- return matrix(a0,a1,a1,a2);
- }
- //! Return a 3x3 symmetric matrix containing specified coefficients.
- static CImg<T> tensor(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
- return matrix(a0,a1,a2,a1,a3,a4,a2,a4,a5);
- }
- //! Return a 1x1 diagonal matrix containing specified coefficients.
- static CImg<T> diagonal(const T& a0) {
- return matrix(a0);
- }
- //! Return a 2x2 diagonal matrix containing specified coefficients.
- static CImg<T> diagonal(const T& a0, const T& a1) {
- return matrix(a0,0,0,a1);
- }
- //! Return a 3x3 diagonal matrix containing specified coefficients.
- static CImg<T> diagonal(const T& a0, const T& a1, const T& a2) {
- return matrix(a0,0,0,0,a1,0,0,0,a2);
- }
- //! Return a 4x4 diagonal matrix containing specified coefficients.
- static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3) {
- return matrix(a0,0,0,0,0,a1,0,0,0,0,a2,0,0,0,0,a3);
- }
- //! Return a 5x5 diagonal matrix containing specified coefficients.
- static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
- return matrix(a0,0,0,0,0,0,a1,0,0,0,0,0,a2,0,0,0,0,0,a3,0,0,0,0,0,a4);
- }
- //! Return a NxN identity matrix.
- /**
- \param N Dimension of the matrix.
- **/
- static CImg<T> identity_matrix(const unsigned int N) {
- CImg<T> res(N,N,1,1,0);
- cimg_forX(res,x) res(x,x) = 1;
- return res;
- }
- //! Return a N-numbered sequence vector from \p a0 to \p a1.
- /**
- \param N Size of the resulting vector.
- \param a0 Starting value of the sequence.
- \param a1 Ending value of the sequence.
- **/
- static CImg<T> sequence(const unsigned int N, const T& a0, const T& a1) {
- if (N) return CImg<T>(1,N).sequence(a0,a1);
- return CImg<T>();
- }
- //! Return a 3x3 rotation matrix from an { axis + angle } or a quaternion.
- /**
- \param x X-coordinate of the rotation axis, or first quaternion coordinate.
- \param y Y-coordinate of the rotation axis, or second quaternion coordinate.
- \param z Z-coordinate of the rotation axis, or third quaternion coordinate.
- \param w Angle of the rotation axis (in degree), or fourth quaternion coordinate.
- \param is_quaternion Tell is the four arguments denotes a set { axis + angle } or a quaternion (x,y,z,w).
- **/
- static CImg<T> rotation_matrix(const float x, const float y, const float z, const float w,
- const bool is_quaternion=false) {
- double X, Y, Z, W, N;
- if (is_quaternion) {
- N = std::sqrt((double)x*x + (double)y*y + (double)z*z + (double)w*w);
- if (N>0) { X = x/N; Y = y/N; Z = z/N; W = w/N; }
- else { X = Y = Z = 0; W = 1; }
- return CImg<T>::matrix((T)(X*X + Y*Y - Z*Z - W*W),(T)(2*Y*Z - 2*X*W),(T)(2*X*Z + 2*Y*W),
- (T)(2*X*W + 2*Y*Z),(T)(X*X - Y*Y + Z*Z - W*W),(T)(2*Z*W - 2*X*Y),
- (T)(2*Y*W - 2*X*Z),(T)(2*X*Y + 2*Z*W),(T)(X*X - Y*Y - Z*Z + W*W));
- }
- N = cimg::hypot((double)x,(double)y,(double)z);
- if (N>0) { X = x/N; Y = y/N; Z = z/N; }
- else { X = Y = 0; Z = 1; }
- const double ang = w*cimg::PI/180, c = std::cos(ang), omc = 1 - c, s = std::sin(ang);
- return CImg<T>::matrix((T)(X*X*omc + c),(T)(X*Y*omc - Z*s),(T)(X*Z*omc + Y*s),
- (T)(X*Y*omc + Z*s),(T)(Y*Y*omc + c),(T)(Y*Z*omc - X*s),
- (T)(X*Z*omc - Y*s),(T)(Y*Z*omc + X*s),(T)(Z*Z*omc + c));
- }
- //@}
- //-----------------------------------
- //
- //! \name Value Manipulation
- //@{
- //-----------------------------------
- //! Fill all pixel values with specified value.
- /**
- \param val Fill value.
- **/
- CImg<T>& fill(const T& val) {
- if (is_empty()) return *this;
- if (val && sizeof(T)!=1) cimg_for(*this,ptrd,T) *ptrd = val;
- else std::memset(_data,(int)(ulongT)val,sizeof(T)*size()); // Double cast to allow val to be (void*)
- return *this;
- }
- //! Fill all pixel values with specified value \newinstance.
- CImg<T> get_fill(const T& val) const {
- return CImg<T>(_width,_height,_depth,_spectrum).fill(val);
- }
- //! Fill sequentially all pixel values with specified values.
- /**
- \param val0 First fill value.
- \param val1 Second fill value.
- **/
- CImg<T>& fill(const T& val0, const T& val1) {
- if (is_empty()) return *this;
- T *ptrd, *ptre = end() - 1;
- for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; }
- if (ptrd!=ptre + 1) *(ptrd++) = val0;
- return *this;
- }
- //! Fill sequentially all pixel values with specified values \newinstance.
- CImg<T> get_fill(const T& val0, const T& val1) const {
- return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1);
- }
- //! Fill sequentially all pixel values with specified values \overloading.
- CImg<T>& fill(const T& val0, const T& val1, const T& val2) {
- if (is_empty()) return *this;
- T *ptrd, *ptre = end() - 2;
- for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; }
- ptre+=2;
- switch (ptre - ptrd) {
- case 2 : *(--ptre) = val1; // Fallthrough
- case 1 : *(--ptre) = val0; // Fallthrough
- }
- return *this;
- }
- //! Fill sequentially all pixel values with specified values \newinstance.
- CImg<T> get_fill(const T& val0, const T& val1, const T& val2) const {
- return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2);
- }
- //! Fill sequentially all pixel values with specified values \overloading.
- CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3) {
- if (is_empty()) return *this;
- T *ptrd, *ptre = end() - 3;
- for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; }
- ptre+=3;
- switch (ptre - ptrd) {
- case 3 : *(--ptre) = val2; // Fallthrough
- case 2 : *(--ptre) = val1; // Fallthrough
- case 1 : *(--ptre) = val0; // Fallthrough
- }
- return *this;
- }
- //! Fill sequentially all pixel values with specified values \newinstance.
- CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3) const {
- return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3);
- }
- //! Fill sequentially all pixel values with specified values \overloading.
- CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) {
- if (is_empty()) return *this;
- T *ptrd, *ptre = end() - 4;
- for (ptrd = _data; ptrd<ptre; ) {
- *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
- }
- ptre+=4;
- switch (ptre - ptrd) {
- case 4 : *(--ptre) = val3; // Fallthrough
- case 3 : *(--ptre) = val2; // Fallthrough
- case 2 : *(--ptre) = val1; // Fallthrough
- case 1 : *(--ptre) = val0; // Fallthrough
- }
- return *this;
- }
- //! Fill sequentially all pixel values with specified values \newinstance.
- CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) const {
- return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4);
- }
- //! Fill sequentially all pixel values with specified values \overloading.
- CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) {
- if (is_empty()) return *this;
- T *ptrd, *ptre = end() - 5;
- for (ptrd = _data; ptrd<ptre; ) {
- *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
- }
- ptre+=5;
- switch (ptre - ptrd) {
- case 5 : *(--ptre) = val4; // Fallthrough
- case 4 : *(--ptre) = val3; // Fallthrough
- case 3 : *(--ptre) = val2; // Fallthrough
- case 2 : *(--ptre) = val1; // Fallthrough
- case 1 : *(--ptre) = val0; // Fallthrough
- }
- return *this;
- }
- //! Fill sequentially all pixel values with specified values \newinstance.
- CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) const {
- return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5);
- }
- //! Fill sequentially all pixel values with specified values \overloading.
- CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
- const T& val6) {
- if (is_empty()) return *this;
- T *ptrd, *ptre = end() - 6;
- for (ptrd = _data; ptrd<ptre; ) {
- *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
- *(ptrd++) = val6;
- }
- ptre+=6;
- switch (ptre - ptrd) {
- case 6 : *(--ptre) = val5; // Fallthrough
- case 5 : *(--ptre) = val4; // Fallthrough
- case 4 : *(--ptre) = val3; // Fallthrough
- case 3 : *(--ptre) = val2; // Fallthrough
- case 2 : *(--ptre) = val1; // Fallthrough
- case 1 : *(--ptre) = val0; // Fallthrough
- }
- return *this;
- }
- //! Fill sequentially all pixel values with specified values \newinstance.
- CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
- const T& val6) const {
- return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6);
- }
- //! Fill sequentially all pixel values with specified values \overloading.
- CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
- const T& val6, const T& val7) {
- if (is_empty()) return *this;
- T *ptrd, *ptre = end() - 7;
- for (ptrd = _data; ptrd<ptre; ) {
- *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3;
- *(ptrd++) = val4; *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7;
- }
- ptre+=7;
- switch (ptre - ptrd) {
- case 7 : *(--ptre) = val6; // Fallthrough
- case 6 : *(--ptre) = val5; // Fallthrough
- case 5 : *(--ptre) = val4; // Fallthrough
- case 4 : *(--ptre) = val3; // Fallthrough
- case 3 : *(--ptre) = val2; // Fallthrough
- case 2 : *(--ptre) = val1; // Fallthrough
- case 1 : *(--ptre) = val0; // Fallthrough
- }
- return *this;
- }
- //! Fill sequentially all pixel values with specified values \newinstance.
- CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
- const T& val6, const T& val7) const {
- return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7);
- }
- //! Fill sequentially all pixel values with specified values \overloading.
- CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
- const T& val6, const T& val7, const T& val8) {
- if (is_empty()) return *this;
- T *ptrd, *ptre = end() - 8;
- for (ptrd = _data; ptrd<ptre; ) {
- *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2;
- *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
- *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8;
- }
- ptre+=8;
- switch (ptre - ptrd) {
- case 8 : *(--ptre) = val7; // Fallthrough
- case 7 : *(--ptre) = val6; // Fallthrough
- case 6 : *(--ptre) = val5; // Fallthrough
- case 5 : *(--ptre) = val4; // Fallthrough
- case 4 : *(--ptre) = val3; // Fallthrough
- case 3 : *(--ptre) = val2; // Fallthrough
- case 2 : *(--ptre) = val1; // Fallthrough
- case 1 : *(--ptre) = val0; // Fallthrough
- }
- return *this;
- }
- //! Fill sequentially all pixel values with specified values \newinstance.
- CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
- const T& val6, const T& val7, const T& val8) const {
- return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8);
- }
- //! Fill sequentially all pixel values with specified values \overloading.
- CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
- const T& val6, const T& val7, const T& val8, const T& val9) {
- if (is_empty()) return *this;
- T *ptrd, *ptre = end() - 9;
- for (ptrd = _data; ptrd<ptre; ) {
- *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
- *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
- }
- ptre+=9;
- switch (ptre - ptrd) {
- case 9 : *(--ptre) = val8; // Fallthrough
- case 8 : *(--ptre) = val7; // Fallthrough
- case 7 : *(--ptre) = val6; // Fallthrough
- case 6 : *(--ptre) = val5; // Fallthrough
- case 5 : *(--ptre) = val4; // Fallthrough
- case 4 : *(--ptre) = val3; // Fallthrough
- case 3 : *(--ptre) = val2; // Fallthrough
- case 2 : *(--ptre) = val1; // Fallthrough
- case 1 : *(--ptre) = val0; // Fallthrough
- }
- return *this;
- }
- //! Fill sequentially all pixel values with specified values \newinstance.
- CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
- const T& val6, const T& val7, const T& val8, const T& val9) const {
- return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9);
- }
- //! Fill sequentially all pixel values with specified values \overloading.
- CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
- const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) {
- if (is_empty()) return *this;
- T *ptrd, *ptre = end() - 10;
- for (ptrd = _data; ptrd<ptre; ) {
- *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
- *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
- *(ptrd++) = val10;
- }
- ptre+=10;
- switch (ptre - ptrd) {
- case 10 : *(--ptre) = val9; // Fallthrough
- case 9 : *(--ptre) = val8; // Fallthrough
- case 8 : *(--ptre) = val7; // Fallthrough
- case 7 : *(--ptre) = val6; // Fallthrough
- case 6 : *(--ptre) = val5; // Fallthrough
- case 5 : *(--ptre) = val4; // Fallthrough
- case 4 : *(--ptre) = val3; // Fallthrough
- case 3 : *(--ptre) = val2; // Fallthrough
- case 2 : *(--ptre) = val1; // Fallthrough
- case 1 : *(--ptre) = val0; // Fallthrough
- }
- return *this;
- }
- //! Fill sequentially all pixel values with specified values \newinstance.
- CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
- const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) const {
- return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10);
- }
- //! Fill sequentially all pixel values with specified values \overloading.
- CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
- const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) {
- if (is_empty()) return *this;
- T *ptrd, *ptre = end() - 11;
- for (ptrd = _data; ptrd<ptre; ) {
- *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
- *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
- }
- ptre+=11;
- switch (ptre - ptrd) {
- case 11 : *(--ptre) = val10; // Fallthrough
- case 10 : *(--ptre) = val9; // Fallthrough
- case 9 : *(--ptre) = val8; // Fallthrough
- case 8 : *(--ptre) = val7; // Fallthrough
- case 7 : *(--ptre) = val6; // Fallthrough
- case 6 : *(--ptre) = val5; // Fallthrough
- case 5 : *(--ptre) = val4; // Fallthrough
- case 4 : *(--ptre) = val3; // Fallthrough
- case 3 : *(--ptre) = val2; // Fallthrough
- case 2 : *(--ptre) = val1; // Fallthrough
- case 1 : *(--ptre) = val0; // Fallthrough
- }
- return *this;
- }
- //! Fill sequentially all pixel values with specified values \newinstance.
- CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
- const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) const {
- return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
- val11);
- }
- //! Fill sequentially all pixel values with specified values \overloading.
- CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
- const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
- const T& val12) {
- if (is_empty()) return *this;
- T *ptrd, *ptre = end() - 12;
- for (ptrd = _data; ptrd<ptre; ) {
- *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
- *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
- *(ptrd++) = val12;
- }
- ptre+=12;
- switch (ptre - ptrd) {
- case 12 : *(--ptre) = val11; // Fallthrough
- case 11 : *(--ptre) = val10; // Fallthrough
- case 10 : *(--ptre) = val9; // Fallthrough
- case 9 : *(--ptre) = val8; // Fallthrough
- case 8 : *(--ptre) = val7; // Fallthrough
- case 7 : *(--ptre) = val6; // Fallthrough
- case 6 : *(--ptre) = val5; // Fallthrough
- case 5 : *(--ptre) = val4; // Fallthrough
- case 4 : *(--ptre) = val3; // Fallthrough
- case 3 : *(--ptre) = val2; // Fallthrough
- case 2 : *(--ptre) = val1; // Fallthrough
- case 1 : *(--ptre) = val0; // Fallthrough
- }
- return *this;
- }
- //! Fill sequentially all pixel values with specified values \newinstance.
- CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
- const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
- const T& val12) const {
- return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
- val11,val12);
- }
- //! Fill sequentially all pixel values with specified values \overloading.
- CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
- const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
- const T& val12, const T& val13) {
- if (is_empty()) return *this;
- T *ptrd, *ptre = end() - 13;
- for (ptrd = _data; ptrd<ptre; ) {
- *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
- *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
- *(ptrd++) = val12; *(ptrd++) = val13;
- }
- ptre+=13;
- switch (ptre - ptrd) {
- case 13 : *(--ptre) = val12; // Fallthrough
- case 12 : *(--ptre) = val11; // Fallthrough
- case 11 : *(--ptre) = val10; // Fallthrough
- case 10 : *(--ptre) = val9; // Fallthrough
- case 9 : *(--ptre) = val8; // Fallthrough
- case 8 : *(--ptre) = val7; // Fallthrough
- case 7 : *(--ptre) = val6; // Fallthrough
- case 6 : *(--ptre) = val5; // Fallthrough
- case 5 : *(--ptre) = val4; // Fallthrough
- case 4 : *(--ptre) = val3; // Fallthrough
- case 3 : *(--ptre) = val2; // Fallthrough
- case 2 : *(--ptre) = val1; // Fallthrough
- case 1 : *(--ptre) = val0; // Fallthrough
- }
- return *this;
- }
- //! Fill sequentially all pixel values with specified values \newinstance.
- CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
- const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
- const T& val12, const T& val13) const {
- return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
- val11,val12,val13);
- }
- //! Fill sequentially all pixel values with specified values \overloading.
- CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
- const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
- const T& val12, const T& val13, const T& val14) {
- if (is_empty()) return *this;
- T *ptrd, *ptre = end() - 14;
- for (ptrd = _data; ptrd<ptre; ) {
- *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
- *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
- *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14;
- }
- ptre+=14;
- switch (ptre - ptrd) {
- case 14 : *(--ptre) = val13; // Fallthrough
- case 13 : *(--ptre) = val12; // Fallthrough
- case 12 : *(--ptre) = val11; // Fallthrough
- case 11 : *(--ptre) = val10; // Fallthrough
- case 10 : *(--ptre) = val9; // Fallthrough
- case 9 : *(--ptre) = val8; // Fallthrough
- case 8 : *(--ptre) = val7; // Fallthrough
- case 7 : *(--ptre) = val6; // Fallthrough
- case 6 : *(--ptre) = val5; // Fallthrough
- case 5 : *(--ptre) = val4; // Fallthrough
- case 4 : *(--ptre) = val3; // Fallthrough
- case 3 : *(--ptre) = val2; // Fallthrough
- case 2 : *(--ptre) = val1; // Fallthrough
- case 1 : *(--ptre) = val0; // Fallthrough
- }
- return *this;
- }
- //! Fill sequentially all pixel values with specified values \newinstance.
- CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
- const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
- const T& val12, const T& val13, const T& val14) const {
- return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
- val11,val12,val13,val14);
- }
- //! Fill sequentially all pixel values with specified values \overloading.
- CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
- const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
- const T& val12, const T& val13, const T& val14, const T& val15) {
- if (is_empty()) return *this;
- T *ptrd, *ptre = end() - 15;
- for (ptrd = _data; ptrd<ptre; ) {
- *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
- *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
- *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14; *(ptrd++) = val15;
- }
- ptre+=15;
- switch (ptre - ptrd) {
- case 15 : *(--ptre) = val14; // Fallthrough
- case 14 : *(--ptre) = val13; // Fallthrough
- case 13 : *(--ptre) = val12; // Fallthrough
- case 12 : *(--ptre) = val11; // Fallthrough
- case 11 : *(--ptre) = val10; // Fallthrough
- case 10 : *(--ptre) = val9; // Fallthrough
- case 9 : *(--ptre) = val8; // Fallthrough
- case 8 : *(--ptre) = val7; // Fallthrough
- case 7 : *(--ptre) = val6; // Fallthrough
- case 6 : *(--ptre) = val5; // Fallthrough
- case 5 : *(--ptre) = val4; // Fallthrough
- case 4 : *(--ptre) = val3; // Fallthrough
- case 3 : *(--ptre) = val2; // Fallthrough
- case 2 : *(--ptre) = val1; // Fallthrough
- case 1 : *(--ptre) = val0; // Fallthrough
- }
- return *this;
- }
- //! Fill sequentially all pixel values with specified values \newinstance.
- CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
- const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
- const T& val12, const T& val13, const T& val14, const T& val15) const {
- return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
- val11,val12,val13,val14,val15);
- }
- //! Fill sequentially pixel values according to a given expression.
- /**
- \param expression C-string describing a math formula, or a sequence of values.
- \param repeat_values In case a list of values is provided, tells if this list must be repeated for the filling.
- \param allow_formula Tells that mathematical formulas are authorized for the filling.
- \param list_images In case of a mathematical expression, attach a list of images to the specified expression.
- **/
- CImg<T>& fill(const char *const expression, const bool repeat_values, const bool allow_formula=true,
- CImgList<T> *const list_images=0) {
- return _fill(expression,repeat_values,allow_formula?3:1,list_images,"fill",0,0);
- }
- // bits of 'mode' can enable/disable these properties:
- // . 1 = Allow list of values.
- // . 2 = Allow formula.
- // . 4 = Evaluate but does not fill image values.
- CImg<T>& _fill(const char *const expression, const bool repeat_values, const unsigned int mode,
- CImgList<T> *const list_images, const char *const calling_function,
- const CImg<T> *provides_copy, CImg<doubleT> *const result_end) {
- if (is_empty() || !expression || !*expression) return *this;
- const unsigned int excmode = cimg::exception_mode();
- cimg::exception_mode(0);
- CImg<charT> is_error_expr;
- bool is_done = false, is_value_sequence = false;
- cimg_abort_init;
- if (result_end) result_end->assign();
- // Detect value sequence.
- if (mode&1) {
- double value;
- char sep;
- const int err = cimg_sscanf(expression,"%lf %c",&value,&sep);
- if (err==1 || (err==2 && sep==',')) {
- if (err==1) { if (mode&4) return *this; return fill((T)value); }
- else is_value_sequence = true;
- }
- }
- // Try to fill values according to a formula.
- if (mode&2 && !is_value_sequence) {
- _cimg_abort_init_openmp;
- try {
- CImg<T> base = provides_copy?provides_copy->get_shared():get_shared();
- _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='+' ||
- *expression=='*' || *expression==':'),
- calling_function,base,this,list_images,true);
- if (!provides_copy && expression &&
- *expression!='>' && *expression!='<' && *expression!=':' &&
- mp.need_input_copy)
- base.assign().assign(*this,false); // Needs input copy
- // Determine M2, smallest image dimension (used as axis for the most inner loop in parallelized iterations).
- // M1 is the total number of parallelized iterations.
- unsigned int M1 = 0, M2 = 0;
- cimg::unused(M1,M2);
- if (mp.result_dim) {
- M2 = cimg::min(_width,_height,_depth);
- M1 = M2==_width?_height*_depth:M2==_height?_width*_depth:_width*_height;
- } else {
- M2 = cimg::min(_width,_height,_depth,_spectrum);
- M1 = M2==_width?_height*_depth*_spectrum:M2==_height?_width*_depth*_spectrum:
- M2==_depth?_width*_height*_spectrum:_width*_height*_depth;
- }
- bool is_parallelizable = false;
- cimg_openmp_if(*expression=='*' || *expression==':' || (mp.is_parallelizable &&
- (M2>=2 || M1>=4096) && M1*M2>=32))
- is_parallelizable = true;
- if (mp.result_dim) { // Vector-valued expression
- const unsigned int N = std::min(mp.result_dim,_spectrum);
- const ulongT whd = (ulongT)_width*_height*_depth;
- T *ptrd = *expression=='<'?_data + _width*_height*_depth - 1:_data;
- if (*expression=='<') {
- CImg<doubleT> res(1,mp.result_dim);
- mp.begin_t();
- cimg_rofYZ(*this,y,z) {
- cimg_abort_test;
- if (mode&4) cimg_rofX(*this,x) mp(x,y,z,0);
- else cimg_rofX(*this,x) {
- mp(x,y,z,0,res._data);
- const double *ptrs = res._data;
- T *_ptrd = ptrd--; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; }
- }
- }
- mp.end_t();
- } else if (*expression=='>' || *expression=='+' || !is_parallelizable) {
- CImg<doubleT> res(1,mp.result_dim);
- mp.begin_t();
- cimg_forYZ(*this,y,z) {
- cimg_abort_test;
- if (mode&4) cimg_forX(*this,x) mp(x,y,z,0);
- else cimg_forX(*this,x) {
- mp(x,y,z,0,res._data);
- const double *ptrs = res._data;
- T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; }
- }
- }
- mp.end_t();
- } else {
- #if cimg_use_openmp!=0
- cimg_pragma_openmp(parallel)
- {
- _cimg_math_parser
- *const _mp = omp_get_thread_num()?new _cimg_math_parser(mp):&mp,
- &lmp = *_mp;
- lmp.is_fill = true;
- cimg_pragma_openmp(barrier)
- lmp.begin_t();
- #define _cimg_fill_openmp_vector(_YZ,_y,_z,_X,_x,_sx,_sy,_sz,_off) \
- cimg_pragma_openmp(for cimg_openmp_collapse(2)) \
- cimg_for##_YZ(*this,_y,_z) _cimg_abort_try_openmp { \
- cimg_abort_test; \
- if (mode&4) cimg_for##_X(*this,_x) lmp(x,y,z,0); \
- else { \
- CImg<doubleT> res(1,lmp.result_dim); \
- T *__ptrd = data(_sx,_sy,_sz,0); \
- const ulongT off = (ulongT)_off; \
- cimg_for##_X(*this,_x) { \
- lmp(x,y,z,0,res._data); \
- const double *ptrs = res._data; \
- T *_ptrd = __ptrd; \
- for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } \
- __ptrd+=off; \
- } \
- } \
- } _cimg_abort_catch_openmp _cimg_abort_catch_fill_openmp
- if (M2==_width) { _cimg_fill_openmp_vector(YZ,y,z,X,x,0,y,z,1) }
- else if (M2==_height) { _cimg_fill_openmp_vector(XZ,x,z,Y,y,x,0,z,_width) }
- else { _cimg_fill_openmp_vector(XY,x,y,Z,z,x,y,0,_width*_height) }
- lmp.end_t();
- cimg_pragma_openmp(barrier) cimg_pragma_openmp(critical) { lmp.merge(mp); }
- if (&lmp!=&mp) delete &lmp;
- }
- #endif
- }
- } else { // Scalar-valued expression
- T *ptrd = *expression=='<'?end() - 1:_data;
- if (*expression=='<') {
- mp.begin_t();
- if (mode&4) cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) mp(x,y,z,c); }
- else cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) *(ptrd--) = (T)mp(x,y,z,c); }
- mp.end_t();
- } else if (*expression=='>' || *expression=='+' || !is_parallelizable) {
- mp.begin_t();
- if (mode&4) cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) mp(x,y,z,c); }
- else cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) *(ptrd++) = (T)mp(x,y,z,c); }
- mp.end_t();
- } else {
- #if cimg_use_openmp!=0
- cimg_pragma_openmp(parallel)
- {
- _cimg_math_parser
- *const _mp = omp_get_thread_num()?new _cimg_math_parser(mp):&mp,
- &lmp = *_mp;
- lmp.is_fill = true;
- cimg_pragma_openmp(barrier)
- lmp.begin_t();
- #define _cimg_fill_openmp_scalar(_YZC,_y,_z,_c,_X,_x,_sx,_sy,_sz,_sc,_off) \
- cimg_pragma_openmp(for cimg_openmp_collapse(3)) \
- cimg_for##_YZC(*this,_y,_z,_c) _cimg_abort_try_openmp { \
- cimg_abort_test; \
- if (mode&4) cimg_for##_X(*this,_x) lmp(x,y,z,c); \
- else { \
- T *_ptrd = data(_sx,_sy,_sz,_sc); \
- const ulongT off = (ulongT)_off; \
- cimg_for##_X(*this,_x) { *_ptrd = (T)lmp(x,y,z,c); _ptrd+=off; } \
- } \
- } _cimg_abort_catch_openmp _cimg_abort_catch_fill_openmp
- if (M2==_width) { _cimg_fill_openmp_scalar(YZC,y,z,c,X,x,0,y,z,c,1) }
- else if (M2==_height) { _cimg_fill_openmp_scalar(XZC,x,z,c,Y,y,x,0,z,c,_width) }
- else if (M2==_depth) { _cimg_fill_openmp_scalar(XYC,x,y,c,Z,z,x,y,0,c,_width*_height) }
- else { _cimg_fill_openmp_scalar(XYZ,x,y,z,C,c,x,y,z,0,_width*_height*_depth) }
- lmp.end_t();
- cimg_pragma_openmp(barrier) cimg_pragma_openmp(critical) { lmp.merge(mp); }
- if (&lmp!=&mp) delete &lmp;
- }
- #endif
- }
- }
- mp.end();
- if (result_end && mp.result_end) // Transfer result of the end() block if requested.
- result_end->assign(mp.result_end + (mp.result_end_dim?1:0),std::max(1U,mp.result_end_dim));
- is_done = true;
- } catch (CImgException& e) { CImg<charT>::string(e._message).move_to(is_error_expr); }
- }
- // Try to fill values according to a value sequence.
- if (!is_done && mode&1) is_done = !_fill_from_values(expression,repeat_values);
- if (!is_done) {
- cimg::exception_mode(excmode);
- if (is_error_expr) throw CImgArgumentException(_cimg_instance
- "%s",
- cimg_instance,is_error_expr._data);
- else throw CImgArgumentException(_cimg_instance
- "%s(): Invalid sequence of filling values '%s'.",
- cimg_instance,calling_function,expression);
- }
- cimg::exception_mode(excmode);
- cimg_abort_test;
- return *this;
- }
- //! Fill sequentially pixel values according to a given expression \newinstance.
- CImg<T> get_fill(const char *const expression, const bool repeat_values, const bool allow_formula=true,
- CImgList<T> *const list_images=0) const {
- return (+*this).fill(expression,repeat_values,allow_formula?1:0,list_images);
- }
- //! Fill sequentially pixel values according to a value sequence, given as a string.
- /**
- \param values C-string describing a sequence of values.
- \param repeat_values Tells if this sequence must be repeated when filling.
- **/
- CImg<T>& fill_from_values(const char *const values, const bool repeat_values) {
- if (_fill_from_values(values,repeat_values))
- throw CImgArgumentException(_cimg_instance
- "Invalid sequence of filling values '%s'.",
- cimg_instance,values);
- return *this;
- }
- //! Fill sequentially pixel values according to a value sequence, given as a string \newinstance.
- CImg<T> get_fill_from_values(const char *const values, const bool repeat_values) const {
- return (+*this).fill_from_values(values,repeat_values);
- }
- // Fill image according to a value sequence, given as a string.
- // Return 'true' if an error occured, 'false' otherwise.
- bool _fill_from_values(const char *const values, const bool repeat_values) {
- CImg<charT> item(256);
- const char *nvalues = values;
- const ulongT siz = size();
- T *ptrd = _data;
- ulongT nb = 0;
- char sep = 0;
- for (double val = 0; *nvalues && nb<siz; ++nb) {
- sep = 0;
- const int err = cimg_sscanf(nvalues,"%255[ \n\t0-9.eEinfa+-]%c",item._data,&sep);
- if (err>0 && cimg_sscanf(item,"%lf",&val)==1 && (sep==',' || sep==';' || err==1)) {
- nvalues+=std::strlen(item) + (err>1);
- *(ptrd++) = (T)val;
- } else break;
- }
- if (nb<siz && (sep || *nvalues)) return true;
- if (repeat_values && nb && nb<siz)
- for (T *ptrs = _data, *const ptre = _data + siz; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
- return false;
- }
- //! Fill sequentially pixel values according to the values found in another image.
- /**
- \param values Image containing the values used for the filling.
- \param repeat_values In case there are less values than necessary in \c values, tells if these values must be
- repeated for the filling.
- **/
- template<typename t>
- CImg<T>& fill(const CImg<t>& values, const bool repeat_values=true) {
- if (is_empty() || !values) return *this;
- T *ptrd = _data, *ptre = ptrd + size();
- for (t *ptrs = values._data, *ptrs_end = ptrs + values.size(); ptrs<ptrs_end && ptrd<ptre; ++ptrs)
- *(ptrd++) = (T)*ptrs;
- if (repeat_values && ptrd<ptre) for (T *ptrs = _data; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
- return *this;
- }
- //! Fill sequentially pixel values according to the values found in another image \newinstance.
- template<typename t>
- CImg<T> get_fill(const CImg<t>& values, const bool repeat_values=true) const {
- return repeat_values?CImg<T>(_width,_height,_depth,_spectrum).fill(values,repeat_values):
- (+*this).fill(values,repeat_values);
- }
- //! Fill pixel values along the X-axis at a specified pixel position.
- /**
- \param y Y-coordinate of the filled column.
- \param z Z-coordinate of the filled column.
- \param c C-coordinate of the filled column.
- \param a0 First fill value.
- **/
- CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const int a0, ...) {
- #define _cimg_fill1(x,y,z,c,off,siz,t) { \
- va_list ap; va_start(ap,a0); T *ptrd = data(x,y,z,c); *ptrd = (T)a0; \
- for (unsigned int k = 1; k<siz; ++k) { ptrd+=off; *ptrd = (T)va_arg(ap,t); } \
- va_end(ap); }
- if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,int);
- return *this;
- }
- //! Fill pixel values along the X-axis at a specified pixel position \overloading.
- CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const double a0, ...) {
- if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,double);
- return *this;
- }
- //! Fill pixel values along the Y-axis at a specified pixel position.
- /**
- \param x X-coordinate of the filled row.
- \param z Z-coordinate of the filled row.
- \param c C-coordinate of the filled row.
- \param a0 First fill value.
- **/
- CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const int a0, ...) {
- if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,int);
- return *this;
- }
- //! Fill pixel values along the Y-axis at a specified pixel position \overloading.
- CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const double a0, ...) {
- if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,double);
- return *this;
- }
- //! Fill pixel values along the Z-axis at a specified pixel position.
- /**
- \param x X-coordinate of the filled slice.
- \param y Y-coordinate of the filled slice.
- \param c C-coordinate of the filled slice.
- \param a0 First fill value.
- **/
- CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const int a0, ...) {
- const ulongT wh = (ulongT)_width*_height;
- if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,int);
- return *this;
- }
- //! Fill pixel values along the Z-axis at a specified pixel position \overloading.
- CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const double a0, ...) {
- const ulongT wh = (ulongT)_width*_height;
- if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,double);
- return *this;
- }
- //! Fill pixel values along the C-axis at a specified pixel position.
- /**
- \param x X-coordinate of the filled channel.
- \param y Y-coordinate of the filled channel.
- \param z Z-coordinate of the filled channel.
- \param a0 First filling value.
- **/
- CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const int a0, ...) {
- const ulongT whd = (ulongT)_width*_height*_depth;
- if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,int);
- return *this;
- }
- //! Fill pixel values along the C-axis at a specified pixel position \overloading.
- CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const double a0, ...) {
- const ulongT whd = (ulongT)_width*_height*_depth;
- if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,double);
- return *this;
- }
- //! Discard specified sequence of values in the image buffer, along a specific axis.
- /**
- \param values Sequence of values to discard.
- \param axis Axis along which the values are discarded. If set to \c 0 (default value)
- the method does it for all the buffer values and returns a one-column vector.
- \note Discarded values will change the image geometry, so the resulting image
- is returned as a one-column vector.
- **/
- template<typename t>
- CImg<T>& discard(const CImg<t>& values, const char axis=0) {
- if (is_empty() || !values) return *this;
- return get_discard(values,axis).move_to(*this);
- }
- template<typename t>
- CImg<T> get_discard(const CImg<t>& values, const char axis=0) const {
- if (!values) return +*this;
- CImg<T> res;
- if (is_empty()) return res;
- const ulongT vsiz = values.size();
- const char _axis = cimg::lowercase(axis);
- ulongT j = 0;
- unsigned int k = 0;
- int i0 = 0;
- res.assign(width(),height(),depth(),spectrum());
- switch (_axis) {
- case 'x' : {
- cimg_forX(*this,i) {
- if ((*this)(i)!=(T)values[j]) {
- if (j) --i;
- res.draw_image(k,get_columns(i0,i));
- k+=i - i0 + 1; i0 = i + 1; j = 0;
- } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
- }
- if (i0<width()) { res.draw_image(k,get_columns(i0,width() - 1)); k+=width() - i0; }
- res.resize(k,-100,-100,-100,0);
- } break;
- case 'y' : {
- cimg_forY(*this,i) {
- if ((*this)(0,i)!=(T)values[j]) {
- if (j) --i;
- res.draw_image(0,k,get_rows(i0,i));
- k+=i - i0 + 1; i0 = i + 1; j = 0;
- } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
- }
- if (i0<height()) { res.draw_image(0,k,get_rows(i0,height() - 1)); k+=height() - i0; }
- res.resize(-100,k,-100,-100,0);
- } break;
- case 'z' : {
- cimg_forZ(*this,i) {
- if ((*this)(0,0,i)!=(T)values[j]) {
- if (j) --i;
- res.draw_image(0,0,k,get_slices(i0,i));
- k+=i - i0 + 1; i0 = i + 1; j = 0;
- } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
- }
- if (i0<depth()) { res.draw_image(0,0,k,get_slices(i0,height() - 1)); k+=depth() - i0; }
- res.resize(-100,-100,k,-100,0);
- } break;
- case 'c' : {
- cimg_forC(*this,i) {
- if ((*this)(0,0,0,i)!=(T)values[j]) {
- if (j) --i;
- res.draw_image(0,0,0,k,get_channels(i0,i));
- k+=i - i0 + 1; i0 = i + 1; j = 0;
- } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
- }
- if (i0<spectrum()) { res.draw_image(0,0,k,get_channels(i0,height() - 1)); k+=spectrum() - i0; }
- res.resize(-100,-100,-100,k,0);
- } break;
- default : {
- const ulongT siz = size();
- res.unroll('y');
- if (vsiz==1) { // Optimized version for a single discard value
- const T val = (T)values[0];
- cimg_foroff(*this,i) {
- const T _val = (T)_data[i];
- if (_val!=val) res[k++] = _val;
- }
- } else { // Generic version
- cimg_foroff(*this,i) {
- if ((*this)[i]!=(T)values[j]) {
- if (j) --i;
- std::memcpy(res._data + k,_data + i0,(i - i0 + 1)*sizeof(T));
- k+=i - i0 + 1; i0 = (int)i + 1; j = 0;
- } else { ++j; if (j>=vsiz) { j = 0; i0 = (int)i + 1; }}
- }
- if ((ulongT)i0<siz) { std::memcpy(res._data + k,_data + i0,(siz - i0)*sizeof(T)); k+=siz - i0; }
- }
- res.resize(1,k,1,1,0);
- }
- }
- return res;
- }
- //! Discard neighboring duplicates in the image buffer, along the specified axis.
- CImg<T>& discard(const char axis=0) {
- return get_discard(axis).move_to(*this);
- }
- //! Discard neighboring duplicates in the image buffer, along the specified axis \newinstance.
- CImg<T> get_discard(const char axis=0) const {
- CImg<T> res;
- if (is_empty()) return res;
- const char _axis = cimg::lowercase(axis);
- T current = *_data?(T)0:(T)1;
- int j = 0;
- res.assign(width(),height(),depth(),spectrum());
- switch (_axis) {
- case 'x' : {
- cimg_forX(*this,i)
- if ((*this)(i)!=current) { res.draw_image(j++,get_column(i)); current = (*this)(i); }
- res.resize(j,-100,-100,-100,0);
- } break;
- case 'y' : {
- cimg_forY(*this,i)
- if ((*this)(0,i)!=current) { res.draw_image(0,j++,get_row(i)); current = (*this)(0,i); }
- res.resize(-100,j,-100,-100,0);
- } break;
- case 'z' : {
- cimg_forZ(*this,i)
- if ((*this)(0,0,i)!=current) { res.draw_image(0,0,j++,get_slice(i)); current = (*this)(0,0,i); }
- res.resize(-100,-100,j,-100,0);
- } break;
- case 'c' : {
- cimg_forC(*this,i)
- if ((*this)(0,0,0,i)!=current) { res.draw_image(0,0,0,j++,get_channel(i)); current = (*this)(0,0,0,i); }
- res.resize(-100,-100,-100,j,0);
- } break;
- default : {
- res.unroll('y');
- cimg_foroff(*this,i) {
- const T val = (*this)[i];
- if (val!=current) res[j++] = current = val;
- }
- res.resize(-100,j,-100,-100,0);
- }
- }
- return res;
- }
- //! Invert endianness of all pixel values.
- /**
- **/
- CImg<T>& invert_endianness() {
- cimg::invert_endianness(_data,size());
- return *this;
- }
- //! Invert endianness of all pixel values \newinstance.
- CImg<T> get_invert_endianness() const {
- return (+*this).invert_endianness();
- }
- //! Fill image with random values in specified range.
- /**
- \param val_min Minimal authorized random value.
- \param val_max Maximal authorized random value.
- \note Random variables are uniformly distributed in [val_min,val_max].
- **/
- CImg<T>& rand(const T& val_min, const T& val_max) {
- const float delta = (float)val_max - (float)val_min + (cimg::type<T>::is_float()?0:1);
- if (cimg::type<T>::is_float()) cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),524288)) {
- cimg_uint64 rng = (cimg::_rand(),cimg::rng());
- #if cimg_use_openmp!=0
- rng+=omp_get_thread_num();
- #endif
- cimg_pragma_openmp(for)
- cimg_rofoff(*this,off) _data[off] = (T)(val_min + delta*cimg::rand(1,&rng));
- cimg::srand(rng);
- } else cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),524288)) {
- cimg_uint64 rng = (cimg::_rand(),cimg::rng());
- #if cimg_use_openmp!=0
- rng+=omp_get_thread_num();
- #endif
- cimg_pragma_openmp(for)
- cimg_rofoff(*this,off) _data[off] = std::min(val_max,(T)(val_min + delta*cimg::rand(1,&rng)));
- cimg::srand(rng);
- }
- return *this;
- }
- //! Fill image with random values in specified range \newinstance.
- CImg<T> get_rand(const T& val_min, const T& val_max) const {
- return (+*this).rand(val_min,val_max);
- }
- //! Round pixel values.
- /**
- \param y Rounding precision.
- \param rounding_type Rounding type. Can be:
- - \c -1: Backward.
- - \c 0: Nearest.
- - \c 1: Forward.
- **/
- CImg<T>& round(const double y=1, const int rounding_type=0) {
- if (y>0) cimg_openmp_for(*this,cimg::round(*ptr,y,rounding_type),8192);
- return *this;
- }
- //! Round pixel values \newinstance.
- CImg<T> get_round(const double y=1, const unsigned int rounding_type=0) const {
- return (+*this).round(y,rounding_type);
- }
- //! Add random noise to pixel values.
- /**
- \param sigma Amplitude of the random additive noise. If \p sigma<0, it stands for a percentage of the
- global value range.
- \param noise_type Type of additive noise (can be \p 0=gaussian, \p 1=uniform, \p 2=Salt and Pepper,
- \p 3=Poisson or \p 4=Rician).
- \return A reference to the modified image instance.
- \note
- - For Poisson noise (\p noise_type=3), parameter \p sigma is ignored, as Poisson noise only depends on
- the image value itself.
- - Function \p CImg<T>::get_noise() is also defined. It returns a non-shared modified copy of the image instance.
- \par Example
- \code
- const CImg<float> img("reference.jpg"), res = img.get_noise(40);
- (img,res.normalize(0,255)).display();
- \endcode
- \image html ref_noise.jpg
- **/
- CImg<T>& noise(const double sigma, const unsigned int noise_type=0) {
- if (is_empty()) return *this;
- const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
- Tfloat nsigma = (Tfloat)sigma, m = 0, M = 0;
- if (nsigma==0 && noise_type!=3) return *this;
- if (nsigma<0 || noise_type==2) m = (Tfloat)min_max(M);
- if (nsigma<0) nsigma = (Tfloat)(-nsigma*(M-m)/100.);
- switch (noise_type) {
- case 0 : { // Gaussian noise
- cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
- cimg_uint64 rng = (cimg::_rand(),cimg::rng());
- #if cimg_use_openmp!=0
- rng+=omp_get_thread_num();
- #endif
- cimg_pragma_openmp(for)
- cimg_rofoff(*this,off) {
- Tfloat val = (Tfloat)(_data[off] + nsigma*cimg::grand(&rng));
- if (val>vmax) val = vmax;
- if (val<vmin) val = vmin;
- _data[off] = (T)val;
- }
- cimg::srand(rng);
- }
- } break;
- case 1 : { // Uniform noise
- cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
- cimg_uint64 rng = (cimg::_rand(),cimg::rng());
- #if cimg_use_openmp!=0
- rng+=omp_get_thread_num();
- #endif
- cimg_pragma_openmp(for)
- cimg_rofoff(*this,off) {
- Tfloat val = (Tfloat)(_data[off] + nsigma*cimg::rand(-1,1,&rng));
- if (val>vmax) val = vmax;
- if (val<vmin) val = vmin;
- _data[off] = (T)val;
- }
- cimg::srand(rng);
- }
- } break;
- case 2 : { // Salt & Pepper noise
- if (nsigma<0) nsigma = -nsigma;
- if (M==m) {
- if (cimg::type<T>::is_float()) { --m; ++M; }
- else { m = (Tfloat)cimg::type<T>::min(); M = (Tfloat)cimg::type<T>::max(); }
- }
- cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
- cimg_uint64 rng = (cimg::_rand(),cimg::rng());
- #if cimg_use_openmp!=0
- rng+=omp_get_thread_num();
- #endif
- cimg_pragma_openmp(for)
- cimg_rofoff(*this,off) if (cimg::rand(100,&rng)<nsigma) _data[off] = (T)(cimg::rand(1,&rng)<0.5?M:m);
- cimg::srand(rng);
- }
- } break;
- case 3 : { // Poisson Noise
- cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
- cimg_uint64 rng = (cimg::_rand(),cimg::rng());
- #if cimg_use_openmp!=0
- rng+=omp_get_thread_num();
- #endif
- cimg_pragma_openmp(for)
- cimg_rofoff(*this,off) _data[off] = (T)cimg::prand(_data[off],&rng);
- cimg::srand(rng);
- }
- } break;
- case 4 : { // Rice noise
- const Tfloat sqrt2 = (Tfloat)std::sqrt(2.);
- cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
- cimg_uint64 rng = (cimg::_rand(),cimg::rng());
- #if cimg_use_openmp!=0
- rng+=omp_get_thread_num();
- #endif
- cimg_pragma_openmp(for)
- cimg_rofoff(*this,off) {
- const Tfloat
- val0 = (Tfloat)_data[off]/sqrt2,
- re = (Tfloat)(val0 + nsigma*cimg::grand(&rng)),
- im = (Tfloat)(val0 + nsigma*cimg::grand(&rng));
- Tfloat val = cimg::hypot(re,im);
- if (val>vmax) val = vmax;
- if (val<vmin) val = vmin;
- _data[off] = (T)val;
- }
- cimg::srand(rng);
- }
- } break;
- default :
- throw CImgArgumentException(_cimg_instance
- "noise(): Invalid specified noise type %d "
- "(should be { 0=gaussian | 1=uniform | 2=salt&Pepper | 3=poisson }).",
- cimg_instance,
- noise_type);
- }
- return *this;
- }
- //! Add random noise to pixel values \newinstance.
- CImg<T> get_noise(const double sigma, const unsigned int noise_type=0) const {
- return (+*this).noise(sigma,noise_type);
- }
- //! Linearly normalize pixel values.
- /**
- \param min_value Minimum desired value of the resulting image.
- \param max_value Maximum desired value of the resulting image.
- \param constant_case_ratio In case of instance image having a constant value, tell what ratio
- of [min_value,max_value] is used to fill the normalized image
- (=0 for min_value, =1 for max_value, =0.5 for (min_value + max_value)/2).
- \par Example
- \code
- const CImg<float> img("reference.jpg"), res = img.get_normalize(160,220);
- (img,res).display();
- \endcode
- \image html ref_normalize2.jpg
- **/
- CImg<T>& normalize(const T& min_value, const T& max_value,
- const float constant_case_ratio=0) {
- if (is_empty()) return *this;
- const T a = min_value<max_value?min_value:max_value, b = min_value<max_value?max_value:min_value;
- T m, M = max_min(m);
- const Tfloat fm = (Tfloat)m, fM = (Tfloat)M;
- if (m==M)
- return fill(constant_case_ratio==0?a:
- constant_case_ratio==1?b:
- (T)((1 - constant_case_ratio)*a + constant_case_ratio*b));
- if (m!=a || M!=b) cimg_rof(*this,ptrd,T) *ptrd = (T)((*ptrd - fm)/(fM - fm)*(b - a) + a);
- return *this;
- }
- //! Linearly normalize pixel values \newinstance.
- CImg<Tfloat> get_normalize(const T& min_value, const T& max_value,
- const float ratio_if_constant_image=0) const {
- return CImg<Tfloat>(*this,false).normalize((Tfloat)min_value,(Tfloat)max_value,ratio_if_constant_image);
- }
- //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm.
- /**
- \par Example
- \code
- const CImg<float> img("reference.jpg"), res = img.get_normalize();
- (img,res.normalize(0,255)).display();
- \endcode
- \image html ref_normalize.jpg
- **/
- CImg<T>& normalize() {
- const ulongT whd = (ulongT)_width*_height*_depth;
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
- _height*_depth>=16))
- cimg_forYZ(*this,y,z) {
- T *ptrd = data(0,y,z,0);
- cimg_forX(*this,x) {
- const T *ptrs = ptrd;
- float n = 0;
- cimg_forC(*this,c) { n+=cimg::sqr((float)*ptrs); ptrs+=whd; }
- n = (float)std::sqrt(n);
- T *_ptrd = ptrd++;
- if (n>0) cimg_forC(*this,c) { *_ptrd = (T)(*_ptrd/n); _ptrd+=whd; }
- else cimg_forC(*this,c) { *_ptrd = (T)0; _ptrd+=whd; }
- }
- }
- return *this;
- }
- //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm \newinstance.
- CImg<Tfloat> get_normalize() const {
- return CImg<Tfloat>(*this,false).normalize();
- }
- //! Compute Lp-norm of each multi-valued pixel of the image instance.
- /**
- \param norm_type Type of computed vector norm (can be \p -1=Linf, or \p greater or equal than 0).
- \par Example
- \code
- const CImg<float> img("reference.jpg"), res = img.get_norm();
- (img,res.normalize(0,255)).display();
- \endcode
- \image html ref_norm.jpg
- **/
- CImg<T>& norm(const int norm_type=2) {
- if (_spectrum==1 && norm_type) return abs();
- return get_norm(norm_type).move_to(*this);
- }
- //! Compute L2-norm of each multi-valued pixel of the image instance \newinstance.
- CImg<Tfloat> get_norm(const int norm_type=2) const {
- if (is_empty()) return *this;
- if (_spectrum==1 && norm_type) return get_abs();
- const ulongT whd = (ulongT)_width*_height*_depth;
- CImg<Tfloat> res(_width,_height,_depth);
- switch (norm_type) {
- case -1 : { // Linf-norm
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
- _height*_depth>=16))
- cimg_forYZ(*this,y,z) {
- const ulongT off = (ulongT)offset(0,y,z);
- const T *ptrs = _data + off;
- Tfloat *ptrd = res._data + off;
- cimg_forX(*this,x) {
- Tfloat n = 0;
- const T *_ptrs = ptrs++;
- cimg_forC(*this,c) { const Tfloat val = (Tfloat)cimg::abs(*_ptrs); if (val>n) n = val; _ptrs+=whd; }
- *(ptrd++) = n;
- }
- }
- } break;
- case 0 : { // L0-norm
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
- _height*_depth>=16))
- cimg_forYZ(*this,y,z) {
- const ulongT off = (ulongT)offset(0,y,z);
- const T *ptrs = _data + off;
- Tfloat *ptrd = res._data + off;
- cimg_forX(*this,x) {
- unsigned int n = 0;
- const T *_ptrs = ptrs++;
- cimg_forC(*this,c) { n+=*_ptrs==0?0:1; _ptrs+=whd; }
- *(ptrd++) = (Tfloat)n;
- }
- }
- } break;
- case 1 : { // L1-norm
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
- _height*_depth>=16))
- cimg_forYZ(*this,y,z) {
- const ulongT off = (ulongT)offset(0,y,z);
- const T *ptrs = _data + off;
- Tfloat *ptrd = res._data + off;
- cimg_forX(*this,x) {
- Tfloat n = 0;
- const T *_ptrs = ptrs++;
- cimg_forC(*this,c) { n+=cimg::abs(*_ptrs); _ptrs+=whd; }
- *(ptrd++) = n;
- }
- }
- } break;
- case 2 : { // L2-norm
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
- _height*_depth>=16))
- cimg_forYZ(*this,y,z) {
- const ulongT off = (ulongT)offset(0,y,z);
- const T *ptrs = _data + off;
- Tfloat *ptrd = res._data + off;
- cimg_forX(*this,x) {
- Tfloat n = 0;
- const T *_ptrs = ptrs++;
- cimg_forC(*this,c) { n+=cimg::sqr((Tfloat)*_ptrs); _ptrs+=whd; }
- *(ptrd++) = (Tfloat)std::sqrt((Tfloat)n);
- }
- }
- } break;
- default : { // Linf-norm
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
- _height*_depth>=16))
- cimg_forYZ(*this,y,z) {
- const ulongT off = (ulongT)offset(0,y,z);
- const T *ptrs = _data + off;
- Tfloat *ptrd = res._data + off;
- cimg_forX(*this,x) {
- Tfloat n = 0;
- const T *_ptrs = ptrs++;
- cimg_forC(*this,c) { n+=std::pow(cimg::abs((Tfloat)*_ptrs),(Tfloat)norm_type); _ptrs+=whd; }
- *(ptrd++) = (Tfloat)std::pow((Tfloat)n,1/(Tfloat)norm_type);
- }
- }
- }
- }
- return res;
- }
- //! Cut pixel values in specified range.
- /**
- \param min_value Minimum desired value of the resulting image.
- \param max_value Maximum desired value of the resulting image.
- \par Example
- \code
- const CImg<float> img("reference.jpg"), res = img.get_cut(160,220);
- (img,res).display();
- \endcode
- \image html ref_cut.jpg
- **/
- CImg<T>& cut(const T& min_value, const T& max_value) {
- if (is_empty()) return *this;
- const T a = min_value<max_value?min_value:max_value, b = min_value<max_value?max_value:min_value;
- cimg_openmp_for(*this,cimg::cut(*ptr,a,b),32768);
- return *this;
- }
- //! Cut pixel values in specified range \newinstance.
- CImg<T> get_cut(const T& min_value, const T& max_value) const {
- return (+*this).cut(min_value,max_value);
- }
- //! Uniformly quantize pixel values.
- /**
- \param nb_levels Number of quantization levels.
- \param keep_range Tells if resulting values keep the same range as the original ones.
- \par Example
- \code
- const CImg<float> img("reference.jpg"), res = img.get_quantize(4);
- (img,res).display();
- \endcode
- \image html ref_quantize.jpg
- **/
- CImg<T>& quantize(const unsigned int nb_levels, const bool keep_range=true) {
- if (!nb_levels)
- throw CImgArgumentException(_cimg_instance
- "quantize(): Invalid quantization request with 0 values.",
- cimg_instance);
- if (is_empty()) return *this;
- Tfloat m, M = (Tfloat)max_min(m), range = M - m;
- if (range>0) {
- if (keep_range)
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768))
- cimg_rofoff(*this,off) {
- const unsigned int val = (unsigned int)((_data[off] - m)*nb_levels/range);
- _data[off] = (T)(m + std::min(val,nb_levels - 1)*range/nb_levels);
- } else
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768))
- cimg_rofoff(*this,off) {
- const unsigned int val = (unsigned int)((_data[off] - m)*nb_levels/range);
- _data[off] = (T)std::min(val,nb_levels - 1);
- }
- }
- return *this;
- }
- //! Uniformly quantize pixel values \newinstance.
- CImg<T> get_quantize(const unsigned int n, const bool keep_range=true) const {
- return (+*this).quantize(n,keep_range);
- }
- //! Return the Otsu threshold.
- /**
- \param nb_levels Number of histogram levels used for the estimation.
- **/
- T otsu(const unsigned int nb_levels=256) const {
- T m,M = max_min(m);
- CImg<ulongT> hist = get_histogram(nb_levels,m,M);
- ulongT sum = 0, sumB = 0, wB = 0;
- double best_variance = 0;
- unsigned int best_t = 0;
- cimg_forX(hist,t) sum+=t*hist[t];
- cimg_forX(hist,t) {
- wB+=hist[t];
- if (wB) {
- const ulongT wF = size() - wB;
- if (!wF) break;
- sumB+=t*hist[t];
- const double
- mB = (double)sumB/wB,
- mF = (double)(sum - sumB)/wF,
- variance = wB*wF*cimg::sqr(mB - mF);
- if (variance>best_variance) { best_variance = variance; best_t = t; }
- }
- }
- return m + best_t*(M - m)/(hist.width() - 1);
- }
- //! Threshold pixel values.
- /**
- \param value Threshold value
- \param soft_threshold Tells if soft thresholding must be applied (instead of hard one).
- \param strict_threshold Tells if threshold value is strict.
- \par Example
- \code
- const CImg<float> img("reference.jpg"), res = img.get_threshold(128);
- (img,res.normalize(0,255)).display();
- \endcode
- \image html ref_threshold.jpg
- **/
- CImg<T>& threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) {
- if (is_empty()) return *this;
- if (strict_threshold) {
- if (soft_threshold)
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768))
- cimg_rofoff(*this,off) {
- const T v = _data[off];
- _data[off] = v>value?(T)(v-value):v<-(float)value?(T)(v + value):(T)0;
- }
- else
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),65536))
- cimg_rofoff(*this,off) _data[off] = _data[off]>value?(T)1:(T)0;
- } else {
- if (soft_threshold)
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768))
- cimg_rofoff(*this,off) {
- const T v = _data[off];
- _data[off] = v>=value?(T)(v-value):v<=-(float)value?(T)(v + value):(T)0;
- }
- else
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),65536))
- cimg_rofoff(*this,off) _data[off] = _data[off]>=value?(T)1:(T)0;
- }
- return *this;
- }
- //! Threshold pixel values \newinstance.
- CImg<T> get_threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) const {
- return (+*this).threshold(value,soft_threshold,strict_threshold);
- }
- //! Compute the histogram of pixel values.
- /**
- \param nb_levels Number of desired histogram levels.
- \param min_value Minimum pixel value considered for the histogram computation.
- All pixel values lower than \p min_value will not be counted.
- \param max_value Maximum pixel value considered for the histogram computation.
- All pixel values higher than \p max_value will not be counted.
- \note
- - The histogram H of an image I is the 1D function where H(x) counts the number of occurrences of the value x
- in the image I.
- - The resulting histogram is always defined in 1D. Histograms of multi-valued images are not multi-dimensional.
- \par Example
- \code
- const CImg<float> img = CImg<float>("reference.jpg").histogram(256);
- img.display_graph(0,3);
- \endcode
- \image html ref_histogram.jpg
- **/
- CImg<T>& histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) {
- return get_histogram(nb_levels,min_value,max_value).move_to(*this);
- }
- //! Compute the histogram of pixel values \overloading.
- CImg<T>& histogram(const unsigned int nb_levels) {
- return get_histogram(nb_levels).move_to(*this);
- }
- //! Compute the histogram of pixel values \newinstance.
- CImg<ulongT> get_histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) const {
- if (!nb_levels || is_empty()) return CImg<ulongT>();
- const double
- vmin = (double)(min_value<max_value?min_value:max_value),
- vmax = (double)(min_value<max_value?max_value:min_value);
- CImg<ulongT> res(nb_levels,1,1,1,0);
- cimg_rof(*this,ptrs,T) {
- const T val = *ptrs;
- if (val>=vmin && val<=vmax) ++res[val==vmax?nb_levels - 1:(unsigned int)((val - vmin)*nb_levels/(vmax - vmin))];
- }
- return res;
- }
- //! Compute the histogram of pixel values \newinstance.
- CImg<ulongT> get_histogram(const unsigned int nb_levels) const {
- if (!nb_levels || is_empty()) return CImg<ulongT>();
- T vmax = 0, vmin = min_max(vmax);
- return get_histogram(nb_levels,vmin,vmax);
- }
- //! Equalize histogram of pixel values.
- /**
- \param nb_levels Number of histogram levels used for the equalization.
- \param min_value Minimum pixel value considered for the histogram computation.
- All pixel values lower than \p min_value will not be counted.
- \param max_value Maximum pixel value considered for the histogram computation.
- All pixel values higher than \p max_value will not be counted.
- \par Example
- \code
- const CImg<float> img("reference.jpg"), res = img.get_equalize(256);
- (img,res).display();
- \endcode
- \image html ref_equalize.jpg
- **/
- CImg<T>& equalize(const unsigned int nb_levels, const T& min_value, const T& max_value) {
- if (!nb_levels || is_empty()) return *this;
- const T
- vmin = min_value<max_value?min_value:max_value,
- vmax = min_value<max_value?max_value:min_value;
- CImg<ulongT> hist = get_histogram(nb_levels,vmin,vmax);
- ulongT cumul = 0;
- cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos] = cumul; }
- if (!cumul) cumul = 1;
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),1048576))
- cimg_rofoff(*this,off) {
- const int pos = (int)((_data[off] - vmin)*(nb_levels - 1.)/(vmax - vmin));
- if (pos>=0 && pos<(int)nb_levels) _data[off] = (T)(vmin + (vmax - vmin)*hist[pos]/cumul);
- }
- return *this;
- }
- //! Equalize histogram of pixel values \overloading.
- CImg<T>& equalize(const unsigned int nb_levels) {
- if (!nb_levels || is_empty()) return *this;
- T vmax = 0, vmin = min_max(vmax);
- return equalize(nb_levels,vmin,vmax);
- }
- //! Equalize histogram of pixel values \newinstance.
- CImg<T> get_equalize(const unsigned int nblevels, const T& val_min, const T& val_max) const {
- return (+*this).equalize(nblevels,val_min,val_max);
- }
- //! Equalize histogram of pixel values \newinstance.
- CImg<T> get_equalize(const unsigned int nblevels) const {
- return (+*this).equalize(nblevels);
- }
- //! Index multi-valued pixels regarding to a specified palette.
- /**
- \param colormap Multi-valued colormap used as the basis for multi-valued pixel indexing.
- \param dithering Level of dithering (0=disable, 1=standard level).
- \param map_indexes Tell if the values of the resulting image are the colormap indices or the colormap vectors.
- \note
- - \p img.index(colormap,dithering,1) is equivalent to <tt>img.index(colormap,dithering,0).map(colormap)</tt>.
- \par Example
- \code
- const CImg<float> img("reference.jpg"), colormap(3,1,1,3, 0,128,255, 0,128,255, 0,128,255);
- const CImg<float> res = img.get_index(colormap,1,true);
- (img,res).display();
- \endcode
- \image html ref_index.jpg
- **/
- template<typename t>
- CImg<T>& index(const CImg<t>& colormap, const float dithering=1, const bool map_indexes=false) {
- return get_index(colormap,dithering,map_indexes).move_to(*this);
- }
- //! Index multi-valued pixels regarding to a specified colormap \newinstance.
- template<typename t>
- CImg<typename CImg<t>::Tuint>
- get_index(const CImg<t>& colormap, const float dithering=1, const bool map_indexes=true) const {
- if (colormap._spectrum!=_spectrum)
- throw CImgArgumentException(_cimg_instance
- "index(): Instance and specified colormap (%u,%u,%u,%u,%p) "
- "have incompatible dimensions.",
- cimg_instance,
- colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
- typedef typename CImg<t>::Tuint tuint;
- if (is_empty()) return CImg<tuint>();
- const ulongT
- whd = (ulongT)_width*_height*_depth,
- pwhd = (ulongT)colormap._width*colormap._height*colormap._depth;
- CImg<tuint> res(_width,_height,_depth,map_indexes?_spectrum:1);
- if (dithering>0) { // Dithered versions
- tuint *ptrd = res._data;
- const float ndithering = cimg::cut(dithering,0,1)/16;
- Tfloat valm = 0, valM = (Tfloat)max_min(valm);
- if (valm==valM && valm>=0 && valM<=255) { valm = 0; valM = 255; }
- CImg<Tfloat> cache = get_crop(-1,0,0,0,_width,1,0,_spectrum - 1);
- Tfloat *cache_current = cache.data(1,0,0,0), *cache_next = cache.data(1,1,0,0);
- const ulongT cwhd = (ulongT)cache._width*cache._height*cache._depth;
- switch (_spectrum) {
- case 1 : { // Optimized for scalars
- cimg_forYZ(*this,y,z) {
- if (y<height() - 2) {
- Tfloat *ptrc0 = cache_next; const T *ptrs0 = data(0,y + 1,z,0);
- cimg_forX(*this,x) *(ptrc0++) = (Tfloat)*(ptrs0++);
- }
- Tfloat *ptrs0 = cache_current, *ptrsn0 = cache_next;
- cimg_forX(*this,x) {
- const Tfloat _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0;
- Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
- for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
- const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
- if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
- }
- const Tfloat err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering;
- *ptrs0+=7*err0; *(ptrsn0 - 1)+=3*err0; *(ptrsn0++)+=5*err0; *ptrsn0+=err0;
- if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
- }
- cimg::swap(cache_current,cache_next);
- }
- } break;
- case 2 : { // Optimized for 2D vectors
- tuint *ptrd1 = ptrd + whd;
- cimg_forYZ(*this,y,z) {
- if (y<height() - 2) {
- Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd;
- const T *ptrs0 = data(0,y + 1,z,0), *ptrs1 = ptrs0 + whd;
- cimg_forX(*this,x) { *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); }
- }
- Tfloat
- *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd,
- *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd;
- cimg_forX(*this,x) {
- const Tfloat
- _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
- _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1;
- Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
- for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
- const Tfloat
- pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
- dist = pval0*pval0 + pval1*pval1;
- if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
- }
- const t *const ptrmin1 = ptrmin0 + pwhd;
- const Tfloat
- err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering,
- err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)*ndithering;
- *ptrs0+=7*err0; *ptrs1+=7*err1;
- *(ptrsn0 - 1)+=3*err0; *(ptrsn1 - 1)+=3*err1;
- *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1;
- *ptrsn0+=err0; *ptrsn1+=err1;
- if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; }
- else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
- }
- cimg::swap(cache_current,cache_next);
- }
- } break;
- case 3 : { // Optimized for 3D vectors (colors)
- tuint *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
- cimg_forYZ(*this,y,z) {
- if (y<height() - 2) {
- Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd, *ptrc2 = ptrc1 + cwhd;
- const T *ptrs0 = data(0,y + 1,z,0), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd;
- cimg_forX(*this,x) {
- *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); *(ptrc2++) = (Tfloat)*(ptrs2++);
- }
- }
- Tfloat
- *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd, *ptrs2 = ptrs1 + cwhd,
- *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd, *ptrsn2 = ptrsn1 + cwhd;
- cimg_forX(*this,x) {
- const Tfloat
- _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
- _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1,
- _val2 = (Tfloat)*ptrs2, val2 = _val2<valm?valm:_val2>valM?valM:_val2;
- Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
- for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd,
- *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
- const Tfloat
- pval0 = (Tfloat)*(ptrp0++) - val0,
- pval1 = (Tfloat)*(ptrp1++) - val1,
- pval2 = (Tfloat)*(ptrp2++) - val2,
- dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
- if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
- }
- const t *const ptrmin1 = ptrmin0 + pwhd, *const ptrmin2 = ptrmin1 + pwhd;
- const Tfloat
- err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering,
- err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)*ndithering,
- err2 = ((*(ptrs2++)=val2) - (Tfloat)*ptrmin2)*ndithering;
- *ptrs0+=7*err0; *ptrs1+=7*err1; *ptrs2+=7*err2;
- *(ptrsn0 - 1)+=3*err0; *(ptrsn1 - 1)+=3*err1; *(ptrsn2 - 1)+=3*err2;
- *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1; *(ptrsn2++)+=5*err2;
- *ptrsn0+=err0; *ptrsn1+=err1; *ptrsn2+=err2;
- if (map_indexes) {
- *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; *(ptrd2++) = (tuint)*ptrmin2;
- } else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
- }
- cimg::swap(cache_current,cache_next);
- }
- } break;
- default : // Generic version
- cimg_forYZ(*this,y,z) {
- if (y<height() - 2) {
- Tfloat *ptrc = cache_next;
- cimg_forC(*this,c) {
- Tfloat *_ptrc = ptrc; const T *_ptrs = data(0,y + 1,z,c);
- cimg_forX(*this,x) *(_ptrc++) = (Tfloat)*(_ptrs++);
- ptrc+=cwhd;
- }
- }
- Tfloat *ptrs = cache_current, *ptrsn = cache_next;
- cimg_forX(*this,x) {
- Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = colormap._data;
- for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
- Tfloat dist = 0; Tfloat *_ptrs = ptrs; const t *_ptrp = ptrp;
- cimg_forC(*this,c) {
- const Tfloat _val = *_ptrs, val = _val<valm?valm:_val>valM?valM:_val;
- dist+=cimg::sqr((*_ptrs=val) - (Tfloat)*_ptrp); _ptrs+=cwhd; _ptrp+=pwhd;
- }
- if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
- }
- const t *_ptrmin = ptrmin; Tfloat *_ptrs = ptrs++, *_ptrsn = (ptrsn++) - 1;
- cimg_forC(*this,c) {
- const Tfloat err = (*(_ptrs++) - (Tfloat)*_ptrmin)*ndithering;
- *_ptrs+=7*err; *(_ptrsn++)+=3*err; *(_ptrsn++)+=5*err; *_ptrsn+=err;
- _ptrmin+=pwhd; _ptrs+=cwhd - 1; _ptrsn+=cwhd - 2;
- }
- if (map_indexes) {
- tuint *_ptrd = ptrd++;
- cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
- }
- else *(ptrd++) = (tuint)(ptrmin - colormap._data);
- }
- cimg::swap(cache_current,cache_next);
- }
- }
- } else { // Non-dithered versions
- switch (_spectrum) {
- case 1 : { // Optimized for scalars
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 &&
- _height*_depth>=16 && pwhd>=16))
- cimg_forYZ(*this,y,z) {
- tuint *ptrd = res.data(0,y,z);
- for (const T *ptrs0 = data(0,y,z), *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
- const Tfloat val0 = (Tfloat)*(ptrs0++);
- Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
- for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
- const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
- if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
- }
- if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
- }
- }
- } break;
- case 2 : { // Optimized for 2D vectors
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 &&
- _height*_depth>=16 && pwhd>=16))
- cimg_forYZ(*this,y,z) {
- tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd;
- for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
- const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++);
- Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
- for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
- const Tfloat
- pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
- dist = pval0*pval0 + pval1*pval1;
- if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
- }
- if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*(ptrmin0 + pwhd); }
- else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
- }
- }
- } break;
- case 3 : { // Optimized for 3D vectors (colors)
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 &&
- _height*_depth>=16 && pwhd>=16))
- cimg_forYZ(*this,y,z) {
- tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
- for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd,
- *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
- const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++), val2 = (Tfloat)*(ptrs2++);
- Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
- for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd,
- *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
- const Tfloat
- pval0 = (Tfloat)*(ptrp0++) - val0,
- pval1 = (Tfloat)*(ptrp1++) - val1,
- pval2 = (Tfloat)*(ptrp2++) - val2,
- dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
- if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
- }
- if (map_indexes) {
- *(ptrd++) = (tuint)*ptrmin0;
- *(ptrd1++) = (tuint)*(ptrmin0 + pwhd);
- *(ptrd2++) = (tuint)*(ptrmin0 + 2*pwhd);
- } else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
- }
- }
- } break;
- default : // Generic version
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 &&
- _height*_depth>=16 && pwhd>=16))
- cimg_forYZ(*this,y,z) {
- tuint *ptrd = res.data(0,y,z);
- for (const T *ptrs = data(0,y,z), *ptrs_end = ptrs + _width; ptrs<ptrs_end; ++ptrs) {
- Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = colormap._data;
- for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
- Tfloat dist = 0; const T *_ptrs = ptrs; const t *_ptrp = ptrp;
- cimg_forC(*this,c) { dist+=cimg::sqr((Tfloat)*_ptrs - (Tfloat)*_ptrp); _ptrs+=whd; _ptrp+=pwhd; }
- if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
- }
- if (map_indexes) {
- tuint *_ptrd = ptrd++;
- cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
- }
- else *(ptrd++) = (tuint)(ptrmin - colormap._data);
- }
- }
- }
- }
- return res;
- }
- //! Map predefined palette on the scalar (indexed) image instance.
- /**
- \param palette Multi-valued palette used for mapping the indexes.
- \param boundary_conditions Boundary conditions.
- Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
- \par Example
- \code
- const CImg<float> img("reference.jpg"),
- palette1(3,1,1,3, 0,128,255, 0,128,255, 0,128,255),
- palette2(3,1,1,3, 255,0,0, 0,255,0, 0,0,255),
- res = img.get_index(palette1,0).map(palette2);
- (img,res).display();
- \endcode
- \image html ref_map.jpg
- **/
- template<typename t>
- CImg<T>& map(const CImg<t>& palette, const unsigned int boundary_conditions=0) {
- return get_map(palette,boundary_conditions).move_to(*this);
- }
- //! Map predefined palette on the scalar (indexed) image instance \newinstance.
- template<typename t>
- CImg<t> get_map(const CImg<t>& palette, const unsigned int boundary_conditions=0) const {
- const ulongT
- whd = (ulongT)_width*_height*_depth, siz = size(),
- cwhd = (ulongT)palette._width*palette._height*palette._depth,
- cwhd2 = 2*cwhd;
- CImg<t> res(_width,_height,_depth,_spectrum*palette._spectrum);
- switch (palette._spectrum) {
- case 1 : { // Optimized for scalars
- switch (boundary_conditions) {
- case 3 : // Mirror
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
- for (longT off = 0; off<(longT)siz; ++off) {
- const ulongT ind = ((ulongT)_data[off])%cwhd2;
- res[off] = palette[ind<cwhd?ind:cwhd2 - ind - 1];
- }
- break;
- case 2 : // Periodic
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
- for (longT off = 0; off<(longT)siz; ++off) {
- const ulongT ind = (ulongT)_data[off];
- res[off] = palette[ind%cwhd];
- }
- break;
- case 1 : // Neumann
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
- for (longT off = 0; off<(longT)siz; ++off) {
- const longT ind = (longT)_data[off];
- res[off] = palette[cimg::cut(ind,(longT)0,(longT)cwhd - 1)];
- } break;
- default : // Dirichlet
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
- for (longT off = 0; off<(longT)siz; ++off) {
- const ulongT ind = (ulongT)_data[off];
- res[off] = ind<cwhd?palette[ind]:(t)0;
- }
- }
- } break;
- case 2 : { // Optimized for 2D vectors
- const t *const ptrp0 = palette._data, *const ptrp1 = ptrp0 + cwhd;
- switch (boundary_conditions) {
- case 3 : // Mirror
- cimg_forC(*this,c) {
- t *const ptrd0 = res.data(0,0,0,2*c), *const ptrd1 = ptrd0 + whd;
- const T *const ptrs = data(0,0,0,c);
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
- for (longT off = 0; off<(longT)whd; ++off) {
- const ulongT
- _ind = ((ulongT)ptrs[off])%cwhd2,
- ind = _ind<cwhd?_ind:cwhd2 - _ind - 1;
- ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind];
- }
- } break;
- case 2 : // Periodic
- cimg_forC(*this,c) {
- t *const ptrd0 = res.data(0,0,0,2*c), *const ptrd1 = ptrd0 + whd;
- const T *const ptrs = data(0,0,0,c);
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
- for (longT off = 0; off<(longT)whd; ++off) {
- const ulongT ind = ((ulongT)ptrs[off])%cwhd;
- ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind];
- }
- } break;
- case 1 : // Neumann
- cimg_forC(*this,c) {
- t *const ptrd0 = res.data(0,0,0,2*c), *const ptrd1 = ptrd0 + whd;
- const T *const ptrs = data(0,0,0,c);
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
- for (longT off = 0; off<(longT)whd; ++off) {
- const longT ind = cimg::cut((longT)ptrs[off],(longT)0,(longT)cwhd - 1);
- ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind];
- }
- } break;
- default : // Dirichlet
- cimg_forC(*this,c) {
- t *const ptrd0 = res.data(0,0,0,2*c), *const ptrd1 = ptrd0 + whd;
- const T *const ptrs = data(0,0,0,c);
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
- for (longT off = 0; off<(longT)whd; ++off) {
- const ulongT ind = (ulongT)ptrs[off];
- if (ind<cwhd) { ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; }
- else ptrd0[off] = ptrd1[off] = (t)0;
- }
- }
- }
- } break;
- case 3 : { // Optimized for 3D vectors (colors)
- const t *const ptrp0 = palette._data, *ptrp1 = ptrp0 + cwhd, *ptrp2 = ptrp0 + 2*cwhd;
- switch (boundary_conditions) {
- case 3 : // Mirror
- cimg_forC(*this,c) {
- t *const ptrd0 = res.data(0,0,0,3*c), *const ptrd1 = ptrd0 + whd, *const ptrd2 = ptrd1 + whd;
- const T *const ptrs = data(0,0,0,c);
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
- for (longT off = 0; off<(longT)whd; ++off) {
- const ulongT
- _ind = ((ulongT)ptrs[off])%cwhd2,
- ind = _ind<cwhd?_ind:cwhd2 - _ind - 1;
- ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; ptrd2[off] = ptrp2[ind];
- }
- } break;
- case 2 : // Periodic
- cimg_forC(*this,c) {
- t *const ptrd0 = res.data(0,0,0,3*c), *const ptrd1 = ptrd0 + whd, *const ptrd2 = ptrd1 + whd;
- const T *const ptrs = data(0,0,0,c);
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
- for (longT off = 0; off<(longT)whd; ++off) {
- const ulongT ind = ((ulongT)ptrs[off])%cwhd;
- ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; ptrd2[off] = ptrp2[ind];
- }
- } break;
- case 1 : // Neumann
- cimg_forC(*this,c) {
- t *const ptrd0 = res.data(0,0,0,3*c), *const ptrd1 = ptrd0 + whd, *const ptrd2 = ptrd1 + whd;
- const T *const ptrs = data(0,0,0,c);
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
- for (longT off = 0; off<(longT)whd; ++off) {
- const longT ind = cimg::cut((longT)ptrs[off],(longT)0,(longT)cwhd - 1);
- ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; ptrd2[off] = ptrp2[ind];
- }
- } break;
- default : // Dirichlet
- cimg_forC(*this,c) {
- t *const ptrd0 = res.data(0,0,0,3*c), *const ptrd1 = ptrd0 + whd, *const ptrd2 = ptrd1 + whd;
- const T *const ptrs = data(0,0,0,c);
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
- for (longT off = 0; off<(longT)whd; ++off) {
- const ulongT ind = (ulongT)ptrs[off];
- if (ind<cwhd) { ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; ptrd2[off] = ptrp2[ind]; }
- else ptrd0[off] = ptrd1[off] = ptrd2[off] = (t)0;
- }
- }
- }
- } break;
- default : { // Generic version
- switch (boundary_conditions) {
- case 3 : // Mirror
- cimg_forC(*this,c) {
- t *const ptrd = res.data(0,0,0,palette._spectrum*c);
- const T *const ptrs = data(0,0,0,c);
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
- for (longT off = 0; off<(longT)whd; ++off) {
- const ulongT
- _ind = ((ulongT)ptrs[off])%cwhd,
- ind = _ind<cwhd?_ind:cwhd2 - _ind - 1;
- t *const _ptrd = ptrd + off;
- const t *const ptrp = &palette[ind];
- cimg_forC(palette,k) _ptrd[k*whd] = ptrp[k*cwhd];
- }
- } break;
- case 2 : // Periodic
- cimg_forC(*this,c) {
- t *const ptrd = res.data(0,0,0,palette._spectrum*c);
- const T *const ptrs = data(0,0,0,c);
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
- for (longT off = 0; off<(longT)whd; ++off) {
- const ulongT ind = ((ulongT)ptrs[off])%cwhd;
- t *const _ptrd = ptrd + off;
- const t *const ptrp = &palette[ind];
- cimg_forC(palette,k) _ptrd[k*whd] = ptrp[k*cwhd];
- }
- } break;
- case 1 : // Neumann
- cimg_forC(*this,c) {
- t *const ptrd = res.data(0,0,0,palette._spectrum*c);
- const T *const ptrs = data(0,0,0,c);
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
- for (longT off = 0; off<(longT)whd; ++off) {
- const longT ind = cimg::cut((longT)ptrs[off],(longT)0,(longT)cwhd - 1);
- t *const _ptrd = ptrd + off;
- const t *const ptrp = &palette[ind];
- cimg_forC(palette,k) _ptrd[k*whd] = ptrp[k*cwhd];
- }
- } break;
- default : // Dirichlet
- cimg_forC(*this,c) {
- t *const ptrd = res.data(0,0,0,palette._spectrum*c);
- const T *const ptrs = data(0,0,0,c);
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
- for (longT off = 0; off<(longT)whd; ++off) {
- const ulongT ind = (ulongT)ptrs[off];
- t *const _ptrd = ptrd + off;
- if (ind<cwhd) {
- const t *const ptrp = &palette[ind];
- cimg_forC(palette,k) _ptrd[k*whd] = ptrp[k*cwhd];
- } else cimg_forC(palette,k) _ptrd[k*whd] = (t)0;
- }
- }
- }
- }
- }
- return res;
- }
- //! Label connected components.
- /**
- \param is_high_connectivity Boolean that choose between 4(false)- or 8(true)-connectivity
- in 2D case, and between 6(false)- or 26(true)-connectivity in 3D case.
- \param tolerance Tolerance used to determine if two neighboring pixels belong to the same region.
- \param is_L2_norm If true, tolerance is compared against L2 difference, otherwise L1 is used.
- \note The algorithm of connected components computation has been primarily done
- by A. Meijster, according to the publication:
- 'W.H. Hesselink, A. Meijster, C. Bron, "Concurrent Determination of Connected Components.",
- In: Science of Computer Programming 41 (2001), pp. 173--194'.
- The submitted code has then been modified to fit CImg coding style and constraints.
- **/
- CImg<T>& label(const bool is_high_connectivity=false, const Tfloat tolerance=0,
- const bool is_L2_norm=true) {
- if (is_empty()) return *this;
- return get_label(is_high_connectivity,tolerance,is_L2_norm).move_to(*this);
- }
- //! Label connected components \newinstance.
- CImg<ulongT> get_label(const bool is_high_connectivity=false, const Tfloat tolerance=0,
- const bool is_L2_norm=true) const {
- if (is_empty()) return CImg<ulongT>();
- // Create neighborhood tables.
- int dx[13], dy[13], dz[13], nb = 0;
- dx[nb] = 1; dy[nb] = 0; dz[nb++] = 0;
- dx[nb] = 0; dy[nb] = 1; dz[nb++] = 0;
- if (is_high_connectivity) {
- dx[nb] = 1; dy[nb] = 1; dz[nb++] = 0;
- dx[nb] = 1; dy[nb] = -1; dz[nb++] = 0;
- }
- if (_depth>1) { // 3D version
- dx[nb] = 0; dy[nb] = 0; dz[nb++]=1;
- if (is_high_connectivity) {
- dx[nb] = 1; dy[nb] = 1; dz[nb++] = -1;
- dx[nb] = 1; dy[nb] = 0; dz[nb++] = -1;
- dx[nb] = 1; dy[nb] = -1; dz[nb++] = -1;
- dx[nb] = 0; dy[nb] = 1; dz[nb++] = -1;
- dx[nb] = 0; dy[nb] = 1; dz[nb++] = 1;
- dx[nb] = 1; dy[nb] = -1; dz[nb++] = 1;
- dx[nb] = 1; dy[nb] = 0; dz[nb++] = 1;
- dx[nb] = 1; dy[nb] = 1; dz[nb++] = 1;
- }
- }
- return _label(nb,dx,dy,dz,tolerance,is_L2_norm);
- }
- //! Label connected components \overloading.
- /**
- \param connectivity_mask Mask of the neighboring pixels.
- \param tolerance Tolerance used to determine if two neighboring pixels belong to the same region.
- \param is_L2_norm If true, tolerance is compared against L2 difference, otherwise L1 is used.
- **/
- template<typename t>
- CImg<T>& label(const CImg<t>& connectivity_mask, const Tfloat tolerance=0,
- const bool is_L2_norm=true) {
- if (is_empty()) return *this;
- return get_label(connectivity_mask,tolerance,is_L2_norm).move_to(*this);
- }
- //! Label connected components \newinstance.
- template<typename t>
- CImg<ulongT> get_label(const CImg<t>& connectivity_mask, const Tfloat tolerance=0,
- const bool is_L2_norm=true) const {
- if (is_empty()) return CImg<ulongT>();
- int nb = 0;
- cimg_for(connectivity_mask,ptr,t) if (*ptr) ++nb;
- CImg<intT> dx(nb,1,1,1,0), dy(nb,1,1,1,0), dz(nb,1,1,1,0);
- nb = 0;
- cimg_forXYZ(connectivity_mask,x,y,z) if ((x || y || z) &&
- connectivity_mask(x,y,z)) {
- dx[nb] = x; dy[nb] = y; dz[nb++] = z;
- }
- return _label(nb,dx,dy,dz,tolerance,is_L2_norm);
- }
- CImg<ulongT> _label(const unsigned int nb, const int *const dx,
- const int *const dy, const int *const dz,
- const Tfloat tolerance, const bool is_L2_norm) const {
- CImg<ulongT> res(_width,_height,_depth);
- const Tfloat _tolerance = _spectrum>1 && is_L2_norm?cimg::sqr(tolerance):tolerance;
- // Init label numbers.
- ulongT *ptr = res.data();
- cimg_foroff(res,p) *(ptr++) = p;
- // For each neighbour-direction, label.
- for (unsigned int n = 0; n<nb; ++n) {
- const int _dx = dx[n], _dy = dy[n], _dz = dz[n];
- if (_dx || _dy || _dz) {
- const int
- x0 = _dx<0?-_dx:0,
- x1 = _dx<0?width():width() - _dx,
- y0 = _dy<0?-_dy:0,
- y1 = _dy<0?height():height() - _dy,
- z0 = _dz<0?-_dz:0,
- z1 = _dz<0?depth():depth() - _dz;
- const longT
- wh = (longT)width()*height(),
- whd = (longT)width()*height()*depth(),
- offset = _dz*wh + _dy*width() + _dx;
- for (longT z = z0, nz = z0 + _dz, pz = z0*wh; z<z1; ++z, ++nz, pz+=wh) {
- for (longT y = y0, ny = y0 + _dy, py = y0*width() + pz; y<y1; ++y, ++ny, py+=width()) {
- for (longT x = x0, nx = x0 + _dx, p = x0 + py; x<x1; ++x, ++nx, ++p) {
- Tfloat diff;
- switch (_spectrum) {
- case 1 :
- diff = cimg::abs((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd));
- break;
- case 2 :
- if (is_L2_norm)
- diff = cimg::sqr((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
- cimg::sqr((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd));
- else
- diff = cimg::abs((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
- cimg::abs((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd));
- break;
- case 3 :
- if (is_L2_norm)
- diff = cimg::sqr((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
- cimg::sqr((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd)) +
- cimg::sqr((Tfloat)(*this)(x,y,z,2,wh,whd) - (Tfloat)(*this)(nx,ny,nz,2,wh,whd));
- else
- diff = cimg::abs((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
- cimg::abs((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd)) +
- cimg::abs((Tfloat)(*this)(x,y,z,2,wh,whd) - (Tfloat)(*this)(nx,ny,nz,2,wh,whd));
- break;
- case 4 :
- if (is_L2_norm)
- diff = cimg::sqr((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
- cimg::sqr((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd)) +
- cimg::sqr((Tfloat)(*this)(x,y,z,2,wh,whd) - (Tfloat)(*this)(nx,ny,nz,2,wh,whd)) +
- cimg::sqr((Tfloat)(*this)(x,y,z,3,wh,whd) - (Tfloat)(*this)(nx,ny,nz,3,wh,whd));
- else
- diff = cimg::abs((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
- cimg::abs((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd)) +
- cimg::abs((Tfloat)(*this)(x,y,z,2,wh,whd) - (Tfloat)(*this)(nx,ny,nz,2,wh,whd)) +
- cimg::abs((Tfloat)(*this)(x,y,z,3,wh,whd) - (Tfloat)(*this)(nx,ny,nz,3,wh,whd));
- break;
- default :
- diff = 0;
- if (is_L2_norm)
- cimg_forC(*this,c)
- diff+=cimg::sqr((Tfloat)(*this)(x,y,z,c,wh,whd) - (Tfloat)(*this)(nx,ny,nz,c,wh,whd));
- else
- cimg_forC(*this,c)
- diff+=cimg::abs((Tfloat)(*this)(x,y,z,c,wh,whd) - (Tfloat)(*this)(nx,ny,nz,c,wh,whd));
- }
- if (diff<=_tolerance) {
- const longT q = p + offset;
- ulongT xk, yk;
- for (xk = (ulongT)(p<q?q:p), yk = (ulongT)(p<q?p:q); xk!=yk && res[xk]!=xk; ) {
- xk = res[xk]; if (xk<yk) cimg::swap(xk,yk);
- }
- if (xk!=yk) res[xk] = (ulongT)yk;
- for (ulongT _p = (ulongT)p; _p!=yk; ) {
- const ulongT h = res[_p];
- res[_p] = (ulongT)yk;
- _p = h;
- }
- for (ulongT _q = (ulongT)q; _q!=yk; ) {
- const ulongT h = res[_q];
- res[_q] = (ulongT)yk;
- _q = h;
- }
- }
- }
- }
- }
- }
- }
- // Resolve equivalences.
- ulongT counter = 0;
- ptr = res.data();
- cimg_foroff(res,p) { *ptr = *ptr==p?counter++:res[*ptr]; ++ptr; }
- return res;
- }
- // [internal] Replace possibly malicious characters for commands to be called by system() by their escaped version.
- CImg<T>& _system_strescape() {
- #define cimg_system_strescape(c,s) case c : if (p!=ptrs) CImg<T>(ptrs,(unsigned int)(p-ptrs),1,1,1,false).\
- move_to(list); \
- CImg<T>(s,(unsigned int)std::strlen(s),1,1,1,false).move_to(list); ptrs = p + 1; break
- CImgList<T> list;
- const T *ptrs = _data;
- cimg_for(*this,p,T) switch ((int)*p) {
- cimg_system_strescape('\\',"\\\\");
- cimg_system_strescape('\"',"\\\"");
- cimg_system_strescape('!',"\"\\!\"");
- cimg_system_strescape('`',"\\`");
- cimg_system_strescape('$',"\\$");
- }
- if (ptrs<end()) CImg<T>(ptrs,(unsigned int)(end()-ptrs),1,1,1,false).move_to(list);
- return (list>'x').move_to(*this);
- }
- //@}
- //---------------------------------
- //
- //! \name Color Base Management
- //@{
- //---------------------------------
- //! Return palette \e "default", containing 256 colors entries in RGB.
- /**
- \return The following \c 256x1x1x3 palette is returned:
- \image html ref_colormap_default.jpg
- **/
- static const CImg<Tuchar>& default_LUT256() {
- static CImg<Tuchar> palette;
- cimg::mutex(8);
- if (!palette) {
- palette.assign(1,256,1,3);
- for (unsigned int index = 0, r = 16; r<256; r+=32)
- for (unsigned int g = 16; g<256; g+=32)
- for (unsigned int b = 32; b<256; b+=64) {
- palette(0,index,0) = (Tuchar)r;
- palette(0,index,1) = (Tuchar)g;
- palette(0,index++,2) = (Tuchar)b;
- }
- }
- cimg::mutex(8,0);
- return palette;
- }
- //! Return palette \e "HSV", containing 256 colors entries in RGB.
- /**
- \return The following \c 256x1x1x3 palette is returned:
- \image html ref_colormap_hsv.jpg
- **/
- static const CImg<Tuchar>& HSV_LUT256() {
- static CImg<Tuchar> palette;
- cimg::mutex(8);
- if (!palette) {
- CImg<Tint> tmp(1,256,1,3,1);
- tmp.get_shared_channel(0).sequence(0,359);
- palette = tmp.HSVtoRGB();
- }
- cimg::mutex(8,0);
- return palette;
- }
- //! Return palette \e "lines", containing 256 colors entries in RGB.
- /**
- \return The following \c 256x1x1x3 palette is returned:
- \image html ref_colormap_lines.jpg
- **/
- static const CImg<Tuchar>& lines_LUT256() {
- static const unsigned char pal[] = {
- 0,255,255,0,0,28,125,125,235,210,186,182,36,0,125,255,
- 53,32,255,210,89,186,65,45,125,210,210,97,130,194,0,125,
- 206,53,190,89,255,146,20,190,154,73,255,36,130,215,0,138,
- 101,210,61,194,206,0,77,45,255,154,174,0,190,239,89,125,
- 16,36,158,223,117,0,97,69,223,255,40,239,0,0,255,0,
- 97,170,93,255,138,40,117,210,0,170,53,158,186,255,0,121,
- 227,121,186,40,20,190,89,255,77,57,130,142,255,73,186,85,
- 210,8,32,166,243,130,210,40,255,45,61,142,223,49,121,255,
- 20,162,158,73,89,255,53,138,210,190,57,235,36,73,255,49,
- 210,0,210,85,57,97,255,121,85,174,40,255,162,178,0,121,
- 166,125,53,146,166,255,97,121,65,89,235,231,12,170,36,190,
- 85,255,166,97,198,77,20,146,109,166,255,28,40,202,121,81,
- 247,0,210,255,49,0,65,255,36,166,93,77,255,85,251,0,
- 170,178,0,182,255,0,162,16,154,142,162,223,223,0,0,81,
- 215,4,215,162,215,125,77,206,121,36,125,231,101,16,255,121,
- 0,57,190,215,65,125,89,142,255,101,73,53,146,223,125,125,
- 0,255,0,255,0,206,93,138,49,255,0,202,154,85,45,219,
- 251,53,0,255,40,130,219,158,16,117,186,130,202,49,65,239,
- 89,202,49,28,247,134,150,0,255,117,202,4,215,81,186,57,
- 202,89,73,210,40,93,45,251,206,28,223,142,40,134,162,125,
- 32,247,97,170,0,255,57,134,73,247,162,0,251,40,142,142,
- 8,166,206,81,154,194,93,89,125,243,28,109,227,0,190,65,
- 194,186,0,255,53,45,109,186,186,0,255,130,49,170,69,210,
- 154,0,109,227,45,255,125,105,81,81,255,0,219,134,170,85,
- 146,28,170,89,223,97,8,210,255,158,49,40,125,174,174,125,
- 0,227,166,28,219,130,0,93,239,0,85,255,81,178,125,49,
- 89,255,53,206,73,113,146,255,0,150,36,219,162,0,210,125,
- 69,134,255,85,40,89,235,49,215,121,0,206,36,223,174,69,
- 40,182,178,130,69,45,255,210,85,77,215,0,231,146,0,194,
- 125,174,0,255,40,89,121,206,57,0,206,170,231,150,81,0,
- 125,255,4,174,4,190,121,255,4,166,109,130,49,239,170,93,
- 16,174,210,0,255,16,105,158,93,255,0,125,0,255,158,85,
- 0,255,0,0,255,170,166,61,121,28,198,215,45,243,61,97,
- 255,53,81,130,109,255,8,117,235,121,40,178,174,0,182,49,
- 162,121,255,69,206,0,219,125,0,101,255,239,121,32,210,130,
- 36,231,32,125,81,142,215,158,4,178,255,0,40,251,125,125,
- 219,89,130,0,166,255,24,65,194,125,255,125,77,125,93,125,
- 202,24,138,174,178,32,255,85,194,40,85,36,174,174,125,210,
- 85,255,53,16,93,206,40,130,170,202,93,255,0,24,117,255,
- 97,113,105,81,255,186,194,57,69,206,57,53,223,190,4,255,
- 85,97,130,255,85,0,125,223,85,219,0,215,146,77,40,239,
- 89,36,142,154,227,0,255,85,162,0,162,0,235,178,45,166,
- 0,247,255,20,69,210,89,142,53,255,40,146,166,255,69,0,
- 174,154,142,130,162,0,215,255,0,89,40,255,166,61,146,69,
- 162,40,255,32,121,255,117,178,0,186,206,0,57,215,215,81,
- 158,77,166,210,77,89,210,0,24,202,150,186,0,255,20,97,
- 57,170,235,251,16,73,142,251,93,0,202,0,255,121,219,4,
- 73,219,8,162,206,16,219,93,117,0,255,8,130,174,223,45 };
- static const CImg<Tuchar> palette(pal,1,256,1,3,false);
- return palette;
- }
- //! Return palette \e "hot", containing 256 colors entries in RGB.
- /**
- \return The following \c 256x1x1x3 palette is returned:
- \image html ref_colormap_hot.jpg
- **/
- static const CImg<Tuchar>& hot_LUT256() {
- static CImg<Tuchar> palette;
- cimg::mutex(8);
- if (!palette) {
- palette.assign(1,4,1,3,(T)0);
- palette[1] = palette[2] = palette[3] = palette[6] = palette[7] = palette[11] = 255;
- palette.resize(1,256,1,3,3);
- }
- cimg::mutex(8,0);
- return palette;
- }
- //! Return palette \e "cool", containing 256 colors entries in RGB.
- /**
- \return The following \c 256x1x1x3 palette is returned:
- \image html ref_colormap_cool.jpg
- **/
- static const CImg<Tuchar>& cool_LUT256() {
- static CImg<Tuchar> palette;
- cimg::mutex(8);
- if (!palette) palette.assign(1,2,1,3).fill((T)0,(T)255,(T)255,(T)0,(T)255,(T)255).resize(1,256,1,3,3);
- cimg::mutex(8,0);
- return palette;
- }
- //! Return palette \e "jet", containing 256 colors entries in RGB.
- /**
- \return The following \c 256x1x1x3 palette is returned:
- \image html ref_colormap_jet.jpg
- **/
- static const CImg<Tuchar>& jet_LUT256() {
- static CImg<Tuchar> palette;
- cimg::mutex(8);
- if (!palette) {
- palette.assign(1,4,1,3,(T)0);
- palette[2] = palette[3] = palette[5] = palette[6] = palette[8] = palette[9] = 255;
- palette.resize(1,256,1,3,3);
- }
- cimg::mutex(8,0);
- return palette;
- }
- //! Return palette \e "flag", containing 256 colors entries in RGB.
- /**
- \return The following \c 256x1x1x3 colormap is returned:
- \image html ref_palette_flag.jpg
- **/
- static const CImg<Tuchar>& flag_LUT256() {
- static CImg<Tuchar> palette;
- cimg::mutex(8);
- if (!palette) {
- palette.assign(1,4,1,3,(T)0);
- palette[0] = palette[1] = palette[5] = palette[9] = palette[10] = 255;
- palette.resize(1,256,1,3,0,2);
- }
- cimg::mutex(8,0);
- return palette;
- }
- //! Return palette \e "cube", containing 256 colors entries in RGB.
- /**
- \return The following \c 256x1x1x3 palette is returned:
- \image html ref_colormap_cube.jpg
- **/
- static const CImg<Tuchar>& cube_LUT256() {
- static CImg<Tuchar> palette;
- cimg::mutex(8);
- if (!palette) {
- palette.assign(1,8,1,3,(T)0);
- palette[1] = palette[3] = palette[5] = palette[7] =
- palette[10] = palette[11] = palette[12] = palette[13] =
- palette[20] = palette[21] = palette[22] = palette[23] = 255;
- palette.resize(1,256,1,3,3);
- }
- cimg::mutex(8,0);
- return palette;
- }
- //! Convert pixel values from sRGB to RGB color spaces.
- CImg<T>& sRGBtoRGB() {
- if (is_empty()) return *this;
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32))
- cimg_rofoff(*this,off) {
- const Tfloat
- sval = (Tfloat)_data[off]/255,
- val = (Tfloat)(sval<=0.04045f?sval/12.92f:std::pow((sval + 0.055f)/(1.055f),2.4f));
- _data[off] = (T)cimg::cut(val*255,0,255);
- }
- return *this;
- }
- //! Convert pixel values from sRGB to RGB color spaces \newinstance.
- CImg<Tfloat> get_sRGBtoRGB() const {
- return CImg<Tfloat>(*this,false).sRGBtoRGB();
- }
- //! Convert pixel values from RGB to sRGB color spaces.
- CImg<T>& RGBtosRGB() {
- if (is_empty()) return *this;
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32))
- cimg_rofoff(*this,off) {
- const Tfloat
- val = (Tfloat)_data[off]/255,
- sval = (Tfloat)(val<=0.0031308f?val*12.92f:1.055f*std::pow(val,0.416667f) - 0.055f);
- _data[off] = (T)cimg::cut(sval*255,0,255);
- }
- return *this;
- }
- //! Convert pixel values from RGB to sRGB color spaces \newinstance.
- CImg<Tfloat> get_RGBtosRGB() const {
- return CImg<Tfloat>(*this,false).RGBtosRGB();
- }
- //! Convert pixel values from RGB to HSI color spaces.
- CImg<T>& RGBtoHSI() {
- if (_spectrum!=3)
- throw CImgInstanceException(_cimg_instance
- "RGBtoHSI(): Instance is not a RGB image.",
- cimg_instance);
- T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
- const longT whd = (longT)width()*height()*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
- for (longT N = 0; N<whd; ++N) {
- const Tfloat
- R = (Tfloat)p1[N],
- G = (Tfloat)p2[N],
- B = (Tfloat)p3[N],
- m = cimg::min(R,G,B),
- M = cimg::max(R,G,B),
- C = M - m,
- sum = R + G + B,
- H = 60*(C==0?0:M==R?cimg::mod((G - B)/C,(Tfloat)6):M==G?(B - R)/C + 2:(R - G)/C + 4),
- S = sum<=0?0:1 - 3*m/sum,
- I = sum/(3*255);
- p1[N] = (T)H;
- p2[N] = (T)S;
- p3[N] = (T)I;
- }
- return *this;
- }
- //! Convert pixel values from RGB to HSI color spaces \newinstance.
- CImg<Tfloat> get_RGBtoHSI() const {
- return CImg<Tfloat>(*this,false).RGBtoHSI();
- }
- //! Convert pixel values from HSI to RGB color spaces.
- CImg<T>& HSItoRGB() {
- if (_spectrum!=3)
- throw CImgInstanceException(_cimg_instance
- "HSItoRGB(): Instance is not a HSI image.",
- cimg_instance);
- T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
- const longT whd = (longT)width()*height()*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
- for (longT N = 0; N<whd; ++N) {
- const Tfloat
- H = cimg::mod((Tfloat)p1[N]/60,(Tfloat)6),
- S = (Tfloat)p2[N],
- I = (Tfloat)p3[N],
- Z = 1 - cimg::abs(cimg::mod(H,(Tfloat)2) - 1),
- C = I*S/(1 + Z),
- X = C*Z,
- m = I*(1 - S)/3;
- Tfloat R, G, B;
- switch ((int)H) {
- case 0 : R = C; G = X; B = 0; break;
- case 1 : R = X; G = C; B = 0; break;
- case 2 : R = 0; G = C; B = X; break;
- case 3 : R = 0; G = X; B = C; break;
- case 4 : R = X; G = 0; B = C; break;
- default : R = C; G = 0; B = X;
- }
- p1[N] = (T)((R + m)*3*255);
- p2[N] = (T)((G + m)*3*255);
- p3[N] = (T)((B + m)*3*255);
- }
- return *this;
- }
- //! Convert pixel values from HSI to RGB color spaces \newinstance.
- CImg<Tfloat> get_HSItoRGB() const {
- return CImg< Tuchar>(*this,false).HSItoRGB();
- }
- //! Convert pixel values from RGB to HSL color spaces.
- CImg<T>& RGBtoHSL() {
- if (_spectrum!=3)
- throw CImgInstanceException(_cimg_instance
- "RGBtoHSL(): Instance is not a RGB image.",
- cimg_instance);
- T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
- const longT whd = (longT)width()*height()*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
- for (longT N = 0; N<whd; ++N) {
- const Tfloat
- R = (Tfloat)p1[N],
- G = (Tfloat)p2[N],
- B = (Tfloat)p3[N],
- m = cimg::min(R,G,B),
- M = cimg::max(R,G,B),
- C = M - m,
- H = 60*(C==0?0:M==R?cimg::mod((G - B)/C,(Tfloat)6):M==G?(B - R)/C + 2:(R - G)/C + 4),
- L = 0.5f*(m + M)/255,
- S = L==1 || L==0?0:C/(1 - cimg::abs(2*L - 1))/255;
- p1[N] = (T)H;
- p2[N] = (T)S;
- p3[N] = (T)L;
- }
- return *this;
- }
- //! Convert pixel values from RGB to HSL color spaces \newinstance.
- CImg<Tfloat> get_RGBtoHSL() const {
- return CImg<Tfloat>(*this,false).RGBtoHSL();
- }
- //! Convert pixel values from HSL to RGB color spaces.
- CImg<T>& HSLtoRGB() {
- if (_spectrum!=3)
- throw CImgInstanceException(_cimg_instance
- "HSLtoRGB(): Instance is not a HSL image.",
- cimg_instance);
- T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
- const longT whd = (longT)width()*height()*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
- for (longT N = 0; N<whd; ++N) {
- const Tfloat
- H = cimg::mod((Tfloat)p1[N]/60,(Tfloat)6),
- S = (Tfloat)p2[N],
- L = (Tfloat)p3[N],
- C = (1 - cimg::abs(2*L - 1))*S,
- X = C*(1 - cimg::abs(cimg::mod(H,(Tfloat)2) - 1)),
- m = L - C/2;
- Tfloat R, G, B;
- switch ((int)H) {
- case 0 : R = C; G = X; B = 0; break;
- case 1 : R = X; G = C; B = 0; break;
- case 2 : R = 0; G = C; B = X; break;
- case 3 : R = 0; G = X; B = C; break;
- case 4 : R = X; G = 0; B = C; break;
- default : R = C; G = 0; B = X;
- }
- p1[N] = (T)((R + m)*255);
- p2[N] = (T)((G + m)*255);
- p3[N] = (T)((B + m)*255);
- }
- return *this;
- }
- //! Convert pixel values from HSL to RGB color spaces \newinstance.
- CImg<Tuchar> get_HSLtoRGB() const {
- return CImg<Tuchar>(*this,false).HSLtoRGB();
- }
- //! Convert pixel values from RGB to HSV color spaces.
- CImg<T>& RGBtoHSV() {
- if (_spectrum!=3)
- throw CImgInstanceException(_cimg_instance
- "RGBtoHSV(): Instance is not a RGB image.",
- cimg_instance);
- T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
- const longT whd = (longT)width()*height()*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
- for (longT N = 0; N<whd; ++N) {
- const Tfloat
- R = (Tfloat)p1[N],
- G = (Tfloat)p2[N],
- B = (Tfloat)p3[N],
- M = cimg::max(R,G,B),
- C = M - cimg::min(R,G,B),
- H = 60*(C==0?0:M==R?cimg::mod((G-B)/C,(Tfloat)6):M==G?(B - R)/C + 2:(R - G)/C + 4),
- S = M<=0?0:C/M;
- p1[N] = (T)H;
- p2[N] = (T)S;
- p3[N] = (T)(M/255);
- }
- return *this;
- }
- //! Convert pixel values from RGB to HSV color spaces \newinstance.
- CImg<Tfloat> get_RGBtoHSV() const {
- return CImg<Tfloat>(*this,false).RGBtoHSV();
- }
- //! Convert pixel values from HSV to RGB color spaces.
- CImg<T>& HSVtoRGB() {
- if (_spectrum!=3)
- throw CImgInstanceException(_cimg_instance
- "HSVtoRGB(): Instance is not a HSV image.",
- cimg_instance);
- T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
- const longT whd = (longT)width()*height()*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
- for (longT N = 0; N<whd; ++N) {
- Tfloat
- H = cimg::mod((Tfloat)p1[N]/60,(Tfloat)6),
- S = (Tfloat)p2[N],
- V = (Tfloat)p3[N],
- C = V*S,
- X = C*(1 - cimg::abs(cimg::mod(H,(Tfloat)2) - 1)),
- m = V - C;
- Tfloat R, G, B;
- switch ((int)H) {
- case 0 : R = C; G = X; B = 0; break;
- case 1 : R = X; G = C; B = 0; break;
- case 2 : R = 0; G = C; B = X; break;
- case 3 : R = 0; G = X; B = C; break;
- case 4 : R = X; G = 0; B = C; break;
- default : R = C; G = 0; B = X;
- }
- p1[N] = (T)((R + m)*255);
- p2[N] = (T)((G + m)*255);
- p3[N] = (T)((B + m)*255);
- }
- return *this;
- }
- //! Convert pixel values from HSV to RGB color spaces \newinstance.
- CImg<Tuchar> get_HSVtoRGB() const {
- return CImg<Tuchar>(*this,false).HSVtoRGB();
- }
- //! Convert pixel values from RGB to YCbCr color spaces.
- CImg<T>& RGBtoYCbCr() {
- if (_spectrum!=3)
- throw CImgInstanceException(_cimg_instance
- "RGBtoYCbCr(): Instance is not a RGB image.",
- cimg_instance);
- T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
- const longT whd = (longT)width()*height()*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,512))
- for (longT N = 0; N<whd; ++N) {
- const Tfloat
- R = (Tfloat)p1[N],
- G = (Tfloat)p2[N],
- B = (Tfloat)p3[N],
- Y = (66*R + 129*G + 25*B + 128)/256 + 16,
- Cb = (-38*R - 74*G + 112*B + 128)/256 + 128,
- Cr = (112*R - 94*G - 18*B + 128)/256 + 128;
- p1[N] = (T)cimg::cut(Y,0,255),
- p2[N] = (T)cimg::cut(Cb,0,255),
- p3[N] = (T)cimg::cut(Cr,0,255);
- }
- return *this;
- }
- //! Convert pixel values from RGB to YCbCr color spaces \newinstance.
- CImg<Tuchar> get_RGBtoYCbCr() const {
- return CImg<Tuchar>(*this,false).RGBtoYCbCr();
- }
- //! Convert pixel values from RGB to YCbCr color spaces.
- CImg<T>& YCbCrtoRGB() {
- if (_spectrum!=3)
- throw CImgInstanceException(_cimg_instance
- "YCbCrtoRGB(): Instance is not a YCbCr image.",
- cimg_instance);
- T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
- const longT whd = (longT)width()*height()*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,512))
- for (longT N = 0; N<whd; ++N) {
- const Tfloat
- Y = (Tfloat)p1[N] - 16,
- Cb = (Tfloat)p2[N] - 128,
- Cr = (Tfloat)p3[N] - 128,
- R = (298*Y + 409*Cr + 128)/256,
- G = (298*Y - 100*Cb - 208*Cr + 128)/256,
- B = (298*Y + 516*Cb + 128)/256;
- p1[N] = (T)cimg::cut(R,0,255),
- p2[N] = (T)cimg::cut(G,0,255),
- p3[N] = (T)cimg::cut(B,0,255);
- }
- return *this;
- }
- //! Convert pixel values from RGB to YCbCr color spaces \newinstance.
- CImg<Tuchar> get_YCbCrtoRGB() const {
- return CImg<Tuchar>(*this,false).YCbCrtoRGB();
- }
- //! Convert pixel values from RGB to YUV color spaces.
- CImg<T>& RGBtoYUV() {
- if (_spectrum!=3)
- throw CImgInstanceException(_cimg_instance
- "RGBtoYUV(): Instance is not a RGB image.",
- cimg_instance);
- T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
- const longT whd = (longT)width()*height()*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,16384))
- for (longT N = 0; N<whd; ++N) {
- const Tfloat
- R = (Tfloat)p1[N]/255,
- G = (Tfloat)p2[N]/255,
- B = (Tfloat)p3[N]/255,
- Y = 0.299f*R + 0.587f*G + 0.114f*B;
- p1[N] = (T)Y;
- p2[N] = (T)(0.492f*(B - Y));
- p3[N] = (T)(0.877*(R - Y));
- }
- return *this;
- }
- //! Convert pixel values from RGB to YUV color spaces \newinstance.
- CImg<Tfloat> get_RGBtoYUV() const {
- return CImg<Tfloat>(*this,false).RGBtoYUV();
- }
- //! Convert pixel values from YUV to RGB color spaces.
- CImg<T>& YUVtoRGB() {
- if (_spectrum!=3)
- throw CImgInstanceException(_cimg_instance
- "YUVtoRGB(): Instance is not a YUV image.",
- cimg_instance);
- T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
- const longT whd = (longT)width()*height()*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,16384))
- for (longT N = 0; N<whd; ++N) {
- const Tfloat
- Y = (Tfloat)p1[N],
- U = (Tfloat)p2[N],
- V = (Tfloat)p3[N],
- R = (Y + 1.140f*V)*255,
- G = (Y - 0.395f*U - 0.581f*V)*255,
- B = (Y + 2.032f*U)*255;
- p1[N] = (T)cimg::cut(R,0,255),
- p2[N] = (T)cimg::cut(G,0,255),
- p3[N] = (T)cimg::cut(B,0,255);
- }
- return *this;
- }
- //! Convert pixel values from YUV to RGB color spaces \newinstance.
- CImg<Tuchar> get_YUVtoRGB() const {
- return CImg< Tuchar>(*this,false).YUVtoRGB();
- }
- //! Convert pixel values from RGB to CMY color spaces.
- CImg<T>& RGBtoCMY() {
- if (_spectrum!=3)
- throw CImgInstanceException(_cimg_instance
- "RGBtoCMY(): Instance is not a RGB image.",
- cimg_instance);
- T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
- const longT whd = (longT)width()*height()*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048))
- for (longT N = 0; N<whd; ++N) {
- const Tfloat
- R = (Tfloat)p1[N],
- G = (Tfloat)p2[N],
- B = (Tfloat)p3[N],
- C = 255 - R,
- M = 255 - G,
- Y = 255 - B;
- p1[N] = (T)cimg::cut(C,0,255),
- p2[N] = (T)cimg::cut(M,0,255),
- p3[N] = (T)cimg::cut(Y,0,255);
- }
- return *this;
- }
- //! Convert pixel values from RGB to CMY color spaces \newinstance.
- CImg<Tuchar> get_RGBtoCMY() const {
- return CImg<Tfloat>(*this,false).RGBtoCMY();
- }
- //! Convert pixel values from CMY to RGB color spaces.
- CImg<T>& CMYtoRGB() {
- if (_spectrum!=3)
- throw CImgInstanceException(_cimg_instance
- "CMYtoRGB(): Instance is not a CMY image.",
- cimg_instance);
- T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
- const longT whd = (longT)width()*height()*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048))
- for (longT N = 0; N<whd; ++N) {
- const Tfloat
- C = (Tfloat)p1[N],
- M = (Tfloat)p2[N],
- Y = (Tfloat)p3[N],
- R = 255 - C,
- G = 255 - M,
- B = 255 - Y;
- p1[N] = (T)cimg::cut(R,0,255),
- p2[N] = (T)cimg::cut(G,0,255),
- p3[N] = (T)cimg::cut(B,0,255);
- }
- return *this;
- }
- //! Convert pixel values from CMY to RGB color spaces \newinstance.
- CImg<Tuchar> get_CMYtoRGB() const {
- return CImg<Tuchar>(*this,false).CMYtoRGB();
- }
- //! Convert pixel values from CMY to CMYK color spaces.
- CImg<T>& CMYtoCMYK() {
- return get_CMYtoCMYK().move_to(*this);
- }
- //! Convert pixel values from CMY to CMYK color spaces \newinstance.
- CImg<Tuchar> get_CMYtoCMYK() const {
- if (_spectrum!=3)
- throw CImgInstanceException(_cimg_instance
- "CMYtoCMYK(): Instance is not a CMY image.",
- cimg_instance);
- CImg<Tfloat> res(_width,_height,_depth,4);
- const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2);
- Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2), *pd4 = res.data(0,0,0,3);
- const longT whd = (longT)width()*height()*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,1024))
- for (longT N = 0; N<whd; ++N) {
- Tfloat
- C = (Tfloat)ps1[N],
- M = (Tfloat)ps2[N],
- Y = (Tfloat)ps3[N],
- K = cimg::min(C,M,Y);
- if (K>=255) C = M = Y = 0;
- else { const Tfloat K1 = 255 - K; C = 255*(C - K)/K1; M = 255*(M - K)/K1; Y = 255*(Y - K)/K1; }
- pd1[N] = (Tfloat)cimg::cut(C,0,255),
- pd2[N] = (Tfloat)cimg::cut(M,0,255),
- pd3[N] = (Tfloat)cimg::cut(Y,0,255),
- pd4[N] = (Tfloat)cimg::cut(K,0,255);
- }
- return res;
- }
- //! Convert pixel values from CMYK to CMY color spaces.
- CImg<T>& CMYKtoCMY() {
- return get_CMYKtoCMY().move_to(*this);
- }
- //! Convert pixel values from CMYK to CMY color spaces \newinstance.
- CImg<Tfloat> get_CMYKtoCMY() const {
- if (_spectrum!=4)
- throw CImgInstanceException(_cimg_instance
- "CMYKtoCMY(): Instance is not a CMYK image.",
- cimg_instance);
- CImg<Tfloat> res(_width,_height,_depth,3);
- const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2), *ps4 = data(0,0,0,3);
- Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2);
- const longT whd = (longT)width()*height()*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,1024))
- for (longT N = 0; N<whd; ++N) {
- const Tfloat
- C = (Tfloat)ps1[N],
- M = (Tfloat)ps2[N],
- Y = (Tfloat)ps3[N],
- K = (Tfloat)ps4[N],
- K1 = 1 - K/255,
- nC = C*K1 + K,
- nM = M*K1 + K,
- nY = Y*K1 + K;
- pd1[N] = (Tfloat)cimg::cut(nC,0,255),
- pd2[N] = (Tfloat)cimg::cut(nM,0,255),
- pd3[N] = (Tfloat)cimg::cut(nY,0,255);
- }
- return res;
- }
- //! Convert pixel values from RGB to XYZ color spaces.
- /**
- \param use_D65 Tell to use the D65 illuminant (D50 otherwise).
- **/
- CImg<T>& RGBtoXYZ(const bool use_D65=true) {
- if (_spectrum!=3)
- throw CImgInstanceException(_cimg_instance
- "RGBtoXYZ(): Instance is not a RGB image.",
- cimg_instance);
- T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
- const longT whd = (longT)width()*height()*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048))
- for (longT N = 0; N<whd; ++N) {
- const Tfloat
- R = (Tfloat)p1[N]/255,
- G = (Tfloat)p2[N]/255,
- B = (Tfloat)p3[N]/255;
- if (use_D65) { // D65
- p1[N] = (T)(0.4124564*R + 0.3575761*G + 0.1804375*B);
- p2[N] = (T)(0.2126729*R + 0.7151522*G + 0.0721750*B);
- p3[N] = (T)(0.0193339*R + 0.1191920*G + 0.9503041*B);
- } else { // D50
- p1[N] = (T)(0.43603516*R + 0.38511658*G + 0.14305115*B);
- p2[N] = (T)(0.22248840*R + 0.71690369*G + 0.06060791*B);
- p3[N] = (T)(0.01391602*R + 0.09706116*G + 0.71392822*B);
- }
- }
- return *this;
- }
- //! Convert pixel values from RGB to XYZ color spaces \newinstance.
- CImg<Tfloat> get_RGBtoXYZ(const bool use_D65=true) const {
- return CImg<Tfloat>(*this,false).RGBtoXYZ(use_D65);
- }
- //! Convert pixel values from XYZ to RGB color spaces.
- /**
- \param use_D65 Tell to use the D65 illuminant (D50 otherwise).
- **/
- CImg<T>& XYZtoRGB(const bool use_D65=true) {
- if (_spectrum!=3)
- throw CImgInstanceException(_cimg_instance
- "XYZtoRGB(): Instance is not a XYZ image.",
- cimg_instance);
- T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
- const longT whd = (longT)width()*height()*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048))
- for (longT N = 0; N<whd; ++N) {
- const Tfloat
- X = (Tfloat)p1[N]*255,
- Y = (Tfloat)p2[N]*255,
- Z = (Tfloat)p3[N]*255;
- if (use_D65) {
- p1[N] = (T)cimg::cut(3.2404542*X - 1.5371385*Y - 0.4985314*Z,0,255);
- p2[N] = (T)cimg::cut(-0.9692660*X + 1.8760108*Y + 0.0415560*Z,0,255);
- p3[N] = (T)cimg::cut(0.0556434*X - 0.2040259*Y + 1.0572252*Z,0,255);
- } else {
- p1[N] = (T)cimg::cut(3.134274799724*X - 1.617275708956*Y - 0.490724283042*Z,0,255);
- p2[N] = (T)cimg::cut(-0.978795575994*X + 1.916161689117*Y + 0.033453331711*Z,0,255);
- p3[N] = (T)cimg::cut(0.071976988401*X - 0.228984974402*Y + 1.405718224383*Z,0,255);
- }
- }
- return *this;
- }
- //! Convert pixel values from XYZ to RGB color spaces \newinstance.
- CImg<Tuchar> get_XYZtoRGB(const bool use_D65=true) const {
- return CImg<Tuchar>(*this,false).XYZtoRGB(use_D65);
- }
- //! Convert pixel values from XYZ to Lab color spaces.
- CImg<T>& XYZtoLab(const bool use_D65=true) {
- #define _cimg_Labf(x) (24389*(x)>216?cimg::cbrt(x):(24389*(x)/27 + 16)/116)
- if (_spectrum!=3)
- throw CImgInstanceException(_cimg_instance
- "XYZtoLab(): Instance is not a XYZ image.",
- cimg_instance);
- const CImg<Tfloat> white = CImg<Tfloat>(1,1,1,3,255).RGBtoXYZ(use_D65);
- T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
- const longT whd = (longT)width()*height()*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,128))
- for (longT N = 0; N<whd; ++N) {
- const Tfloat
- X = (Tfloat)(p1[N]/white[0]),
- Y = (Tfloat)(p2[N]/white[1]),
- Z = (Tfloat)(p3[N]/white[2]),
- fX = (Tfloat)_cimg_Labf(X),
- fY = (Tfloat)_cimg_Labf(Y),
- fZ = (Tfloat)_cimg_Labf(Z);
- p1[N] = (T)cimg::cut(116*fY - 16,0,100);
- p2[N] = (T)(500*(fX - fY));
- p3[N] = (T)(200*(fY - fZ));
- }
- return *this;
- }
- //! Convert pixel values from XYZ to Lab color spaces \newinstance.
- CImg<Tfloat> get_XYZtoLab(const bool use_D65=true) const {
- return CImg<Tfloat>(*this,false).XYZtoLab(use_D65);
- }
- //! Convert pixel values from Lab to XYZ color spaces.
- CImg<T>& LabtoXYZ(const bool use_D65=true) {
- if (_spectrum!=3)
- throw CImgInstanceException(_cimg_instance
- "LabtoXYZ(): Instance is not a Lab image.",
- cimg_instance);
- const CImg<Tfloat> white = CImg<Tfloat>(1,1,1,3,255).RGBtoXYZ(use_D65);
- T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
- const longT whd = (longT)width()*height()*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,128))
- for (longT N = 0; N<whd; ++N) {
- const Tfloat
- L = (Tfloat)p1[N],
- a = (Tfloat)p2[N],
- b = (Tfloat)p3[N],
- cY = (L + 16)/116,
- cZ = cY - b/200,
- cX = a/500 + cY,
- X = (Tfloat)(24389*cX>216?cX*cX*cX:(116*cX - 16)*27/24389),
- Y = (Tfloat)(27*L>216?cY*cY*cY:27*L/24389),
- Z = (Tfloat)(24389*cZ>216?cZ*cZ*cZ:(116*cZ - 16)*27/24389);
- p1[N] = (T)(X*white[0]);
- p2[N] = (T)(Y*white[1]);
- p3[N] = (T)(Z*white[2]);
- }
- return *this;
- }
- //! Convert pixel values from Lab to XYZ color spaces \newinstance.
- CImg<Tfloat> get_LabtoXYZ(const bool use_D65=true) const {
- return CImg<Tfloat>(*this,false).LabtoXYZ(use_D65);
- }
- //! Convert pixel values from XYZ to xyY color spaces.
- CImg<T>& XYZtoxyY() {
- if (_spectrum!=3)
- throw CImgInstanceException(_cimg_instance
- "XYZtoxyY(): Instance is not a XYZ image.",
- cimg_instance);
- T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
- const longT whd = (longT)width()*height()*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,4096))
- for (longT N = 0; N<whd; ++N) {
- const Tfloat
- X = (Tfloat)p1[N],
- Y = (Tfloat)p2[N],
- Z = (Tfloat)p3[N],
- sum = X + Y + Z,
- nsum = sum>0?sum:1;
- p1[N] = (T)(X/nsum);
- p2[N] = (T)(Y/nsum);
- p3[N] = (T)Y;
- }
- return *this;
- }
- //! Convert pixel values from XYZ to xyY color spaces \newinstance.
- CImg<Tfloat> get_XYZtoxyY() const {
- return CImg<Tfloat>(*this,false).XYZtoxyY();
- }
- //! Convert pixel values from xyY pixels to XYZ color spaces.
- CImg<T>& xyYtoXYZ() {
- if (_spectrum!=3)
- throw CImgInstanceException(_cimg_instance
- "xyYtoXYZ(): Instance is not a xyY image.",
- cimg_instance);
- T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
- const longT whd = (longT)width()*height()*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,4096))
- for (longT N = 0; N<whd; ++N) {
- const Tfloat
- px = (Tfloat)p1[N],
- py = (Tfloat)p2[N],
- Y = (Tfloat)p3[N],
- ny = py>0?py:1;
- p1[N] = (T)(px*Y/ny);
- p2[N] = (T)Y;
- p3[N] = (T)((1 - px - py)*Y/ny);
- }
- return *this;
- }
- //! Convert pixel values from xyY pixels to XYZ color spaces \newinstance.
- CImg<Tfloat> get_xyYtoXYZ() const {
- return CImg<Tfloat>(*this,false).xyYtoXYZ();
- }
- //! Convert pixel values from RGB to Lab color spaces.
- CImg<T>& RGBtoLab(const bool use_D65=true) {
- return RGBtoXYZ(use_D65).XYZtoLab(use_D65);
- }
- //! Convert pixel values from RGB to Lab color spaces \newinstance.
- CImg<Tfloat> get_RGBtoLab(const bool use_D65=true) const {
- return CImg<Tfloat>(*this,false).RGBtoLab(use_D65);
- }
- //! Convert pixel values from Lab to RGB color spaces.
- CImg<T>& LabtoRGB(const bool use_D65=true) {
- return LabtoXYZ().XYZtoRGB(use_D65);
- }
- //! Convert pixel values from Lab to RGB color spaces \newinstance.
- CImg<Tuchar> get_LabtoRGB(const bool use_D65=true) const {
- return CImg<Tuchar>(*this,false).LabtoRGB(use_D65);
- }
- //! Convert pixel values from RGB to xyY color spaces.
- CImg<T>& RGBtoxyY(const bool use_D65=true) {
- return RGBtoXYZ(use_D65).XYZtoxyY();
- }
- //! Convert pixel values from RGB to xyY color spaces \newinstance.
- CImg<Tfloat> get_RGBtoxyY(const bool use_D65=true) const {
- return CImg<Tfloat>(*this,false).RGBtoxyY(use_D65);
- }
- //! Convert pixel values from xyY to RGB color spaces.
- CImg<T>& xyYtoRGB(const bool use_D65=true) {
- return xyYtoXYZ().XYZtoRGB(use_D65);
- }
- //! Convert pixel values from xyY to RGB color spaces \newinstance.
- CImg<Tuchar> get_xyYtoRGB(const bool use_D65=true) const {
- return CImg<Tuchar>(*this,false).xyYtoRGB(use_D65);
- }
- //! Convert pixel values from RGB to CMYK color spaces.
- CImg<T>& RGBtoCMYK() {
- return RGBtoCMY().CMYtoCMYK();
- }
- //! Convert pixel values from RGB to CMYK color spaces \newinstance.
- CImg<Tfloat> get_RGBtoCMYK() const {
- return CImg<Tfloat>(*this,false).RGBtoCMYK();
- }
- //! Convert pixel values from CMYK to RGB color spaces.
- CImg<T>& CMYKtoRGB() {
- return CMYKtoCMY().CMYtoRGB();
- }
- //! Convert pixel values from CMYK to RGB color spaces \newinstance.
- CImg<Tuchar> get_CMYKtoRGB() const {
- return CImg<Tuchar>(*this,false).CMYKtoRGB();
- }
- //@}
- //------------------------------------------
- //
- //! \name Geometric / Spatial Manipulation
- //@{
- //------------------------------------------
- static float _cimg_lanczos(const float x) {
- if (x<=-2 || x>=2) return 0;
- const float a = (float)cimg::PI*x, b = 0.5f*a;
- return (float)(x?std::sin(a)*std::sin(b)/(a*b):1);
- }
- //! Resize image to new dimensions.
- /**
- \param size_x Number of columns (new size along the X-axis).
- \param size_y Number of rows (new size along the Y-axis).
- \param size_z Number of slices (new size along the Z-axis).
- \param size_c Number of vector-channels (new size along the C-axis).
- \param interpolation_type Method of interpolation:
- - -1 = no interpolation: raw memory resizing.
- - 0 = no interpolation: additional space is filled according to \p boundary_conditions.
- - 1 = nearest-neighbor interpolation.
- - 2 = moving average interpolation.
- - 3 = linear interpolation.
- - 4 = grid interpolation.
- - 5 = cubic interpolation.
- - 6 = lanczos interpolation.
- \param boundary_conditions Type of boundary conditions used if necessary.
- \param centering_x Set centering type (only if \p interpolation_type=0).
- \param centering_y Set centering type (only if \p interpolation_type=0).
- \param centering_z Set centering type (only if \p interpolation_type=0).
- \param centering_c Set centering type (only if \p interpolation_type=0).
- \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100).
- **/
- CImg<T>& resize(const int size_x, const int size_y=-100,
- const int size_z=-100, const int size_c=-100,
- const int interpolation_type=1, const unsigned int boundary_conditions=0,
- const float centering_x = 0, const float centering_y = 0,
- const float centering_z = 0, const float centering_c = 0) {
- if (!size_x || !size_y || !size_z || !size_c) return assign();
- const unsigned int
- _sx = (unsigned int)(size_x<0?-size_x*width()/100:size_x),
- _sy = (unsigned int)(size_y<0?-size_y*height()/100:size_y),
- _sz = (unsigned int)(size_z<0?-size_z*depth()/100:size_z),
- _sc = (unsigned int)(size_c<0?-size_c*spectrum()/100:size_c),
- sx = _sx?_sx:1, sy = _sy?_sy:1, sz = _sz?_sz:1, sc = _sc?_sc:1;
- if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return *this;
- if (is_empty()) return assign(sx,sy,sz,sc,(T)0);
- if (interpolation_type==-1 && sx*sy*sz*sc==size()) {
- _width = sx; _height = sy; _depth = sz; _spectrum = sc;
- return *this;
- }
- return get_resize(sx,sy,sz,sc,interpolation_type,boundary_conditions,
- centering_x,centering_y,centering_z,centering_c).move_to(*this);
- }
- //! Resize image to new dimensions \newinstance.
- CImg<T> get_resize(const int size_x, const int size_y = -100,
- const int size_z = -100, const int size_c = -100,
- const int interpolation_type=1, const unsigned int boundary_conditions=0,
- const float centering_x = 0, const float centering_y = 0,
- const float centering_z = 0, const float centering_c = 0) const {
- if (centering_x<0 || centering_x>1 || centering_y<0 || centering_y>1 ||
- centering_z<0 || centering_z>1 || centering_c<0 || centering_c>1)
- throw CImgArgumentException(_cimg_instance
- "resize(): Specified centering arguments (%g,%g,%g,%g) are outside range [0,1].",
- cimg_instance,
- centering_x,centering_y,centering_z,centering_c);
- if (!size_x || !size_y || !size_z || !size_c) return CImg<T>();
- const unsigned int
- sx = std::max(1U,(unsigned int)(size_x>=0?size_x:-size_x*width()/100)),
- sy = std::max(1U,(unsigned int)(size_y>=0?size_y:-size_y*height()/100)),
- sz = std::max(1U,(unsigned int)(size_z>=0?size_z:-size_z*depth()/100)),
- sc = std::max(1U,(unsigned int)(size_c>=0?size_c:-size_c*spectrum()/100));
- if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return +*this;
- if (is_empty()) return CImg<T>(sx,sy,sz,sc,(T)0);
- CImg<T> res;
- switch (interpolation_type) {
- // Raw resizing.
- //
- case -1 :
- std::memcpy(res.assign(sx,sy,sz,sc,(T)0)._data,_data,sizeof(T)*std::min(size(),(ulongT)sx*sy*sz*sc));
- break;
- // No interpolation.
- //
- case 0 : {
- const int
- xc = (int)(centering_x*((int)sx - width())),
- yc = (int)(centering_y*((int)sy - height())),
- zc = (int)(centering_z*((int)sz - depth())),
- cc = (int)(centering_c*((int)sc - spectrum()));
- switch (boundary_conditions) {
- case 3 : { // Mirror
- res.assign(sx,sy,sz,sc);
- const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1024*1024))
- cimg_forXYZC(res,x,y,z,c) {
- const int
- mx = cimg::mod(x - xc,w2), my = cimg::mod(y - yc,h2),
- mz = cimg::mod(z - zc,d2), mc = cimg::mod(c - cc,s2);
- res(x,y,z,c) = (*this)(mx<width()?mx:w2 - mx - 1,
- my<height()?my:h2 - my - 1,
- mz<depth()?mz:d2 - mz - 1,
- mc<spectrum()?mc:s2 - mc - 1);
- }
- } break;
- case 2 : { // Periodic
- res.assign(sx,sy,sz,sc);
- const int
- x0 = ((int)xc%width()) - width(),
- y0 = ((int)yc%height()) - height(),
- z0 = ((int)zc%depth()) - depth(),
- c0 = ((int)cc%spectrum()) - spectrum(),
- dx = width(), dy = height(), dz = depth(), dc = spectrum();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1024*1024))
- for (int c = c0; c<(int)sc; c+=dc)
- for (int z = z0; z<(int)sz; z+=dz)
- for (int y = y0; y<(int)sy; y+=dy)
- for (int x = x0; x<(int)sx; x+=dx)
- res.draw_image(x,y,z,c,*this);
- } break;
- case 1 : { // Neumann
- res.assign(sx,sy,sz,sc).draw_image(xc,yc,zc,cc,*this);
- CImg<T> sprite;
- if (xc>0) { // X-backward
- res.get_crop(xc,yc,zc,cc,xc,yc + height() - 1,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
- for (int x = xc - 1; x>=0; --x) res.draw_image(x,yc,zc,cc,sprite);
- }
- if (xc + width()<(int)sx) { // X-forward
- res.get_crop(xc + width() - 1,yc,zc,cc,xc + width() - 1,yc + height() - 1,
- zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
- for (int x = xc + width(); x<(int)sx; ++x) res.draw_image(x,yc,zc,cc,sprite);
- }
- if (yc>0) { // Y-backward
- res.get_crop(0,yc,zc,cc,sx - 1,yc,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
- for (int y = yc - 1; y>=0; --y) res.draw_image(0,y,zc,cc,sprite);
- }
- if (yc + height()<(int)sy) { // Y-forward
- res.get_crop(0,yc + height() - 1,zc,cc,sx - 1,yc + height() - 1,
- zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
- for (int y = yc + height(); y<(int)sy; ++y) res.draw_image(0,y,zc,cc,sprite);
- }
- if (zc>0) { // Z-backward
- res.get_crop(0,0,zc,cc,sx - 1,sy - 1,zc,cc + spectrum() - 1).move_to(sprite);
- for (int z = zc - 1; z>=0; --z) res.draw_image(0,0,z,cc,sprite);
- }
- if (zc + depth()<(int)sz) { // Z-forward
- res.get_crop(0,0,zc +depth() - 1,cc,sx - 1,sy - 1,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
- for (int z = zc + depth(); z<(int)sz; ++z) res.draw_image(0,0,z,cc,sprite);
- }
- if (cc>0) { // C-backward
- res.get_crop(0,0,0,cc,sx - 1,sy - 1,sz - 1,cc).move_to(sprite);
- for (int c = cc - 1; c>=0; --c) res.draw_image(0,0,0,c,sprite);
- }
- if (cc + spectrum()<(int)sc) { // C-forward
- res.get_crop(0,0,0,cc + spectrum() - 1,sx - 1,sy - 1,sz - 1,cc + spectrum() - 1).move_to(sprite);
- for (int c = cc + spectrum(); c<(int)sc; ++c) res.draw_image(0,0,0,c,sprite);
- }
- } break;
- default : // Dirichlet
- res.assign(sx,sy,sz,sc,(T)0).draw_image(xc,yc,zc,cc,*this);
- }
- break;
- } break;
- // Nearest neighbor interpolation.
- //
- case 1 : {
- res.assign(sx,sy,sz,sc);
- CImg<ulongT> off_x(sx), off_y(sy + 1), off_z(sz + 1), off_c(sc + 1);
- const ulongT
- wh = (ulongT)_width*_height,
- whd = (ulongT)_width*_height*_depth,
- sxy = (ulongT)sx*sy,
- sxyz = (ulongT)sx*sy*sz,
- one = (ulongT)1;
- if (sx==_width) off_x.fill(1);
- else {
- ulongT *poff_x = off_x._data, curr = 0;
- cimg_forX(res,x) {
- const ulongT old = curr;
- curr = (x + one)*_width/sx;
- *(poff_x++) = curr - old;
- }
- }
- if (sy==_height) off_y.fill(_width);
- else {
- ulongT *poff_y = off_y._data, curr = 0;
- cimg_forY(res,y) {
- const ulongT old = curr;
- curr = (y + one)*_height/sy;
- *(poff_y++) = _width*(curr - old);
- }
- *poff_y = 0;
- }
- if (sz==_depth) off_z.fill(wh);
- else {
- ulongT *poff_z = off_z._data, curr = 0;
- cimg_forZ(res,z) {
- const ulongT old = curr;
- curr = (z + one)*_depth/sz;
- *(poff_z++) = wh*(curr - old);
- }
- *poff_z = 0;
- }
- if (sc==_spectrum) off_c.fill(whd);
- else {
- ulongT *poff_c = off_c._data, curr = 0;
- cimg_forC(res,c) {
- const ulongT old = curr;
- curr = (c + one)*_spectrum/sc;
- *(poff_c++) = whd*(curr - old);
- }
- *poff_c = 0;
- }
- T *ptrd = res._data;
- const T* ptrc = _data;
- const ulongT *poff_c = off_c._data;
- for (unsigned int c = 0; c<sc; ) {
- const T *ptrz = ptrc;
- const ulongT *poff_z = off_z._data;
- for (unsigned int z = 0; z<sz; ) {
- const T *ptry = ptrz;
- const ulongT *poff_y = off_y._data;
- for (unsigned int y = 0; y<sy; ) {
- const T *ptrx = ptry;
- const ulongT *poff_x = off_x._data;
- cimg_forX(res,x) { *(ptrd++) = *ptrx; ptrx+=*(poff_x++); }
- ++y;
- ulongT dy = *(poff_y++);
- for ( ; !dy && y<dy; std::memcpy(ptrd,ptrd - sx,sizeof(T)*sx), ++y, ptrd+=sx, dy = *(poff_y++)) {}
- ptry+=dy;
- }
- ++z;
- ulongT dz = *(poff_z++);
- for ( ; !dz && z<dz; std::memcpy(ptrd,ptrd - sxy,sizeof(T)*sxy), ++z, ptrd+=sxy, dz = *(poff_z++)) {}
- ptrz+=dz;
- }
- ++c;
- ulongT dc = *(poff_c++);
- for ( ; !dc && c<dc; std::memcpy(ptrd,ptrd - sxyz,sizeof(T)*sxyz), ++c, ptrd+=sxyz, dc = *(poff_c++)) {}
- ptrc+=dc;
- }
- } break;
- // Moving average.
- //
- case 2 : {
- bool instance_first = true;
- if (sx!=_width) {
- if (sx>_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(res);
- else {
- CImg<Tfloat> tmp(sx,_height,_depth,_spectrum,0);
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
- cimg_openmp_if(sx>=256 && _height*_depth*_spectrum>=256))
- cimg_forYZC(tmp,y,z,v) {
- for (unsigned int a = _width*sx, b = _width, c = sx, s = 0, t = 0; a; ) {
- const unsigned int d = std::min(b,c);
- a-=d; b-=d; c-=d;
- tmp(t,y,z,v)+=(Tfloat)(*this)(s,y,z,v)*d;
- if (!b) { tmp(t++,y,z,v)/=_width; b = _width; }
- if (!c) { ++s; c = sx; }
- }
- }
- tmp.move_to(res);
- }
- instance_first = false;
- }
- if (sy!=_height) {
- if (sy>_height) get_resize(sx,sy,_depth,_spectrum,1).move_to(res);
- else {
- CImg<Tfloat> tmp(sx,sy,_depth,_spectrum,0);
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
- cimg_openmp_if(sy>=256 && _width*_depth*_spectrum>=256))
- cimg_forXZC(tmp,x,z,v) {
- for (unsigned int a = _height*sy, b = _height, c = sy, s = 0, t = 0; a; ) {
- const unsigned int d = std::min(b,c);
- a-=d; b-=d; c-=d;
- if (instance_first) tmp(x,t,z,v)+=(Tfloat)(*this)(x,s,z,v)*d;
- else tmp(x,t,z,v)+=(Tfloat)res(x,s,z,v)*d;
- if (!b) { tmp(x,t++,z,v)/=_height; b = _height; }
- if (!c) { ++s; c = sy; }
- }
- }
- tmp.move_to(res);
- }
- instance_first = false;
- }
- if (sz!=_depth) {
- if (sz>_depth) get_resize(sx,sy,sz,_spectrum,1).move_to(res);
- else {
- CImg<Tfloat> tmp(sx,sy,sz,_spectrum,0);
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
- cimg_openmp_if(sz>=256 && _width*_height*_spectrum>=256))
- cimg_forXYC(tmp,x,y,v) {
- for (unsigned int a = _depth*sz, b = _depth, c = sz, s = 0, t = 0; a; ) {
- const unsigned int d = std::min(b,c);
- a-=d; b-=d; c-=d;
- if (instance_first) tmp(x,y,t,v)+=(Tfloat)(*this)(x,y,s,v)*d;
- else tmp(x,y,t,v)+=(Tfloat)res(x,y,s,v)*d;
- if (!b) { tmp(x,y,t++,v)/=_depth; b = _depth; }
- if (!c) { ++s; c = sz; }
- }
- }
- tmp.move_to(res);
- }
- instance_first = false;
- }
- if (sc!=_spectrum) {
- if (sc>_spectrum) get_resize(sx,sy,sz,sc,1).move_to(res);
- else {
- CImg<Tfloat> tmp(sx,sy,sz,sc,0);
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
- cimg_openmp_if(sc>=256 && _width*_height*_depth>=256))
- cimg_forXYZ(tmp,x,y,z) {
- for (unsigned int a = _spectrum*sc, b = _spectrum, c = sc, s = 0, t = 0; a; ) {
- const unsigned int d = std::min(b,c);
- a-=d; b-=d; c-=d;
- if (instance_first) tmp(x,y,z,t)+=(Tfloat)(*this)(x,y,z,s)*d;
- else tmp(x,y,z,t)+=(Tfloat)res(x,y,z,s)*d;
- if (!b) { tmp(x,y,z,t++)/=_spectrum; b = _spectrum; }
- if (!c) { ++s; c = sc; }
- }
- }
- tmp.move_to(res);
- }
- instance_first = false;
- }
- } break;
- // Linear interpolation.
- //
- case 3 : {
- CImg<uintT> off(cimg::max(sx,sy,sz,sc));
- CImg<doubleT> foff(off._width);
- CImg<T> resx, resy, resz, resc;
- double curr, old;
- if (sx!=_width) {
- if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
- else if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
- else {
- const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0):
- (double)_width/sx;
- resx.assign(sx,_height,_depth,_spectrum);
- curr = old = 0;
- {
- unsigned int *poff = off._data;
- double *pfoff = foff._data;
- cimg_forX(resx,x) {
- *(pfoff++) = curr - (unsigned int)curr;
- old = curr;
- curr = std::min(width() - 1.,curr + fx);
- *(poff++) = (unsigned int)curr - (unsigned int)old;
- }
- }
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
- cimg_openmp_if(resx._width>=256 && resx._height*resx._depth*resx._spectrum>=256))
- cimg_forYZC(resx,y,z,c) {
- const T *ptrs = data(0,y,z,c), *const ptrsmax = ptrs + _width - 1;
- T *ptrd = resx.data(0,y,z,c);
- const unsigned int *poff = off._data;
- const double *pfoff = foff._data;
- cimg_forX(resx,x) {
- const double alpha = *(pfoff++);
- const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + 1):val1;
- *(ptrd++) = (T)((1 - alpha)*val1 + alpha*val2);
- ptrs+=*(poff++);
- }
- }
- }
- } else resx.assign(*this,true);
- if (sy!=_height) {
- if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
- else {
- if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
- else {
- const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0):
- (double)_height/sy;
- resy.assign(sx,sy,_depth,_spectrum);
- curr = old = 0;
- {
- unsigned int *poff = off._data;
- double *pfoff = foff._data;
- cimg_forY(resy,y) {
- *(pfoff++) = curr - (unsigned int)curr;
- old = curr;
- curr = std::min(height() - 1.,curr + fy);
- *(poff++) = sx*((unsigned int)curr - (unsigned int)old);
- }
- }
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
- cimg_openmp_if(resy._height>=256 && resy._width*resy._depth*resy._spectrum>=256))
- cimg_forXZC(resy,x,z,c) {
- const T *ptrs = resx.data(x,0,z,c), *const ptrsmax = ptrs + (_height - 1)*sx;
- T *ptrd = resy.data(x,0,z,c);
- const unsigned int *poff = off._data;
- const double *pfoff = foff._data;
- cimg_forY(resy,y) {
- const double alpha = *(pfoff++);
- const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sx):val1;
- *ptrd = (T)((1 - alpha)*val1 + alpha*val2);
- ptrd+=sx;
- ptrs+=*(poff++);
- }
- }
- }
- }
- resx.assign();
- } else resy.assign(resx,true);
- if (sz!=_depth) {
- if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
- else {
- if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
- else {
- const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0):
- (double)_depth/sz;
- const unsigned int sxy = sx*sy;
- resz.assign(sx,sy,sz,_spectrum);
- curr = old = 0;
- {
- unsigned int *poff = off._data;
- double *pfoff = foff._data;
- cimg_forZ(resz,z) {
- *(pfoff++) = curr - (unsigned int)curr;
- old = curr;
- curr = std::min(depth() - 1.,curr + fz);
- *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
- }
- }
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
- cimg_openmp_if(resz._depth>=256 && resz._width*resz._height*resz._spectrum>=256))
- cimg_forXYC(resz,x,y,c) {
- const T *ptrs = resy.data(x,y,0,c), *const ptrsmax = ptrs + (_depth - 1)*sxy;
- T *ptrd = resz.data(x,y,0,c);
- const unsigned int *poff = off._data;
- const double *pfoff = foff._data;
- cimg_forZ(resz,z) {
- const double alpha = *(pfoff++);
- const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sxy):val1;
- *ptrd = (T)((1 - alpha)*val1 + alpha*val2);
- ptrd+=sxy;
- ptrs+=*(poff++);
- }
- }
- }
- }
- resy.assign();
- } else resz.assign(resy,true);
- if (sc!=_spectrum) {
- if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
- else {
- if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
- else {
- const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0):
- (double)_spectrum/sc;
- const unsigned int sxyz = sx*sy*sz;
- resc.assign(sx,sy,sz,sc);
- curr = old = 0;
- {
- unsigned int *poff = off._data;
- double *pfoff = foff._data;
- cimg_forC(resc,c) {
- *(pfoff++) = curr - (unsigned int)curr;
- old = curr;
- curr = std::min(spectrum() - 1.,curr + fc);
- *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
- }
- }
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
- cimg_openmp_if(resc._spectrum>=256 && resc._width*resc._height*resc._depth>=256))
- cimg_forXYZ(resc,x,y,z) {
- const T *ptrs = resz.data(x,y,z,0), *const ptrsmax = ptrs + (_spectrum - 1)*sxyz;
- T *ptrd = resc.data(x,y,z,0);
- const unsigned int *poff = off._data;
- const double *pfoff = foff._data;
- cimg_forC(resc,c) {
- const double alpha = *(pfoff++);
- const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sxyz):val1;
- *ptrd = (T)((1 - alpha)*val1 + alpha*val2);
- ptrd+=sxyz;
- ptrs+=*(poff++);
- }
- }
- }
- }
- resz.assign();
- } else resc.assign(resz,true);
- return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
- } break;
- // Grid interpolation.
- //
- case 4 : {
- CImg<T> resx, resy, resz, resc;
- if (sx!=_width) {
- if (sx<_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
- else {
- resx.assign(sx,_height,_depth,_spectrum,(T)0);
- const int dx = (int)(2*sx), dy = 2*width();
- int err = (int)(dy + centering_x*(sx*dy/width() - dy)), xs = 0;
- cimg_forX(resx,x) if ((err-=dy)<=0) {
- cimg_forYZC(resx,y,z,c) resx(x,y,z,c) = (*this)(xs,y,z,c);
- ++xs;
- err+=dx;
- }
- }
- } else resx.assign(*this,true);
- if (sy!=_height) {
- if (sy<_height) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
- else {
- resy.assign(sx,sy,_depth,_spectrum,(T)0);
- const int dx = (int)(2*sy), dy = 2*height();
- int err = (int)(dy + centering_y*(sy*dy/height() - dy)), ys = 0;
- cimg_forY(resy,y) if ((err-=dy)<=0) {
- cimg_forXZC(resy,x,z,c) resy(x,y,z,c) = resx(x,ys,z,c);
- ++ys;
- err+=dx;
- }
- }
- resx.assign();
- } else resy.assign(resx,true);
- if (sz!=_depth) {
- if (sz<_depth) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
- else {
- resz.assign(sx,sy,sz,_spectrum,(T)0);
- const int dx = (int)(2*sz), dy = 2*depth();
- int err = (int)(dy + centering_z*(sz*dy/depth() - dy)), zs = 0;
- cimg_forZ(resz,z) if ((err-=dy)<=0) {
- cimg_forXYC(resz,x,y,c) resz(x,y,z,c) = resy(x,y,zs,c);
- ++zs;
- err+=dx;
- }
- }
- resy.assign();
- } else resz.assign(resy,true);
- if (sc!=_spectrum) {
- if (sc<_spectrum) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
- else {
- resc.assign(sx,sy,sz,sc,(T)0);
- const int dx = (int)(2*sc), dy = 2*spectrum();
- int err = (int)(dy + centering_c*(sc*dy/spectrum() - dy)), cs = 0;
- cimg_forC(resc,c) if ((err-=dy)<=0) {
- cimg_forXYZ(resc,x,y,z) resc(x,y,z,c) = resz(x,y,z,cs);
- ++cs;
- err+=dx;
- }
- }
- resz.assign();
- } else resc.assign(resz,true);
- return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
- } break;
- // Cubic interpolation.
- //
- case 5 : {
- const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
- CImg<uintT> off(cimg::max(sx,sy,sz,sc));
- CImg<doubleT> foff(off._width);
- CImg<T> resx, resy, resz, resc;
- double curr, old;
- if (sx!=_width) {
- if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
- else {
- if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
- else {
- const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0):
- (double)_width/sx;
- resx.assign(sx,_height,_depth,_spectrum);
- curr = old = 0;
- {
- unsigned int *poff = off._data;
- double *pfoff = foff._data;
- cimg_forX(resx,x) {
- *(pfoff++) = curr - (unsigned int)curr;
- old = curr;
- curr = std::min(width() - 1.,curr + fx);
- *(poff++) = (unsigned int)curr - (unsigned int)old;
- }
- }
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
- cimg_openmp_if(resx._width>=256 && resx._height*resx._depth*resx._spectrum>=256))
- cimg_forYZC(resx,y,z,c) {
- const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_width - 2);
- T *ptrd = resx.data(0,y,z,c);
- const unsigned int *poff = off._data;
- const double *pfoff = foff._data;
- cimg_forX(resx,x) {
- const double
- t = *(pfoff++),
- val1 = (double)*ptrs,
- val0 = ptrs>ptrs0?(double)*(ptrs - 1):val1,
- val2 = ptrs<=ptrsmax?(double)*(ptrs + 1):val1,
- val3 = ptrs<ptrsmax?(double)*(ptrs + 2):val2,
- val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
- t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
- *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val);
- ptrs+=*(poff++);
- }
- }
- }
- }
- } else resx.assign(*this,true);
- if (sy!=_height) {
- if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
- else {
- if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
- else {
- const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0):
- (double)_height/sy;
- resy.assign(sx,sy,_depth,_spectrum);
- curr = old = 0;
- {
- unsigned int *poff = off._data;
- double *pfoff = foff._data;
- cimg_forY(resy,y) {
- *(pfoff++) = curr - (unsigned int)curr;
- old = curr;
- curr = std::min(height() - 1.,curr + fy);
- *(poff++) = sx*((unsigned int)curr - (unsigned int)old);
- }
- }
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
- cimg_openmp_if(resy._height>=256 && resy._width*resy._depth*resy._spectrum>=256))
- cimg_forXZC(resy,x,z,c) {
- const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_height - 2)*sx;
- T *ptrd = resy.data(x,0,z,c);
- const unsigned int *poff = off._data;
- const double *pfoff = foff._data;
- cimg_forY(resy,y) {
- const double
- t = *(pfoff++),
- val1 = (double)*ptrs,
- val0 = ptrs>ptrs0?(double)*(ptrs - sx):val1,
- val2 = ptrs<=ptrsmax?(double)*(ptrs + sx):val1,
- val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sx):val2,
- val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
- t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
- *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
- ptrd+=sx;
- ptrs+=*(poff++);
- }
- }
- }
- }
- resx.assign();
- } else resy.assign(resx,true);
- if (sz!=_depth) {
- if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
- else {
- if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
- else {
- const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0):
- (double)_depth/sz;
- const unsigned int sxy = sx*sy;
- resz.assign(sx,sy,sz,_spectrum);
- curr = old = 0;
- {
- unsigned int *poff = off._data;
- double *pfoff = foff._data;
- cimg_forZ(resz,z) {
- *(pfoff++) = curr - (unsigned int)curr;
- old = curr;
- curr = std::min(depth() - 1.,curr + fz);
- *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
- }
- }
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
- cimg_openmp_if(resz._depth>=256 && resz._width*resz._height*resz._spectrum>=256))
- cimg_forXYC(resz,x,y,c) {
- const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_depth - 2)*sxy;
- T *ptrd = resz.data(x,y,0,c);
- const unsigned int *poff = off._data;
- const double *pfoff = foff._data;
- cimg_forZ(resz,z) {
- const double
- t = *(pfoff++),
- val1 = (double)*ptrs,
- val0 = ptrs>ptrs0?(double)*(ptrs - sxy):val1,
- val2 = ptrs<=ptrsmax?(double)*(ptrs + sxy):val1,
- val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sxy):val2,
- val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
- t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
- *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
- ptrd+=sxy;
- ptrs+=*(poff++);
- }
- }
- }
- }
- resy.assign();
- } else resz.assign(resy,true);
- if (sc!=_spectrum) {
- if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
- else {
- if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
- else {
- const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0):
- (double)_spectrum/sc;
- const unsigned int sxyz = sx*sy*sz;
- resc.assign(sx,sy,sz,sc);
- curr = old = 0;
- {
- unsigned int *poff = off._data;
- double *pfoff = foff._data;
- cimg_forC(resc,c) {
- *(pfoff++) = curr - (unsigned int)curr;
- old = curr;
- curr = std::min(spectrum() - 1.,curr + fc);
- *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
- }
- }
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
- cimg_openmp_if(resc._spectrum>=256 && resc._width*resc._height*resc._depth>=256))
- cimg_forXYZ(resc,x,y,z) {
- const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmax = ptrs + (_spectrum - 2)*sxyz;
- T *ptrd = resc.data(x,y,z,0);
- const unsigned int *poff = off._data;
- const double *pfoff = foff._data;
- cimg_forC(resc,c) {
- const double
- t = *(pfoff++),
- val1 = (double)*ptrs,
- val0 = ptrs>ptrs0?(double)*(ptrs - sxyz):val1,
- val2 = ptrs<=ptrsmax?(double)*(ptrs + sxyz):val1,
- val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sxyz):val2,
- val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
- t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
- *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
- ptrd+=sxyz;
- ptrs+=*(poff++);
- }
- }
- }
- }
- resz.assign();
- } else resc.assign(resz,true);
- return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
- } break;
- // Lanczos interpolation.
- //
- case 6 : {
- const double vmin = (double)cimg::type<T>::min(), vmax = (double)cimg::type<T>::max();
- CImg<uintT> off(cimg::max(sx,sy,sz,sc));
- CImg<doubleT> foff(off._width);
- CImg<T> resx, resy, resz, resc;
- double curr, old;
- if (sx!=_width) {
- if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
- else {
- if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
- else {
- const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0):
- (double)_width/sx;
- resx.assign(sx,_height,_depth,_spectrum);
- curr = old = 0;
- {
- unsigned int *poff = off._data;
- double *pfoff = foff._data;
- cimg_forX(resx,x) {
- *(pfoff++) = curr - (unsigned int)curr;
- old = curr;
- curr = std::min(width() - 1.,curr + fx);
- *(poff++) = (unsigned int)curr - (unsigned int)old;
- }
- }
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
- cimg_openmp_if(resx._width>=256 && resx._height*resx._depth*resx._spectrum>=256))
- cimg_forYZC(resx,y,z,c) {
- const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + 1,
- *const ptrsmax = ptrs0 + (_width - 2);
- T *ptrd = resx.data(0,y,z,c);
- const unsigned int *poff = off._data;
- const double *pfoff = foff._data;
- cimg_forX(resx,x) {
- const double
- t = *(pfoff++),
- w0 = _cimg_lanczos(t + 2),
- w1 = _cimg_lanczos(t + 1),
- w2 = _cimg_lanczos(t),
- w3 = _cimg_lanczos(t - 1),
- w4 = _cimg_lanczos(t - 2),
- val2 = (double)*ptrs,
- val1 = ptrs>=ptrsmin?(double)*(ptrs - 1):val2,
- val0 = ptrs>ptrsmin?(double)*(ptrs - 2):val1,
- val3 = ptrs<=ptrsmax?(double)*(ptrs + 1):val2,
- val4 = ptrs<ptrsmax?(double)*(ptrs + 2):val3,
- val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
- *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val);
- ptrs+=*(poff++);
- }
- }
- }
- }
- } else resx.assign(*this,true);
- if (sy!=_height) {
- if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
- else {
- if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
- else {
- const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0):
- (double)_height/sy;
- resy.assign(sx,sy,_depth,_spectrum);
- curr = old = 0;
- {
- unsigned int *poff = off._data;
- double *pfoff = foff._data;
- cimg_forY(resy,y) {
- *(pfoff++) = curr - (unsigned int)curr;
- old = curr;
- curr = std::min(height() - 1.,curr + fy);
- *(poff++) = sx*((unsigned int)curr - (unsigned int)old);
- }
- }
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
- cimg_openmp_if(resy._height>=256 && resy._width*resy._depth*resy._spectrum>=256))
- cimg_forXZC(resy,x,z,c) {
- const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sx,
- *const ptrsmax = ptrs0 + (_height - 2)*sx;
- T *ptrd = resy.data(x,0,z,c);
- const unsigned int *poff = off._data;
- const double *pfoff = foff._data;
- cimg_forY(resy,y) {
- const double
- t = *(pfoff++),
- w0 = _cimg_lanczos(t + 2),
- w1 = _cimg_lanczos(t + 1),
- w2 = _cimg_lanczos(t),
- w3 = _cimg_lanczos(t - 1),
- w4 = _cimg_lanczos(t - 2),
- val2 = (double)*ptrs,
- val1 = ptrs>=ptrsmin?(double)*(ptrs - sx):val2,
- val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sx):val1,
- val3 = ptrs<=ptrsmax?(double)*(ptrs + sx):val2,
- val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sx):val3,
- val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
- *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
- ptrd+=sx;
- ptrs+=*(poff++);
- }
- }
- }
- }
- resx.assign();
- } else resy.assign(resx,true);
- if (sz!=_depth) {
- if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
- else {
- if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
- else {
- const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0):
- (double)_depth/sz;
- const unsigned int sxy = sx*sy;
- resz.assign(sx,sy,sz,_spectrum);
- curr = old = 0;
- {
- unsigned int *poff = off._data;
- double *pfoff = foff._data;
- cimg_forZ(resz,z) {
- *(pfoff++) = curr - (unsigned int)curr;
- old = curr;
- curr = std::min(depth() - 1.,curr + fz);
- *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
- }
- }
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
- cimg_openmp_if(resz._depth>=256 && resz._width*resz._height*resz._spectrum>=256))
- cimg_forXYC(resz,x,y,c) {
- const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxy,
- *const ptrsmax = ptrs0 + (_depth - 2)*sxy;
- T *ptrd = resz.data(x,y,0,c);
- const unsigned int *poff = off._data;
- const double *pfoff = foff._data;
- cimg_forZ(resz,z) {
- const double
- t = *(pfoff++),
- w0 = _cimg_lanczos(t + 2),
- w1 = _cimg_lanczos(t + 1),
- w2 = _cimg_lanczos(t),
- w3 = _cimg_lanczos(t - 1),
- w4 = _cimg_lanczos(t - 2),
- val2 = (double)*ptrs,
- val1 = ptrs>=ptrsmin?(double)*(ptrs - sxy):val2,
- val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sxy):val1,
- val3 = ptrs<=ptrsmax?(double)*(ptrs + sxy):val2,
- val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sxy):val3,
- val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
- *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
- ptrd+=sxy;
- ptrs+=*(poff++);
- }
- }
- }
- }
- resy.assign();
- } else resz.assign(resy,true);
- if (sc!=_spectrum) {
- if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
- else {
- if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
- else {
- const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0):
- (double)_spectrum/sc;
- const unsigned int sxyz = sx*sy*sz;
- resc.assign(sx,sy,sz,sc);
- curr = old = 0;
- {
- unsigned int *poff = off._data;
- double *pfoff = foff._data;
- cimg_forC(resc,c) {
- *(pfoff++) = curr - (unsigned int)curr;
- old = curr;
- curr = std::min(spectrum() - 1.,curr + fc);
- *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
- }
- }
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
- cimg_openmp_if(resc._spectrum>=256 && resc._width*resc._height*resc._depth>=256))
- cimg_forXYZ(resc,x,y,z) {
- const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxyz,
- *const ptrsmax = ptrs + (_spectrum - 2)*sxyz;
- T *ptrd = resc.data(x,y,z,0);
- const unsigned int *poff = off._data;
- const double *pfoff = foff._data;
- cimg_forC(resc,c) {
- const double
- t = *(pfoff++),
- w0 = _cimg_lanczos(t + 2),
- w1 = _cimg_lanczos(t + 1),
- w2 = _cimg_lanczos(t),
- w3 = _cimg_lanczos(t - 1),
- w4 = _cimg_lanczos(t - 2),
- val2 = (double)*ptrs,
- val1 = ptrs>=ptrsmin?(double)*(ptrs - sxyz):val2,
- val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sxyz):val1,
- val3 = ptrs<=ptrsmax?(double)*(ptrs + sxyz):val2,
- val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sxyz):val3,
- val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
- *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
- ptrd+=sxyz;
- ptrs+=*(poff++);
- }
- }
- }
- }
- resz.assign();
- } else resc.assign(resz,true);
- return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
- } break;
- // Unknown interpolation.
- //
- default :
- throw CImgArgumentException(_cimg_instance
- "resize(): Invalid specified interpolation %d "
- "(should be { -1=raw | 0=none | 1=nearest | 2=average | 3=linear | 4=grid | "
- "5=cubic | 6=lanczos }).",
- cimg_instance,
- interpolation_type);
- }
- return res;
- }
- //! Resize image to dimensions of another image.
- /**
- \param src Reference image used for dimensions.
- \param interpolation_type Interpolation method.
- \param boundary_conditions Boundary conditions.
- Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
- \param centering_x Set centering type (only if \p interpolation_type=0).
- \param centering_y Set centering type (only if \p interpolation_type=0).
- \param centering_z Set centering type (only if \p interpolation_type=0).
- \param centering_c Set centering type (only if \p interpolation_type=0).
- **/
- template<typename t>
- CImg<T>& resize(const CImg<t>& src,
- const int interpolation_type=1, const unsigned int boundary_conditions=0,
- const float centering_x = 0, const float centering_y = 0,
- const float centering_z = 0, const float centering_c = 0) {
- return resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions,
- centering_x,centering_y,centering_z,centering_c);
- }
- //! Resize image to dimensions of another image \newinstance.
- template<typename t>
- CImg<T> get_resize(const CImg<t>& src,
- const int interpolation_type=1, const unsigned int boundary_conditions=0,
- const float centering_x = 0, const float centering_y = 0,
- const float centering_z = 0, const float centering_c = 0) const {
- return get_resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions,
- centering_x,centering_y,centering_z,centering_c);
- }
- //! Resize image to dimensions of a display window.
- /**
- \param disp Reference display window used for dimensions.
- \param interpolation_type Interpolation method.
- \param boundary_conditions Boundary conditions.
- Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
- \param centering_x Set centering type (only if \p interpolation_type=0).
- \param centering_y Set centering type (only if \p interpolation_type=0).
- \param centering_z Set centering type (only if \p interpolation_type=0).
- \param centering_c Set centering type (only if \p interpolation_type=0).
- **/
- CImg<T>& resize(const CImgDisplay& disp,
- const int interpolation_type=1, const unsigned int boundary_conditions=0,
- const float centering_x = 0, const float centering_y = 0,
- const float centering_z = 0, const float centering_c = 0) {
- return resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions,
- centering_x,centering_y,centering_z,centering_c);
- }
- //! Resize image to dimensions of a display window \newinstance.
- CImg<T> get_resize(const CImgDisplay& disp,
- const int interpolation_type=1, const unsigned int boundary_conditions=0,
- const float centering_x = 0, const float centering_y = 0,
- const float centering_z = 0, const float centering_c = 0) const {
- return get_resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions,
- centering_x,centering_y,centering_z,centering_c);
- }
- //! Resize image to half-size along XY axes, using an optimized filter.
- CImg<T>& resize_halfXY() {
- return get_resize_halfXY().move_to(*this);
- }
- //! Resize image to half-size along XY axes, using an optimized filter \newinstance.
- CImg<T> get_resize_halfXY() const {
- if (is_empty()) return *this;
- static const Tfloat kernel[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f,
- 0.1231940459f, 0.1935127547f, 0.1231940459f,
- 0.07842776544f, 0.1231940459f, 0.07842776544f };
- CImg<T> I(9), res(_width/2,_height/2,_depth,_spectrum);
- T *ptrd = res._data;
- cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,T)
- if (x%2 && y%2) *(ptrd++) = (T)
- (I[0]*kernel[0] + I[1]*kernel[1] + I[2]*kernel[2] +
- I[3]*kernel[3] + I[4]*kernel[4] + I[5]*kernel[5] +
- I[6]*kernel[6] + I[7]*kernel[7] + I[8]*kernel[8]);
- return res;
- }
- //! Resize image to double-size, using the Scale2X algorithm.
- /**
- \note Use anisotropic upscaling algorithm
- <a href="http://scale2x.sourceforge.net/algorithm.html">described here</a>.
- **/
- CImg<T>& resize_doubleXY() {
- return get_resize_doubleXY().move_to(*this);
- }
- //! Resize image to double-size, using the Scale2X algorithm \newinstance.
- CImg<T> get_resize_doubleXY() const {
- #define _cimg_gs2x_for3(bound,i) \
- for (int i = 0, _p1##i = 0, \
- _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
- _n1##i<(int)(bound) || i==--_n1##i; \
- _p1##i = i++, ++_n1##i, ptrd1+=(res)._width, ptrd2+=(res)._width)
- #define _cimg_gs2x_for3x3(img,x,y,z,c,I,T) \
- _cimg_gs2x_for3((img)._height,y) for (int x = 0, \
- _p1##x = 0, \
- _n1##x = (int)( \
- (I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
- (I[3] = I[4] = (T)(img)(0,y,z,c)), \
- (I[7] = (T)(img)(0,_n1##y,z,c)), \
- 1>=(img)._width?(img).width() - 1:1); \
- (_n1##x<(img).width() && ( \
- (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
- (I[5] = (T)(img)(_n1##x,y,z,c)), \
- (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
- x==--_n1##x; \
- I[1] = I[2], \
- I[3] = I[4], I[4] = I[5], \
- I[7] = I[8], \
- _p1##x = x++, ++_n1##x)
- if (is_empty()) return *this;
- CImg<T> res(_width<<1,_height<<1,_depth,_spectrum);
- CImg_3x3(I,T);
- cimg_forZC(*this,z,c) {
- T
- *ptrd1 = res.data(0,0,z,c),
- *ptrd2 = ptrd1 + res._width;
- _cimg_gs2x_for3x3(*this,x,y,z,c,I,T) {
- if (Icp!=Icn && Ipc!=Inc) {
- *(ptrd1++) = Ipc==Icp?Ipc:Icc;
- *(ptrd1++) = Icp==Inc?Inc:Icc;
- *(ptrd2++) = Ipc==Icn?Ipc:Icc;
- *(ptrd2++) = Icn==Inc?Inc:Icc;
- } else { *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; }
- }
- }
- return res;
- }
- //! Resize image to triple-size, using the Scale3X algorithm.
- /**
- \note Use anisotropic upscaling algorithm
- <a href="http://scale2x.sourceforge.net/algorithm.html">described here</a>.
- **/
- CImg<T>& resize_tripleXY() {
- return get_resize_tripleXY().move_to(*this);
- }
- //! Resize image to triple-size, using the Scale3X algorithm \newinstance.
- CImg<T> get_resize_tripleXY() const {
- #define _cimg_gs3x_for3(bound,i) \
- for (int i = 0, _p1##i = 0, \
- _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
- _n1##i<(int)(bound) || i==--_n1##i; \
- _p1##i = i++, ++_n1##i, ptrd1+=2*(res)._width, ptrd2+=2*(res)._width, ptrd3+=2*(res)._width)
- #define _cimg_gs3x_for3x3(img,x,y,z,c,I,T) \
- _cimg_gs3x_for3((img)._height,y) for (int x = 0, \
- _p1##x = 0, \
- _n1##x = (int)( \
- (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
- (I[3] = I[4] = (T)(img)(0,y,z,c)), \
- (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)), \
- 1>=(img)._width?(img).width() - 1:1); \
- (_n1##x<(img).width() && ( \
- (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
- (I[5] = (T)(img)(_n1##x,y,z,c)), \
- (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
- x==--_n1##x; \
- I[0] = I[1], I[1] = I[2], \
- I[3] = I[4], I[4] = I[5], \
- I[6] = I[7], I[7] = I[8], \
- _p1##x = x++, ++_n1##x)
- if (is_empty()) return *this;
- CImg<T> res(3*_width,3*_height,_depth,_spectrum);
- CImg_3x3(I,T);
- cimg_forZC(*this,z,c) {
- T
- *ptrd1 = res.data(0,0,z,c),
- *ptrd2 = ptrd1 + res._width,
- *ptrd3 = ptrd2 + res._width;
- _cimg_gs3x_for3x3(*this,x,y,z,c,I,T) {
- if (Icp != Icn && Ipc != Inc) {
- *(ptrd1++) = Ipc==Icp?Ipc:Icc;
- *(ptrd1++) = (Ipc==Icp && Icc!=Inp) || (Icp==Inc && Icc!=Ipp)?Icp:Icc;
- *(ptrd1++) = Icp==Inc?Inc:Icc;
- *(ptrd2++) = (Ipc==Icp && Icc!=Ipn) || (Ipc==Icn && Icc!=Ipp)?Ipc:Icc;
- *(ptrd2++) = Icc;
- *(ptrd2++) = (Icp==Inc && Icc!=Inn) || (Icn==Inc && Icc!=Inp)?Inc:Icc;
- *(ptrd3++) = Ipc==Icn?Ipc:Icc;
- *(ptrd3++) = (Ipc==Icn && Icc!=Inn) || (Icn==Inc && Icc!=Ipn)?Icn:Icc;
- *(ptrd3++) = Icn==Inc?Inc:Icc;
- } else {
- *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd1++) = Icc;
- *(ptrd2++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc;
- *(ptrd3++) = Icc; *(ptrd3++) = Icc; *(ptrd3++) = Icc;
- }
- }
- }
- return res;
- }
- //! Mirror image content along specified axis.
- /**
- \param axis Mirror axis
- **/
- CImg<T>& mirror(const char axis) {
- if (is_empty()) return *this;
- T *pf, *pb, *buf = 0;
- switch (cimg::lowercase(axis)) {
- case 'x' : {
- pf = _data; pb = data(_width - 1);
- const unsigned int width2 = _width/2;
- for (unsigned int yzv = 0; yzv<_height*_depth*_spectrum; ++yzv) {
- for (unsigned int x = 0; x<width2; ++x) { const T val = *pf; *(pf++) = *pb; *(pb--) = val; }
- pf+=_width - width2;
- pb+=_width + width2;
- }
- } break;
- case 'y' : {
- buf = new T[_width];
- pf = _data; pb = data(0,_height - 1);
- const unsigned int height2 = _height/2;
- for (unsigned int zv = 0; zv<_depth*_spectrum; ++zv) {
- for (unsigned int y = 0; y<height2; ++y) {
- std::memcpy(buf,pf,_width*sizeof(T));
- std::memcpy(pf,pb,_width*sizeof(T));
- std::memcpy(pb,buf,_width*sizeof(T));
- pf+=_width;
- pb-=_width;
- }
- pf+=(ulongT)_width*(_height - height2);
- pb+=(ulongT)_width*(_height + height2);
- }
- } break;
- case 'z' : {
- buf = new T[(ulongT)_width*_height];
- pf = _data; pb = data(0,0,_depth - 1);
- const unsigned int depth2 = _depth/2;
- cimg_forC(*this,c) {
- for (unsigned int z = 0; z<depth2; ++z) {
- std::memcpy(buf,pf,_width*_height*sizeof(T));
- std::memcpy(pf,pb,_width*_height*sizeof(T));
- std::memcpy(pb,buf,_width*_height*sizeof(T));
- pf+=(ulongT)_width*_height;
- pb-=(ulongT)_width*_height;
- }
- pf+=(ulongT)_width*_height*(_depth - depth2);
- pb+=(ulongT)_width*_height*(_depth + depth2);
- }
- } break;
- case 'c' : {
- buf = new T[(ulongT)_width*_height*_depth];
- pf = _data; pb = data(0,0,0,_spectrum - 1);
- const unsigned int _spectrum2 = _spectrum/2;
- for (unsigned int v = 0; v<_spectrum2; ++v) {
- std::memcpy(buf,pf,_width*_height*_depth*sizeof(T));
- std::memcpy(pf,pb,_width*_height*_depth*sizeof(T));
- std::memcpy(pb,buf,_width*_height*_depth*sizeof(T));
- pf+=(ulongT)_width*_height*_depth;
- pb-=(ulongT)_width*_height*_depth;
- }
- } break;
- default :
- throw CImgArgumentException(_cimg_instance
- "mirror(): Invalid specified axis '%c'.",
- cimg_instance,
- axis);
- }
- delete[] buf;
- return *this;
- }
- //! Mirror image content along specified axis \newinstance.
- CImg<T> get_mirror(const char axis) const {
- return (+*this).mirror(axis);
- }
- //! Mirror image content along specified axes.
- /**
- \param axes Mirror axes, as a C-string.
- \note \c axes may contains multiple characters, e.g. \c "xyz"
- **/
- CImg<T>& mirror(const char *const axes) {
- for (const char *s = axes; *s; ++s) mirror(*s);
- return *this;
- }
- //! Mirror image content along specified axes \newinstance.
- CImg<T> get_mirror(const char *const axes) const {
- return (+*this).mirror(axes);
- }
- //! Shift image content.
- /**
- \param delta_x Amount of displacement along the X-axis.
- \param delta_y Amount of displacement along the Y-axis.
- \param delta_z Amount of displacement along the Z-axis.
- \param delta_c Amount of displacement along the C-axis.
- \param boundary_conditions Boundary conditions.
- Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
- **/
- CImg<T>& shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0,
- const unsigned int boundary_conditions=0) {
- if (is_empty()) return *this;
- if (boundary_conditions==3)
- return get_crop(-delta_x,-delta_y,-delta_z,-delta_c,
- width() - delta_x - 1,
- height() - delta_y - 1,
- depth() - delta_z - 1,
- spectrum() - delta_c - 1,3).move_to(*this);
- if (delta_x) // Shift along X-axis
- switch (boundary_conditions) {
- case 2 : { // Periodic
- const int ml = cimg::mod(-delta_x,width()), ndelta_x = (ml<=width()/2)?ml:(ml-width());
- if (!ndelta_x) return *this;
- CImg<T> buf(cimg::abs(ndelta_x));
- if (ndelta_x>0) cimg_forYZC(*this,y,z,c) {
- std::memcpy(buf,data(0,y,z,c),ndelta_x*sizeof(T));
- std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T));
- std::memcpy(data(_width-ndelta_x,y,z,c),buf,ndelta_x*sizeof(T));
- } else cimg_forYZC(*this,y,z,c) {
- std::memcpy(buf,data(_width + ndelta_x,y,z,c),-ndelta_x*sizeof(T));
- std::memmove(data(-ndelta_x,y,z,c),data(0,y,z,c),(_width + ndelta_x)*sizeof(T));
- std::memcpy(data(0,y,z,c),buf,-ndelta_x*sizeof(T));
- }
- } break;
- case 1 : // Neumann
- if (delta_x<0) {
- const int ndelta_x = (-delta_x>=width())?width() - 1:-delta_x;
- if (!ndelta_x) return *this;
- cimg_forYZC(*this,y,z,c) {
- std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T));
- T *ptrd = data(_width - 1,y,z,c);
- const T val = *ptrd;
- for (int l = 0; l<ndelta_x - 1; ++l) *(--ptrd) = val;
- }
- } else {
- const int ndelta_x = (delta_x>=width())?width() - 1:delta_x;
- if (!ndelta_x) return *this;
- cimg_forYZC(*this,y,z,c) {
- std::memmove(data(ndelta_x,y,z,c),data(0,y,z,c),(_width-ndelta_x)*sizeof(T));
- T *ptrd = data(0,y,z,c);
- const T val = *ptrd;
- for (int l = 0; l<ndelta_x - 1; ++l) *(++ptrd) = val;
- }
- }
- break;
- default : // Dirichlet
- if (delta_x<=-width() || delta_x>=width()) return fill((T)0);
- if (delta_x<0) cimg_forYZC(*this,y,z,c) {
- std::memmove(data(0,y,z,c),data(-delta_x,y,z,c),(_width + delta_x)*sizeof(T));
- std::memset(data(_width + delta_x,y,z,c),0,-delta_x*sizeof(T));
- } else cimg_forYZC(*this,y,z,c) {
- std::memmove(data(delta_x,y,z,c),data(0,y,z,c),(_width-delta_x)*sizeof(T));
- std::memset(data(0,y,z,c),0,delta_x*sizeof(T));
- }
- }
- if (delta_y) // Shift along Y-axis
- switch (boundary_conditions) {
- case 2 : { // Periodic
- const int ml = cimg::mod(-delta_y,height()), ndelta_y = (ml<=height()/2)?ml:(ml-height());
- if (!ndelta_y) return *this;
- CImg<T> buf(width(),cimg::abs(ndelta_y));
- if (ndelta_y>0) cimg_forZC(*this,z,c) {
- std::memcpy(buf,data(0,0,z,c),_width*ndelta_y*sizeof(T));
- std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T));
- std::memcpy(data(0,_height-ndelta_y,z,c),buf,_width*ndelta_y*sizeof(T));
- } else cimg_forZC(*this,z,c) {
- std::memcpy(buf,data(0,_height + ndelta_y,z,c),-ndelta_y*_width*sizeof(T));
- std::memmove(data(0,-ndelta_y,z,c),data(0,0,z,c),_width*(_height + ndelta_y)*sizeof(T));
- std::memcpy(data(0,0,z,c),buf,-ndelta_y*_width*sizeof(T));
- }
- } break;
- case 1 : // Neumann
- if (delta_y<0) {
- const int ndelta_y = (-delta_y>=height())?height() - 1:-delta_y;
- if (!ndelta_y) return *this;
- cimg_forZC(*this,z,c) {
- std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T));
- T *ptrd = data(0,_height-ndelta_y,z,c), *ptrs = data(0,_height - 1,z,c);
- for (int l = 0; l<ndelta_y - 1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
- }
- } else {
- const int ndelta_y = (delta_y>=height())?height() - 1:delta_y;
- if (!ndelta_y) return *this;
- cimg_forZC(*this,z,c) {
- std::memmove(data(0,ndelta_y,z,c),data(0,0,z,c),_width*(_height-ndelta_y)*sizeof(T));
- T *ptrd = data(0,1,z,c), *ptrs = data(0,0,z,c);
- for (int l = 0; l<ndelta_y - 1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
- }
- }
- break;
- default : // Dirichlet
- if (delta_y<=-height() || delta_y>=height()) return fill((T)0);
- if (delta_y<0) cimg_forZC(*this,z,c) {
- std::memmove(data(0,0,z,c),data(0,-delta_y,z,c),_width*(_height + delta_y)*sizeof(T));
- std::memset(data(0,_height + delta_y,z,c),0,-delta_y*_width*sizeof(T));
- } else cimg_forZC(*this,z,c) {
- std::memmove(data(0,delta_y,z,c),data(0,0,z,c),_width*(_height-delta_y)*sizeof(T));
- std::memset(data(0,0,z,c),0,delta_y*_width*sizeof(T));
- }
- }
- if (delta_z) // Shift along Z-axis
- switch (boundary_conditions) {
- case 2 : { // Periodic
- const int ml = cimg::mod(-delta_z,depth()), ndelta_z = (ml<=depth()/2)?ml:(ml-depth());
- if (!ndelta_z) return *this;
- CImg<T> buf(width(),height(),cimg::abs(ndelta_z));
- if (ndelta_z>0) cimg_forC(*this,c) {
- std::memcpy(buf,data(0,0,0,c),_width*_height*ndelta_z*sizeof(T));
- std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
- std::memcpy(data(0,0,_depth-ndelta_z,c),buf,_width*_height*ndelta_z*sizeof(T));
- } else cimg_forC(*this,c) {
- std::memcpy(buf,data(0,0,_depth + ndelta_z,c),-ndelta_z*_width*_height*sizeof(T));
- std::memmove(data(0,0,-ndelta_z,c),data(0,0,0,c),_width*_height*(_depth + ndelta_z)*sizeof(T));
- std::memcpy(data(0,0,0,c),buf,-ndelta_z*_width*_height*sizeof(T));
- }
- } break;
- case 1 : // Neumann
- if (delta_z<0) {
- const int ndelta_z = (-delta_z>=depth())?depth() - 1:-delta_z;
- if (!ndelta_z) return *this;
- cimg_forC(*this,c) {
- std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
- T *ptrd = data(0,0,_depth-ndelta_z,c), *ptrs = data(0,0,_depth - 1,c);
- for (int l = 0; l<ndelta_z - 1; ++l) {
- std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=(ulongT)_width*_height;
- }
- }
- } else {
- const int ndelta_z = (delta_z>=depth())?depth() - 1:delta_z;
- if (!ndelta_z) return *this;
- cimg_forC(*this,c) {
- std::memmove(data(0,0,ndelta_z,c),data(0,0,0,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
- T *ptrd = data(0,0,1,c), *ptrs = data(0,0,0,c);
- for (int l = 0; l<ndelta_z - 1; ++l) {
- std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=(ulongT)_width*_height;
- }
- }
- }
- break;
- default : // Dirichlet
- if (delta_z<=-depth() || delta_z>=depth()) return fill((T)0);
- if (delta_z<0) cimg_forC(*this,c) {
- std::memmove(data(0,0,0,c),data(0,0,-delta_z,c),_width*_height*(_depth + delta_z)*sizeof(T));
- std::memset(data(0,0,_depth + delta_z,c),0,_width*_height*(-delta_z)*sizeof(T));
- } else cimg_forC(*this,c) {
- std::memmove(data(0,0,delta_z,c),data(0,0,0,c),_width*_height*(_depth-delta_z)*sizeof(T));
- std::memset(data(0,0,0,c),0,delta_z*_width*_height*sizeof(T));
- }
- }
- if (delta_c) // Shift along C-axis
- switch (boundary_conditions) {
- case 2 : { // Periodic
- const int ml = cimg::mod(-delta_c,spectrum()), ndelta_c = (ml<=spectrum()/2)?ml:(ml-spectrum());
- if (!ndelta_c) return *this;
- CImg<T> buf(width(),height(),depth(),cimg::abs(ndelta_c));
- if (ndelta_c>0) {
- std::memcpy(buf,_data,_width*_height*_depth*ndelta_c*sizeof(T));
- std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
- std::memcpy(data(0,0,0,_spectrum-ndelta_c),buf,_width*_height*_depth*ndelta_c*sizeof(T));
- } else {
- std::memcpy(buf,data(0,0,0,_spectrum + ndelta_c),-ndelta_c*_width*_height*_depth*sizeof(T));
- std::memmove(data(0,0,0,-ndelta_c),_data,_width*_height*_depth*(_spectrum + ndelta_c)*sizeof(T));
- std::memcpy(_data,buf,-ndelta_c*_width*_height*_depth*sizeof(T));
- }
- } break;
- case 1 : // Neumann
- if (delta_c<0) {
- const int ndelta_c = (-delta_c>=spectrum())?spectrum() - 1:-delta_c;
- if (!ndelta_c) return *this;
- std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
- T *ptrd = data(0,0,0,_spectrum-ndelta_c), *ptrs = data(0,0,0,_spectrum - 1);
- for (int l = 0; l<ndelta_c - 1; ++l) {
- std::memcpy(ptrd,ptrs,_width*_height*_depth*sizeof(T)); ptrd+=(ulongT)_width*_height*_depth;
- }
- } else {
- const int ndelta_c = (delta_c>=spectrum())?spectrum() - 1:delta_c;
- if (!ndelta_c) return *this;
- std::memmove(data(0,0,0,ndelta_c),_data,_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
- T *ptrd = data(0,0,0,1);
- for (int l = 0; l<ndelta_c - 1; ++l) {
- std::memcpy(ptrd,_data,_width*_height*_depth*sizeof(T)); ptrd+=(ulongT)_width*_height*_depth;
- }
- }
- break;
- default : // Dirichlet
- if (delta_c<=-spectrum() || delta_c>=spectrum()) return fill((T)0);
- if (delta_c<0) {
- std::memmove(_data,data(0,0,0,-delta_c),_width*_height*_depth*(_spectrum + delta_c)*sizeof(T));
- std::memset(data(0,0,0,_spectrum + delta_c),0,_width*_height*_depth*(-delta_c)*sizeof(T));
- } else {
- std::memmove(data(0,0,0,delta_c),_data,_width*_height*_depth*(_spectrum-delta_c)*sizeof(T));
- std::memset(_data,0,delta_c*_width*_height*_depth*sizeof(T));
- }
- }
- return *this;
- }
- //! Shift image content \newinstance.
- CImg<T> get_shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0,
- const unsigned int boundary_conditions=0) const {
- return (+*this).shift(delta_x,delta_y,delta_z,delta_c,boundary_conditions);
- }
- //! Permute axes order.
- /**
- \param axes_order Axes permutations, as a C-string of 4 characters.
- This function permutes image content regarding the specified axes permutation.
- **/
- CImg<T>& permute_axes(const char *const axes_order) {
- if (is_empty() || !axes_order) return *this;
- const unsigned uicase = _permute_axes_uicase(axes_order);
- if (_permute_axes_is_optim(uicase)) { // Data layout allow to do nothing but set the new dimensions
- CImg<T> res(*this,true);
- for (unsigned int i = 0; i<4; ++i) {
- const unsigned int
- axis = (uicase>>(4*(3 - i)))&15,
- value = !axis?_width:axis==1?_height:axis==2?_depth:_spectrum;
- if (!i) res._width = value; else if (i==1) res._height = value;
- else if (i==2) res._depth = value; else res._spectrum = value;
- }
- _width = res._width; _height = res._height; _depth = res._depth; _spectrum = res._spectrum;
- return *this;
- }
- return get_permute_axes(axes_order).move_to(*this);
- }
- //! Permute axes order \newinstance.
- CImg<T> get_permute_axes(const char *const axes_order) const {
- const T foo = (T)0;
- return _permute_axes(axes_order,foo);
- }
- unsigned int _permute_axes_uicase(const char *const axes_order) const { // Convert axes to integer case number
- unsigned char s_axes[4] = { 0,1,2,3 }, n_axes[4] = { };
- bool is_error = false;
- if (axes_order) for (unsigned int l = 0; axes_order[l]; ++l) {
- int c = cimg::lowercase(axes_order[l]);
- if (l>=4 || (c!='x' && c!='y' && c!='z' && c!='c')) { is_error = true; break; }
- else { ++n_axes[c%=4]; s_axes[l] = (unsigned char)c; }
- }
- is_error|=(*n_axes>1) || (n_axes[1]>1) || (n_axes[2]>1) || (n_axes[3]>1);
- if (is_error)
- throw CImgArgumentException(_cimg_instance
- "permute_axes(): Invalid specified axes order '%s'.",
- cimg_instance,
- axes_order);
- return (s_axes[0]<<12) | (s_axes[1]<<8) | (s_axes[2]<<4) | (s_axes[3]);
- }
- bool _permute_axes_is_optim(const unsigned int uicase) const { // Determine cases where nothing has to be done
- const unsigned int co = ((_width>1)<<3)|((_height>1)<<2)|((_depth>1)<<1)|(_spectrum>1);
- if (co<=2 || uicase==0x0123) return true;
- switch (uicase) {
- case (0x0132) : if ((co>=4 && co<=6) || (co>=8 && co<=10) || (co>=12 && co<=14)) return true; break;
- case (0x0213) : if ((co>=3 && co<=5) || (co>=8 && co<=13)) return true; break;
- case (0x0231) : if (co==3 || co==4 || (co>=8 && co<=12)) return true; break;
- case (0x0312) : if (co==4 || co==6 || co==8 || co==9 || co==10 || co==12 || co==14) return true; break;
- case (0x0321) : if (co==4 || (co>=8 && co<=10) || co==12) return true; break;
- case (0x1023) : if (co>=3 && co<=11) return true; break;
- case (0x1032) : if ((co>=4 && co<=6) || (co>=8 && co<=10)) return true; break;
- case (0x1203) : if (co>=3 && co<=9) return true; break;
- case (0x1230) : if (co>=3 && co<=8) return true; break;
- case (0x1302) : if ((co>=4 && co<=6) || co==8 || co==10) return true; break;
- case (0x1320) : if ((co>=4 && co<=6) || co==8) return true; break;
- case (0x2013) : if ((co>=3 && co<=5) || co==8 || co==9 || co==12 || co==13) return true; break;
- case (0x2031) : if (co==3 || co==4 || co==8 || co==9 || co==12) return true; break;
- case (0x2103) : if ((co>=3 && co<=5) || co==8 || co==9) return true; break;
- case (0x2130) : if ((co>=3 && co<=5) || co==8) return true; break;
- case (0x2301) : if (co==3 || co==4 || co==8 || co==12) return true; break;
- case (0x2310) : if (co==3 || co==4 || co==8) return true; break;
- case (0x3012) : if (co==4 || co==6 || co==8 || co==10 || co==12 || co==14) return true; break;
- case (0x3021) : if (co==4 || co==8 || co==10 || co==12) return true; break;
- case (0x3102) : if (co==4 || co==6 || co==8 || co==10) return true; break;
- case (0x3120) : if (co==4 || co==6 || co==8) return true; break;
- case (0x3201) : if (co==4 || co==8 || co==12) return true; break;
- case (0x3210) : if (co==4 || co==8) return true; break;
- }
- return false;
- }
- template<typename t>
- CImg<t> _permute_axes(const char *const axes_order, const t&) const {
- if (is_empty() || !axes_order) return CImg<t>(*this,false);
- CImg<t> res;
- const unsigned uicase = _permute_axes_uicase(axes_order);
- if (_permute_axes_is_optim(uicase)) { // Data layout allow to do nothing but set the new dimensions
- res.assign(*this,false);
- for (unsigned int i = 0; i<4; ++i) {
- const unsigned int
- axis = (uicase>>(4*(3 - i)))&15,
- value = !axis?_width:axis==1?_height:axis==2?_depth:_spectrum;
- if (!i) res._width = value; else if (i==1) res._height = value;
- else if (i==2) res._depth = value; else res._spectrum = value;
- }
- return res;
- }
- const T* ptrs = _data;
- ulongT wh, whd;
- switch (uicase) {
- case 0x0123 : // xyzc
- return +*this;
- case 0x0132 : // xycz
- res.assign(_width,_height,_spectrum,_depth);
- wh = (ulongT)res._width*res._height; whd = wh*res._depth;
- cimg_forXYZC(*this,x,y,z,c) res(x,y,c,z,wh,whd) = (t)*(ptrs++);
- break;
- case 0x0213 : // xzyc
- res.assign(_width,_depth,_height,_spectrum);
- wh = (ulongT)res._width*res._height; whd = wh*res._depth;
- cimg_forXYZC(*this,x,y,z,c) res(x,z,y,c,wh,whd) = (t)*(ptrs++);
- break;
- case 0x0231 : // xzcy
- res.assign(_width,_depth,_spectrum,_height);
- wh = (ulongT)res._width*res._height; whd = wh*res._depth;
- cimg_forXYZC(*this,x,y,z,c) res(x,z,c,y,wh,whd) = (t)*(ptrs++);
- break;
- case 0x0312 : // xcyz
- res.assign(_width,_spectrum,_height,_depth);
- wh = (ulongT)res._width*res._height; whd = wh*res._depth;
- cimg_forXYZC(*this,x,y,z,c) res(x,c,y,z,wh,whd) = (t)*(ptrs++);
- break;
- case 0x0321 : // xczy
- res.assign(_width,_spectrum,_depth,_height);
- wh = (ulongT)res._width*res._height; whd = wh*res._depth;
- cimg_forXYZC(*this,x,y,z,c) res(x,c,z,y,wh,whd) = (t)*(ptrs++);
- break;
- case 0x1023 : // yxzc
- res.assign(_height,_width,_depth,_spectrum);
- wh = (ulongT)res._width*res._height; whd = wh*res._depth;
- cimg_forXYZC(*this,x,y,z,c) res(y,x,z,c,wh,whd) = (t)*(ptrs++);
- break;
- case 0x1032 : // yxcz
- res.assign(_height,_width,_spectrum,_depth);
- wh = (ulongT)res._width*res._height; whd = wh*res._depth;
- cimg_forXYZC(*this,x,y,z,c) res(y,x,c,z,wh,whd) = (t)*(ptrs++);
- break;
- case 0x1203 : // yzxc
- res.assign(_height,_depth,_width,_spectrum);
- wh = (ulongT)res._width*res._height; whd = wh*res._depth;
- cimg_forXYZC(*this,x,y,z,c) res(y,z,x,c,wh,whd) = (t)*(ptrs++);
- break;
- case 0x1230 : // yzcx
- res.assign(_height,_depth,_spectrum,_width);
- switch (_width) {
- case 1 : {
- t *ptr_r = res.data(0,0,0,0);
- for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
- *(ptr_r++) = (t)*(ptrs++);
- }
- } break;
- case 2 : {
- t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1);
- for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
- *(ptr_r++) = (t)ptrs[0];
- *(ptr_g++) = (t)ptrs[1];
- ptrs+=2;
- }
- } break;
- case 3 : { // Optimization for the classical conversion from interleaved RGB to planar RGB
- t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1), *ptr_b = res.data(0,0,0,2);
- for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
- *(ptr_r++) = (t)ptrs[0];
- *(ptr_g++) = (t)ptrs[1];
- *(ptr_b++) = (t)ptrs[2];
- ptrs+=3;
- }
- } break;
- case 4 : { // Optimization for the classical conversion from interleaved RGBA to planar RGBA
- t
- *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1),
- *ptr_b = res.data(0,0,0,2), *ptr_a = res.data(0,0,0,3);
- for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
- *(ptr_r++) = (t)ptrs[0];
- *(ptr_g++) = (t)ptrs[1];
- *(ptr_b++) = (t)ptrs[2];
- *(ptr_a++) = (t)ptrs[3];
- ptrs+=4;
- }
- } break;
- default : {
- wh = (ulongT)res._width*res._height; whd = wh*res._depth;
- cimg_forXYZC(*this,x,y,z,c) res(y,z,c,x,wh,whd) = *(ptrs++);
- return res;
- }
- }
- break;
- case 0x1302 : // ycxz
- res.assign(_height,_spectrum,_width,_depth);
- wh = (ulongT)res._width*res._height; whd = wh*res._depth;
- cimg_forXYZC(*this,x,y,z,c) res(y,c,x,z,wh,whd) = (t)*(ptrs++);
- break;
- case 0x1320 : // yczx
- res.assign(_height,_spectrum,_depth,_width);
- wh = (ulongT)res._width*res._height; whd = wh*res._depth;
- cimg_forXYZC(*this,x,y,z,c) res(y,c,z,x,wh,whd) = (t)*(ptrs++);
- break;
- case 0x2013 : // zxyc
- res.assign(_depth,_width,_height,_spectrum);
- wh = (ulongT)res._width*res._height; whd = wh*res._depth;
- cimg_forXYZC(*this,x,y,z,c) res(z,x,y,c,wh,whd) = (t)*(ptrs++);
- break;
- case 0x2031 : // zxcy
- res.assign(_depth,_width,_spectrum,_height);
- wh = (ulongT)res._width*res._height; whd = wh*res._depth;
- cimg_forXYZC(*this,x,y,z,c) res(z,x,c,y,wh,whd) = (t)*(ptrs++);
- break;
- case 0x2103 : // zyxc
- res.assign(_depth,_height,_width,_spectrum);
- wh = (ulongT)res._width*res._height; whd = wh*res._depth;
- cimg_forXYZC(*this,x,y,z,c) res(z,y,x,c,wh,whd) = (t)*(ptrs++);
- break;
- case 0x2130 : // zycx
- res.assign(_depth,_height,_spectrum,_width);
- wh = (ulongT)res._width*res._height; whd = wh*res._depth;
- cimg_forXYZC(*this,x,y,z,c) res(z,y,c,x,wh,whd) = (t)*(ptrs++);
- break;
- case 0x2301 : // zcxy
- res.assign(_depth,_spectrum,_width,_height);
- wh = (ulongT)res._width*res._height; whd = wh*res._depth;
- cimg_forXYZC(*this,x,y,z,c) res(z,c,x,y,wh,whd) = (t)*(ptrs++);
- break;
- case 0x2310 : // zcyx
- res.assign(_depth,_spectrum,_height,_width);
- wh = (ulongT)res._width*res._height; whd = wh*res._depth;
- cimg_forXYZC(*this,x,y,z,c) res(z,c,y,x,wh,whd) = (t)*(ptrs++);
- break;
- case 0x3012 : // cxyz
- res.assign(_spectrum,_width,_height,_depth);
- switch (_spectrum) {
- case 1 : {
- const T *ptr_r = data(0,0,0,0);
- t *ptrd = res._data;
- for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) *(ptrd++) = (t)*(ptr_r++);
- } break;
- case 2 : {
- const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1);
- t *ptrd = res._data;
- for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) {
- ptrd[0] = (t)*(ptr_r++);
- ptrd[1] = (t)*(ptr_g++);
- ptrd+=2;
- }
- } break;
- case 3 : { // Optimization for the classical conversion from planar RGB to interleaved RGB
- const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
- t *ptrd = res._data;
- for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) {
- ptrd[0] = (t)*(ptr_r++);
- ptrd[1] = (t)*(ptr_g++);
- ptrd[2] = (t)*(ptr_b++);
- ptrd+=3;
- }
- } break;
- case 4 : { // Optimization for the classical conversion from planar RGBA to interleaved RGBA
- const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3);
- t *ptrd = res._data;
- for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) {
- ptrd[0] = (t)*(ptr_r++);
- ptrd[1] = (t)*(ptr_g++);
- ptrd[2] = (t)*(ptr_b++);
- ptrd[3] = (t)*(ptr_a++);
- ptrd+=4;
- }
- } break;
- default : {
- wh = (ulongT)res._width*res._height; whd = wh*res._depth;
- cimg_forXYZC(*this,x,y,z,c) res(c,x,y,z,wh,whd) = (t)*(ptrs++);
- }
- }
- break;
- case 0x3021 : // cxzy
- res.assign(_spectrum,_width,_depth,_height);
- wh = (ulongT)res._width*res._height; whd = wh*res._depth;
- cimg_forXYZC(*this,x,y,z,c) res(c,x,z,y,wh,whd) = (t)*(ptrs++);
- break;
- case 0x3102 : // cyxz
- res.assign(_spectrum,_height,_width,_depth);
- wh = (ulongT)res._width*res._height; whd = wh*res._depth;
- cimg_forXYZC(*this,x,y,z,c) res(c,y,x,z,wh,whd) = (t)*(ptrs++);
- break;
- case 0x3120 : // cyzx
- res.assign(_spectrum,_height,_depth,_width);
- wh = (ulongT)res._width*res._height; whd = wh*res._depth;
- cimg_forXYZC(*this,x,y,z,c) res(c,y,z,x,wh,whd) = (t)*(ptrs++);
- break;
- case 0x3201 : // czxy
- res.assign(_spectrum,_depth,_width,_height);
- wh = (ulongT)res._width*res._height; whd = wh*res._depth;
- cimg_forXYZC(*this,x,y,z,c) res(c,z,x,y,wh,whd) = (t)*(ptrs++);
- break;
- case 0x3210 : // czyx
- res.assign(_spectrum,_depth,_height,_width);
- wh = (ulongT)res._width*res._height; whd = wh*res._depth;
- cimg_forXYZC(*this,x,y,z,c) res(c,z,y,x,wh,whd) = (t)*(ptrs++);
- break;
- }
- return res;
- }
- //! Unroll pixel values along specified axis.
- /**
- \param axis Unroll axis (can be \c 'x', \c 'y', \c 'z' or c 'c').
- **/
- CImg<T>& unroll(const char axis) {
- const unsigned int siz = (unsigned int)size();
- if (siz) switch (cimg::lowercase(axis)) {
- case 'x' : _width = siz; _height = _depth = _spectrum = 1; break;
- case 'y' : _height = siz; _width = _depth = _spectrum = 1; break;
- case 'z' : _depth = siz; _width = _height = _spectrum = 1; break;
- case 'c' : _spectrum = siz; _width = _height = _depth = 1; break;
- }
- return *this;
- }
- //! Unroll pixel values along specified axis \newinstance.
- CImg<T> get_unroll(const char axis) const {
- return (+*this).unroll(axis);
- }
- //! Rotate image with arbitrary angle.
- /**
- \param angle Rotation angle, in degrees.
- \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
- \param boundary_conditions Boundary conditions.
- Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
- \note The size of the image is modified.
- **/
- CImg<T>& rotate(const float angle, const unsigned int interpolation=1,
- const unsigned int boundary_conditions=0) {
- const float nangle = cimg::mod(angle,360.f);
- if (nangle==0.f) return *this;
- return get_rotate(nangle,interpolation,boundary_conditions).move_to(*this);
- }
- //! Rotate image with arbitrary angle \newinstance.
- CImg<T> get_rotate(const float angle, const unsigned int interpolation=1,
- const unsigned int boundary_conditions=0) const {
- if (is_empty()) return *this;
- CImg<T> res;
- const float nangle = cimg::mod(angle,360.f);
- if (boundary_conditions!=1 && cimg::mod(nangle,90.f)==0) { // Optimized version for orthogonal angles
- const int wm1 = width() - 1, hm1 = height() - 1;
- const int iangle = (int)nangle/90;
- switch (iangle) {
- case 1 : { // 90 deg
- res.assign(_height,_width,_depth,_spectrum);
- T *ptrd = res._data;
- cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(y,hm1 - x,z,c);
- } break;
- case 2 : { // 180 deg
- res.assign(_width,_height,_depth,_spectrum);
- T *ptrd = res._data;
- cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - x,hm1 - y,z,c);
- } break;
- case 3 : { // 270 deg
- res.assign(_height,_width,_depth,_spectrum);
- T *ptrd = res._data;
- cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - y,x,z,c);
- } break;
- default : // 0 deg
- return *this;
- }
- } else { // Generic angle
- const float
- rad = (float)(nangle*cimg::PI/180.),
- ca = (float)std::cos(rad), sa = (float)std::sin(rad),
- ux = cimg::abs((_width - 1)*ca), uy = cimg::abs((_width - 1)*sa),
- vx = cimg::abs((_height - 1)*sa), vy = cimg::abs((_height - 1)*ca),
- w2 = 0.5f*(_width - 1), h2 = 0.5f*(_height - 1);
- res.assign((int)cimg::round(1 + ux + vx),(int)cimg::round(1 + uy + vy),_depth,_spectrum);
- const float rw2 = 0.5f*(res._width - 1), rh2 = 0.5f*(res._height - 1);
- _rotate(res,nangle,interpolation,boundary_conditions,w2,h2,rw2,rh2);
- }
- return res;
- }
- //! Rotate image with arbitrary angle, around a center point.
- /**
- \param angle Rotation angle, in degrees.
- \param cx X-coordinate of the rotation center.
- \param cy Y-coordinate of the rotation center.
- \param interpolation Type of interpolation, <tt>{ 0=nearest | 1=linear | 2=cubic | 3=mirror }</tt>.
- \param boundary_conditions Boundary conditions, <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
- **/
- CImg<T>& rotate(const float angle, const float cx, const float cy,
- const unsigned int interpolation, const unsigned int boundary_conditions=0) {
- return get_rotate(angle,cx,cy,interpolation,boundary_conditions).move_to(*this);
- }
- //! Rotate image with arbitrary angle, around a center point \newinstance.
- CImg<T> get_rotate(const float angle, const float cx, const float cy,
- const unsigned int interpolation, const unsigned int boundary_conditions=0) const {
- if (is_empty()) return *this;
- CImg<T> res(_width,_height,_depth,_spectrum);
- _rotate(res,angle,interpolation,boundary_conditions,cx,cy,cx,cy);
- return res;
- }
- // [internal] Perform 2D rotation with arbitrary angle.
- void _rotate(CImg<T>& res, const float angle,
- const unsigned int interpolation, const unsigned int boundary_conditions,
- const float w2, const float h2,
- const float rw2, const float rh2) const {
- const float
- rad = (float)(angle*cimg::PI/180.),
- ca = (float)std::cos(rad), sa = (float)std::sin(rad);
- switch (boundary_conditions) {
- case 3 : { // Mirror
- switch (interpolation) {
- case 2 : { // Cubic interpolation
- const float ww = 2.f*width(), hh = 2.f*height();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
- cimg_forXYZC(res,x,y,z,c) {
- const float xc = x - rw2, yc = y - rh2,
- mx = cimg::mod(w2 + xc*ca + yc*sa,ww),
- my = cimg::mod(h2 - xc*sa + yc*ca,hh);
- res(x,y,z,c) = _cubic_atXY_c(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c);
- }
- } break;
- case 1 : { // Linear interpolation
- const float ww = 2.f*width(), hh = 2.f*height();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
- cimg_forXYZC(res,x,y,z,c) {
- const float xc = x - rw2, yc = y - rh2,
- mx = cimg::mod(w2 + xc*ca + yc*sa,ww),
- my = cimg::mod(h2 - xc*sa + yc*ca,hh);
- res(x,y,z,c) = (T)_linear_atXY(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c);
- }
- } break;
- default : { // Nearest-neighbor interpolation
- const int ww = 2*width(), hh = 2*height();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
- cimg_forXYZC(res,x,y,z,c) {
- const float xc = x - rw2, yc = y - rh2,
- mx = cimg::mod((int)cimg::round(w2 + xc*ca + yc*sa),ww),
- my = cimg::mod((int)cimg::round(h2 - xc*sa + yc*ca),hh);
- res(x,y,z,c) = (*this)(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c);
- }
- }
- }
- } break;
- case 2 : // Periodic
- switch (interpolation) {
- case 2 : { // Cubic interpolation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
- cimg_forXYZC(res,x,y,z,c) {
- const float xc = x - rw2, yc = y - rh2;
- res(x,y,z,c) = _cubic_atXY_pc(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
- }
- } break;
- case 1 : { // Linear interpolation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
- cimg_forXYZC(res,x,y,z,c) {
- const float xc = x - rw2, yc = y - rh2;
- res(x,y,z,c) = (T)_linear_atXY_p(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
- }
- } break;
- default : { // Nearest-neighbor interpolation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
- cimg_forXYZC(res,x,y,z,c) {
- const float xc = x - rw2, yc = y - rh2;
- res(x,y,z,c) = (*this)(cimg::mod((int)cimg::round(w2 + xc*ca + yc*sa),(float)width()),
- cimg::mod((int)cimg::round(h2 - xc*sa + yc*ca),(float)height()),z,c);
- }
- }
- } break;
- case 1 : // Neumann
- switch (interpolation) {
- case 2 : { // Cubic interpolation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
- cimg_forXYZC(res,x,y,z,c) {
- const float xc = x - rw2, yc = y - rh2;
- res(x,y,z,c) = _cubic_atXY_c(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
- }
- } break;
- case 1 : { // Linear interpolation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
- cimg_forXYZC(res,x,y,z,c) {
- const float xc = x - rw2, yc = y - rh2;
- res(x,y,z,c) = (T)_linear_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
- }
- } break;
- default : { // Nearest-neighbor interpolation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
- cimg_forXYZC(res,x,y,z,c) {
- const float xc = x - rw2, yc = y - rh2;
- res(x,y,z,c) = _atXY((int)cimg::round(w2 + xc*ca + yc*sa),
- (int)cimg::round(h2 - xc*sa + yc*ca),z,c);
- }
- }
- } break;
- default : // Dirichlet
- switch (interpolation) {
- case 2 : { // Cubic interpolation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
- cimg_forXYZC(res,x,y,z,c) {
- const float xc = x - rw2, yc = y - rh2;
- res(x,y,z,c) = cubic_atXY_c(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c,(T)0);
- }
- } break;
- case 1 : { // Linear interpolation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
- cimg_forXYZC(res,x,y,z,c) {
- const float xc = x - rw2, yc = y - rh2;
- res(x,y,z,c) = (T)linear_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c,(T)0);
- }
- } break;
- default : { // Nearest-neighbor interpolation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
- cimg_forXYZC(res,x,y,z,c) {
- const float xc = x - rw2, yc = y - rh2;
- res(x,y,z,c) = atXY((int)cimg::round(w2 + xc*ca + yc*sa),
- (int)cimg::round(h2 - xc*sa + yc*ca),z,c,(T)0);
- }
- }
- }
- }
- }
- //! Rotate volumetric image with arbitrary angle and axis.
- /**
- \param u X-coordinate of the 3D rotation axis.
- \param v Y-coordinate of the 3D rotation axis.
- \param w Z-coordinate of the 3D rotation axis.
- \param angle Rotation angle, in degrees.
- \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
- \param boundary_conditions Boundary conditions.
- Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
- \note Most of the time, size of the image is modified.
- **/
- CImg<T> rotate(const float u, const float v, const float w, const float angle,
- const unsigned int interpolation, const unsigned int boundary_conditions) {
- const float nangle = cimg::mod(angle,360.f);
- if (nangle==0.f) return *this;
- return get_rotate(u,v,w,nangle,interpolation,boundary_conditions).move_to(*this);
- }
- //! Rotate volumetric image with arbitrary angle and axis \newinstance.
- CImg<T> get_rotate(const float u, const float v, const float w, const float angle,
- const unsigned int interpolation, const unsigned int boundary_conditions) const {
- if (is_empty()) return *this;
- CImg<T> res;
- const float
- w1 = _width - 1, h1 = _height - 1, d1 = _depth -1,
- w2 = 0.5f*w1, h2 = 0.5f*h1, d2 = 0.5f*d1;
- CImg<floatT> R = CImg<floatT>::rotation_matrix(u,v,w,angle);
- const CImg<Tfloat>
- X = R*CImg<Tfloat>(8,3,1,1,
- 0.f,w1,w1,0.f,0.f,w1,w1,0.f,
- 0.f,0.f,h1,h1,0.f,0.f,h1,h1,
- 0.f,0.f,0.f,0.f,d1,d1,d1,d1);
- float
- xm, xM = X.get_shared_row(0).max_min(xm),
- ym, yM = X.get_shared_row(1).max_min(ym),
- zm, zM = X.get_shared_row(2).max_min(zm);
- const int
- dx = (int)cimg::round(xM - xm),
- dy = (int)cimg::round(yM - ym),
- dz = (int)cimg::round(zM - zm);
- R.transpose();
- res.assign(1 + dx,1 + dy,1 + dz,_spectrum);
- const float rw2 = 0.5f*dx, rh2 = 0.5f*dy, rd2 = 0.5f*dz;
- _rotate(res,R,interpolation,boundary_conditions,w2,h2,d2,rw2,rh2,rd2);
- return res;
- }
- //! Rotate volumetric image with arbitrary angle and axis, around a center point.
- /**
- \param u X-coordinate of the 3D rotation axis.
- \param v Y-coordinate of the 3D rotation axis.
- \param w Z-coordinate of the 3D rotation axis.
- \param angle Rotation angle, in degrees.
- \param cx X-coordinate of the rotation center.
- \param cy Y-coordinate of the rotation center.
- \param cz Z-coordinate of the rotation center.
- \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic | 3=mirror }</tt>.
- \param boundary_conditions Boundary conditions.
- Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic }</tt>.
- \note Most of the time, size of the image is modified.
- **/
- CImg<T> rotate(const float u, const float v, const float w, const float angle,
- const float cx, const float cy, const float cz,
- const unsigned int interpolation=1, const unsigned int boundary_conditions=0) {
- const float nangle = cimg::mod(angle,360.f);
- if (nangle==0.f) return *this;
- return get_rotate(u,v,w,nangle,cx,cy,cz,interpolation,boundary_conditions).move_to(*this);
- }
- //! Rotate volumetric image with arbitrary angle and axis, around a center point \newinstance.
- CImg<T> get_rotate(const float u, const float v, const float w, const float angle,
- const float cx, const float cy, const float cz,
- const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const {
- if (is_empty()) return *this;
- CImg<T> res(_width,_height,_depth,_spectrum);
- CImg<floatT> R = CImg<floatT>::rotation_matrix(u,v,w,-angle);
- _rotate(res,R,interpolation,boundary_conditions,cx,cy,cz,cx,cy,cz);
- return res;
- }
- // [internal] Perform 3D rotation with arbitrary axis and angle.
- void _rotate(CImg<T>& res, const CImg<Tfloat>& R,
- const unsigned int interpolation, const unsigned int boundary_conditions,
- const float w2, const float h2, const float d2,
- const float rw2, const float rh2, const float rd2) const {
- switch (boundary_conditions) {
- case 3 : // Mirror
- switch (interpolation) {
- case 2 : { // Cubic interpolation
- const float ww = 2.f*width(), hh = 2.f*height(), dd = 2.f*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
- cimg_forXYZ(res,x,y,z) {
- const float
- xc = x - rw2, yc = y - rh2, zc = z - rd2,
- X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww),
- Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh),
- Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd);
- cimg_forC(res,c) res(x,y,z,c) = _cubic_atXYZ_c(X<width()?X:ww - X - 1,
- Y<height()?Y:hh - Y - 1,
- Z<depth()?Z:dd - Z - z,c);
- }
- } break;
- case 1 : { // Linear interpolation
- const float ww = 2.f*width(), hh = 2.f*height(), dd = 2.f*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
- cimg_forXYZ(res,x,y,z) {
- const float
- xc = x - rw2, yc = y - rh2, zc = z - rd2,
- X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww),
- Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh),
- Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd);
- cimg_forC(res,c) res(x,y,z,c) = (T)_linear_atXYZ(X<width()?X:ww - X - 1,
- Y<height()?Y:hh - Y - 1,
- Z<depth()?Z:dd - Z - 1,c);
- }
- } break;
- default : { // Nearest-neighbor interpolation
- const int ww = 2*width(), hh = 2*height(), dd = 2*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
- cimg_forXYZ(res,x,y,z) {
- const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
- const int
- X = cimg::mod((int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww),
- Y = cimg::mod((int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh),
- Z = cimg::mod((int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd);
- cimg_forC(res,c) res(x,y,z,c) = (*this)(X<width()?X:ww - X - 1,
- Y<height()?Y:hh - Y - 1,
- Z<depth()?Z:dd - Z - 1,c);
- }
- }
- } break;
- case 2 : // Periodic
- switch (interpolation) {
- case 2 : { // Cubic interpolation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
- cimg_forXYZ(res,x,y,z) {
- const float
- xc = x - rw2, yc = y - rh2, zc = z - rd2,
- X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
- Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
- Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
- cimg_forC(res,c) res(x,y,z,c) = _cubic_atXYZ_pc(X,Y,Z,c);
- }
- } break;
- case 1 : { // Linear interpolation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
- cimg_forXYZ(res,x,y,z) {
- const float
- xc = x - rw2, yc = y - rh2, zc = z - rd2,
- X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
- Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
- Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
- cimg_forC(res,c) res(x,y,z,c) = (T)_linear_atXYZ_p(X,Y,Z,c);
- }
- } break;
- default : { // Nearest-neighbor interpolation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
- cimg_forXYZ(res,x,y,z) {
- const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
- const int
- X = cimg::mod((int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),width()),
- Y = cimg::mod((int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),height()),
- Z = cimg::mod((int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),depth());
- cimg_forC(res,c) res(x,y,z,c) = (*this)(X,Y,Z,c);
- }
- }
- } break;
- case 1 : // Neumann
- switch (interpolation) {
- case 2 : { // Cubic interpolation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
- cimg_forXYZ(res,x,y,z) {
- const float
- xc = x - rw2, yc = y - rh2, zc = z - rd2,
- X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
- Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
- Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
- cimg_forC(res,c) res(x,y,z,c) = _cubic_atXYZ_c(X,Y,Z,c);
- }
- } break;
- case 1 : { // Linear interpolation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
- cimg_forXYZ(res,x,y,z) {
- const float
- xc = x - rw2, yc = y - rh2, zc = z - rd2,
- X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
- Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
- Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
- cimg_forC(res,c) res(x,y,z,c) = _linear_atXYZ(X,Y,Z,c);
- }
- } break;
- default : { // Nearest-neighbor interpolation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
- cimg_forXYZ(res,x,y,z) {
- const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
- const int
- X = (int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),
- Y = (int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),
- Z = (int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc);
- cimg_forC(res,c) res(x,y,z,c) = _atXYZ(X,Y,Z,c);
- }
- }
- } break;
- default : // Dirichlet
- switch (interpolation) {
- case 2 : { // Cubic interpolation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
- cimg_forXYZ(res,x,y,z) {
- const float
- xc = x - rw2, yc = y - rh2, zc = z - rd2,
- X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
- Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
- Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
- cimg_forC(res,c) res(x,y,z,c) = cubic_atXYZ_c(X,Y,Z,c,(T)0);
- }
- } break;
- case 1 : { // Linear interpolation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
- cimg_forXYZ(res,x,y,z) {
- const float
- xc = x - rw2, yc = y - rh2, zc = z - rd2,
- X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
- Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
- Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
- cimg_forC(res,c) res(x,y,z,c) = linear_atXYZ(X,Y,Z,c,(T)0);
- }
- } break;
- default : { // Nearest-neighbor interpolation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
- cimg_forXYZ(res,x,y,z) {
- const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
- const int
- X = (int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),
- Y = (int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),
- Z = (int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc);
- cimg_forC(res,c) res(x,y,z,c) = atXYZ(X,Y,Z,c,(T)0);
- }
- }
- } break;
- }
- }
- //! Warp image content by a warping field.
- /**
- \param warp Warping field.
- \param mode Can be { 0=backward-absolute | 1=backward-relative | 2=forward-absolute | 3=foward-relative }
- \param interpolation Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
- \param boundary_conditions Boundary conditions <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
- **/
- template<typename t>
- CImg<T>& warp(const CImg<t>& p_warp, const unsigned int mode=0,
- const unsigned int interpolation=1, const unsigned int boundary_conditions=0) {
- return get_warp(p_warp,mode,interpolation,boundary_conditions).move_to(*this);
- }
- //! Warp image content by a warping field \newinstance
- template<typename t>
- CImg<T> get_warp(const CImg<t>& p_warp, const unsigned int mode=0,
- const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const {
- if (is_empty() || !p_warp) return *this;
- if (mode && !is_sameXYZ(p_warp))
- throw CImgArgumentException(_cimg_instance
- "warp(): Instance and specified relative warping field (%u,%u,%u,%u,%p) "
- "have different XYZ dimensions.",
- cimg_instance,
- p_warp._width,p_warp._height,p_warp._depth,p_warp._spectrum,p_warp._data);
- CImg<T> res(p_warp._width,p_warp._height,p_warp._depth,_spectrum);
- if (p_warp._spectrum==1) { // 1D warping
- if (mode>=3) { // Forward-relative warp
- res.fill((T)0);
- if (interpolation>=1) // Linear interpolation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
- cimg_forX(res,x) res.set_linear_atX(*(ptrs++),x + (float)*(ptrs0++),y,z,c);
- }
- else // Nearest-neighbor interpolation
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
- cimg_forX(res,x) {
- const int X = x + (int)cimg::round(*(ptrs0++));
- if (X>=0 && X<width()) res(X,y,z,c) = *(ptrs++);
- }
- }
- } else if (mode==2) { // Forward-absolute warp
- res.fill((T)0);
- if (interpolation>=1) // Linear interpolation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
- cimg_forX(res,x) res.set_linear_atX(*(ptrs++),(float)*(ptrs0++),y,z,c);
- }
- else // Nearest-neighbor interpolation
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
- cimg_forX(res,x) {
- const int X = (int)cimg::round(*(ptrs0++));
- if (X>=0 && X<width()) res(X,y,z,c) = *(ptrs++);
- }
- }
- } else if (mode==1) { // Backward-relative warp
- if (interpolation==2) // Cubic interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float w2 = 2.f*width();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) {
- const float mx = cimg::mod(x - (float)*(ptrs0++),w2);
- *(ptrd++) = _cubic_atX_c(mx<width()?mx:w2 - mx - 1,y,z,c);
- }
- }
- } break;
- case 2 : // Periodic
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = _cubic_atX_pc(x - (float)*(ptrs0++),y,z,c);
- }
- break;
- case 1 : // Neumann
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = _cubic_atX_c(x - (float)*(ptrs0++),y,z,c);
- }
- break;
- default : // Dirichlet
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = cubic_atX_c(x - (float)*(ptrs0++),y,z,c,(T)0);
- }
- }
- else if (interpolation==1) // Linear interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float w2 = 2.f*width();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) {
- const float mx = cimg::mod(x - (float)*(ptrs0++),w2);
- *(ptrd++) = (T)_linear_atX(mx<width()?mx:w2 - mx - 1,y,z,c);
- }
- }
- } break;
- case 2 : // Periodic
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = (T)_linear_atX_p(x - (float)*(ptrs0++),y,z,c);
- }
- break;
- case 1 : // Neumann
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = (T)_linear_atX(x - (float)*(ptrs0++),y,z,c);
- }
- break;
- default : // Dirichlet
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = (T)linear_atX(x - (float)*(ptrs0++),y,z,c,(T)0);
- }
- }
- else // Nearest-neighbor interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const int w2 = 2*width();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) {
- const int mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2);
- *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,y,z,c);
- }
- }
- } break;
- case 2 : // Periodic
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),width()),y,z,c);
- }
- break;
- case 1 : // Neumann
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = _atX(x - (int)cimg::round(*(ptrs0++)),y,z,c);
- }
- break;
- default : // Dirichlet
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = atX(x - (int)cimg::round(*(ptrs0++)),y,z,c,(T)0);
- }
- }
- }
- else { // Backward-absolute warp
- if (interpolation==2) // Cubic interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float w2 = 2.f*width();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) {
- const float mx = cimg::mod((float)*(ptrs0++),w2);
- *(ptrd++) = _cubic_atX_c(mx<width()?mx:w2 - mx - 1,0,0,c);
- }
- }
- } break;
- case 2 : // Periodic
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = _cubic_atX_pc((float)*(ptrs0++),0,0,c);
- }
- break;
- case 1 : // Neumann
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = _cubic_atX_c((float)*(ptrs0++),0,0,c);
- }
- break;
- default : // Dirichlet
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = cubic_atX_c((float)*(ptrs0++),0,0,c,(T)0);
- }
- }
- else if (interpolation==1) // Linear interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float w2 = 2.f*width();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) {
- const float mx = cimg::mod((float)*(ptrs0++),w2);
- *(ptrd++) = (T)_linear_atX(mx<width()?mx:w2 - mx - 1,0,0,c);
- }
- }
- } break;
- case 2 : // Periodic
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = (T)_linear_atX_p((float)*(ptrs0++),0,0,c);
- }
- break;
- case 1 : // Neumann
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = (T)_linear_atX((float)*(ptrs0++),0,0,c);
- }
- break;
- default : // Dirichlet
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = (T)linear_atX((float)*(ptrs0++),0,0,c,(T)0);
- }
- }
- else // Nearest-neighbor interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const int w2 = 2*width();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) {
- const int mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2);
- *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,0,0,c);
- }
- }
- } break;
- case 2 : // Periodic
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),width()),0,0,c);
- }
- break;
- case 1 : // Neumann
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = _atX((int)cimg::round(*(ptrs0++)),0,0,c);
- }
- break;
- default : // Dirichlet
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = atX((int)cimg::round(*(ptrs0++)),0,0,c,(T)0);
- }
- }
- }
- } else if (p_warp._spectrum==2) { // 2D warping
- if (mode>=3) { // Forward-relative warp
- res.fill((T)0);
- if (interpolation>=1) // Linear interpolation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c);
- cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++),z,c);
- }
- else // Nearest-neighbor interpolation
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c);
- cimg_forX(res,x) {
- const int X = x + (int)cimg::round(*(ptrs0++)), Y = y + (int)cimg::round(*(ptrs1++));
- if (X>=0 && X<width() && Y>=0 && Y<height()) res(X,Y,z,c) = *(ptrs++);
- }
- }
- } else if (mode==2) { // Forward-absolute warp
- res.fill((T)0);
- if (interpolation>=1) // Linear interpolation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c);
- cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),z,c);
- }
- else // Nearest-neighbor interpolation
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); const T *ptrs = data(0,y,z,c);
- cimg_forX(res,x) {
- const int X = (int)cimg::round(*(ptrs0++)), Y = (int)cimg::round(*(ptrs1++));
- if (X>=0 && X<width() && Y>=0 && Y<height()) res(X,Y,z,c) = *(ptrs++);
- }
- }
- } else if (mode==1) { // Backward-relative warp
- if (interpolation==2) // Cubic interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float w2 = 2.f*width(), h2 = 2.f*height();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) {
- const float
- mx = cimg::mod(x - (float)*(ptrs0++),w2),
- my = cimg::mod(y - (float)*(ptrs1++),h2);
- *(ptrd++) = _cubic_atXY_c(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c);
- }
- }
- } break;
- case 2 : // Periodic
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = _cubic_atXY_pc(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
- }
- break;
- case 1 : // Neumann
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = _cubic_atXY_c(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
- }
- break;
- default : // Dirichlet
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = cubic_atXY_c(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,(T)0);
- }
- }
- else if (interpolation==1) // Linear interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float w2 = 2.f*width(), h2 = 2.f*height();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) {
- const float
- mx = cimg::mod(x - (float)*(ptrs0++),w2),
- my = cimg::mod(y - (float)*(ptrs1++),h2);
- *(ptrd++) = (T)_linear_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c);
- }
- }
- } break;
- case 2 : // Periodic
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY_p(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
- }
- break;
- case 1 : // Neumann
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
- }
- break;
- default : // Dirichlet
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = (T)linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,(T)0);
- }
- }
- else // Nearest-neighbor interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const int w2 = 2*width(), h2 = 2*height();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) {
- const int
- mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2),
- my = cimg::mod(y - (int)cimg::round(*(ptrs1++)),h2);
- *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c);
- }
- }
- } break;
- case 2 : // Periodic
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),width()),
- cimg::mod(y - (int)cimg::round(*(ptrs1++)),height()),z,c);
- }
- break;
- case 1 : // Neumann
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = _atXY(x - (int)cimg::round(*(ptrs0++)),
- y - (int)cimg::round(*(ptrs1++)),z,c);
- }
- break;
- default : // Dirichlet
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = atXY(x - (int)cimg::round(*(ptrs0++)),
- y - (int)cimg::round(*(ptrs1++)),z,c,(T)0);
- }
- }
- } else { // Backward-absolute warp
- if (interpolation==2) // Cubic interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float w2 = 2.f*width(), h2 = 2.f*height();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) {
- const float
- mx = cimg::mod((float)*(ptrs0++),w2),
- my = cimg::mod((float)*(ptrs1++),h2);
- *(ptrd++) = _cubic_atXY_c(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c);
- }
- }
- } break;
- case 2 : // Periodic
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = _cubic_atXY_pc((float)*(ptrs0++),(float)*(ptrs1++),0,c);
- }
- break;
- case 1 : // Neumann
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = _cubic_atXY_c((float)*(ptrs0++),(float)*(ptrs1++),0,c);
- }
- break;
- default : // Dirichlet
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = cubic_atXY_c((float)*(ptrs0++),(float)*(ptrs1++),0,c,(T)0);
- }
- }
- else if (interpolation==1) // Linear interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float w2 = 2.f*width(), h2 = 2.f*height();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) {
- const float
- mx = cimg::mod((float)*(ptrs0++),w2),
- my = cimg::mod((float)*(ptrs1++),h2);
- *(ptrd++) = (T)_linear_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c);
- }
- }
- } break;
- case 2 : // Periodic
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY_p((float)*(ptrs0++),(float)*(ptrs1++),0,c);
- }
- break;
- case 1 : // Neumann
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c);
- }
- break;
- default : // Dirichlet
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = (T)linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c,(T)0);
- }
- }
- else // Nearest-neighbor interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const int w2 = 2*width(), h2 = 2*height();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) {
- const int
- mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2),
- my = cimg::mod((int)cimg::round(*(ptrs1++)),h2);
- *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c);
- }
- }
- } break;
- case 2 : // Periodic
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),width()),
- cimg::mod((int)cimg::round(*(ptrs1++)),height()),0,c);
- }
- break;
- case 1 : // Neumann
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = _atXY((int)cimg::round(*(ptrs0++)),
- (int)cimg::round(*(ptrs1++)),0,c);
- }
- break;
- default : // Dirichlet
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1); T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = atXY((int)cimg::round(*(ptrs0++)),
- (int)cimg::round(*(ptrs1++)),0,c,(T)0);
- }
- }
- }
- } else { // 3D warping
- if (mode>=3) { // Forward-relative warp
- res.fill((T)0);
- if (interpolation>=1) // Linear interpolation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- const T *ptrs = data(0,y,z,c);
- cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++),
- z + (float)*(ptrs2++),c);
- }
- else // Nearest-neighbor interpolation
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- const T *ptrs = data(0,y,z,c);
- cimg_forX(res,x) {
- const int
- X = x + (int)cimg::round(*(ptrs0++)),
- Y = y + (int)cimg::round(*(ptrs1++)),
- Z = z + (int)cimg::round(*(ptrs2++));
- if (X>=0 && X<width() && Y>=0 && Y<height() && Z>=0 && Z<depth()) res(X,Y,Z,c) = *(ptrs++);
- }
- }
- } else if (mode==2) { // Forward-absolute warp
- res.fill((T)0);
- if (interpolation>=1) // Linear interpolation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- const T *ptrs = data(0,y,z,c);
- cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
- }
- else // Nearest-neighbor interpolation
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- const T *ptrs = data(0,y,z,c);
- cimg_forX(res,x) {
- const int
- X = (int)cimg::round(*(ptrs0++)),
- Y = (int)cimg::round(*(ptrs1++)),
- Z = (int)cimg::round(*(ptrs2++));
- if (X>=0 && X<width() && Y>=0 && Y<height() && Z>=0 && Z<depth()) res(X,Y,Z,c) = *(ptrs++);
- }
- }
- } else if (mode==1) { // Backward-relative warp
- if (interpolation==2) // Cubic interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float w2 = 2.f*width(), h2 = 2.f*height(), d2 = 2.f*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) {
- const float
- mx = cimg::mod(x - (float)*(ptrs0++),w2),
- my = cimg::mod(y - (float)*(ptrs1++),h2),
- mz = cimg::mod(z - (float)*(ptrs2++),d2);
- *(ptrd++) = _cubic_atXYZ_c(mx<width()?mx:w2 - mx - 1,
- my<height()?my:h2 - my - 1,
- mz<depth()?mz:d2 - mz - 1,c);
- }
- }
- } break;
- case 2 : // Periodic
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x)
- *(ptrd++) = _cubic_atXYZ_pc(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
- }
- break;
- case 1 : // Neumann
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x)
- *(ptrd++) = _cubic_atXYZ_c(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
- }
- break;
- default : // Dirichlet
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x)
- *(ptrd++) = cubic_atXYZ_c(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,(T)0);
- }
- }
- else if (interpolation==1) // Linear interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float w2 = 2.f*width(), h2 = 2.f*height(), d2 = 2.f*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) {
- const float
- mx = cimg::mod(x - (float)*(ptrs0++),w2),
- my = cimg::mod(y - (float)*(ptrs1++),h2),
- mz = cimg::mod(z - (float)*(ptrs2++),d2);
- *(ptrd++) = (T)_linear_atXYZ(mx<width()?mx:w2 - mx - 1,
- my<height()?my:h2 - my - 1,
- mz<depth()?mz:d2 - mz - 1,c);
- }
- }
- } break;
- case 2 : // Periodic
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ_p(x - (float)*(ptrs0++),y - (float)*(ptrs1++),
- z - (float)*(ptrs2++),c);
- }
- break;
- case 1 : // Neumann
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x)
- *(ptrd++) = (T)_linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
- }
- break;
- default : // Dirichlet
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x)
- *(ptrd++) = (T)linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,(T)0);
- }
- }
- else // Nearest neighbor interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) {
- const int
- mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2),
- my = cimg::mod(y - (int)cimg::round(*(ptrs1++)),h2),
- mz = cimg::mod(z - (int)cimg::round(*(ptrs2++)),d2);
- *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,
- my<height()?my:h2 - my - 1,
- mz<depth()?mz:d2 - mz - 1,c);
- }
- }
- } break;
- case 2 : // Periodic
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),width()),
- cimg::mod(y - (int)cimg::round(*(ptrs1++)),height()),
- cimg::mod(z - (int)cimg::round(*(ptrs2++)),depth()),c);
- }
- break;
- case 1 : // Neumann
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = _atXYZ(x - (int)cimg::round(*(ptrs0++)),
- y - (int)cimg::round(*(ptrs1++)),
- z - (int)cimg::round(*(ptrs2++)),c);
- }
- break;
- default : // Dirichlet
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = atXYZ(x - (int)cimg::round(*(ptrs0++)),
- y - (int)cimg::round(*(ptrs1++)),
- z - (int)cimg::round(*(ptrs2++)),c,(T)0);
- }
- }
- } else { // Backward-absolute warp
- if (interpolation==2) // Cubic interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float w2 = 2.f*width(), h2 = 2.f*height(), d2 = 2.f*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) {
- const float
- mx = cimg::mod((float)*(ptrs0++),w2),
- my = cimg::mod((float)*(ptrs1++),h2),
- mz = cimg::mod((float)*(ptrs2++),d2);
- *(ptrd++) = _cubic_atXYZ_c(mx<width()?mx:w2 - mx - 1,
- my<height()?my:h2 - my - 1,
- mz<depth()?mz:d2 - mz - 1,c);
- }
- }
- } break;
- case 2 : // Periodic
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = _cubic_atXYZ_pc((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
- }
- break;
- case 1 : // Neumann
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = _cubic_atXYZ_c((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
- }
- break;
- default : // Dirichlet
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = cubic_atXYZ_c((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),
- c,(T)0);
- }
- }
- else if (interpolation==1) // Linear interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const float w2 = 2.f*width(), h2 = 2.f*height(), d2 = 2.f*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) {
- const float
- mx = cimg::mod((float)*(ptrs0++),w2),
- my = cimg::mod((float)*(ptrs1++),h2),
- mz = cimg::mod((float)*(ptrs2++),d2);
- *(ptrd++) = (T)_linear_atXYZ(mx<width()?mx:w2 - mx - 1,
- my<height()?my:h2 - my - 1,
- mz<depth()?mz:d2 - mz - 1,c);
- }
- }
- } break;
- case 2 :// Periodic
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ_p((float)*(ptrs0++),(float)*(ptrs1++),
- (float)*(ptrs2++),c);
- }
- break;
- case 1 : // Neumann
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
- }
- break;
- default : // Dirichlet
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = (T)linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),
- c,(T)0);
- }
- }
- else // Nearest-neighbor interpolation
- switch (boundary_conditions) {
- case 3 : { // Mirror
- const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) {
- const int
- mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2),
- my = cimg::mod((int)cimg::round(*(ptrs1++)),h2),
- mz = cimg::mod((int)cimg::round(*(ptrs2++)),d2);
- *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,
- my<height()?my:h2 - my - 1,
- mz<depth()?mz:d2 - mz - 1,c);
- }
- }
- } break;
- case 2 : // Periodic
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),width()),
- cimg::mod((int)cimg::round(*(ptrs1++)),height()),
- cimg::mod((int)cimg::round(*(ptrs2++)),depth()),c);
- }
- break;
- case 1 : // Neumann
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = _atXYZ((int)cimg::round(*(ptrs0++)),
- (int)cimg::round(*(ptrs1++)),
- (int)cimg::round(*(ptrs2++)),c);
- }
- break;
- default : // Dirichlet
- cimg_forYZC(res,y,z,c) {
- const t *ptrs0 = p_warp.data(0,y,z,0), *ptrs1 = p_warp.data(0,y,z,1), *ptrs2 = p_warp.data(0,y,z,2);
- T *ptrd = res.data(0,y,z,c);
- cimg_forX(res,x) *(ptrd++) = atXYZ((int)cimg::round(*(ptrs0++)),
- (int)cimg::round(*(ptrs1++)),
- (int)cimg::round(*(ptrs2++)),c,(T)0);
- }
- }
- }
- }
- return res;
- }
- //! Generate a 2D representation of a 3D image, with XY,XZ and YZ views.
- /**
- \param x0 X-coordinate of the projection point.
- \param y0 Y-coordinate of the projection point.
- \param z0 Z-coordinate of the projection point.
- **/
- CImg<T> get_projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) const {
- if (is_empty() || _depth<2) return +*this;
- const unsigned int
- _x0 = (x0>=_width)?_width - 1:x0,
- _y0 = (y0>=_height)?_height - 1:y0,
- _z0 = (z0>=_depth)?_depth - 1:z0;
- const CImg<T>
- img_xy = get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1),
- img_zy = get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1).permute_axes("xzyc").
- resize(_depth,_height,1,-100,-1),
- img_xz = get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1).resize(_width,_depth,1,-100,-1);
- return CImg<T>(_width + _depth,_height + _depth,1,_spectrum,cimg::min(img_xy.min(),img_zy.min(),img_xz.min())).
- draw_image(0,0,img_xy).draw_image(img_xy._width,0,img_zy).
- draw_image(0,img_xy._height,img_xz);
- }
- //! Construct a 2D representation of a 3D image, with XY,XZ and YZ views \inplace.
- CImg<T>& projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) {
- if (_depth<2) return *this;
- return get_projections2d(x0,y0,z0).move_to(*this);
- }
- //! Crop image region.
- /**
- \param x0 = X-coordinate of the upper-left crop rectangle corner.
- \param y0 = Y-coordinate of the upper-left crop rectangle corner.
- \param z0 = Z-coordinate of the upper-left crop rectangle corner.
- \param c0 = C-coordinate of the upper-left crop rectangle corner.
- \param x1 = X-coordinate of the lower-right crop rectangle corner.
- \param y1 = Y-coordinate of the lower-right crop rectangle corner.
- \param z1 = Z-coordinate of the lower-right crop rectangle corner.
- \param c1 = C-coordinate of the lower-right crop rectangle corner.
- \param boundary_conditions = Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
- **/
- CImg<T>& crop(const int x0, const int y0, const int z0, const int c0,
- const int x1, const int y1, const int z1, const int c1,
- const unsigned int boundary_conditions=0) {
- return get_crop(x0,y0,z0,c0,x1,y1,z1,c1,boundary_conditions).move_to(*this);
- }
- //! Crop image region \newinstance.
- CImg<T> get_crop(const int x0, const int y0, const int z0, const int c0,
- const int x1, const int y1, const int z1, const int c1,
- const unsigned int boundary_conditions=0) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "crop(): Empty instance.",
- cimg_instance);
- const int
- nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
- ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0,
- nz0 = z0<z1?z0:z1, nz1 = z0^z1^nz0,
- nc0 = c0<c1?c0:c1, nc1 = c0^c1^nc0;
- const unsigned int
- _boundary_conditions = nx0>=0 && nx1<width() &&
- ny0>=0 && ny1<height() &&
- nz0>=0 && nz1<depth() &&
- nc0>=0 && nc1<spectrum()?0:boundary_conditions;
- CImg<T> res(1U + nx1 - nx0,1U + ny1 - ny0,1U + nz1 - nz0,1U + nc1 - nc0);
- if (nx0<0 || nx1>=width() || ny0<0 || ny1>=height() || nz0<0 || nz1>=depth() || nc0<0 || nc1>=spectrum())
- switch (_boundary_conditions) {
- case 3 : { // Mirror
- const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum();
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
- _height*_depth*_spectrum>=4))
- cimg_forXYZC(res,x,y,z,c) {
- const int
- mx = cimg::mod(nx0 + x,w2),
- my = cimg::mod(ny0 + y,h2),
- mz = cimg::mod(nz0 + z,d2),
- mc = cimg::mod(nc0 + c,s2);
- res(x,y,z,c) = (*this)(mx<width()?mx:w2 - mx - 1,
- my<height()?my:h2 - my - 1,
- mz<depth()?mz:d2 - mz - 1,
- mc<spectrum()?mc:s2 - mc - 1);
- }
- } break;
- case 2 : { // Periodic
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
- _height*_depth*_spectrum>=4))
- cimg_forXYZC(res,x,y,z,c) {
- res(x,y,z,c) = (*this)(cimg::mod(nx0 + x,width()),cimg::mod(ny0 + y,height()),
- cimg::mod(nz0 + z,depth()),cimg::mod(nc0 + c,spectrum()));
- }
- } break;
- case 1 : // Neumann
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
- _height*_depth*_spectrum>=4))
- cimg_forXYZC(res,x,y,z,c) res(x,y,z,c) = _atXYZC(nx0 + x,ny0 + y,nz0 + z,nc0 + c);
- break;
- default : // Dirichlet
- res.fill((T)0).draw_image(-nx0,-ny0,-nz0,-nc0,*this);
- }
- else res.draw_image(-nx0,-ny0,-nz0,-nc0,*this);
- return res;
- }
- //! Crop image region \overloading.
- CImg<T>& crop(const int x0, const int y0, const int z0,
- const int x1, const int y1, const int z1,
- const unsigned int boundary_conditions=0) {
- return crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions);
- }
- //! Crop image region \newinstance.
- CImg<T> get_crop(const int x0, const int y0, const int z0,
- const int x1, const int y1, const int z1,
- const unsigned int boundary_conditions=0) const {
- return get_crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions);
- }
- //! Crop image region \overloading.
- CImg<T>& crop(const int x0, const int y0,
- const int x1, const int y1,
- const unsigned int boundary_conditions=0) {
- return crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions);
- }
- //! Crop image region \newinstance.
- CImg<T> get_crop(const int x0, const int y0,
- const int x1, const int y1,
- const unsigned int boundary_conditions=0) const {
- return get_crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions);
- }
- //! Crop image region \overloading.
- CImg<T>& crop(const int x0, const int x1, const unsigned int boundary_conditions=0) {
- return crop(x0,0,0,0,x1,_height - 1,_depth - 1,_spectrum - 1,boundary_conditions);
- }
- //! Crop image region \newinstance.
- CImg<T> get_crop(const int x0, const int x1, const unsigned int boundary_conditions=0) const {
- return get_crop(x0,0,0,0,x1,_height - 1,_depth - 1,_spectrum - 1,boundary_conditions);
- }
- //! Autocrop image region, regarding the specified background value.
- CImg<T>& autocrop(const T& value, const char *const axes="czyx") {
- if (is_empty()) return *this;
- for (const char *s = axes; *s; ++s) {
- const char axis = cimg::lowercase(*s);
- const CImg<intT> coords = _autocrop(value,axis);
- if (coords[0]==-1 && coords[1]==-1) return assign(); // Image has only 'value' pixels
- else switch (axis) {
- case 'x' : {
- const int x0 = coords[0], x1 = coords[1];
- if (x0>=0 && x1>=0) crop(x0,x1);
- } break;
- case 'y' : {
- const int y0 = coords[0], y1 = coords[1];
- if (y0>=0 && y1>=0) crop(0,y0,_width - 1,y1);
- } break;
- case 'z' : {
- const int z0 = coords[0], z1 = coords[1];
- if (z0>=0 && z1>=0) crop(0,0,z0,_width - 1,_height - 1,z1);
- } break;
- default : {
- const int c0 = coords[0], c1 = coords[1];
- if (c0>=0 && c1>=0) crop(0,0,0,c0,_width - 1,_height - 1,_depth - 1,c1);
- }
- }
- }
- return *this;
- }
- //! Autocrop image region, regarding the specified background value \newinstance.
- CImg<T> get_autocrop(const T& value, const char *const axes="czyx") const {
- return (+*this).autocrop(value,axes);
- }
- //! Autocrop image region, regarding the specified background color.
- /**
- \param color Color used for the crop. If \c 0, color is guessed.
- \param axes Axes used for the crop.
- **/
- CImg<T>& autocrop(const T *const color=0, const char *const axes="zyx") {
- if (is_empty()) return *this;
- if (!color) { // Guess color
- const CImg<T> col1 = get_vector_at(0,0,0);
- const unsigned int w = _width, h = _height, d = _depth, s = _spectrum;
- autocrop(col1,axes);
- if (_width==w && _height==h && _depth==d && _spectrum==s) {
- const CImg<T> col2 = get_vector_at(w - 1,h - 1,d - 1);
- autocrop(col2,axes);
- }
- return *this;
- }
- for (const char *s = axes; *s; ++s) {
- const char axis = cimg::lowercase(*s);
- switch (axis) {
- case 'x' : {
- int x0 = width(), x1 = -1;
- cimg_forC(*this,c) {
- const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'x');
- const int nx0 = coords[0], nx1 = coords[1];
- if (nx0>=0 && nx1>=0) { x0 = std::min(x0,nx0); x1 = std::max(x1,nx1); }
- }
- if (x0==width() && x1==-1) return assign(); else crop(x0,x1);
- } break;
- case 'y' : {
- int y0 = height(), y1 = -1;
- cimg_forC(*this,c) {
- const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'y');
- const int ny0 = coords[0], ny1 = coords[1];
- if (ny0>=0 && ny1>=0) { y0 = std::min(y0,ny0); y1 = std::max(y1,ny1); }
- }
- if (y0==height() && y1==-1) return assign(); else crop(0,y0,_width - 1,y1);
- } break;
- default : {
- int z0 = depth(), z1 = -1;
- cimg_forC(*this,c) {
- const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'z');
- const int nz0 = coords[0], nz1 = coords[1];
- if (nz0>=0 && nz1>=0) { z0 = std::min(z0,nz0); z1 = std::max(z1,nz1); }
- }
- if (z0==depth() && z1==-1) return assign(); else crop(0,0,z0,_width - 1,_height - 1,z1);
- }
- }
- }
- return *this;
- }
- //! Autocrop image region, regarding the specified background color \newinstance.
- CImg<T> get_autocrop(const T *const color=0, const char *const axes="zyx") const {
- return (+*this).autocrop(color,axes);
- }
- CImg<intT> _autocrop(const T& value, const char axis) const {
- CImg<intT> res;
- switch (cimg::lowercase(axis)) {
- case 'x' : {
- int x0 = -1, x1 = -1;
- cimg_forX(*this,x) cimg_forYZC(*this,y,z,c)
- if ((*this)(x,y,z,c)!=value) { x0 = x; x = width(); y = height(); z = depth(); c = spectrum(); }
- if (x0>=0) {
- for (int x = width() - 1; x>=0; --x) cimg_forYZC(*this,y,z,c)
- if ((*this)(x,y,z,c)!=value) { x1 = x; x = 0; y = height(); z = depth(); c = spectrum(); }
- }
- res = CImg<intT>::vector(x0,x1);
- } break;
- case 'y' : {
- int y0 = -1, y1 = -1;
- cimg_forY(*this,y) cimg_forXZC(*this,x,z,c)
- if ((*this)(x,y,z,c)!=value) { y0 = y; x = width(); y = height(); z = depth(); c = spectrum(); }
- if (y0>=0) {
- for (int y = height() - 1; y>=0; --y) cimg_forXZC(*this,x,z,c)
- if ((*this)(x,y,z,c)!=value) { y1 = y; x = width(); y = 0; z = depth(); c = spectrum(); }
- }
- res = CImg<intT>::vector(y0,y1);
- } break;
- case 'z' : {
- int z0 = -1, z1 = -1;
- cimg_forZ(*this,z) cimg_forXYC(*this,x,y,c)
- if ((*this)(x,y,z,c)!=value) { z0 = z; x = width(); y = height(); z = depth(); c = spectrum(); }
- if (z0>=0) {
- for (int z = depth() - 1; z>=0; --z) cimg_forXYC(*this,x,y,c)
- if ((*this)(x,y,z,c)!=value) { z1 = z; x = width(); y = height(); z = 0; c = spectrum(); }
- }
- res = CImg<intT>::vector(z0,z1);
- } break;
- default : {
- int c0 = -1, c1 = -1;
- cimg_forC(*this,c) cimg_forXYZ(*this,x,y,z)
- if ((*this)(x,y,z,c)!=value) { c0 = c; x = width(); y = height(); z = depth(); c = spectrum(); }
- if (c0>=0) {
- for (int c = spectrum() - 1; c>=0; --c) cimg_forXYZ(*this,x,y,z)
- if ((*this)(x,y,z,c)!=value) { c1 = c; x = width(); y = height(); z = depth(); c = 0; }
- }
- res = CImg<intT>::vector(c0,c1);
- }
- }
- return res;
- }
- //! Return specified image column.
- /**
- \param x0 Image column.
- **/
- CImg<T> get_column(const int x0) const {
- return get_columns(x0,x0);
- }
- //! Return specified image column \inplace.
- CImg<T>& column(const int x0) {
- return columns(x0,x0);
- }
- //! Return specified range of image columns.
- /**
- \param x0 Starting image column.
- \param x1 Ending image column.
- **/
- CImg<T>& columns(const int x0, const int x1) {
- return get_columns(x0,x1).move_to(*this);
- }
- //! Return specified range of image columns \inplace.
- CImg<T> get_columns(const int x0, const int x1) const {
- return get_crop(x0,0,0,0,x1,height() - 1,depth() - 1,spectrum() - 1);
- }
- //! Return specified image row.
- CImg<T> get_row(const int y0) const {
- return get_rows(y0,y0);
- }
- //! Return specified image row \inplace.
- /**
- \param y0 Image row.
- **/
- CImg<T>& row(const int y0) {
- return rows(y0,y0);
- }
- //! Return specified range of image rows.
- /**
- \param y0 Starting image row.
- \param y1 Ending image row.
- **/
- CImg<T> get_rows(const int y0, const int y1) const {
- return get_crop(0,y0,0,0,width() - 1,y1,depth() - 1,spectrum() - 1);
- }
- //! Return specified range of image rows \inplace.
- CImg<T>& rows(const int y0, const int y1) {
- return get_rows(y0,y1).move_to(*this);
- }
- //! Return specified image slice.
- /**
- \param z0 Image slice.
- **/
- CImg<T> get_slice(const int z0) const {
- return get_slices(z0,z0);
- }
- //! Return specified image slice \inplace.
- CImg<T>& slice(const int z0) {
- return slices(z0,z0);
- }
- //! Return specified range of image slices.
- /**
- \param z0 Starting image slice.
- \param z1 Ending image slice.
- **/
- CImg<T> get_slices(const int z0, const int z1) const {
- return get_crop(0,0,z0,0,width() - 1,height() - 1,z1,spectrum() - 1);
- }
- //! Return specified range of image slices \inplace.
- CImg<T>& slices(const int z0, const int z1) {
- return get_slices(z0,z1).move_to(*this);
- }
- //! Return specified image channel.
- /**
- \param c0 Image channel.
- **/
- CImg<T> get_channel(const int c0) const {
- return get_channels(c0,c0);
- }
- //! Return specified image channel \inplace.
- CImg<T>& channel(const int c0) {
- return channels(c0,c0);
- }
- //! Return specified range of image channels.
- /**
- \param c0 Starting image channel.
- \param c1 Ending image channel.
- **/
- CImg<T> get_channels(const int c0, const int c1) const {
- return get_crop(0,0,0,c0,width() - 1,height() - 1,depth() - 1,c1);
- }
- //! Return specified range of image channels \inplace.
- CImg<T>& channels(const int c0, const int c1) {
- return get_channels(c0,c1).move_to(*this);
- }
- //! Return stream line of a 2D or 3D vector field.
- CImg<floatT> get_streamline(const float x, const float y, const float z,
- const float L=256, const float dl=0.1f,
- const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
- const bool is_oriented_only=false) const {
- if (_spectrum!=2 && _spectrum!=3)
- throw CImgInstanceException(_cimg_instance
- "streamline(): Instance is not a 2D or 3D vector field.",
- cimg_instance);
- if (_spectrum==2) {
- if (is_oriented_only) {
- typename CImg<T>::_functor4d_streamline2d_oriented func(*this);
- return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,
- 0,0,0,_width - 1.f,_height - 1.f,0.f);
- } else {
- typename CImg<T>::_functor4d_streamline2d_directed func(*this);
- return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,
- 0,0,0,_width - 1.f,_height - 1.f,0.f);
- }
- }
- if (is_oriented_only) {
- typename CImg<T>::_functor4d_streamline3d_oriented func(*this);
- return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,
- 0,0,0,_width - 1.f,_height - 1.f,_depth - 1.f);
- }
- typename CImg<T>::_functor4d_streamline3d_directed func(*this);
- return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,
- 0,0,0,_width - 1.f,_height - 1.f,_depth - 1.f);
- }
- //! Return stream line of a 3D vector field.
- /**
- \param func Vector field function.
- \param x X-coordinate of the starting point of the streamline.
- \param y Y-coordinate of the starting point of the streamline.
- \param z Z-coordinate of the starting point of the streamline.
- \param L Streamline length.
- \param dl Streamline length increment.
- \param interpolation_type Type of interpolation.
- Can be <tt>{ 0=nearest int | 1=linear | 2=2nd-order RK | 3=4th-order RK. }</tt>.
- \param is_backward_tracking Tells if the streamline is estimated forward or backward.
- \param is_oriented_only Tells if the direction of the vectors must be ignored.
- \param x0 X-coordinate of the first bounding-box vertex.
- \param y0 Y-coordinate of the first bounding-box vertex.
- \param z0 Z-coordinate of the first bounding-box vertex.
- \param x1 X-coordinate of the second bounding-box vertex.
- \param y1 Y-coordinate of the second bounding-box vertex.
- \param z1 Z-coordinate of the second bounding-box vertex.
- **/
- template<typename tfunc>
- static CImg<floatT> streamline(const tfunc& func,
- const float x, const float y, const float z,
- const float L=256, const float dl=0.1f,
- const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
- const bool is_oriented_only=false,
- const float x0=0, const float y0=0, const float z0=0,
- const float x1=0, const float y1=0, const float z1=0) {
- if (dl<=0)
- throw CImgArgumentException("CImg<%s>::streamline(): Invalid specified integration length %g "
- "(should be >0).",
- pixel_type(),
- dl);
- const bool is_bounded = (x0!=x1 || y0!=y1 || z0!=z1);
- if (L<=0 || (is_bounded && (x<x0 || x>x1 || y<y0 || y>y1 || z<z0 || z>z1))) return CImg<floatT>();
- const unsigned int size_L = (unsigned int)cimg::round(L/dl + 1);
- CImg<floatT> coordinates(size_L,3);
- const float dl2 = dl/2;
- float
- *ptr_x = coordinates.data(0,0),
- *ptr_y = coordinates.data(0,1),
- *ptr_z = coordinates.data(0,2),
- pu = (float)(dl*func(x,y,z,0)),
- pv = (float)(dl*func(x,y,z,1)),
- pw = (float)(dl*func(x,y,z,2)),
- X = x, Y = y, Z = z;
- switch (interpolation_type) {
- case 0 : { // Nearest integer interpolation
- cimg_forX(coordinates,l) {
- *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
- const int
- xi = (int)(X>0?X + 0.5f:X - 0.5f),
- yi = (int)(Y>0?Y + 0.5f:Y - 0.5f),
- zi = (int)(Z>0?Z + 0.5f:Z - 0.5f);
- float
- u = (float)(dl*func((float)xi,(float)yi,(float)zi,0)),
- v = (float)(dl*func((float)xi,(float)yi,(float)zi,1)),
- w = (float)(dl*func((float)xi,(float)yi,(float)zi,2));
- if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
- if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
- if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
- }
- } break;
- case 1 : { // First-order interpolation
- cimg_forX(coordinates,l) {
- *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
- float
- u = (float)(dl*func(X,Y,Z,0)),
- v = (float)(dl*func(X,Y,Z,1)),
- w = (float)(dl*func(X,Y,Z,2));
- if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
- if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
- if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
- }
- } break;
- case 2 : { // Second order interpolation
- cimg_forX(coordinates,l) {
- *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
- float
- u0 = (float)(dl2*func(X,Y,Z,0)),
- v0 = (float)(dl2*func(X,Y,Z,1)),
- w0 = (float)(dl2*func(X,Y,Z,2));
- if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
- float
- u = (float)(dl*func(X + u0,Y + v0,Z + w0,0)),
- v = (float)(dl*func(X + u0,Y + v0,Z + w0,1)),
- w = (float)(dl*func(X + u0,Y + v0,Z + w0,2));
- if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
- if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
- if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
- }
- } break;
- default : { // Fourth order interpolation
- cimg_forX(coordinates,k) {
- *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
- float
- u0 = (float)(dl2*func(X,Y,Z,0)),
- v0 = (float)(dl2*func(X,Y,Z,1)),
- w0 = (float)(dl2*func(X,Y,Z,2));
- if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
- float
- u1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,0)),
- v1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,1)),
- w1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,2));
- if (is_oriented_only && u1*pu + v1*pv + w1*pw<0) { u1 = -u1; v1 = -v1; w1 = -w1; }
- float
- u2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,0)),
- v2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,1)),
- w2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,2));
- if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u2 = -u2; v2 = -v2; w2 = -w2; }
- float
- u3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,0)),
- v3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,1)),
- w3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,2));
- if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u3 = -u3; v3 = -v3; w3 = -w3; }
- const float
- u = (u0 + u3)/3 + (u1 + u2)/1.5f,
- v = (v0 + v3)/3 + (v1 + v2)/1.5f,
- w = (w0 + w3)/3 + (w1 + w2)/1.5f;
- if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
- if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
- }
- }
- }
- if (ptr_x!=coordinates.data(0,1)) coordinates.resize((int)(ptr_x-coordinates.data()),3,1,1,0);
- return coordinates;
- }
- //! Return stream line of a 3D vector field \overloading.
- static CImg<floatT> streamline(const char *const expression,
- const float x, const float y, const float z,
- const float L=256, const float dl=0.1f,
- const unsigned int interpolation_type=2, const bool is_backward_tracking=true,
- const bool is_oriented_only=false,
- const float x0=0, const float y0=0, const float z0=0,
- const float x1=0, const float y1=0, const float z1=0) {
- _functor4d_streamline_expr func(expression);
- return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,is_oriented_only,x0,y0,z0,x1,y1,z1);
- }
- struct _functor4d_streamline2d_directed {
- const CImg<T>& ref;
- _functor4d_streamline2d_directed(const CImg<T>& pref):ref(pref) {}
- float operator()(const float x, const float y, const float z, const unsigned int c) const {
- return c<2?(float)ref._linear_atXY(x,y,(int)z,c):0;
- }
- };
- struct _functor4d_streamline3d_directed {
- const CImg<T>& ref;
- _functor4d_streamline3d_directed(const CImg<T>& pref):ref(pref) {}
- float operator()(const float x, const float y, const float z, const unsigned int c) const {
- return (float)ref._linear_atXYZ(x,y,z,c);
- }
- };
- struct _functor4d_streamline2d_oriented {
- const CImg<T>& ref;
- CImg<floatT> *pI;
- _functor4d_streamline2d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,1,2); }
- ~_functor4d_streamline2d_oriented() { delete pI; }
- float operator()(const float x, const float y, const float z, const unsigned int c) const {
- #define _cimg_vecalign2d(i,j) \
- if (I(i,j,0)*I(0,0,0) + I(i,j,1)*I(0,0,1)<0) { I(i,j,0) = -I(i,j,0); I(i,j,1) = -I(i,j,1); }
- int
- xi = (int)x - (x>=0?0:1), nxi = xi + 1,
- yi = (int)y - (y>=0?0:1), nyi = yi + 1,
- zi = (int)z;
- const float
- dx = x - xi,
- dy = y - yi;
- if (c==0) {
- CImg<floatT>& I = *pI;
- if (xi<0) xi = 0;
- if (nxi<0) nxi = 0;
- if (xi>=ref.width()) xi = ref.width() - 1;
- if (nxi>=ref.width()) nxi = ref.width() - 1;
- if (yi<0) yi = 0;
- if (nyi<0) nyi = 0;
- if (yi>=ref.height()) yi = ref.height() - 1;
- if (nyi>=ref.height()) nyi = ref.height() - 1;
- I(0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,1) = (float)ref(xi,yi,zi,1);
- I(1,0,0) = (float)ref(nxi,yi,zi,0); I(1,0,1) = (float)ref(nxi,yi,zi,1);
- I(1,1,0) = (float)ref(nxi,nyi,zi,0); I(1,1,1) = (float)ref(nxi,nyi,zi,1);
- I(0,1,0) = (float)ref(xi,nyi,zi,0); I(0,1,1) = (float)ref(xi,nyi,zi,1);
- _cimg_vecalign2d(1,0); _cimg_vecalign2d(1,1); _cimg_vecalign2d(0,1);
- }
- return c<2?(float)pI->_linear_atXY(dx,dy,0,c):0;
- }
- };
- struct _functor4d_streamline3d_oriented {
- const CImg<T>& ref;
- CImg<floatT> *pI;
- _functor4d_streamline3d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,2,3); }
- ~_functor4d_streamline3d_oriented() { delete pI; }
- float operator()(const float x, const float y, const float z, const unsigned int c) const {
- #define _cimg_vecalign3d(i,j,k) if (I(i,j,k,0)*I(0,0,0,0) + I(i,j,k,1)*I(0,0,0,1) + I(i,j,k,2)*I(0,0,0,2)<0) { \
- I(i,j,k,0) = -I(i,j,k,0); I(i,j,k,1) = -I(i,j,k,1); I(i,j,k,2) = -I(i,j,k,2); }
- int
- xi = (int)x - (x>=0?0:1), nxi = xi + 1,
- yi = (int)y - (y>=0?0:1), nyi = yi + 1,
- zi = (int)z - (z>=0?0:1), nzi = zi + 1;
- const float
- dx = x - xi,
- dy = y - yi,
- dz = z - zi;
- if (c==0) {
- CImg<floatT>& I = *pI;
- if (xi<0) xi = 0;
- if (nxi<0) nxi = 0;
- if (xi>=ref.width()) xi = ref.width() - 1;
- if (nxi>=ref.width()) nxi = ref.width() - 1;
- if (yi<0) yi = 0;
- if (nyi<0) nyi = 0;
- if (yi>=ref.height()) yi = ref.height() - 1;
- if (nyi>=ref.height()) nyi = ref.height() - 1;
- if (zi<0) zi = 0;
- if (nzi<0) nzi = 0;
- if (zi>=ref.depth()) zi = ref.depth() - 1;
- if (nzi>=ref.depth()) nzi = ref.depth() - 1;
- I(0,0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,0,1) = (float)ref(xi,yi,zi,1);
- I(0,0,0,2) = (float)ref(xi,yi,zi,2); I(1,0,0,0) = (float)ref(nxi,yi,zi,0);
- I(1,0,0,1) = (float)ref(nxi,yi,zi,1); I(1,0,0,2) = (float)ref(nxi,yi,zi,2);
- I(1,1,0,0) = (float)ref(nxi,nyi,zi,0); I(1,1,0,1) = (float)ref(nxi,nyi,zi,1);
- I(1,1,0,2) = (float)ref(nxi,nyi,zi,2); I(0,1,0,0) = (float)ref(xi,nyi,zi,0);
- I(0,1,0,1) = (float)ref(xi,nyi,zi,1); I(0,1,0,2) = (float)ref(xi,nyi,zi,2);
- I(0,0,1,0) = (float)ref(xi,yi,nzi,0); I(0,0,1,1) = (float)ref(xi,yi,nzi,1);
- I(0,0,1,2) = (float)ref(xi,yi,nzi,2); I(1,0,1,0) = (float)ref(nxi,yi,nzi,0);
- I(1,0,1,1) = (float)ref(nxi,yi,nzi,1); I(1,0,1,2) = (float)ref(nxi,yi,nzi,2);
- I(1,1,1,0) = (float)ref(nxi,nyi,nzi,0); I(1,1,1,1) = (float)ref(nxi,nyi,nzi,1);
- I(1,1,1,2) = (float)ref(nxi,nyi,nzi,2); I(0,1,1,0) = (float)ref(xi,nyi,nzi,0);
- I(0,1,1,1) = (float)ref(xi,nyi,nzi,1); I(0,1,1,2) = (float)ref(xi,nyi,nzi,2);
- _cimg_vecalign3d(1,0,0); _cimg_vecalign3d(1,1,0); _cimg_vecalign3d(0,1,0);
- _cimg_vecalign3d(0,0,1); _cimg_vecalign3d(1,0,1); _cimg_vecalign3d(1,1,1); _cimg_vecalign3d(0,1,1);
- }
- return (float)pI->_linear_atXYZ(dx,dy,dz,c);
- }
- };
- struct _functor4d_streamline_expr {
- _cimg_math_parser *mp;
- ~_functor4d_streamline_expr() { mp->end(); delete mp; }
- _functor4d_streamline_expr(const char *const expr):mp(0) {
- mp = new _cimg_math_parser(expr,"streamline",CImg<T>::const_empty(),0);
- }
- float operator()(const float x, const float y, const float z, const unsigned int c) const {
- return (float)(*mp)(x,y,z,c);
- }
- };
- //! Return a shared-memory image referencing a range of pixels of the image instance.
- /**
- \param x0 X-coordinate of the starting pixel.
- \param x1 X-coordinate of the ending pixel.
- \param y0 Y-coordinate.
- \param z0 Z-coordinate.
- \param c0 C-coordinate.
- **/
- CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
- const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) {
- const ulongT
- beg = (ulongT)offset(x0,y0,z0,c0),
- end = (ulongT)offset(x1,y0,z0,c0);
- if (beg>end || beg>=size() || end>=size())
- throw CImgArgumentException(_cimg_instance
- "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
- cimg_instance,
- x0,x1,y0,z0,c0);
- return CImg<T>(_data + beg,x1 - x0 + 1,1,1,1,true);
- }
- //! Return a shared-memory image referencing a range of pixels of the image instance \const.
- const CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
- const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) const {
- const ulongT
- beg = (ulongT)offset(x0,y0,z0,c0),
- end = (ulongT)offset(x1,y0,z0,c0);
- if (beg>end || beg>=size() || end>=size())
- throw CImgArgumentException(_cimg_instance
- "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
- cimg_instance,
- x0,x1,y0,z0,c0);
- return CImg<T>(_data + beg,x1 - x0 + 1,1,1,1,true);
- }
- //! Return a shared-memory image referencing a range of rows of the image instance.
- /**
- \param y0 Y-coordinate of the starting row.
- \param y1 Y-coordinate of the ending row.
- \param z0 Z-coordinate.
- \param c0 C-coordinate.
- **/
- CImg<T> get_shared_rows(const unsigned int y0, const unsigned int y1,
- const unsigned int z0=0, const unsigned int c0=0) {
- const ulongT
- beg = (ulongT)offset(0,y0,z0,c0),
- end = (ulongT)offset(0,y1,z0,c0);
- if (beg>end || beg>=size() || end>=size())
- throw CImgArgumentException(_cimg_instance
- "get_shared_rows(): Invalid request of a shared-memory subset "
- "(0->%u,%u->%u,%u,%u).",
- cimg_instance,
- _width - 1,y0,y1,z0,c0);
- return CImg<T>(_data + beg,_width,y1 - y0 + 1,1,1,true);
- }
- //! Return a shared-memory image referencing a range of rows of the image instance \const.
- const CImg<T> get_shared_rows(const unsigned int y0, const unsigned int y1,
- const unsigned int z0=0, const unsigned int c0=0) const {
- const ulongT
- beg = (ulongT)offset(0,y0,z0,c0),
- end = (ulongT)offset(0,y1,z0,c0);
- if (beg>end || beg>=size() || end>=size())
- throw CImgArgumentException(_cimg_instance
- "get_shared_rows(): Invalid request of a shared-memory subset "
- "(0->%u,%u->%u,%u,%u).",
- cimg_instance,
- _width - 1,y0,y1,z0,c0);
- return CImg<T>(_data + beg,_width,y1 - y0 + 1,1,1,true);
- }
- //! Return a shared-memory image referencing one row of the image instance.
- /**
- \param y0 Y-coordinate.
- \param z0 Z-coordinate.
- \param c0 C-coordinate.
- **/
- CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) {
- return get_shared_rows(y0,y0,z0,c0);
- }
- //! Return a shared-memory image referencing one row of the image instance \const.
- const CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) const {
- return get_shared_rows(y0,y0,z0,c0);
- }
- //! Return a shared memory image referencing a range of slices of the image instance.
- /**
- \param z0 Z-coordinate of the starting slice.
- \param z1 Z-coordinate of the ending slice.
- \param c0 C-coordinate.
- **/
- CImg<T> get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) {
- const ulongT
- beg = (ulongT)offset(0,0,z0,c0),
- end = (ulongT)offset(0,0,z1,c0);
- if (beg>end || beg>=size() || end>=size())
- throw CImgArgumentException(_cimg_instance
- "get_shared_slices(): Invalid request of a shared-memory subset "
- "(0->%u,0->%u,%u->%u,%u).",
- cimg_instance,
- _width - 1,_height - 1,z0,z1,c0);
- return CImg<T>(_data + beg,_width,_height,z1 - z0 + 1,1,true);
- }
- //! Return a shared memory image referencing a range of slices of the image instance \const.
- const CImg<T> get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) const {
- const ulongT
- beg = (ulongT)offset(0,0,z0,c0),
- end = (ulongT)offset(0,0,z1,c0);
- if (beg>end || beg>=size() || end>=size())
- throw CImgArgumentException(_cimg_instance
- "get_shared_slices(): Invalid request of a shared-memory subset "
- "(0->%u,0->%u,%u->%u,%u).",
- cimg_instance,
- _width - 1,_height - 1,z0,z1,c0);
- return CImg<T>(_data + beg,_width,_height,z1 - z0 + 1,1,true);
- }
- //! Return a shared-memory image referencing one slice of the image instance.
- /**
- \param z0 Z-coordinate.
- \param c0 C-coordinate.
- **/
- CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) {
- return get_shared_slices(z0,z0,c0);
- }
- //! Return a shared-memory image referencing one slice of the image instance \const.
- const CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) const {
- return get_shared_slices(z0,z0,c0);
- }
- //! Return a shared-memory image referencing a range of channels of the image instance.
- /**
- \param c0 C-coordinate of the starting channel.
- \param c1 C-coordinate of the ending channel.
- **/
- CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) {
- const ulongT
- beg = (ulongT)offset(0,0,0,c0),
- end = (ulongT)offset(0,0,0,c1);
- if (beg>end || beg>=size() || end>=size())
- throw CImgArgumentException(_cimg_instance
- "get_shared_channels(): Invalid request of a shared-memory subset "
- "(0->%u,0->%u,0->%u,%u->%u).",
- cimg_instance,
- _width - 1,_height - 1,_depth - 1,c0,c1);
- return CImg<T>(_data + beg,_width,_height,_depth,c1 - c0 + 1,true);
- }
- //! Return a shared-memory image referencing a range of channels of the image instance \const.
- const CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) const {
- const ulongT
- beg = (ulongT)offset(0,0,0,c0),
- end = (ulongT)offset(0,0,0,c1);
- if (beg>end || beg>=size() || end>=size())
- throw CImgArgumentException(_cimg_instance
- "get_shared_channels(): Invalid request of a shared-memory subset "
- "(0->%u,0->%u,0->%u,%u->%u).",
- cimg_instance,
- _width - 1,_height - 1,_depth - 1,c0,c1);
- return CImg<T>(_data + beg,_width,_height,_depth,c1 - c0 + 1,true);
- }
- //! Return a shared-memory image referencing one channel of the image instance.
- /**
- \param c0 C-coordinate.
- **/
- CImg<T> get_shared_channel(const unsigned int c0) {
- return get_shared_channels(c0,c0);
- }
- //! Return a shared-memory image referencing one channel of the image instance \const.
- const CImg<T> get_shared_channel(const unsigned int c0) const {
- return get_shared_channels(c0,c0);
- }
- //! Return a shared-memory version of the image instance.
- CImg<T> get_shared() {
- return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
- }
- //! Return a shared-memory version of the image instance \const.
- const CImg<T> get_shared() const {
- return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
- }
- //! Split image into a list along specified axis.
- /**
- \param axis Splitting axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
- \param nb Number of split parts.
- \note
- - If \c nb==0, instance image is split into blocs of equal values along the specified axis.
- - If \c nb<=0, instance image is split into blocs of -\c nb pixel wide.
- - If \c nb>0, instance image is split into \c nb blocs.
- **/
- CImgList<T> get_split(const char axis, const int nb=-1) const {
- CImgList<T> res;
- if (is_empty()) return res;
- const char _axis = cimg::lowercase(axis);
- if (nb<0) { // Split by block size
- const unsigned int dp = (unsigned int)(nb?-nb:1);
- switch (_axis) {
- case 'x': {
- if (_width>dp) {
- res.assign(_width/dp + (_width%dp?1:0),1,1);
- const unsigned int pe = _width - dp;
- cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 &&
- _height*_depth*_spectrum>=128))
- for (int p = 0; p<(int)pe; p+=dp)
- get_crop(p,0,0,0,p + dp - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res[p/dp]);
- get_crop((res._width - 1)*dp,0,0,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
- } else res.assign(*this);
- } break;
- case 'y': {
- if (_height>dp) {
- res.assign(_height/dp + (_height%dp?1:0),1,1);
- const unsigned int pe = _height - dp;
- cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 &&
- _width*_depth*_spectrum>=128))
- for (int p = 0; p<(int)pe; p+=dp)
- get_crop(0,p,0,0,_width - 1,p + dp - 1,_depth - 1,_spectrum - 1).move_to(res[p/dp]);
- get_crop(0,(res._width - 1)*dp,0,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
- } else res.assign(*this);
- } break;
- case 'z': {
- if (_depth>dp) {
- res.assign(_depth/dp + (_depth%dp?1:0),1,1);
- const unsigned int pe = _depth - dp;
- cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 &&
- _width*_height*_spectrum>=128))
- for (int p = 0; p<(int)pe; p+=dp)
- get_crop(0,0,p,0,_width - 1,_height - 1,p + dp - 1,_spectrum - 1).move_to(res[p/dp]);
- get_crop(0,0,(res._width - 1)*dp,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
- } else res.assign(*this);
- } break;
- case 'c' : {
- if (_spectrum>dp) {
- res.assign(_spectrum/dp + (_spectrum%dp?1:0),1,1);
- const unsigned int pe = _spectrum - dp;
- cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 &&
- _width*_height*_depth>=128))
- for (int p = 0; p<(int)pe; p+=dp)
- get_crop(0,0,0,p,_width - 1,_height - 1,_depth - 1,p + dp - 1).move_to(res[p/dp]);
- get_crop(0,0,0,(res._width - 1)*dp,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
- } else res.assign(*this);
- }
- }
- } else if (nb>0) { // Split by number of (non-homogeneous) blocs
- const unsigned int siz = _axis=='x'?_width:_axis=='y'?_height:_axis=='z'?_depth:_axis=='c'?_spectrum:0;
- if ((unsigned int)nb>siz)
- throw CImgArgumentException(_cimg_instance
- "get_split(): Instance cannot be split along %c-axis into %u blocs.",
- cimg_instance,
- axis,nb);
- if (nb==1) res.assign(*this);
- else {
- int err = (int)siz;
- unsigned int _p = 0;
- switch (_axis) {
- case 'x' : {
- cimg_forX(*this,p) if ((err-=nb)<=0) {
- get_crop(_p,0,0,0,p,_height - 1,_depth - 1,_spectrum - 1).move_to(res);
- err+=(int)siz;
- _p = p + 1U;
- }
- } break;
- case 'y' : {
- cimg_forY(*this,p) if ((err-=nb)<=0) {
- get_crop(0,_p,0,0,_width - 1,p,_depth - 1,_spectrum - 1).move_to(res);
- err+=(int)siz;
- _p = p + 1U;
- }
- } break;
- case 'z' : {
- cimg_forZ(*this,p) if ((err-=nb)<=0) {
- get_crop(0,0,_p,0,_width - 1,_height - 1,p,_spectrum - 1).move_to(res);
- err+=(int)siz;
- _p = p + 1U;
- }
- } break;
- case 'c' : {
- cimg_forC(*this,p) if ((err-=nb)<=0) {
- get_crop(0,0,0,_p,_width - 1,_height - 1,_depth - 1,p).move_to(res);
- err+=(int)siz;
- _p = p + 1U;
- }
- }
- }
- }
- } else { // Split by equal values according to specified axis
- T current = *_data;
- switch (_axis) {
- case 'x' : {
- int i0 = 0;
- cimg_forX(*this,i)
- if ((*this)(i)!=current) { get_columns(i0,i - 1).move_to(res); i0 = i; current = (*this)(i); }
- get_columns(i0,width() - 1).move_to(res);
- } break;
- case 'y' : {
- int i0 = 0;
- cimg_forY(*this,i)
- if ((*this)(0,i)!=current) { get_rows(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,i); }
- get_rows(i0,height() - 1).move_to(res);
- } break;
- case 'z' : {
- int i0 = 0;
- cimg_forZ(*this,i)
- if ((*this)(0,0,i)!=current) { get_slices(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,i); }
- get_slices(i0,depth() - 1).move_to(res);
- } break;
- case 'c' : {
- int i0 = 0;
- cimg_forC(*this,i)
- if ((*this)(0,0,0,i)!=current) { get_channels(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,0,i); }
- get_channels(i0,spectrum() - 1).move_to(res);
- } break;
- default : {
- longT i0 = 0;
- cimg_foroff(*this,i)
- if ((*this)[i]!=current) {
- CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res);
- i0 = (longT)i; current = (*this)[i];
- }
- CImg<T>(_data + i0,1,(unsigned int)(size() - i0)).move_to(res);
- }
- }
- }
- return res;
- }
- //! Split image into a list of sub-images, according to a specified splitting value sequence and optionally axis.
- /**
- \param values Splitting value sequence.
- \param axis Axis along which the splitting is performed. Can be '0' to ignore axis.
- \param keep_values Tells if the splitting sequence must be kept in the split blocs.
- **/
- template<typename t>
- CImgList<T> get_split(const CImg<t>& values, const char axis=0, const bool keep_values=true) const {
- typedef _cimg_Tt Tt;
- CImgList<T> res;
- if (is_empty()) return res;
- const ulongT vsiz = values.size();
- const char _axis = cimg::lowercase(axis);
- if (!vsiz) return CImgList<T>(*this);
- if (vsiz==1) { // Split according to a single value
- const T value = (T)*values;
- switch (_axis) {
- case 'x' : {
- unsigned int i0 = 0, i = 0;
- do {
- while (i<_width && (*this)(i)==value) ++i;
- if (i>i0) { if (keep_values) get_columns(i0,i - 1).move_to(res); i0 = i; }
- while (i<_width && (*this)(i)!=value) ++i;
- if (i>i0) { get_columns(i0,i - 1).move_to(res); i0 = i; }
- } while (i<_width);
- } break;
- case 'y' : {
- unsigned int i0 = 0, i = 0;
- do {
- while (i<_height && (*this)(0,i)==value) ++i;
- if (i>i0) { if (keep_values) get_rows(i0,i - 1).move_to(res); i0 = i; }
- while (i<_height && (*this)(0,i)!=value) ++i;
- if (i>i0) { get_rows(i0,i - 1).move_to(res); i0 = i; }
- } while (i<_height);
- } break;
- case 'z' : {
- unsigned int i0 = 0, i = 0;
- do {
- while (i<_depth && (*this)(0,0,i)==value) ++i;
- if (i>i0) { if (keep_values) get_slices(i0,i - 1).move_to(res); i0 = i; }
- while (i<_depth && (*this)(0,0,i)!=value) ++i;
- if (i>i0) { get_slices(i0,i - 1).move_to(res); i0 = i; }
- } while (i<_depth);
- } break;
- case 'c' : {
- unsigned int i0 = 0, i = 0;
- do {
- while (i<_spectrum && (*this)(0,0,0,i)==value) ++i;
- if (i>i0) { if (keep_values) get_channels(i0,i - 1).move_to(res); i0 = i; }
- while (i<_spectrum && (*this)(0,0,0,i)!=value) ++i;
- if (i>i0) { get_channels(i0,i - 1).move_to(res); i0 = i; }
- } while (i<_spectrum);
- } break;
- default : {
- const ulongT siz = size();
- ulongT i0 = 0, i = 0;
- do {
- while (i<siz && (*this)[i]==value) ++i;
- if (i>i0) {
- if (keep_values) CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res);
- i0 = i;
- }
- while (i<siz && (*this)[i]!=value) ++i;
- if (i>i0) { CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res); i0 = i; }
- } while (i<siz);
- }
- }
- } else { // Split according to multiple values
- ulongT j = 0;
- switch (_axis) {
- case 'x' : {
- unsigned int i0 = 0, i1 = 0, i = 0;
- do {
- if ((Tt)(*this)(i)==(Tt)*values) {
- i1 = i; j = 0;
- while (i<_width && (Tt)(*this)(i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
- i-=(unsigned int)j;
- if (i>i1) {
- if (i1>i0) get_columns(i0,i1 - 1).move_to(res);
- if (keep_values) get_columns(i1,i - 1).move_to(res);
- i0 = i;
- } else ++i;
- } else ++i;
- } while (i<_width);
- if (i0<_width) get_columns(i0,width() - 1).move_to(res);
- } break;
- case 'y' : {
- unsigned int i0 = 0, i1 = 0, i = 0;
- do {
- if ((Tt)(*this)(0,i)==(Tt)*values) {
- i1 = i; j = 0;
- while (i<_height && (Tt)(*this)(0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
- i-=(unsigned int)j;
- if (i>i1) {
- if (i1>i0) get_rows(i0,i1 - 1).move_to(res);
- if (keep_values) get_rows(i1,i - 1).move_to(res);
- i0 = i;
- } else ++i;
- } else ++i;
- } while (i<_height);
- if (i0<_height) get_rows(i0,height() - 1).move_to(res);
- } break;
- case 'z' : {
- unsigned int i0 = 0, i1 = 0, i = 0;
- do {
- if ((Tt)(*this)(0,0,i)==(Tt)*values) {
- i1 = i; j = 0;
- while (i<_depth && (Tt)(*this)(0,0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
- i-=(unsigned int)j;
- if (i>i1) {
- if (i1>i0) get_slices(i0,i1 - 1).move_to(res);
- if (keep_values) get_slices(i1,i - 1).move_to(res);
- i0 = i;
- } else ++i;
- } else ++i;
- } while (i<_depth);
- if (i0<_depth) get_slices(i0,depth() - 1).move_to(res);
- } break;
- case 'c' : {
- unsigned int i0 = 0, i1 = 0, i = 0;
- do {
- if ((Tt)(*this)(0,0,0,i)==(Tt)*values) {
- i1 = i; j = 0;
- while (i<_spectrum && (Tt)(*this)(0,0,0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
- i-=(unsigned int)j;
- if (i>i1) {
- if (i1>i0) get_channels(i0,i1 - 1).move_to(res);
- if (keep_values) get_channels(i1,i - 1).move_to(res);
- i0 = i;
- } else ++i;
- } else ++i;
- } while (i<_spectrum);
- if (i0<_spectrum) get_channels(i0,spectrum() - 1).move_to(res);
- } break;
- default : {
- const ulongT siz = size();
- ulongT i0 = 0, i1 = 0, i = 0;
- do {
- if ((Tt)(*this)[i]==(Tt)*values) {
- i1 = i; j = 0;
- while (i<siz && (Tt)(*this)[i]==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
- i-=(unsigned int)j;
- if (i>i1) {
- if (i1>i0) CImg<T>(_data + i0,1,(unsigned int)(i1 - i0)).move_to(res);
- if (keep_values) CImg<T>(_data + i1,1,(unsigned int)(i - i1)).move_to(res);
- i0 = i;
- } else ++i;
- } else ++i;
- } while (i<siz);
- if (i0<siz) CImg<T>(_data + i0,1,(unsigned int)(siz - i0)).move_to(res);
- } break;
- }
- }
- return res;
- }
- //! Append two images along specified axis.
- /**
- \param img Image to append with instance image.
- \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
- \param align Append alignment in \c [0,1].
- **/
- template<typename t>
- CImg<T>& append(const CImg<t>& img, const char axis='x', const float align=0) {
- if (is_empty()) return assign(img,false);
- if (!img) return *this;
- return CImgList<T>(*this,true).insert(img).get_append(axis,align).move_to(*this);
- }
- //! Append two images along specified axis \specialization.
- CImg<T>& append(const CImg<T>& img, const char axis='x', const float align=0) {
- if (is_empty()) return assign(img,false);
- if (!img) return *this;
- return CImgList<T>(*this,img,true).get_append(axis,align).move_to(*this);
- }
- //! Append two images along specified axis \const.
- template<typename t>
- CImg<_cimg_Tt> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
- if (is_empty()) return +img;
- if (!img) return +*this;
- return CImgList<_cimg_Tt>(*this,true).insert(img).get_append(axis,align);
- }
- //! Append two images along specified axis \specialization.
- CImg<T> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
- if (is_empty()) return +img;
- if (!img) return +*this;
- return CImgList<T>(*this,img,true).get_append(axis,align);
- }
- //@}
- //---------------------------------------
- //
- //! \name Filtering / Transforms
- //@{
- //---------------------------------------
- //! Correlate image by a kernel.
- /**
- \param kernel = the correlation kernel.
- \param boundary_conditions Boundary condition. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
- \param is_normalized = enable local normalization.
- \param channel_mode Channel processing mode.
- Can be { 0=all | 1=one for one (default) | 2=partial sum | 3=full sum }.
- \param xcenter X-coordinate of the kernel center (~0U>>1 means 'centered').
- \param ycenter Y-coordinate of the kernel center (~0U>>1 means 'centered').
- \param zcenter Z-coordinate of the kernel center (~0U>>1 means 'centered').
- \param xstart Starting X-coordinate of the instance image.
- \param ystart Starting Y-coordinate of the instance image.
- \param zstart Starting Z-coordinate of the instance image.
- \param xend Ending X-coordinate of the instance image.
- \param yend Ending Y-coordinate of the instance image.
- \param zend Ending Z-coordinate of the instance image.
- \param xstride Stride along the X-axis.
- \param ystride Stride along the Y-axis.
- \param zstride Stride along the Z-axis.
- \param xdilation Dilation along the X-axis.
- \param ydilation Dilation along the Y-axis.
- \param zdilation Dilation along the Z-axis.
- \param interpolation_type Can be { false=nearest | true=linear }.
- \note
- - The correlation of the image instance \p *this by the kernel \p kernel is defined to be:
- res(x,y,z) = sum_{i,j,k} (*this)(\alpha_x\;x + \beta_x\;(i - c_x),\alpha_y\;y + \beta_y\;(j -
- c_y),\alpha_z\;z + \beta_z\;(k - c_z))*kernel(i,j,k).
- **/
- template<typename t>
- CImg<T>& correlate(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
- const bool is_normalized=false, const unsigned int channel_mode=1,
- const int xcenter=(int)(~0U>>1),
- const int ycenter=(int)(~0U>>1),
- const int zcenter=(int)(~0U>>1),
- const int xstart=0,
- const int ystart=0,
- const int zstart=0,
- const int xend=(int)(~0U>>1),
- const int yend=(int)(~0U>>1),
- const int zend=(int)(~0U>>1),
- const float xstride=1, const float ystride=1, const float zstride=1,
- const float xdilation=1, const float ydilation=1, const float zdilation=1,
- const bool interpolation_type=false) {
- if (is_empty() || !kernel) return *this;
- return get_correlate(kernel,boundary_conditions,is_normalized,channel_mode,
- xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend,
- xstride,ystride,zstride,xdilation,ydilation,zdilation,
- interpolation_type).move_to(*this);
- }
- template<typename t>
- CImg<_cimg_Ttfloat> get_correlate(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
- const bool is_normalized=false, const unsigned int channel_mode=1,
- const int xcenter=(int)(~0U>>1),
- const int ycenter=(int)(~0U>>1),
- const int zcenter=(int)(~0U>>1),
- const int xstart=0,
- const int ystart=0,
- const int zstart=0,
- const int xend=(int)(~0U>>1),
- const int yend=(int)(~0U>>1),
- const int zend=(int)(~0U>>1),
- const float xstride=1, const float ystride=1, const float zstride=1,
- const float xdilation=1, const float ydilation=1, const float zdilation=1,
- const bool interpolation_type=false) const {
- return _correlate(kernel,boundary_conditions,is_normalized,channel_mode,
- xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend,
- xstride,ystride,zstride,xdilation,ydilation,zdilation,
- interpolation_type,false);
- }
- //! Correlate image by a kernel \newinstance.
- template<typename t>
- CImg<_cimg_Ttfloat> _correlate(const CImg<t>& kernel, const unsigned int boundary_conditions,
- const bool is_normalized, const unsigned int channel_mode,
- const int xcenter, const int ycenter, const int zcenter,
- const int xstart, const int ystart, const int zstart,
- const int xend, const int yend, const int zend,
- const float xstride, const float ystride, const float zstride,
- const float xdilation, const float ydilation, const float zdilation,
- const bool interpolation_type, const bool is_convolve) const {
- typedef _cimg_Ttfloat Ttfloat;
- CImg<Ttfloat> res;
- _cimg_abort_init_openmp;
- cimg_abort_init;
- if (xstart>xend || ystart>yend || zstart>zend)
- throw CImgArgumentException(_cimg_instance
- "%s(): Invalid xyz-start/end arguments (start = (%d,%d,%d), end = (%d,%d,%d)).",
- cimg_instance,
- is_convolve?"convolve":"correlate",
- xstart,ystart,zstart,xend,yend,zend);
- if (xstride<=0 || ystride<=0 || zstride<=0)
- throw CImgArgumentException(_cimg_instance
- "%s(): Invalid stride arguments (%g,%g,%g).",
- cimg_instance,
- is_convolve?"convolve":"correlate",
- xstride,ystride,zstride);
- if (is_empty() || !kernel) return *this;
- int
- _xcenter = xcenter==(int)(~0U>>1)?kernel.width()/2 - 1 + (kernel.width()%2):xcenter,
- _ycenter = ycenter==(int)(~0U>>1)?kernel.height()/2 - 1 + (kernel.height()%2):ycenter,
- _zcenter = zcenter==(int)(~0U>>1)?kernel.depth()/2 - 1 + (kernel.depth()%2):zcenter;
- float _xdilation = xdilation, _ydilation = ydilation, _zdilation = zdilation;
- CImg<t> _kernel;
- if (is_convolve) { // If convolution, go back to correlation
- if (kernel.size()/kernel.spectrum()<=27) {
- _kernel = CImg<t>(kernel._data,kernel.size()/kernel._spectrum,1,1,kernel._spectrum,true).
- get_mirror('x').resize(kernel,-1);
- _xcenter = kernel.width() - 1 - _xcenter;
- _ycenter = kernel.height() - 1 - _ycenter;
- _zcenter = kernel.depth() - _zcenter - 1;
- } else { _kernel = kernel.get_shared(); _xdilation*=-1; _ydilation*=-1; _zdilation*=-1; }
- } else _kernel = kernel.get_shared();
- const int
- _xend = xend==(int)(~0U>>1)?width() - 1:xend,
- _yend = yend==(int)(~0U>>1)?height() - 1:yend,
- _zend = zend==(int)(~0U>>1)?depth() - 1:zend,
- i_xstride = (int)cimg::round(xstride),
- i_ystride = (int)cimg::round(ystride),
- i_zstride = (int)cimg::round(zstride),
- i_xdilation = (int)cimg::round(_xdilation),
- i_ydilation = (int)cimg::round(_ydilation),
- i_zdilation = (int)cimg::round(_zdilation),
- res_width = _xend - xstart + 1,
- res_height = _yend - ystart + 1,
- res_depth = _zend - zstart + 1,
- smin = std::min(spectrum(),_kernel.spectrum()),
- smax = std::max(spectrum(),_kernel.spectrum()),
- cend = !channel_mode?spectrum()*_kernel.spectrum():smax;
- const ulongT
- res_wh = (ulongT)res_width*res_height,
- res_whd = res_wh*res_depth;
- if (!res_whd) return CImg<Ttfloat>();
- res.assign(res_width,res_height,res_depth,
- !channel_mode?_spectrum*_kernel._spectrum:
- channel_mode==1?smax:
- channel_mode==2?(int)std::ceil((float)smax/smin):1);
- if (channel_mode>=2) res.fill(0);
- const ulongT res_siz = res_whd*res._spectrum;
- const bool
- #if cimg_use_openmp==1
- is_master_thread = !omp_get_thread_num(),
- #else
- is_master_thread = true,
- #endif
- is_outer_parallel = is_master_thread &&
- (res._spectrum>=cimg::nb_cpus() || (res_siz<=(cimg_openmp_sizefactor)*32768 && res._spectrum>1)),
- is_inner_parallel = is_master_thread &&
- (!is_outer_parallel && res_whd>=(cimg_openmp_sizefactor)*32768),
- is_int_stride_dilation = xstride==i_xstride && ystride==i_ystride && zstride==i_zstride &&
- _xdilation==i_xdilation && _ydilation==i_ydilation && _zdilation==i_zdilation;
- cimg::unused(is_inner_parallel,is_outer_parallel);
- const int
- w = width(), h = height(), d = depth(),
- w1 = w - 1, h1 = h - 1, d1 = d - 1,
- w2 = 2*w, h2 = 2*h, d2 = 2*d;
- const ulongT wh = (ulongT)w*h, whd = wh*d;
- // Reshape kernel to enable optimizations for a few cases.
- if (boundary_conditions==1 &&
- _kernel._width>1 && _kernel._height>1 &&
- ((_kernel._depth==1 && _kernel._width<=5 && _kernel._height<=5) ||
- (_kernel._depth<=3 && _kernel._width<=3 && _kernel._height<=3)) &&
- xstart>=0 && ystart>=0 && zstart>=0 &&
- _xend<width() && _yend<height() && _zend<depth() &&
- is_int_stride_dilation &&
- xstride==1 && ystride==1 && zstride==1 &&
- i_xdilation>=0 && i_ydilation>=0 && i_zdilation>=0) {
- const unsigned int M = cimg::max(_kernel._width,_kernel._height,_kernel._depth);
- _kernel.assign(_kernel.get_resize(M + 1 - (M%2),M + 1 - (M%2),_kernel._depth>1?M + 1 - (M%2):1,-100,
- 0,0,
- 1,1,1),false);
- _xcenter = _ycenter = (int)M/2;
- if (_kernel._depth>1) _ycenter = (int)M/2;
- }
- // Optimized version for a few particular cases (3x3, 5x5 and 3x3x3 kernels, with a few other conditions).
- if (boundary_conditions==1 &&
- _kernel._width==_kernel._height &&
- ((_kernel._depth==1 && (_kernel._width==3 || _kernel._width==5)) ||
- (_kernel._depth==_kernel._width && _kernel._width==3)) &&
- _xcenter==_kernel.width()/2 && _ycenter==_kernel.height()/2 && _zcenter==_kernel.depth()/2 &&
- xstart>=0 && ystart>=0 && zstart>=0 &&
- _xend<width() && _yend<height() && _zend<depth() &&
- is_int_stride_dilation &&
- xstride==1 && ystride==1 && zstride==1 &&
- i_xdilation>=0 && i_ydilation>=0 && i_zdilation>=0) {
- switch (_kernel._depth) {
- case 3 : { // 3x3x3 centered kernel
- cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
- for (int c = 0; c<cend; ++c) _cimg_abort_try_openmp2 {
- cimg_abort_test2;
- const CImg<T> I = get_shared_channel(c%_spectrum);
- const CImg<t> K = _kernel.get_shared_channel(!channel_mode?c/_spectrum:c%_kernel._spectrum);
- CImg<Ttfloat> _resu = channel_mode<=1?res.get_shared_channel(c):
- CImg<Ttfloat>(res.width(),res.height(),res.depth(),1);
- if (is_normalized) {
- const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M;
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
- cimg_forXYZ(res,X,Y,Z) {
- const int
- x = xstart + X, y = ystart + Y, z = zstart + Z,
- px = x - i_xdilation>0?x - i_xdilation:0, nx = x + i_xdilation<w1?x + i_xdilation:w1,
- py = y - i_ydilation>0?y - i_ydilation:0, ny = y + i_ydilation<h1?y + i_ydilation:h1,
- pz = z - i_zdilation>0?z - i_zdilation:0, nz = z + i_zdilation<d1?z + i_zdilation:d1;
- const Ttfloat N = M2*(cimg::sqr(I(px,py,pz)) + cimg::sqr(I(x,py,pz)) + cimg::sqr(I(nx,py,pz)) +
- cimg::sqr(I(px,y,pz)) + cimg::sqr(I(x,y,pz)) + cimg::sqr(I(nx,y,pz)) +
- cimg::sqr(I(px,ny,pz)) + cimg::sqr(I(x,ny,pz)) + cimg::sqr(I(nx,ny,pz)) +
- cimg::sqr(I(px,py,z)) + cimg::sqr(I(x,py,z)) + cimg::sqr(I(nx,py,z)) +
- cimg::sqr(I(px,y,z)) + cimg::sqr(I(x,y,z)) + cimg::sqr(I(nx,y,z)) +
- cimg::sqr(I(px,ny,z)) + cimg::sqr(I(x,ny,z)) + cimg::sqr(I(nx,ny,z)) +
- cimg::sqr(I(px,py,nz)) + cimg::sqr(I(x,py,nz)) + cimg::sqr(I(nx,py,nz)) +
- cimg::sqr(I(px,y,nz)) + cimg::sqr(I(x,y,nz)) + cimg::sqr(I(nx,y,nz)) +
- cimg::sqr(I(px,ny,nz)) + cimg::sqr(I(x,ny,nz)) + cimg::sqr(I(nx,ny,nz)));
- _resu(X,Y,Z) = (Ttfloat)(N?(K[0]*I(px,py,pz) + K[1]*I(x,py,pz) + K[2]*I(nx,py,pz) +
- K[3]*I(px,y,pz) + K[4]*I(x,y,pz) + K[5]*I(nx,y,pz) +
- K[6]*I(px,ny,pz) + K[7]*I(x,ny,pz) + K[8]*I(nx,ny,pz) +
- K[9]*I(px,py,z) + K[10]*I(x,py,z) + K[11]*I(nx,py,z) +
- K[12]*I(px,y,z) + K[13]*I(x,y,z) + K[14]*I(nx,y,z) +
- K[15]*I(px,ny,z) + K[16]*I(x,ny,z) + K[17]*I(nx,ny,z) +
- K[18]*I(px,py,nz) + K[19]*I(x,py,nz) + K[20]*I(nx,py,nz) +
- K[21]*I(px,y,nz) + K[22]*I(x,y,nz) + K[23]*I(nx,y,nz) +
- K[24]*I(px,ny,nz) + K[25]*I(x,ny,nz) + K[26]*I(nx,ny,nz))/std::sqrt(N):0);
- }
- } else {
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
- cimg_forXYZ(res,X,Y,Z) {
- const int
- x = xstart + X, y = ystart + Y, z = zstart + Z,
- px = x - i_xdilation>0?x - i_xdilation:0, nx = x + i_xdilation<w1?x + i_xdilation:w1,
- py = y - i_ydilation>0?y - i_ydilation:0, ny = y + i_ydilation<h1?y + i_ydilation:h1,
- pz = z - i_zdilation>0?z - i_zdilation:0, nz = z + i_zdilation<d1?z + i_zdilation:d1;
- _resu(X,Y,Z) = (Ttfloat)(K[0]*I(px,py,pz) + K[1]*I(x,py,pz) + K[2]*I(nx,py,pz) +
- K[3]*I(px,y,pz) + K[4]*I(x,y,pz) + K[5]*I(nx,y,pz) +
- K[6]*I(px,ny,pz) + K[7]*I(x,ny,pz) + K[8]*I(nx,ny,pz) +
- K[9]*I(px,py,z) + K[10]*I(x,py,z) + K[11]*I(nx,py,z) +
- K[12]*I(px,y,z) + K[13]*I(x,y,z) + K[14]*I(nx,y,z) +
- K[15]*I(px,ny,z) + K[16]*I(x,ny,z) + K[17]*I(nx,ny,z) +
- K[18]*I(px,py,nz) + K[19]*I(x,py,nz) + K[20]*I(nx,py,nz) +
- K[21]*I(px,y,nz) + K[22]*I(x,y,nz) + K[23]*I(nx,y,nz) +
- K[24]*I(px,ny,nz) + K[25]*I(x,ny,nz) + K[26]*I(nx,ny,nz));
- }
- }
- if (channel_mode==2)
- cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(c/smin)+=_resu;
- else if (channel_mode==3)
- cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(0)+=_resu;
- } _cimg_abort_catch_openmp2
- } break;
- default :
- case 1 :
- switch (_kernel._width) {
- case 5 : { // 5x5 centered kernel
- cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
- for (int c = 0; c<cend; ++c) _cimg_abort_try_openmp2 {
- cimg_abort_test2;
- const CImg<T> I = get_shared_channel(c%_spectrum);
- const CImg<t> K = _kernel.get_shared_channel(!channel_mode?c/_spectrum:c%_kernel._spectrum);
- CImg<Ttfloat> _resu = channel_mode<=1?res.get_shared_channel(c):
- CImg<Ttfloat>(res.width(),res.height(),res.depth(),1);
- if (is_normalized) {
- const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M;
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
- cimg_forXYZ(res,X,Y,z) {
- const int
- x = xstart + X, y = ystart + Y,
- px = x - i_xdilation>0?x - i_xdilation:0, bx = px - i_xdilation>0?px - i_xdilation:0,
- nx = x + i_xdilation<w1?x + i_xdilation:w1, ax = nx + i_xdilation<w1?nx + i_xdilation:w1,
- py = y - i_ydilation>0?y - i_ydilation:0, by = py - i_ydilation>0?py - i_ydilation:0,
- ny = y + i_ydilation<h1?y + i_ydilation:h1, ay = ny + i_ydilation<h1?ny + i_ydilation:h1;
- const Ttfloat N = M2*(cimg::sqr(I(bx,by,z)) + cimg::sqr(I(px,by,z)) + cimg::sqr(I(x,by,z)) +
- cimg::sqr(I(nx,by,z)) + cimg::sqr(I(ax,by,z)) +
- cimg::sqr(I(bx,py,z)) + cimg::sqr(I(px,py,z)) + cimg::sqr(I(x,py,z)) +
- cimg::sqr(I(nx,py,z)) + cimg::sqr(I(ax,py,z)) +
- cimg::sqr(I(bx,y,z)) + cimg::sqr(I(px,y,z)) + cimg::sqr(I(x,y,z)) +
- cimg::sqr(I(nx,y,z)) + cimg::sqr(I(ax,y,z)) +
- cimg::sqr(I(bx,ny,z)) + cimg::sqr(I(px,ny,z)) + cimg::sqr(I(x,ny,z)) +
- cimg::sqr(I(nx,ny,z)) + cimg::sqr(I(ax,ny,z)) +
- cimg::sqr(I(bx,ay,z)) + cimg::sqr(I(px,ay,z)) + cimg::sqr(I(x,ay,z)) +
- cimg::sqr(I(nx,ay,z)) + cimg::sqr(I(ax,ay,z)));
- _resu(X,Y,z) = (Ttfloat)(N?(K[0]*I(bx,by,z) + K[1]*I(px,by,z) + K[2]*I(x,by,z) +
- K[3]*I(nx,by,z) + K[4]*I(ax,by,z) +
- K[5]*I(bx,py,z) + K[6]*I(px,py,z) + K[7]*I(x,py,z) +
- K[8]*I(nx,py,z) + K[9]*I(ax,py,z) +
- K[10]*I(bx,y,z) + K[11]*I(px,y,z) + K[12]*I(x,y,z) +
- K[13]*I(nx,y,z) + K[14]*I(ax,y,z) +
- K[15]*I(bx,ny,z) + K[16]*I(px,ny,z) + K[17]*I(x,ny,z) +
- K[18]*I(nx,ny,z) + K[19]*I(ax,ny,z) +
- K[20]*I(bx,ay,z) + K[21]*I(px,ay,z) + K[22]*I(x,ay,z) +
- K[23]*I(nx,ay,z) + K[24]*I(ax,ay,z))/std::sqrt(N):0);
- }
- } else {
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
- cimg_forXYZ(res,X,Y,z) {
- const int
- x = xstart + X, y = ystart + Y,
- px = x - i_xdilation>0?x - i_xdilation:0, bx = px - i_xdilation>0?px - i_xdilation:0,
- nx = x + i_xdilation<w1?x + i_xdilation:w1, ax = nx + i_xdilation<w1?nx + i_xdilation:w1,
- py = y - i_ydilation>0?y - i_ydilation:0, by = py - i_ydilation>0?py - i_ydilation:0,
- ny = y + i_ydilation<h1?y + i_ydilation:h1, ay = ny + i_ydilation<h1?ny + i_ydilation:h1;
- _resu(X,Y,z) = (Ttfloat)(K[0]*I(bx,by,z) + K[1]*I(px,by,z) + K[2]*I(x,by,z) +
- K[3]*I(nx,by,z) + K[4]*I(ax,by,z) +
- K[5]*I(bx,py,z) + K[6]*I(px,py,z) + K[7]*I(x,py,z) +
- K[8]*I(nx,py,z) + K[9]*I(ax,py,z) +
- K[10]*I(bx,y,z) + K[11]*I(px,y,z) + K[12]*I(x,y,z) +
- K[13]*I(nx,y,z) + K[14]*I(ax,y,z) +
- K[15]*I(bx,ny,z) + K[16]*I(px,ny,z) + K[17]*I(x,ny,z) +
- K[18]*I(nx,ny,z) + K[19]*I(ax,ny,z) +
- K[20]*I(bx,ay,z) + K[21]*I(px,ay,z) + K[22]*I(x,ay,z) +
- K[23]*I(nx,ay,z) + K[24]*I(ax,ay,z));
- }
- }
- if (channel_mode==2)
- cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(c/smin)+=_resu;
- else if (channel_mode==3)
- cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(0)+=_resu;
- } _cimg_abort_catch_openmp2
- } break;
- case 3 : { // 3x3 centered kernel
- cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
- for (int c = 0; c<cend; ++c) _cimg_abort_try_openmp2 {
- cimg_abort_test2;
- const CImg<T> I = get_shared_channel(c%_spectrum);
- const CImg<t> K = _kernel.get_shared_channel(!channel_mode?c/_spectrum:c%_kernel._spectrum);
- CImg<Ttfloat> _resu = channel_mode<=1?res.get_shared_channel(c):
- CImg<Ttfloat>(res.width(),res.height(),res.depth(),1);
- if (is_normalized) {
- const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M;
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
- cimg_forXYZ(res,X,Y,z) {
- const int
- x = xstart + X, y = ystart + Y,
- px = x - i_xdilation>0?x - i_xdilation:0, nx = x + i_xdilation<w1?x + i_xdilation:w1,
- py = y - i_ydilation>0?y - i_ydilation:0, ny = y + i_ydilation<h1?y + i_ydilation:h1;
- const Ttfloat N = M2*(cimg::sqr(I(px,py,z)) + cimg::sqr(I(x,py,z)) + cimg::sqr(I(nx,py,z)) +
- cimg::sqr(I(px,y,z)) + cimg::sqr(I(x,y,z)) + cimg::sqr(I(nx,y,z)) +
- cimg::sqr(I(px,ny,z)) + cimg::sqr(I(x,ny,z)) + cimg::sqr(I(nx,ny,z)));
- _resu(X,Y,z) = (Ttfloat)(N?(K[0]*I(px,py,z) + K[1]*I(x,py,z) + K[2]*I(nx,py,z) +
- K[3]*I(px,y,z) + K[4]*I(x,y,z) + K[5]*I(nx,y,z) +
- K[6]*I(px,ny,z) + K[7]*I(x,ny,z) + K[8]*I(nx,ny,z))/std::sqrt(N):0);
- }
- } else {
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
- cimg_forXYZ(res,X,Y,z) {
- const int
- x = xstart + X, y = ystart + Y,
- px = x - i_xdilation>0?x - i_xdilation:0, nx = x + i_xdilation<w1?x + i_xdilation:w1,
- py = y - i_ydilation>0?y - i_ydilation:0, ny = y + i_ydilation<h1?y + i_ydilation:h1;
- _resu(X,Y,z) = (Ttfloat)(K[0]*I(px,py,z) + K[1]*I(x,py,z) + K[2]*I(nx,py,z) +
- K[3]*I(px,y,z) + K[4]*I(x,y,z) + K[5]*I(nx,y,z) +
- K[6]*I(px,ny,z) + K[7]*I(x,ny,z) + K[8]*I(nx,ny,z));
- }
- }
- if (channel_mode==2)
- cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(c/smin)+=_resu;
- else if (channel_mode==3)
- cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(0)+=_resu;
- } _cimg_abort_catch_openmp2
- } break;
- }
- }
- } else if (_kernel._width==1 && _kernel._height==1 && _kernel._depth==1 &&
- !_xcenter && !_ycenter && !_zcenter &&
- xstart>=0 && ystart>=0 && zstart>=0 &&
- _xend<width() && _yend<height() && _zend<depth() &&
- xstride==1 && ystride==1 && zstride==1) {
- // Special optimization for 1x1 kernel.
- cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
- for (int c = 0; c<cend; ++c) {
- const t valK = _kernel[!channel_mode?c/_spectrum:c%_kernel._spectrum];
- CImg<T> I = get_crop(xstart,ystart,zstart,c%_spectrum,_xend,_yend,_zend,c%_spectrum);
- if (valK!=1) I*=valK;
- if (is_normalized) I.sign();
- switch (channel_mode) {
- case 0 : // All
- case 1 : // One for one
- res.get_shared_channel(c) = I;
- break;
- case 2 : // Partial sum
- cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(c/smin)+=I;
- break;
- case 3 : // Full sum
- cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(0)+=I;
- break;
- }
- }
- } else { // Generic version
- cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
- for (int c = 0; c<cend; ++c) _cimg_abort_try_openmp2 {
- cimg_abort_test2;
- const CImg<T> I = get_shared_channel(c%_spectrum);
- const CImg<t> K = _kernel.get_shared_channel(!channel_mode?c/_spectrum:c%_kernel._spectrum);
- CImg<Ttfloat> _resu = channel_mode<=1?res.get_shared_channel(c):
- CImg<Ttfloat>(res.width(),res.height(),res.depth(),1);
- Ttfloat M = 0, M2 = 0;
- if (is_normalized) { M = (Ttfloat)K.magnitude(2); M2 = cimg::sqr(M); }
- #define _cimg_correlate_x_int const int ix = xstart + i_xstride*x + i_xdilation*(p - _xcenter)
- #define _cimg_correlate_y_int const int iy = ystart + i_ystride*y + i_ydilation*(q - _ycenter)
- #define _cimg_correlate_z_int const int iz = zstart + i_zstride*z + i_zdilation*(r - _zcenter)
- #define _cimg_correlate_x_float const float ix = xstart + xstride*x + _xdilation*(p - _xcenter)
- #define _cimg_correlate_y_float const float iy = ystart + ystride*y + _ydilation*(q - _ycenter)
- #define _cimg_correlate_z_float const float iz = zstart + zstride*z + _zdilation*(r - _zcenter)
- #define _cimg_correlate_x_int_dirichlet const bool is_in_x = ix>=0 && ix<w
- #define _cimg_correlate_y_int_dirichlet const bool is_in_y = iy>=0 && iy<h
- #define _cimg_correlate_z_int_dirichlet const bool is_in_z = iz>=0 && iz<d
- #define _cimg_correlate_x_float_dirichlet _cimg_correlate_x_int_dirichlet
- #define _cimg_correlate_y_float_dirichlet _cimg_correlate_y_int_dirichlet
- #define _cimg_correlate_z_float_dirichlet _cimg_correlate_z_int_dirichlet
- #define _cimg_correlate_x_int_neumann const int nix = cimg::cut(ix,0,w1)
- #define _cimg_correlate_y_int_neumann const int niy = cimg::cut(iy,0,h1)
- #define _cimg_correlate_z_int_neumann const int niz = cimg::cut(iz,0,d1)
- #define _cimg_correlate_x_float_neumann const float nix = cimg::cut(ix,0,w1)
- #define _cimg_correlate_y_float_neumann const float niy = cimg::cut(iy,0,h1)
- #define _cimg_correlate_z_float_neumann const float niz = cimg::cut(iz,0,d1)
- #define _cimg_correlate_x_int_periodic const int nix = cimg::mod(ix,w)
- #define _cimg_correlate_y_int_periodic const int niy = cimg::mod(iy,h)
- #define _cimg_correlate_z_int_periodic const int niz = cimg::mod(iz,d)
- #define _cimg_correlate_x_float_periodic const float nix = cimg::mod(ix,w)
- #define _cimg_correlate_y_float_periodic const float niy = cimg::mod(iy,h)
- #define _cimg_correlate_z_float_periodic const float niz = cimg::mod(iz,d)
- #define _cimg_correlate_x_int_mirror const int mx = cimg::mod(ix,w2), nix = mx<w?mx:w2 - mx - 1
- #define _cimg_correlate_y_int_mirror const int my = cimg::mod(iy,h2), niy = my<h?my:h2 - my - 1
- #define _cimg_correlate_z_int_mirror const int mz = cimg::mod(iz,d2), niz = mz<d?mz:d2 - mz - 1
- #define _cimg_correlate_x_float_mirror const float mx = cimg::mod(ix,w2), nix = mx<w?mx:w2 - mx - 1
- #define _cimg_correlate_y_float_mirror const float my = cimg::mod(iy,h2), niy = my<h?my:h2 - my - 1
- #define _cimg_correlate_z_float_mirror const float mz = cimg::mod(iz,d2), niz = mz<d?mz:d2 - mz - 1
- #define _cimg_correlate(type,boundary,access) \
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) \
- cimg_forXYZ(res,x,y,z) { \
- Ttfloat val = 0; \
- const t *pK = K._data; \
- cimg_forZ(_kernel,r) { _cimg_correlate_z_##type; _cimg_correlate_z_##type##_##boundary; \
- cimg_forY(_kernel,q) { _cimg_correlate_y_##type; _cimg_correlate_y_##type##_##boundary; \
- cimg_forX(_kernel,p) { _cimg_correlate_x_##type; _cimg_correlate_x_##type##_##boundary; \
- val+=*(pK++)*(access); \
- } \
- } \
- } \
- _resu(x,y,z,0,res_wh,res_whd) = val; \
- }
- #define _cimg_correlate_n(type,boundary,access) \
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) \
- cimg_forXYZ(res,x,y,z) { \
- Ttfloat val = 0, N = 0; \
- const t *pK = K._data; \
- cimg_forZ(_kernel,r) { _cimg_correlate_z_##type; _cimg_correlate_z_##type##_##boundary; \
- cimg_forY(_kernel,q) { _cimg_correlate_y_##type; _cimg_correlate_y_##type##_##boundary; \
- cimg_forX(_kernel,p) { _cimg_correlate_x_##type; _cimg_correlate_x_##type##_##boundary; \
- Ttfloat _val = access; \
- val+=*(pK++)*_val; \
- _val*=_val; N+=_val; \
- } \
- } \
- } \
- N*=M2; _resu(x,y,z,0,res_wh,res_whd) = N?val/std::sqrt(N):0; \
- }
- if (is_normalized) { // Normalized convolution/correlation
- if (is_int_stride_dilation) // Integer stride and dilation
- switch (boundary_conditions) {
- case 0 : // Dirichlet
- _cimg_correlate_n(int,dirichlet,is_in_x && is_in_y && is_in_z?I(ix,iy,iz,0,wh,whd):(T)0);
- break;
- case 1 : // Neumann
- _cimg_correlate_n(int,neumann,I(nix,niy,niz,0,wh,whd));
- break;
- case 2 : // Periodic
- _cimg_correlate_n(int,periodic,I(nix,niy,niz,0,wh,whd));
- break;
- case 3 : // Mirror
- _cimg_correlate_n(int,mirror,I(nix,niy,niz,0,wh,whd));
- break;
- }
- else if (interpolation_type) // Non-integer stride or dilation, linear interpolation
- switch (boundary_conditions) {
- case 0 : // Dirichlet
- _cimg_correlate_n(float,dirichlet,is_in_x && is_in_y && is_in_z?I.linear_atXYZ(ix,iy,iz,0,0):0);
- break;
- case 1 : // Neumann
- _cimg_correlate_n(float,neumann,I._linear_atXYZ(nix,niy,niz,0));
- break;
- case 2 : // Periodic
- _cimg_correlate_n(float,periodic,I._linear_atXYZ(nix,niy,niz,0));
- break;
- case 3 : // Mirror
- _cimg_correlate_n(float,mirror,I._linear_atXYZ(nix,niy,niz,0));
- break;
- }
- else // Non-integer stride or dilation, nearest-neighbor interpolation
- switch (boundary_conditions) {
- case 0 : // Dirichlet
- _cimg_correlate_n(float,dirichlet,is_in_x && is_in_y && is_in_z?I((int)ix,(int)iy,(int)iz,0,0):(T)0);
- break;
- case 1 : // Neumann
- _cimg_correlate_n(float,neumann,I((int)nix,(int)niy,(int)niz,0));
- break;
- case 2 : // Periodic
- _cimg_correlate_n(float,periodic,I((int)nix,(int)niy,(int)niz,0));
- break;
- case 3 : // Mirror
- _cimg_correlate_n(float,mirror,I((int)nix,(int)niy,(int)niz,0));
- break;
- }
- } else { // Standard convolution/correlation
- if (is_int_stride_dilation) // Integer stride and dilation
- switch (boundary_conditions) {
- case 0 : // Dirichlet
- _cimg_correlate(int,dirichlet,is_in_x && is_in_y && is_in_z?I(ix,iy,iz,0,wh,whd):(T)0);
- break;
- case 1 : // Neumann
- _cimg_correlate(int,neumann,I(nix,niy,niz,0,wh,whd));
- break;
- case 2 : // Periodic
- _cimg_correlate(int,periodic,I(nix,niy,niz,0,wh,whd));
- break;
- case 3 : // Mirror
- _cimg_correlate(int,mirror,I(nix,niy,niz,0,wh,whd));
- break;
- }
- else if (interpolation_type) // Non-integer stride or dilation, linear interpolation
- switch (boundary_conditions) {
- case 0 : // Dirichlet
- _cimg_correlate(float,dirichlet,is_in_x && is_in_y && is_in_z?I.linear_atXYZ(ix,iy,iz,0,0):0);
- break;
- case 1 : // Neumann
- _cimg_correlate(float,neumann,I._linear_atXYZ(nix,niy,niz,0));
- break;
- case 2 : // Periodic
- _cimg_correlate(float,periodic,I._linear_atXYZ(nix,niy,niz,0));
- break;
- case 3 : // Mirror
- _cimg_correlate(float,mirror,I._linear_atXYZ(nix,niy,niz,0));
- break;
- }
- else // Non-integer stride or dilation, nearest-neighbor interpolation
- switch (boundary_conditions) {
- case 0 : // Dirichlet
- _cimg_correlate(float,dirichlet,is_in_x && is_in_y && is_in_z?I((int)ix,(int)iy,(int)iz,0,0):(T)0);
- break;
- case 1 : // Neumann
- _cimg_correlate(float,neumann,I((int)nix,(int)niy,(int)niz,0));
- break;
- case 2 : // Periodic
- _cimg_correlate(float,periodic,I((int)nix,(int)niy,(int)niz,0));
- break;
- case 3 : // Mirror
- _cimg_correlate(float,mirror,I((int)nix,(int)niy,(int)niz,0));
- break;
- }
- }
- if (channel_mode==2)
- cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(c/smin)+=_resu;
- else if (channel_mode==3)
- cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(0)+=_resu;
- } _cimg_abort_catch_openmp2
- }
- cimg_abort_test;
- return res;
- }
- //! Convolve image by a kernel.
- /**
- \param kernel = the correlation kernel.
- \param boundary_conditions Boundary condition. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
- \param is_normalized = enable local normalization.
- \param channel_mode Channel processing mode.
- Can be { 0=all | 1=one for one (default) | 2=partial sum | 3=full sum }.
- \param xcenter X-coordinate of the kernel center (~0U means 'centered').
- \param ycenter Y-coordinate of the kernel center (~0U means 'centered').
- \param zcenter Z-coordinate of the kernel center (~0U means 'centered').
- \param xstart Starting X-coordinate of the instance image.
- \param ystart Starting Y-coordinate of the instance image.
- \param zstart Starting Z-coordinate of the instance image.
- \param xend Ending X-coordinate of the instance image.
- \param yend Ending Y-coordinate of the instance image.
- \param zend Ending Z-coordinate of the instance image.
- \param xstride Stride along the X-axis.
- \param ystride Stride along the Y-axis.
- \param zstride Stride along the Z-axis.
- \param xdilation Dilation along the X-axis.
- \param ydilation Dilation along the Y-axis.
- \param zdilation Dilation along the Z-axis.
- \param interpolation_type Can be { false=nearest | true=linear }.
- \note
- - The convolution of the image instance \p *this by the kernel \p kernel is defined to be:
- res(x,y,z) = sum_{i,j,k} (*this)(\alpha_x\;x - \beta_x\;(i - c_x),\alpha_y\;y
- - \beta_y\;(j - c_y),\alpha_z\;z - \beta_z\;(k - c_z))*kernel(i,j,k).
- **/
- template<typename t>
- CImg<T>& convolve(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
- const bool is_normalized=false, const unsigned int channel_mode=1,
- const int xcenter=(int)(~0U>>1),
- const int ycenter=(int)(~0U>>1),
- const int zcenter=(int)(~0U>>1),
- const int xstart=0,
- const int ystart=0,
- const int zstart=0,
- const int xend=(int)(~0U>>1),
- const int yend=(int)(~0U>>1),
- const int zend=(int)(~0U>>1),
- const float xstride=1, const float ystride=1, const float zstride=1,
- const float xdilation=1, const float ydilation=1, const float zdilation=1,
- const bool interpolation_type=false) {
- if (is_empty() || !kernel) return *this;
- return get_convolve(kernel,boundary_conditions,is_normalized,channel_mode,
- xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend,
- xstride,ystride,zstride,xdilation,ydilation,zdilation,
- interpolation_type).move_to(*this);
- }
- //! Convolve image by a kernel \newinstance.
- template<typename t>
- CImg<_cimg_Ttfloat> get_convolve(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
- const bool is_normalized=false, const unsigned int channel_mode=1,
- const int xcenter=(int)(~0U>>1),
- const int ycenter=(int)(~0U>>1),
- const int zcenter=(int)(~0U>>1),
- const int xstart=0,
- const int ystart=0,
- const int zstart=0,
- const int xend=(int)(~0U>>1),
- const int yend=(int)(~0U>>1),
- const int zend=(int)(~0U>>1),
- const float xstride=1, const float ystride=1, const float zstride=1,
- const float xdilation=1, const float ydilation=1, const float zdilation=1,
- const bool interpolation_type=false) const {
- return _correlate(kernel,boundary_conditions,is_normalized,channel_mode,
- xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend,
- xstride,ystride,zstride,xdilation,ydilation,zdilation,
- interpolation_type,true);
- }
- //! Cumulate image values, optionally along specified axis.
- /**
- \param axis Cumulation axis. Set it to 0 to cumulate all values globally without taking axes into account.
- **/
- CImg<T>& cumulate(const char axis=0) {
- switch (cimg::lowercase(axis)) {
- case 'x' :
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
- _height*_depth*_spectrum>=16))
- cimg_forYZC(*this,y,z,c) {
- T *ptrd = data(0,y,z,c);
- Tlong cumul = (Tlong)0;
- cimg_forX(*this,x) { cumul+=(Tlong)*ptrd; *(ptrd++) = (T)cumul; }
- }
- break;
- case 'y' : {
- const ulongT w = (ulongT)_width;
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*512 &&
- _width*_depth*_spectrum>=16))
- cimg_forXZC(*this,x,z,c) {
- T *ptrd = data(x,0,z,c);
- Tlong cumul = (Tlong)0;
- cimg_forY(*this,y) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=w; }
- }
- } break;
- case 'z' : {
- const ulongT wh = (ulongT)_width*_height;
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_depth>=(cimg_openmp_sizefactor)*512 &&
- _width*_depth*_spectrum>=16))
- cimg_forXYC(*this,x,y,c) {
- T *ptrd = data(x,y,0,c);
- Tlong cumul = (Tlong)0;
- cimg_forZ(*this,z) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=wh; }
- }
- } break;
- case 'c' : {
- const ulongT whd = (ulongT)_width*_height*_depth;
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
- cimg_openmp_if(_spectrum>=(cimg_openmp_sizefactor)*512 && _width*_height*_depth>=16))
- cimg_forXYZ(*this,x,y,z) {
- T *ptrd = data(x,y,z,0);
- Tlong cumul = (Tlong)0;
- cimg_forC(*this,c) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=whd; }
- }
- } break;
- default : { // Global cumulation
- Tlong cumul = (Tlong)0;
- cimg_for(*this,ptrd,T) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; }
- }
- }
- return *this;
- }
- //! Cumulate image values, optionally along specified axis \newinstance.
- CImg<Tlong> get_cumulate(const char axis=0) const {
- return CImg<Tlong>(*this,false).cumulate(axis);
- }
- //! Cumulate image values, along specified axes.
- /**
- \param axes Cumulation axes, as a C-string.
- \note \c axes may contains multiple characters, e.g. \c "xyz"
- **/
- CImg<T>& cumulate(const char *const axes) {
- for (const char *s = axes; *s; ++s) cumulate(*s);
- return *this;
- }
- //! Cumulate image values, along specified axes \newinstance.
- CImg<Tlong> get_cumulate(const char *const axes) const {
- return CImg<Tlong>(*this,false).cumulate(axes);
- }
- //! Erode image by a structuring element.
- /**
- \param kernel Structuring element.
- \param boundary_conditions Boundary conditions.
- Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
- \param is_real Do the erosion in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false).
- **/
- template<typename t>
- CImg<T>& erode(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
- const bool is_real=false) {
- if (is_empty() || !kernel) return *this;
- return get_erode(kernel,boundary_conditions,is_real).move_to(*this);
- }
- //! Erode image by a structuring element \newinstance.
- template<typename t>
- CImg<_cimg_Tt> get_erode(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
- const bool is_real=false) const {
- if (is_empty() || !kernel) return *this;
- if (!is_real && kernel==0) return CImg<T>(width(),height(),depth(),spectrum(),0);
- typedef _cimg_Tt Tt;
- CImg<Tt> res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum));
- const int
- mx2 = kernel.width()/2, my2 = kernel.height()/2, mz2 = kernel.depth()/2,
- mx1 = kernel.width() - mx2 - 1, my1 = kernel.height() - my2 - 1, mz1 = kernel.depth() - mz2 - 1,
- mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2,
- w2 = 2*width(), h2 = 2*height(), d2 = 2*depth();
- const bool
- is_inner_parallel = _width*_height*_depth>=(cimg_openmp_sizefactor)*32768,
- is_outer_parallel = res.size()>=(cimg_openmp_sizefactor)*32768;
- cimg::unused(is_inner_parallel,is_outer_parallel);
- _cimg_abort_init_openmp;
- cimg_abort_init;
- cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel))
- cimg_forC(res,c) _cimg_abort_try_openmp {
- cimg_abort_test;
- const CImg<T> img = get_shared_channel(c%_spectrum);
- const CImg<t> K = kernel.get_shared_channel(c%kernel._spectrum);
- if (is_real) { // Real erosion
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
- for (int z = mz1; z<mze; ++z)
- for (int y = my1; y<mye; ++y)
- for (int x = mx1; x<mxe; ++x) _cimg_abort_try_openmp2 {
- cimg_abort_test2;
- Tt min_val = cimg::type<Tt>::max();
- for (int zm = -mz1; zm<=mz2; ++zm)
- for (int ym = -my1; ym<=my2; ++ym)
- for (int xm = -mx1; xm<=mx2; ++xm) {
- const t mval = K(mx1 + xm,my1 + ym,mz1 + zm);
- const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) - mval);
- if (cval<min_val) min_val = cval;
- }
- res(x,y,z,c) = min_val;
- } _cimg_abort_catch_openmp2
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
- cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
- cimg_abort_test2;
- for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
- Tt min_val = cimg::type<Tt>::max();
- for (int zm = -mz1; zm<=mz2; ++zm)
- for (int ym = -my1; ym<=my2; ++ym)
- for (int xm = -mx1; xm<=mx2; ++xm) {
- const t mval = K(mx1 + xm,my1 + ym,mz1 + zm);
- Tt cval;
- switch (boundary_conditions) {
- case 0 : cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) - mval); break;
- case 1 : cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) - mval); break;
- case 2 : {
- const int
- nx = cimg::mod(x + xm,width()),
- ny = cimg::mod(y + ym,height()),
- nz = cimg::mod(z + zm,depth());
- cval = img(nx,ny,nz) - mval;
- } break;
- default : {
- const int
- tx = cimg::mod(x + xm,w2),
- ty = cimg::mod(y + ym,h2),
- tz = cimg::mod(z + zm,d2),
- nx = tx<width()?tx:w2 - tx - 1,
- ny = ty<height()?ty:h2 - ty - 1,
- nz = tz<depth()?tz:d2 - tz - 1;
- cval = img(nx,ny,nz) - mval;
- }
- }
- if (cval<min_val) min_val = cval;
- }
- res(x,y,z,c) = min_val;
- }
- } _cimg_abort_catch_openmp2
- } else { // Binary erosion
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
- for (int z = mz1; z<mze; ++z)
- for (int y = my1; y<mye; ++y)
- for (int x = mx1; x<mxe; ++x) _cimg_abort_try_openmp2 {
- cimg_abort_test2;
- Tt min_val = cimg::type<Tt>::max();
- for (int zm = -mz1; zm<=mz2; ++zm)
- for (int ym = -my1; ym<=my2; ++ym)
- for (int xm = -mx1; xm<=mx2; ++xm)
- if (K(mx1 + xm,my1 + ym,mz1 + zm)) {
- const Tt cval = (Tt)img(x + xm,y + ym,z + zm);
- if (cval<min_val) min_val = cval;
- }
- res(x,y,z,c) = min_val;
- } _cimg_abort_catch_openmp2
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
- cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
- cimg_abort_test2;
- for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
- Tt min_val = cimg::type<Tt>::max();
- for (int zm = -mz1; zm<=mz2; ++zm)
- for (int ym = -my1; ym<=my2; ++ym)
- for (int xm = -mx1; xm<=mx2; ++xm) {
- if (K(mx1 + xm,my1 + ym,mz1 + zm)) {
- Tt cval;
- switch (boundary_conditions) {
- case 0 : cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); break;
- case 1 : cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm); break;
- case 2 : {
- const int
- nx = cimg::mod(x + xm,width()),
- ny = cimg::mod(y + ym,height()),
- nz = cimg::mod(z + zm,depth());
- cval = img(nx,ny,nz);
- } break;
- default : {
- const int
- tx = cimg::mod(x + xm,w2),
- ty = cimg::mod(y + ym,h2),
- tz = cimg::mod(z + zm,d2),
- nx = tx<width()?tx:w2 - tx - 1,
- ny = ty<height()?ty:h2 - ty - 1,
- nz = tz<depth()?tz:d2 - tz - 1;
- cval = img(nx,ny,nz);
- }
- }
- if (cval<min_val) min_val = cval;
- }
- }
- res(x,y,z,c) = min_val;
- }
- } _cimg_abort_catch_openmp2
- }
- } _cimg_abort_catch_openmp
- cimg_abort_test;
- return res;
- }
- //! Erode image by a rectangular structuring element of specified size.
- /**
- \param sx Width of the structuring element.
- \param sy Height of the structuring element.
- \param sz Depth of the structuring element.
- **/
- CImg<T>& erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
- if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this;
- if (sx>1 && _width>1) { // Along X-axis
- const int L = width(), off = 1, s = (int)sx, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2;
- CImg<T> buf(L);
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
- cimg_forYZC(*this,y,z,c) {
- T *const ptrdb = buf._data, *ptrd = buf._data, *const ptrde = buf._data + L - 1;
- const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + (ulongT)L*off - off;
- T cur = *ptrs; ptrs+=off; bool is_first = true;
- for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
- const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }}
- *(ptrd++) = cur;
- if (ptrs>=ptrse) {
- T *pd = data(0,y,z,c); cur = std::min(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
- } else {
- for (int p = s1; p>0 && ptrd<=ptrde; --p) {
- const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
- *(ptrd++) = cur;
- }
- for (int p = L - s - 1; p>0; --p) {
- const T val = *ptrs; ptrs+=off;
- if (is_first) {
- const T *nptrs = ptrs - off; cur = val;
- for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
- nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
- } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
- *(ptrd++) = cur;
- }
- ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
- for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
- const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
- }
- *(ptrd--) = cur;
- for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
- const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
- }
- T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
- }
- }
- }
- if (sy>1 && _height>1) { // Along Y-axis
- const int L = height(), off = width(), s = (int)sy, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1,
- s2 = _s2>L?L:_s2;
- CImg<T> buf(L);
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
- cimg_forXZC(*this,x,z,c) {
- T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
- const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + (ulongT)L*off - off;
- T cur = *ptrs; ptrs+=off; bool is_first = true;
- for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
- const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
- }
- *(ptrd++) = cur;
- if (ptrs>=ptrse) {
- T *pd = data(x,0,z,c); cur = std::min(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
- } else {
- for (int p = s1; p>0 && ptrd<=ptrde; --p) {
- const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
- *(ptrd++) = cur;
- }
- for (int p = L - s - 1; p>0; --p) {
- const T val = *ptrs; ptrs+=off;
- if (is_first) {
- const T *nptrs = ptrs - off; cur = val;
- for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
- nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
- } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
- *(ptrd++) = cur;
- }
- ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
- for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
- const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
- }
- *(ptrd--) = cur;
- for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
- const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
- }
- T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
- }
- }
- }
- if (sz>1 && _depth>1) { // Along Z-axis
- const int L = depth(), off = width()*height(), s = (int)sz, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1,
- s2 = _s2>L?L:_s2;
- CImg<T> buf(L);
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
- cimg_forXYC(*this,x,y,c) {
- T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
- const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + (ulongT)L*off - off;
- T cur = *ptrs; ptrs+=off; bool is_first = true;
- for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
- const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
- }
- *(ptrd++) = cur;
- if (ptrs>=ptrse) {
- T *pd = data(x,y,0,c); cur = std::min(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
- } else {
- for (int p = s1; p>0 && ptrd<=ptrde; --p) {
- const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
- *(ptrd++) = cur;
- }
- for (int p = L - s - 1; p>0; --p) {
- const T val = *ptrs; ptrs+=off;
- if (is_first) {
- const T *nptrs = ptrs - off; cur = val;
- for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
- nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
- } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
- *(ptrd++) = cur;
- }
- ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
- for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
- const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
- }
- *(ptrd--) = cur;
- for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
- const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
- }
- T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
- }
- }
- }
- return *this;
- }
- //! Erode image by a rectangular structuring element of specified size \newinstance.
- CImg<T> get_erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
- return (+*this).erode(sx,sy,sz);
- }
- //! Erode the image by a square structuring element of specified size.
- /**
- \param s Size of the structuring element.
- **/
- CImg<T>& erode(const unsigned int s) {
- return erode(s,s,s);
- }
- //! Erode the image by a square structuring element of specified size \newinstance.
- CImg<T> get_erode(const unsigned int s) const {
- return (+*this).erode(s);
- }
- //! Dilate image by a structuring element.
- /**
- \param kernel Structuring element.
- \param boundary_conditions Boundary conditions.
- Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
- \param is_real Do the dilation in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false).
- **/
- template<typename t>
- CImg<T>& dilate(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
- const bool is_real=false) {
- if (is_empty() || !kernel) return *this;
- return get_dilate(kernel,boundary_conditions,is_real).move_to(*this);
- }
- //! Dilate image by a structuring element \newinstance.
- template<typename t>
- CImg<_cimg_Tt> get_dilate(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
- const bool is_real=false) const {
- if (is_empty() || !kernel || (!is_real && kernel==0)) return *this;
- typedef _cimg_Tt Tt;
- CImg<Tt> res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum));
- const int
- mx1 = kernel.width()/2, my1 = kernel.height()/2, mz1 = kernel.depth()/2,
- mx2 = kernel.width() - mx1 - 1, my2 = kernel.height() - my1 - 1, mz2 = kernel.depth() - mz1 - 1,
- mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2,
- w2 = 2*width(), h2 = 2*height(), d2 = 2*depth();
- const bool
- is_inner_parallel = _width*_height*_depth>=(cimg_openmp_sizefactor)*32768,
- is_outer_parallel = res.size()>=(cimg_openmp_sizefactor)*32768;
- cimg::unused(is_inner_parallel,is_outer_parallel);
- _cimg_abort_init_openmp;
- cimg_abort_init;
- cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel))
- cimg_forC(res,c) _cimg_abort_try_openmp {
- cimg_abort_test;
- const CImg<T> img = get_shared_channel(c%_spectrum);
- const CImg<t> K = kernel.get_shared_channel(c%kernel._spectrum);
- if (is_real) { // Real dilation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
- for (int z = mz1; z<mze; ++z)
- for (int y = my1; y<mye; ++y)
- for (int x = mx1; x<mxe; ++x) _cimg_abort_try_openmp2 {
- cimg_abort_test2;
- Tt max_val = cimg::type<Tt>::min();
- for (int zm = -mz1; zm<=mz2; ++zm)
- for (int ym = -my1; ym<=my2; ++ym)
- for (int xm = -mx1; xm<=mx2; ++xm) {
- const t mval = K(mx2 - xm,my2 - ym,mz2 - zm);
- const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) + mval);
- if (cval>max_val) max_val = cval;
- }
- res(x,y,z,c) = max_val;
- } _cimg_abort_catch_openmp2
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
- cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
- cimg_abort_test2;
- for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
- Tt max_val = cimg::type<Tt>::min();
- for (int zm = -mz1; zm<=mz2; ++zm)
- for (int ym = -my1; ym<=my2; ++ym)
- for (int xm = -mx1; xm<=mx2; ++xm) {
- const t mval = K(mx2 - xm,my2 - ym,mz2 - zm);
- Tt cval;
- switch (boundary_conditions) {
- case 0 : cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) + mval); break;
- case 1 : cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) + mval); break;
- case 2 : {
- const int
- nx = cimg::mod(x + xm,width()),
- ny = cimg::mod(y + ym,height()),
- nz = cimg::mod(z + zm,depth());
- cval = img(nx,ny,nz) + mval;
- } break;
- default : {
- const int
- tx = cimg::mod(x + xm,w2),
- ty = cimg::mod(y + ym,h2),
- tz = cimg::mod(z + zm,d2),
- nx = tx<width()?tx:w2 - tx - 1,
- ny = ty<height()?ty:h2 - ty - 1,
- nz = tz<depth()?tz:d2 - tz - 1;
- cval = img(nx,ny,nz) + mval;
- }
- }
- if (cval>max_val) max_val = cval;
- }
- res(x,y,z,c) = max_val;
- }
- } _cimg_abort_catch_openmp2
- } else { // Binary dilation
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
- for (int z = mz1; z<mze; ++z)
- for (int y = my1; y<mye; ++y)
- for (int x = mx1; x<mxe; ++x) _cimg_abort_try_openmp2 {
- cimg_abort_test2;
- Tt max_val = cimg::type<Tt>::min();
- for (int zm = -mz1; zm<=mz2; ++zm)
- for (int ym = -my1; ym<=my2; ++ym)
- for (int xm = -mx1; xm<=mx2; ++xm)
- if (K(mx2 - xm,my2 - ym,mz2 - zm)) {
- const Tt cval = (Tt)img(x + xm,y + ym,z + zm);
- if (cval>max_val) max_val = cval;
- }
- res(x,y,z,c) = max_val;
- } _cimg_abort_catch_openmp2
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
- cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
- cimg_abort_test2;
- for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
- Tt max_val = cimg::type<Tt>::min();
- for (int zm = -mz1; zm<=mz2; ++zm)
- for (int ym = -my1; ym<=my2; ++ym)
- for (int xm = -mx1; xm<=mx2; ++xm) {
- if (K(mx2 - xm,my2 - ym,mz2 - zm)) {
- Tt cval;
- switch (boundary_conditions) {
- case 0 : cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); break;
- case 1 : cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm); break;
- case 2 : {
- const int
- nx = cimg::mod(x + xm,width()),
- ny = cimg::mod(y + ym,height()),
- nz = cimg::mod(z + zm,depth());
- cval = img(nx,ny,nz);
- } break;
- default : {
- const int
- tx = cimg::mod(x + xm,w2),
- ty = cimg::mod(y + ym,h2),
- tz = cimg::mod(z + zm,d2),
- nx = tx<width()?tx:w2 - tx - 1,
- ny = ty<height()?ty:h2 - ty - 1,
- nz = tz<depth()?tz:d2 - tz - 1;
- cval = img(nx,ny,nz);
- }
- }
- if (cval>max_val) max_val = cval;
- }
- }
- res(x,y,z,c) = max_val;
- }
- } _cimg_abort_catch_openmp2
- }
- } _cimg_abort_catch_openmp
- cimg_abort_test;
- return res;
- }
- //! Dilate image by a rectangular structuring element of specified size.
- /**
- \param sx Width of the structuring element.
- \param sy Height of the structuring element.
- \param sz Depth of the structuring element.
- **/
- CImg<T>& dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
- if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this;
- if (sx>1 && _width>1) { // Along X-axis
- const int L = width(), off = 1, s = (int)sx, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1, s2 = _s2>L?L:_s2;
- CImg<T> buf(L);
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
- cimg_forYZC(*this,y,z,c) {
- T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
- const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + (ulongT)L*off - off;
- T cur = *ptrs; ptrs+=off; bool is_first = true;
- for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
- const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
- }
- *(ptrd++) = cur;
- if (ptrs>=ptrse) {
- T *pd = data(0,y,z,c); cur = std::max(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
- } else {
- for (int p = s1; p>0 && ptrd<=ptrde; --p) {
- const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
- *(ptrd++) = cur;
- }
- for (int p = L - s - 1; p>0; --p) {
- const T val = *ptrs; ptrs+=off;
- if (is_first) {
- const T *nptrs = ptrs - off; cur = val;
- for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
- nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
- } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
- *(ptrd++) = cur;
- }
- ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
- for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
- const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
- }
- *(ptrd--) = cur;
- for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
- const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
- }
- T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
- }
- }
- }
- if (sy>1 && _height>1) { // Along Y-axis
- const int L = height(), off = width(), s = (int)sy, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1,
- s2 = _s2>L?L:_s2;
- CImg<T> buf(L);
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
- cimg_forXZC(*this,x,z,c) {
- T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
- const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + (ulongT)L*off - off;
- T cur = *ptrs; ptrs+=off; bool is_first = true;
- for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
- const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
- }
- *(ptrd++) = cur;
- if (ptrs>=ptrse) {
- T *pd = data(x,0,z,c); cur = std::max(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
- } else {
- for (int p = s1; p>0 && ptrd<=ptrde; --p) {
- const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
- *(ptrd++) = cur;
- }
- for (int p = L - s - 1; p>0; --p) {
- const T val = *ptrs; ptrs+=off;
- if (is_first) {
- const T *nptrs = ptrs - off; cur = val;
- for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
- nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
- } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
- *(ptrd++) = cur;
- }
- ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
- for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
- const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
- }
- *(ptrd--) = cur;
- for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
- const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
- }
- T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
- }
- }
- }
- if (sz>1 && _depth>1) { // Along Z-axis
- const int L = depth(), off = width()*height(), s = (int)sz, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1,
- s2 = _s2>L?L:_s2;
- CImg<T> buf(L);
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
- cimg_forXYC(*this,x,y,c) {
- T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
- const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + (ulongT)L*off - off;
- T cur = *ptrs; ptrs+=off; bool is_first = true;
- for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
- const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
- }
- *(ptrd++) = cur;
- if (ptrs>=ptrse) {
- T *pd = data(x,y,0,c); cur = std::max(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
- } else {
- for (int p = s1; p>0 && ptrd<=ptrde; --p) {
- const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
- *(ptrd++) = cur;
- }
- for (int p = L - s - 1; p>0; --p) {
- const T val = *ptrs; ptrs+=off;
- if (is_first) {
- const T *nptrs = ptrs - off; cur = val;
- for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
- nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
- } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
- *(ptrd++) = cur;
- }
- ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
- for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
- const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
- }
- *(ptrd--) = cur;
- for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
- const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
- }
- T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
- }
- }
- }
- return *this;
- }
- //! Dilate image by a rectangular structuring element of specified size \newinstance.
- CImg<T> get_dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
- return (+*this).dilate(sx,sy,sz);
- }
- //! Dilate image by a square structuring element of specified size.
- /**
- \param s Size of the structuring element.
- **/
- CImg<T>& dilate(const unsigned int s) {
- return dilate(s,s,s);
- }
- //! Dilate image by a square structuring element of specified size \newinstance.
- CImg<T> get_dilate(const unsigned int s) const {
- return (+*this).dilate(s);
- }
- //! Apply morphological closing by a structuring element.
- /**
- \param kernel Structuring element.
- \param boundary_conditions Boundary conditions.
- Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
- \param is_real Do the closing in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false).
- **/
- template<typename t>
- CImg<T>& closing(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
- const bool is_real=false) {
- const int sx = kernel.width(), sy = kernel.height(), sz = kernel.depth();
- if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this;
- return get_closing(kernel,boundary_conditions,is_real).move_to(*this);
- }
- //! Apply morphological closing by a structuring element \newinstance.
- template<typename t>
- CImg<T> get_closing(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
- const bool is_real=false) const {
- const int sx = kernel.width(), sy = kernel.height(), sz = kernel.depth();
- if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this;
- const int sx1 = (int)(sx - 1)/2, sy1 = (int)(sy - 1)/2, sz1 = (int)(sz - 1)/2;
- CImg<T> res;
- if (_depth>1) { // 3D
- get_resize(width() + sx + 1,height() + sy + 1,depth() + sz + 1,spectrum(),0,boundary_conditions,0.5,0.5,0.5).
- dilate(kernel,1,is_real).erode(kernel,1,is_real).
- crop(sx1 + 1,sy1 + 1,sz1 + 1,sx1 + width(),sy1 + height(),sz1 + depth()).move_to(res);
- } else if (_height>1) { // 2D
- get_resize(width() + sx + 1,height() + sy + 1,1,spectrum(),0,boundary_conditions,0.5,0.5).
- dilate(kernel,1,is_real).erode(kernel,1,is_real).
- crop(sx1 + 1,sy1 + 1,sx1 + width(),sy1 + height()).move_to(res);
- } else if (_width>1) { // 1D
- get_resize(width() + sx + 1,1,1,spectrum(),0,boundary_conditions,0.5).
- dilate(kernel,1,is_real).erode(kernel,1,is_real).
- crop(sx1 + 1,sx1 + width()).move_to(res);
- }
- return res;
- }
- //! Apply morphological closing by a rectangular structuring element of specified size.
- CImg<T>& closing(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
- if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this;
- return get_closing(sx,sy,sz).move_to(*this);
- }
- //! Apply morphological closing by a rectangular structuring element of specified size \newinstance.
- CImg<T> get_closing(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
- if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this;
- const int sx1 = (int)(sx - 1)/2, sy1 = (int)(sy - 1)/2, sz1 = (int)(sz - 1)/2;
- CImg<T> res;
- if (_depth>1) { // 3D
- get_resize(width() + sx + 1,height() + sy + 1,depth() + sz + 1,spectrum(),0,1,0.5,0.5,0.5).
- dilate(sx,sy,sz).erode(sx,sy,sz).
- crop(sx1 + 1,sy1 + 1,sz1 + 1,sx1 + width(),sy1 + height(),sz1 + depth()).move_to(res);
- } else if (_height>1) { // 2D
- get_resize(width() + sx + 1,height() + sy + 1,1,spectrum(),0,1,0.5,0.5).
- dilate(sx,sy).erode(sx,sy).
- crop(sx1 + 1,sy1 + 1,sx1 + width(),sy1 + height()).move_to(res);
- } else if (_width>1) { // 1D
- get_resize(width() + sx + 1,1,1,spectrum(),0,1,0.5).
- dilate(sx,1).erode(sx,1).
- crop(sx1 + 1,sx1 + width()).move_to(res);
- }
- return res;
- }
- //! Apply morphological closing by a square structuring element of specified size.
- /**
- \param s Size of the structuring element.
- **/
- CImg<T>& closing(const unsigned int s) {
- return closing(s,s,s);
- }
- //! Apply morphological closing by a square structuring element of specified size \newinstance.
- CImg<T> get_closing(const unsigned int s) const {
- return (+*this).closing(s);
- }
- //! Apply morphological opening by a structuring element.
- /**
- \param kernel Structuring element.
- \param boundary_conditions Boundary conditions.
- Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
- \param is_real Do the opening in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false).
- **/
- template<typename t>
- CImg<T>& opening(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
- const bool is_real=false) {
- const int sx = kernel.width(), sy = kernel.height(), sz = kernel.depth();
- if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this;
- return get_opening(kernel,boundary_conditions,is_real).move_to(*this);
- }
- //! Apply morphological opening by a structuring element \newinstance.
- template<typename t>
- CImg<T> get_opening(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
- const bool is_real=false) const {
- const int sx = kernel.width(), sy = kernel.height(), sz = kernel.depth();
- if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this;
- const int sx1 = (int)(sx - 1)/2, sy1 = (int)(sy - 1)/2, sz1 = (int)(sz - 1)/2;
- CImg<T> res;
- if (_depth>1) { // 3D
- get_resize(width() + sx + 1,height() + sy + 1,depth() + sz + 1,spectrum(),0,boundary_conditions,0.5,0.5,0.5).
- erode(kernel,1,is_real).dilate(kernel,1,is_real).
- crop(sx1 + 1,sy1 + 1,sz1 + 1,sx1 + width(),sy1 + height(),sz1 + depth()).move_to(res);
- } else if (_height>1) { // 2D
- get_resize(width() + sx + 1,height() + sy + 1,1,spectrum(),0,boundary_conditions,0.5,0.5).
- erode(kernel,1,is_real).dilate(kernel,1,is_real).
- crop(sx1 + 1,sy1 + 1,sx1 + width(),sy1 + height()).move_to(res);
- } else if (_width>1) { // 1D
- get_resize(width() + sx + 1,1,1,spectrum(),0,boundary_conditions,0.5).
- erode(kernel,1,is_real).dilate(kernel,1,is_real).
- crop(sx1 + 1,sx1 + width()).move_to(res);
- }
- return res;
- }
- //! Apply morphological opening by a rectangular structuring element of specified size.
- CImg<T>& opening(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
- if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this;
- return get_opening(sx,sy,sz).move_to(*this);
- }
- //! Apply morphological opening by a rectangular structuring element of specified size \newinstance.
- CImg<T> get_opening(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
- if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this;
- const int sx1 = (int)(sx - 1)/2, sy1 = (int)(sy - 1)/2, sz1 = (int)(sz - 1)/2;
- CImg<T> res;
- if (_depth>1) { // 3D
- get_resize(width() + sx + 1,height() + sy + 1,depth() + sz + 1,spectrum(),0,1,0.5,0.5,0.5).
- erode(sx,sy,sz).dilate(sx,sy,sz).
- crop(sx1 + 1,sy1 + 1,sz1 + 1,sx1 + width(),sy1 + height(),sz1 + depth()).move_to(res);
- } else if (_height>1) { // 2D
- get_resize(width() + sx + 1,height() + sy + 1,1,spectrum(),0,1,0.5,0.5).
- erode(sx,sy).dilate(sx,sy).
- crop(sx1 + 1,sy1 + 1,sx1 + width(),sy1 + height()).move_to(res);
- } else if (_width>1) { // 1D
- get_resize(width() + sx + 1,1,1,spectrum(),0,1,0.5).
- erode(sx,1).dilate(sx,1).
- crop(sx1 + 1,sx1 + width()).move_to(res);
- }
- return res;
- }
- //! Apply morphological opening by a square structuring element of specified size.
- /**
- \param s Size of the structuring element.
- **/
- CImg<T>& opening(const unsigned int s) {
- return opening(s,s,s);
- }
- //! Apply morphological opening by a square structuring element of specified size \newinstance.
- CImg<T> get_opening(const unsigned int s) const {
- return (+*this).opening(s);
- }
- //! Compute watershed transform.
- /**
- \param priority Priority map.
- \param is_high_connectivity Boolean that choose between 4(false)- or 8(true)-connectivity
- in 2D case, and between 6(false)- or 26(true)-connectivity in 3D case.
- \note Non-zero values of the instance instance are propagated to zero-valued ones according to
- specified the priority map.
- **/
- template<typename t>
- CImg<T>& watershed(const CImg<t>& priority, const bool is_high_connectivity=false) {
- #define _cimg_watershed_init(cond,X,Y,Z) \
- if (cond && !(*this)(X,Y,Z)) Q._priority_queue_insert(labels,sizeQ,priority(X,Y,Z),X,Y,Z,nb_seeds)
- #define _cimg_watershed_propagate(cond,X,Y,Z) \
- if (cond) { \
- if ((*this)(X,Y,Z)) { \
- ns = labels(X,Y,Z) - 1; xs = seeds(ns,0); ys = seeds(ns,1); zs = seeds(ns,2); \
- d = cimg::sqr((float)x - xs) + cimg::sqr((float)y - ys) + cimg::sqr((float)z - zs); \
- if (d<dmin) { dmin = d; nmin = ns; nlabel = (*this)(xs,ys,zs); } \
- } else Q._priority_queue_insert(labels,sizeQ,priority(X,Y,Z),X,Y,Z,n); \
- }
- if (is_empty()) return *this;
- if (!is_sameXYZ(priority))
- throw CImgArgumentException(_cimg_instance
- "watershed(): image instance and specified priority (%u,%u,%u,%u,%p) "
- "have different dimensions.",
- cimg_instance,
- priority._width,priority._height,priority._depth,priority._spectrum,priority._data);
- if (_spectrum!=1) {
- cimg_forC(*this,c)
- get_shared_channel(c).watershed(priority.get_shared_channel(c%priority._spectrum));
- return *this;
- }
- CImg<uintT> labels(_width,_height,_depth,1,0), seeds(64,3);
- CImg<typename cimg::superset2<T,t,int>::type> Q;
- unsigned int sizeQ = 0;
- int px, nx, py, ny, pz, nz;
- bool is_px, is_nx, is_py, is_ny, is_pz, is_nz;
- const bool is_3d = _depth>1;
- // Find seed points and insert them in priority queue.
- unsigned int nb_seeds = 0;
- const T *ptrs = _data;
- cimg_forXYZ(*this,x,y,z) if (*(ptrs++)) { // 3D version
- if (nb_seeds>=seeds._width) seeds.resize(2*seeds._width,3,1,1,0);
- seeds(nb_seeds,0) = x; seeds(nb_seeds,1) = y; seeds(nb_seeds++,2) = z;
- px = x - 1; nx = x + 1;
- py = y - 1; ny = y + 1;
- pz = z - 1; nz = z + 1;
- is_px = px>=0; is_nx = nx<width();
- is_py = py>=0; is_ny = ny<height();
- is_pz = pz>=0; is_nz = nz<depth();
- _cimg_watershed_init(is_px,px,y,z);
- _cimg_watershed_init(is_nx,nx,y,z);
- _cimg_watershed_init(is_py,x,py,z);
- _cimg_watershed_init(is_ny,x,ny,z);
- if (is_3d) {
- _cimg_watershed_init(is_pz,x,y,pz);
- _cimg_watershed_init(is_nz,x,y,nz);
- }
- if (is_high_connectivity) {
- _cimg_watershed_init(is_px && is_py,px,py,z);
- _cimg_watershed_init(is_nx && is_py,nx,py,z);
- _cimg_watershed_init(is_px && is_ny,px,ny,z);
- _cimg_watershed_init(is_nx && is_ny,nx,ny,z);
- if (is_3d) {
- _cimg_watershed_init(is_px && is_pz,px,y,pz);
- _cimg_watershed_init(is_nx && is_pz,nx,y,pz);
- _cimg_watershed_init(is_px && is_nz,px,y,nz);
- _cimg_watershed_init(is_nx && is_nz,nx,y,nz);
- _cimg_watershed_init(is_py && is_pz,x,py,pz);
- _cimg_watershed_init(is_ny && is_pz,x,ny,pz);
- _cimg_watershed_init(is_py && is_nz,x,py,nz);
- _cimg_watershed_init(is_ny && is_nz,x,ny,nz);
- _cimg_watershed_init(is_px && is_py && is_pz,px,py,pz);
- _cimg_watershed_init(is_nx && is_py && is_pz,nx,py,pz);
- _cimg_watershed_init(is_px && is_ny && is_pz,px,ny,pz);
- _cimg_watershed_init(is_nx && is_ny && is_pz,nx,ny,pz);
- _cimg_watershed_init(is_px && is_py && is_nz,px,py,nz);
- _cimg_watershed_init(is_nx && is_py && is_nz,nx,py,nz);
- _cimg_watershed_init(is_px && is_ny && is_nz,px,ny,nz);
- _cimg_watershed_init(is_nx && is_ny && is_nz,nx,ny,nz);
- }
- }
- labels(x,y,z) = nb_seeds;
- }
- // Start watershed computation.
- while (sizeQ) {
- // Get and remove point with maximal priority from the queue.
- const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
- const unsigned int n = labels(x,y,z);
- px = x - 1; nx = x + 1;
- py = y - 1; ny = y + 1;
- pz = z - 1; nz = z + 1;
- is_px = px>=0; is_nx = nx<width();
- is_py = py>=0; is_ny = ny<height();
- is_pz = pz>=0; is_nz = nz<depth();
- // Check labels of the neighbors.
- Q._priority_queue_remove(sizeQ);
- unsigned int xs, ys, zs, ns, nmin = 0;
- float d, dmin = cimg::type<float>::inf();
- T nlabel = (T)0;
- _cimg_watershed_propagate(is_px,px,y,z);
- _cimg_watershed_propagate(is_nx,nx,y,z);
- _cimg_watershed_propagate(is_py,x,py,z);
- _cimg_watershed_propagate(is_ny,x,ny,z);
- if (is_3d) {
- _cimg_watershed_propagate(is_pz,x,y,pz);
- _cimg_watershed_propagate(is_nz,x,y,nz);
- }
- if (is_high_connectivity) {
- _cimg_watershed_propagate(is_px && is_py,px,py,z);
- _cimg_watershed_propagate(is_nx && is_py,nx,py,z);
- _cimg_watershed_propagate(is_px && is_ny,px,ny,z);
- _cimg_watershed_propagate(is_nx && is_ny,nx,ny,z);
- if (is_3d) {
- _cimg_watershed_propagate(is_px && is_pz,px,y,pz);
- _cimg_watershed_propagate(is_nx && is_pz,nx,y,pz);
- _cimg_watershed_propagate(is_px && is_nz,px,y,nz);
- _cimg_watershed_propagate(is_nx && is_nz,nx,y,nz);
- _cimg_watershed_propagate(is_py && is_pz,x,py,pz);
- _cimg_watershed_propagate(is_ny && is_pz,x,ny,pz);
- _cimg_watershed_propagate(is_py && is_nz,x,py,nz);
- _cimg_watershed_propagate(is_ny && is_nz,x,ny,nz);
- _cimg_watershed_propagate(is_px && is_py && is_pz,px,py,pz);
- _cimg_watershed_propagate(is_nx && is_py && is_pz,nx,py,pz);
- _cimg_watershed_propagate(is_px && is_ny && is_pz,px,ny,pz);
- _cimg_watershed_propagate(is_nx && is_ny && is_pz,nx,ny,pz);
- _cimg_watershed_propagate(is_px && is_py && is_nz,px,py,nz);
- _cimg_watershed_propagate(is_nx && is_py && is_nz,nx,py,nz);
- _cimg_watershed_propagate(is_px && is_ny && is_nz,px,ny,nz);
- _cimg_watershed_propagate(is_nx && is_ny && is_nz,nx,ny,nz);
- }
- }
- (*this)(x,y,z) = nlabel;
- labels(x,y,z) = ++nmin;
- }
- return *this;
- }
- //! Compute watershed transform \newinstance.
- template<typename t>
- CImg<T> get_watershed(const CImg<t>& priority, const bool is_high_connectivity=false) const {
- return (+*this).watershed(priority,is_high_connectivity);
- }
- // [internal] Insert/Remove items in priority queue, for watershed/distance transforms.
- template<typename tq, typename tv>
- bool _priority_queue_insert(CImg<tq>& is_queued, unsigned int& siz, const tv value,
- const unsigned int x, const unsigned int y, const unsigned int z,
- const unsigned int n=1) {
- if (is_queued(x,y,z)) return false;
- is_queued(x,y,z) = (tq)n;
- if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); }
- (*this)(siz - 1,0) = (T)value;
- (*this)(siz - 1,1) = (T)x;
- (*this)(siz - 1,2) = (T)y;
- (*this)(siz - 1,3) = (T)z;
- for (unsigned int pos = siz - 1, par = 0; pos && value>(tv)(*this)(par=(pos + 1)/2 - 1,0); pos = par) {
- cimg::swap((*this)(pos,0),(*this)(par,0));
- cimg::swap((*this)(pos,1),(*this)(par,1));
- cimg::swap((*this)(pos,2),(*this)(par,2));
- cimg::swap((*this)(pos,3),(*this)(par,3));
- }
- return true;
- }
- CImg<T>& _priority_queue_remove(unsigned int& siz) {
- (*this)(0,0) = (*this)(--siz,0);
- (*this)(0,1) = (*this)(siz,1);
- (*this)(0,2) = (*this)(siz,2);
- (*this)(0,3) = (*this)(siz,3);
- const float value = (*this)(0,0);
- unsigned int pos = 0, swap = 0;
- do {
- const unsigned int left = 2*pos + 1, right = left + 1;
- if (right<siz && value<(*this)(right,0)) swap = (*this)(left,0)>(*this)(right,0)?left:right;
- else if (left<siz && value<(*this)(left,0)) swap = left;
- else break;
- cimg::swap((*this)(pos,0),(*this)(swap,0));
- cimg::swap((*this)(pos,1),(*this)(swap,1));
- cimg::swap((*this)(pos,2),(*this)(swap,2));
- cimg::swap((*this)(pos,3),(*this)(swap,3));
- pos = swap;
- } while (true);
- return *this;
- }
- //! Apply recursive Deriche filter.
- /**
- \param sigma Standard deviation of the filter.
- \param order Order of the filter. Can be <tt>{ 0=smooth-filter | 1=1st-derivative | 2=2nd-derivative }</tt>.
- \param axis Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
- \param boundary_conditions Boundary conditions.
- Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
- **/
- CImg<T>& deriche(const float sigma, const unsigned int order=0, const char axis='x',
- const unsigned int boundary_conditions=1) {
- #define _cimg_deriche_apply \
- CImg<doubleT> Y(N); \
- double *ptrY = Y._data, yb = 0, yp = 0; \
- T xp = (T)0; \
- if (boundary_conditions) { xp = *ptrX; yb = yp = (double)(coefp*xp); } \
- for (int m = 0; m<N; ++m) { \
- const T xc = *ptrX; ptrX+=off; \
- const double yc = *(ptrY++) = (double)(a0*xc + a1*xp - b1*yp - b2*yb); \
- xp = xc; yb = yp; yp = yc; \
- } \
- T xn = (T)0, xa = (T)0; \
- double yn = 0, ya = 0; \
- if (boundary_conditions) { xn = xa = *(ptrX - off); yn = ya = (double)coefn*xn; } \
- for (int n = N - 1; n>=0; --n) { \
- const T xc = *(ptrX-=off); \
- const double yc = (double)(a2*xn + a3*xa - b1*yn - b2*ya); \
- xa = xn; xn = xc; ya = yn; yn = yc; \
- *ptrX = (T)(*(--ptrY)+yc); \
- }
- if (order>2)
- throw CImgArgumentException(_cimg_instance
- "deriche(): Invalid specified order '%d' "
- "('order' can be { 0=smoothing | 1=1st-derivative | 2=2nd-derivative }).",
- cimg_instance,
- order);
- const char naxis = cimg::lowercase(axis);
- if (naxis!='x' && naxis!='y' && naxis!='z' && naxis!='c')
- throw CImgArgumentException(_cimg_instance
- "deriche(): Invalid specified axis '%c'.",
- cimg_instance,
- axis);
- const double
- nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:
- naxis=='y'?_height:
- naxis=='z'?_depth:_spectrum)/100,
- nnsigma = nsigma<0.1f?0.1f:nsigma;
- if (is_empty() || (nsigma<0.1f && !order)) return *this;
- if (boundary_conditions>1) {
- const int w = width(), h = height(), d = depth(), s = spectrum(), border = (int)cimg::round(1 + 3*nnsigma);
- switch (naxis) {
- case 'x' :
- return draw_image(get_resize(w + 2*border,h,d,s,0,boundary_conditions,0.5).
- deriche(nnsigma,order,naxis,1).columns(border,w - 1 + border));
- case 'y' :
- return draw_image(get_resize(w,h + 2*border,d,s,0,boundary_conditions,0,0.5).
- deriche(nnsigma,order,naxis,1).rows(border,h - 1 + border));
- case 'z' :
- return draw_image(get_resize(w,h,d + 2*border,s,0,boundary_conditions,0,0,0.5).
- deriche(nnsigma,order,naxis,1).slices(border,d - 1 + border));
- default :
- return draw_image(get_resize(w,h,d,s + 2*border,0,boundary_conditions,0,0,0,0.5).
- deriche(nnsigma,order,naxis,1).channels(border,d - 1 + border));
- }
- }
- const double
- alpha = 1.695f/nnsigma,
- ema = std::exp(-alpha),
- ema2 = std::exp(-2*alpha),
- b1 = -2*ema,
- b2 = ema2;
- double a0 = 0, a1 = 0, a2 = 0, a3 = 0, coefp = 0, coefn = 0;
- switch (order) {
- case 0 : {
- const double k = (1-ema)*(1-ema)/(1 + 2*alpha*ema-ema2);
- a0 = k;
- a1 = k*(alpha - 1)*ema;
- a2 = k*(alpha + 1)*ema;
- a3 = -k*ema2;
- } break;
- case 1 : {
- const double k = -(1-ema)*(1-ema)*(1-ema)/(2*(ema + 1)*ema);
- a0 = a3 = 0;
- a1 = k*ema;
- a2 = -a1;
- } break;
- default : {
- const double
- ea = std::exp(-alpha),
- k = -(ema2 - 1)/(2*alpha*ema),
- kn = (-2*(-1 + 3*ea - 3*ea*ea + ea*ea*ea)/(3*ea + 1 + 3*ea*ea + ea*ea*ea));
- a0 = kn;
- a1 = -kn*(1 + k*alpha)*ema;
- a2 = kn*(1 - k*alpha)*ema;
- a3 = -kn*ema2;
- } break;
- }
- coefp = (a0 + a1)/(1 + b1 + b2);
- coefn = (a2 + a3)/(1 + b1 + b2);
- switch (naxis) {
- case 'x' : {
- const int N = width();
- const ulongT off = 1U;
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
- _height*_depth*_spectrum>=16))
- cimg_forYZC(*this,y,z,c) { T *ptrX = data(0,y,z,c); _cimg_deriche_apply; }
- } break;
- case 'y' : {
- const int N = height();
- const ulongT off = (ulongT)_width;
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
- _height*_depth*_spectrum>=16))
- cimg_forXZC(*this,x,z,c) { T *ptrX = data(x,0,z,c); _cimg_deriche_apply; }
- } break;
- case 'z' : {
- const int N = depth();
- const ulongT off = (ulongT)_width*_height;
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
- _height*_depth*_spectrum>=16))
- cimg_forXYC(*this,x,y,c) { T *ptrX = data(x,y,0,c); _cimg_deriche_apply; }
- } break;
- default : {
- const int N = spectrum();
- const ulongT off = (ulongT)_width*_height*_depth;
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
- _height*_depth*_spectrum>=16))
- cimg_forXYZ(*this,x,y,z) { T *ptrX = data(x,y,z,0); _cimg_deriche_apply; }
- }
- }
- return *this;
- }
- //! Apply recursive Deriche filter \newinstance.
- CImg<Tfloat> get_deriche(const float sigma, const unsigned int order=0, const char axis='x',
- const unsigned int boundary_conditions=1) const {
- return CImg<Tfloat>(*this,false).deriche(sigma,order,axis,boundary_conditions);
- }
- // [internal] Apply a recursive filter (used by CImg<T>::vanvliet()).
- /*
- \param ptr the pointer of the data
- \param filter the coefficient of the filter in the following order [n,n - 1,n - 2,n - 3].
- \param N size of the data
- \param off the offset between two data point
- \param order the order of the filter 0 (smoothing), 1st derivative, 2nd derivative, 3rd derivative
- \param boundary_conditions Boundary conditions.
- Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
- \note Boundary condition using B. Triggs method (IEEE trans on Sig Proc 2005).
- */
- static void _cimg_recursive_apply(T *data, const double filter[], const int N, const ulongT off,
- const unsigned int order, const bool boundary_conditions) {
- double val[4] = {}; // res[n,n - 1,n - 2,n - 3,..] or res[n,n + 1,n + 2,n + 3,..]
- const double
- sumsq = filter[0], sum = sumsq * sumsq,
- a1 = filter[1], a2 = filter[2], a3 = filter[3],
- scaleM = 1. / ( (1. + a1 - a2 + a3) * (1. - a1 - a2 - a3) * (1. + a2 + (a1 - a3) * a3) );
- double M[9]; // Triggs matrix
- M[0] = scaleM * (-a3 * a1 + 1. - a3 * a3 - a2);
- M[1] = scaleM * (a3 + a1) * (a2 + a3 * a1);
- M[2] = scaleM * a3 * (a1 + a3 * a2);
- M[3] = scaleM * (a1 + a3 * a2);
- M[4] = -scaleM * (a2 - 1.) * (a2 + a3 * a1);
- M[5] = -scaleM * a3 * (a3 * a1 + a3 * a3 + a2 - 1.);
- M[6] = scaleM * (a3 * a1 + a2 + a1 * a1 - a2 * a2);
- M[7] = scaleM * (a1 * a2 + a3 * a2 * a2 - a1 * a3 * a3 - a3 * a3 * a3 - a3 * a2 + a3);
- M[8] = scaleM * a3 * (a1 + a3 * a2);
- switch (order) {
- case 0 : {
- const double iplus = (boundary_conditions?data[(N - 1)*off]:(T)0);
- for (int pass = 0; pass<2; ++pass) {
- if (!pass) {
- for (int k = 1; k<4; ++k) val[k] = (boundary_conditions?*data/sumsq:0);
- } else {
- // Apply Triggs boundary conditions
- const double
- uplus = iplus/(1. - a1 - a2 - a3), vplus = uplus/(1. - a1 - a2 - a3),
- unp = val[1] - uplus, unp1 = val[2] - uplus, unp2 = val[3] - uplus;
- val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2 + vplus) * sum;
- val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2 + vplus) * sum;
- val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2 + vplus) * sum;
- *data = (T)val[0];
- data -= off;
- for (int k = 3; k>0; --k) val[k] = val[k - 1];
- }
- for (int n = pass; n<N; ++n) {
- val[0] = (*data);
- if (pass) val[0] *= sum;
- for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k];
- *data = (T)val[0];
- if (!pass) data += off; else data -= off;
- for (int k = 3; k>0; --k) val[k] = val[k - 1];
- }
- if (!pass) data -= off;
- }
- } break;
- case 1 : {
- double x[3]; // [front,center,back]
- for (int pass = 0; pass<2; ++pass) {
- if (!pass) {
- for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0);
- for (int k = 0; k<4; ++k) val[k] = 0;
- } else {
- // Apply Triggs boundary conditions
- const double
- unp = val[1], unp1 = val[2], unp2 = val[3];
- val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum;
- val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum;
- val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum;
- *data = (T)val[0];
- data -= off;
- for (int k = 3; k>0; --k) val[k] = val[k - 1];
- }
- for (int n = pass; n<N - 1; ++n) {
- if (!pass) {
- x[0] = *(data + off);
- val[0] = 0.5f * (x[0] - x[2]);
- } else val[0] = (*data) * sum;
- for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k];
- *data = (T)val[0];
- if (!pass) {
- data += off;
- for (int k = 2; k>0; --k) x[k] = x[k - 1];
- } else { data-=off;}
- for (int k = 3; k>0; --k) val[k] = val[k - 1];
- }
- *data = (T)0;
- }
- } break;
- case 2: {
- double x[3]; // [front,center,back]
- for (int pass = 0; pass<2; ++pass) {
- if (!pass) {
- for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0);
- for (int k = 0; k<4; ++k) val[k] = 0;
- } else {
- // Apply Triggs boundary conditions
- const double
- unp = val[1], unp1 = val[2], unp2 = val[3];
- val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum;
- val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum;
- val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum;
- *data = (T)val[0];
- data -= off;
- for (int k = 3; k>0; --k) val[k] = val[k - 1];
- }
- for (int n = pass; n<N - 1; ++n) {
- if (!pass) { x[0] = *(data + off); val[0] = (x[1] - x[2]); }
- else { x[0] = *(data - off); val[0] = (x[2] - x[1]) * sum; }
- for (int k = 1; k<4; ++k) val[0] += val[k]*filter[k];
- *data = (T)val[0];
- if (!pass) data += off; else data -= off;
- for (int k = 2; k>0; --k) x[k] = x[k - 1];
- for (int k = 3; k>0; --k) val[k] = val[k - 1];
- }
- *data = (T)0;
- }
- } break;
- case 3: {
- double x[3]; // [front,center,back]
- for (int pass = 0; pass<2; ++pass) {
- if (!pass) {
- for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0);
- for (int k = 0; k<4; ++k) val[k] = 0;
- } else {
- // Apply Triggs boundary conditions
- const double
- unp = val[1], unp1 = val[2], unp2 = val[3];
- val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum;
- val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum;
- val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum;
- *data = (T)val[0];
- data -= off;
- for (int k = 3; k>0; --k) val[k] = val[k - 1];
- }
- for (int n = pass; n<N - 1; ++n) {
- if (!pass) { x[0] = *(data + off); val[0] = (x[0] - 2*x[1] + x[2]); }
- else { x[0] = *(data - off); val[0] = 0.5f * (x[2] - x[0]) * sum; }
- for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k];
- *data = (T)val[0];
- if (!pass) data += off; else data -= off;
- for (int k = 2; k>0; --k) x[k] = x[k - 1];
- for (int k = 3; k>0; --k) val[k] = val[k - 1];
- }
- *data = (T)0;
- }
- } break;
- }
- }
- //! Van Vliet recursive Gaussian filter.
- /**
- \param sigma standard deviation of the Gaussian filter
- \param order the order of the filter 0,1,2,3
- \param axis Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
- \param boundary_conditions Boundary conditions.
- Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
- \note dirichlet boundary condition has a strange behavior
- I.T. Young, L.J. van Vliet, M. van Ginkel, Recursive Gabor filtering.
- IEEE Trans. Sig. Proc., vol. 50, pp. 2799-2805, 2002.
- (this is an improvement over Young-Van Vliet, Sig. Proc. 44, 1995)
- Boundary conditions (only for order 0) using Triggs matrix, from
- B. Triggs and M. Sdika. Boundary conditions for Young-van Vliet
- recursive filtering. IEEE Trans. Signal Processing,
- vol. 54, pp. 2365-2367, 2006.
- **/
- CImg<T>& vanvliet(const float sigma, const unsigned int order, const char axis='x',
- const unsigned int boundary_conditions=1) {
- if (order>2)
- throw CImgArgumentException(_cimg_instance
- "deriche(): Invalid specified order '%d' "
- "('order' can be { 0=smoothing | 1=1st-derivative | 2=2nd-derivative }).",
- cimg_instance,
- order);
- const char naxis = cimg::lowercase(axis);
- if (naxis!='x' && naxis!='y' && naxis!='z' && naxis!='c')
- throw CImgArgumentException(_cimg_instance
- "deriche(): Invalid specified axis '%c'.",
- cimg_instance,
- axis);
- const double
- nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:
- naxis=='y'?_height:
- naxis=='z'?_depth:_spectrum)/100,
- nnsigma = nsigma<0.5f?0.5f:nsigma;
- if (is_empty() || (nsigma<0.1f && !order)) return *this;
- if (nsigma<0.5f) return deriche(nsigma,order,axis,boundary_conditions);
- if (!cimg::type<T>::is_float())
- return CImg<Tfloat>(*this,false).vanvliet(sigma,order,axis,boundary_conditions).move_to(*this);
- if (boundary_conditions>1) {
- const int w = width(), h = height(), d = depth(), s = spectrum(), border = (int)cimg::round(1 + 3*nnsigma);
- switch (naxis) {
- case 'x' :
- return draw_image(get_resize(w + 2*border,h,d,s,0,boundary_conditions,0.5).
- vanvliet(nnsigma,order,naxis,1).columns(border,w - 1 + border));
- case 'y' :
- return draw_image(get_resize(w,h + 2*border,d,s,0,boundary_conditions,0,0.5).
- vanvliet(nnsigma,order,naxis,1).rows(border,h - 1 + border));
- case 'z' :
- return draw_image(get_resize(w,h,d + 2*border,s,0,boundary_conditions,0,0,0.5).
- vanvliet(nnsigma,order,naxis,1).slices(border,d - 1 + border));
- default :
- return draw_image(get_resize(w,h,d,s + 2*border,0,boundary_conditions,0,0,0,0.5).
- vanvliet(nnsigma,order,naxis,1).channels(border,d - 1 + border));
- }
- }
- const double
- m0 = 1.16680, m1 = 1.10783, m2 = 1.40586,
- m1sq = m1 * m1, m2sq = m2 * m2,
- q = (nnsigma<3.556?-0.2568 + 0.5784*nnsigma + 0.0561*nnsigma*nnsigma:2.5091 + 0.9804*(nnsigma - 3.556)),
- qsq = q * q,
- scale = (m0 + q) * (m1sq + m2sq + 2 * m1 * q + qsq),
- b1 = -q * (2 * m0 * m1 + m1sq + m2sq + (2 * m0 + 4 * m1) * q + 3 * qsq) / scale,
- b2 = qsq * (m0 + 2 * m1 + 3 * q) / scale,
- b3 = -qsq * q / scale,
- B = ( m0 * (m1sq + m2sq) ) / scale;
- double filter[4];
- filter[0] = B; filter[1] = -b1; filter[2] = -b2; filter[3] = -b3;
- switch (naxis) {
- case 'x' : {
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
- _height*_depth*_spectrum>=16))
- cimg_forYZC(*this,y,z,c)
- _cimg_recursive_apply(data(0,y,z,c),filter,_width,1U,order,boundary_conditions);
- } break;
- case 'y' : {
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
- _height*_depth*_spectrum>=16))
- cimg_forXZC(*this,x,z,c)
- _cimg_recursive_apply(data(x,0,z,c),filter,_height,(ulongT)_width,order,boundary_conditions);
- } break;
- case 'z' : {
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
- _height*_depth*_spectrum>=16))
- cimg_forXYC(*this,x,y,c)
- _cimg_recursive_apply(data(x,y,0,c),filter,_depth,(ulongT)_width*_height,
- order,boundary_conditions);
- } break;
- default : {
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
- _height*_depth*_spectrum>=16))
- cimg_forXYZ(*this,x,y,z)
- _cimg_recursive_apply(data(x,y,z,0),filter,_spectrum,(ulongT)_width*_height*_depth,
- order,boundary_conditions);
- }
- }
- return *this;
- }
- //! Blur image using Van Vliet recursive Gaussian filter. \newinstance.
- CImg<Tfloat> get_vanvliet(const float sigma, const unsigned int order, const char axis='x',
- const unsigned int boundary_conditions=1) const {
- return CImg<Tfloat>(*this,false).vanvliet(sigma,order,axis,boundary_conditions);
- }
- //! Blur image.
- /**
- \param sigma_x Standard deviation of the blur, along the X-axis.
- \param sigma_y Standard deviation of the blur, along the Y-axis.
- \param sigma_z Standard deviation of the blur, along the Z-axis.
- \param boundary_conditions Boundary conditions.
- Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
- \param is_gaussian Tells if the blur uses a gaussian (\c true) or quasi-gaussian (\c false) kernel.
- \note
- - The blur is computed as a 0-order Vanvliet (gaussian) or Deriche filter (quasi-gaussian).
- - This is a recursive algorithm, not depending on the values of the standard deviations.
- \see deriche(), vanvliet().
- **/
- CImg<T>& blur(const float sigma_x, const float sigma_y, const float sigma_z,
- const unsigned int boundary_conditions=1, const bool is_gaussian=true) {
- if (is_empty()) return *this;
- if (is_gaussian) {
- if (_width>1) vanvliet(sigma_x,0,'x',boundary_conditions);
- if (_height>1) vanvliet(sigma_y,0,'y',boundary_conditions);
- if (_depth>1) vanvliet(sigma_z,0,'z',boundary_conditions);
- } else {
- if (_width>1) deriche(sigma_x,0,'x',boundary_conditions);
- if (_height>1) deriche(sigma_y,0,'y',boundary_conditions);
- if (_depth>1) deriche(sigma_z,0,'z',boundary_conditions);
- }
- return *this;
- }
- //! Blur image \newinstance.
- CImg<Tfloat> get_blur(const float sigma_x, const float sigma_y, const float sigma_z,
- const unsigned int boundary_conditions=1, const bool is_gaussian=true) const {
- return CImg<Tfloat>(*this,false).blur(sigma_x,sigma_y,sigma_z,boundary_conditions,is_gaussian);
- }
- //! Blur image isotropically.
- /**
- \param sigma Standard deviation of the blur.
- \param boundary_conditions Boundary conditions.
- Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.a
- \param is_gaussian Use a gaussian kernel (VanVliet) is set, a quasi-gaussian (Deriche) otherwise.
- \see deriche(), vanvliet().
- **/
- CImg<T>& blur(const float sigma, const unsigned int boundary_conditions=1, const bool is_gaussian=true) {
- const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100;
- return blur(nsigma,nsigma,nsigma,boundary_conditions,is_gaussian);
- }
- //! Blur image isotropically \newinstance.
- CImg<Tfloat> get_blur(const float sigma, const unsigned int boundary_conditions=1,
- const bool is_gaussian=true) const {
- return CImg<Tfloat>(*this,false).blur(sigma,boundary_conditions,is_gaussian);
- }
- //! Blur image anisotropically, directed by a field of diffusion tensors.
- /**
- \param G Field of square roots of diffusion tensors/vectors used to drive the smoothing.
- \param amplitude Amplitude of the smoothing.
- \param dl Spatial discretization.
- \param da Angular discretization.
- \param gauss_prec Precision of the diffusion process.
- \param interpolation_type Interpolation scheme.
- Can be <tt>{ 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }</tt>.
- \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
- **/
- template<typename t>
- CImg<T>& blur_anisotropic(const CImg<t>& G,
- const float amplitude=60, const float dl=0.8f, const float da=30,
- const float gauss_prec=2, const unsigned int interpolation_type=0,
- const bool is_fast_approx=1) {
- // Check arguments and init variables
- if (!is_sameXYZ(G) || (G._spectrum!=3 && G._spectrum!=6))
- throw CImgArgumentException(_cimg_instance
- "blur_anisotropic(): Invalid specified diffusion tensor field (%u,%u,%u,%u,%p).",
- cimg_instance,
- G._width,G._height,G._depth,G._spectrum,G._data);
- if (is_empty() || dl<0) return *this;
- const float namplitude = amplitude>=0?amplitude:-amplitude*cimg::max(_width,_height,_depth)/100;
- unsigned int iamplitude = cimg::round(namplitude);
- const bool is_3d = (G._spectrum==6);
- T val_min, val_max = max_min(val_min);
- _cimg_abort_init_openmp;
- cimg_abort_init;
- if (da<=0) { // Iterated oriented Laplacians
- CImg<Tfloat> velocity(_width,_height,_depth,_spectrum);
- for (unsigned int iteration = 0; iteration<iamplitude; ++iteration) {
- Tfloat *ptrd = velocity._data, veloc_max = 0;
- if (is_3d) // 3D version
- cimg_forC(*this,c) {
- cimg_abort_test;
- CImg_3x3x3(I,Tfloat);
- cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
- const Tfloat
- ixx = Incc + Ipcc - 2*Iccc,
- ixy = (Innc + Ippc - Inpc - Ipnc)/4,
- ixz = (Incn + Ipcp - Incp - Ipcn)/4,
- iyy = Icnc + Icpc - 2*Iccc,
- iyz = (Icnn + Icpp - Icnp - Icpn)/4,
- izz = Iccn + Iccp - 2*Iccc,
- veloc = (Tfloat)(G(x,y,z,0)*ixx + 2*G(x,y,z,1)*ixy + 2*G(x,y,z,2)*ixz +
- G(x,y,z,3)*iyy + 2*G(x,y,z,4)*iyz + G(x,y,z,5)*izz);
- *(ptrd++) = veloc;
- if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
- }
- }
- else // 2D version
- cimg_forZC(*this,z,c) {
- cimg_abort_test;
- CImg_3x3(I,Tfloat);
- cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
- const Tfloat
- ixx = Inc + Ipc - 2*Icc,
- ixy = (Inn + Ipp - Inp - Ipn)/4,
- iyy = Icn + Icp - 2*Icc,
- veloc = (Tfloat)(G(x,y,0,0)*ixx + 2*G(x,y,0,1)*ixy + G(x,y,0,2)*iyy);
- *(ptrd++) = veloc;
- if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
- }
- }
- if (veloc_max>0) *this+=(velocity*=dl/veloc_max);
- }
- } else { // LIC-based smoothing
- const ulongT whd = (ulongT)_width*_height*_depth;
- const float sqrt2amplitude = (float)std::sqrt(2*namplitude);
- const int dx1 = width() - 1, dy1 = height() - 1, dz1 = depth() - 1;
- CImg<Tfloat> res(_width,_height,_depth,_spectrum,0), W(_width,_height,_depth,is_3d?4:3), val(_spectrum,1,1,1,0);
- int N = 0;
- if (is_3d) { // 3D version
- for (float phi = cimg::mod(180.f,da)/2.f; phi<=180; phi+=da) {
- const float phir = (float)(phi*cimg::PI/180), datmp = (float)(da/std::cos(phir)),
- da2 = datmp<1?360.f:datmp;
- for (float theta = 0; theta<360; (theta+=da2),++N) {
- const float
- thetar = (float)(theta*cimg::PI/180),
- vx = (float)(std::cos(thetar)*std::cos(phir)),
- vy = (float)(std::sin(thetar)*std::cos(phir)),
- vz = (float)std::sin(phir);
- const t
- *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2),
- *pd = G.data(0,0,0,3), *pe = G.data(0,0,0,4), *pf = G.data(0,0,0,5);
- Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2), *pd3 = W.data(0,0,0,3);
- cimg_forXYZ(G,xg,yg,zg) {
- const t a = *(pa++), b = *(pb++), c = *(pc++), d = *(pd++), e = *(pe++), f = *(pf++);
- const float
- u = (float)(a*vx + b*vy + c*vz),
- v = (float)(b*vx + d*vy + e*vz),
- w = (float)(c*vx + e*vy + f*vz),
- n = 1e-5f + cimg::hypot(u,v,w),
- dln = dl/n;
- *(pd0++) = (Tfloat)(u*dln);
- *(pd1++) = (Tfloat)(v*dln);
- *(pd2++) = (Tfloat)(w*dln);
- *(pd3++) = (Tfloat)n;
- }
- cimg_abort_test;
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
- cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && _height*_depth>=2)
- firstprivate(val))
- cimg_forYZ(*this,y,z) _cimg_abort_try_openmp2 {
- cimg_abort_test2;
- cimg_forX(*this,x) {
- val.fill(0);
- const float
- n = (float)W(x,y,z,3),
- fsigma = (float)(n*sqrt2amplitude),
- fsigma2 = 2*fsigma*fsigma,
- length = gauss_prec*fsigma;
- float
- S = 0,
- X = (float)x,
- Y = (float)y,
- Z = (float)z;
- switch (interpolation_type) {
- case 0 : { // Nearest neighbor
- for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
- const int
- cx = (int)(X + 0.5f),
- cy = (int)(Y + 0.5f),
- cz = (int)(Z + 0.5f);
- const float
- u = (float)W(cx,cy,cz,0),
- v = (float)W(cx,cy,cz,1),
- w = (float)W(cx,cy,cz,2);
- if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,cz,c); ++S; }
- else {
- const float coef = (float)std::exp(-l*l/fsigma2);
- cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,cz,c));
- S+=coef;
- }
- X+=u; Y+=v; Z+=w;
- }
- } break;
- case 1 : { // Linear interpolation
- for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
- const float
- u = (float)(W._linear_atXYZ(X,Y,Z,0)),
- v = (float)(W._linear_atXYZ(X,Y,Z,1)),
- w = (float)(W._linear_atXYZ(X,Y,Z,2));
- if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
- else {
- const float coef = (float)std::exp(-l*l/fsigma2);
- cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
- S+=coef;
- }
- X+=u; Y+=v; Z+=w;
- }
- } break;
- default : { // 2nd order Runge Kutta
- for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
- const float
- u0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,0)),
- v0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,1)),
- w0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,2)),
- u = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,0)),
- v = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,1)),
- w = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,2));
- if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
- else {
- const float coef = (float)std::exp(-l*l/fsigma2);
- cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
- S+=coef;
- }
- X+=u; Y+=v; Z+=w;
- }
- } break;
- }
- Tfloat *ptrd = res.data(x,y,z);
- if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; }
- else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,z,c)); ptrd+=whd; }
- }
- } _cimg_abort_catch_openmp2
- }
- }
- } else { // 2D LIC algorithm
- for (float theta = cimg::mod(360.f,da)/2.f; theta<360; (theta+=da),++N) {
- const float thetar = (float)(theta*cimg::PI/180),
- vx = (float)(std::cos(thetar)), vy = (float)(std::sin(thetar));
- const t *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2);
- Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2);
- cimg_forXY(G,xg,yg) {
- const t a = *(pa++), b = *(pb++), c = *(pc++);
- const float
- u = (float)(a*vx + b*vy),
- v = (float)(b*vx + c*vy),
- n = std::max(1e-5f,cimg::hypot(u,v)),
- dln = dl/n;
- *(pd0++) = (Tfloat)(u*dln);
- *(pd1++) = (Tfloat)(v*dln);
- *(pd2++) = (Tfloat)n;
- }
- cimg_abort_test;
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && _height>=2)
- firstprivate(val))
- cimg_forY(*this,y) _cimg_abort_try_openmp2 {
- cimg_abort_test2;
- cimg_forX(*this,x) {
- val.fill(0);
- const float
- n = (float)W(x,y,0,2),
- fsigma = (float)(n*sqrt2amplitude),
- fsigma2 = 2*fsigma*fsigma,
- length = gauss_prec*fsigma;
- float
- S = 0,
- X = (float)x,
- Y = (float)y;
- switch (interpolation_type) {
- case 0 : { // Nearest-neighbor
- for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
- const int
- cx = (int)(X + 0.5f),
- cy = (int)(Y + 0.5f);
- const float
- u = (float)W(cx,cy,0,0),
- v = (float)W(cx,cy,0,1);
- if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,0,c); ++S; }
- else {
- const float coef = (float)std::exp(-l*l/fsigma2);
- cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,0,c));
- S+=coef;
- }
- X+=u; Y+=v;
- }
- } break;
- case 1 : { // Linear interpolation
- for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
- const float
- u = (float)(W._linear_atXY(X,Y,0,0)),
- v = (float)(W._linear_atXY(X,Y,0,1));
- if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
- else {
- const float coef = (float)std::exp(-l*l/fsigma2);
- cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
- S+=coef;
- }
- X+=u; Y+=v;
- }
- } break;
- default : { // 2nd-order Runge-kutta interpolation
- for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
- const float
- u0 = (float)(0.5f*W._linear_atXY(X,Y,0,0)),
- v0 = (float)(0.5f*W._linear_atXY(X,Y,0,1)),
- u = (float)(W._linear_atXY(X + u0,Y + v0,0,0)),
- v = (float)(W._linear_atXY(X + u0,Y + v0,0,1));
- if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
- else {
- const float coef = (float)std::exp(-l*l/fsigma2);
- cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
- S+=coef;
- }
- X+=u; Y+=v;
- }
- }
- }
- Tfloat *ptrd = res.data(x,y);
- if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; }
- else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,0,c)); ptrd+=whd; }
- }
- } _cimg_abort_catch_openmp2
- }
- }
- const Tfloat *ptrs = res._data;
- cimg_for(*this,ptrd,T) {
- const Tfloat _val = *(ptrs++)/N;
- *ptrd = _val<val_min?val_min:(_val>val_max?val_max:(T)_val);
- }
- }
- cimg_abort_test;
- return *this;
- }
- //! Blur image anisotropically, directed by a field of diffusion tensors \newinstance.
- template<typename t>
- CImg<Tfloat> get_blur_anisotropic(const CImg<t>& G,
- const float amplitude=60, const float dl=0.8f, const float da=30,
- const float gauss_prec=2, const unsigned int interpolation_type=0,
- const bool is_fast_approx=true) const {
- return CImg<Tfloat>(*this,false).blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx);
- }
- //! Blur image anisotropically, in an edge-preserving way.
- /**
- \param amplitude Amplitude of the smoothing.
- \param sharpness Sharpness.
- \param anisotropy Anisotropy.
- \param alpha Standard deviation of the gradient blur.
- \param sigma Standard deviation of the structure tensor blur.
- \param dl Spatial discretization.
- \param da Angular discretization.
- \param gauss_prec Precision of the diffusion process.
- \param interpolation_type Interpolation scheme.
- Can be <tt>{ 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }</tt>.
- \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
- **/
- CImg<T>& blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
- const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30,
- const float gauss_prec=2, const unsigned int interpolation_type=0,
- const bool is_fast_approx=true) {
- const float nalpha = alpha>=0?alpha:-alpha*cimg::max(_width,_height,_depth)/100;
- const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100;
- return blur_anisotropic(get_diffusion_tensors(sharpness,anisotropy,nalpha,nsigma,interpolation_type!=3),
- amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx);
- }
- //! Blur image anisotropically, in an edge-preserving way \newinstance.
- CImg<Tfloat> get_blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
- const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f,
- const float da=30, const float gauss_prec=2,
- const unsigned int interpolation_type=0,
- const bool is_fast_approx=true) const {
- return CImg<Tfloat>(*this,false).blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,
- interpolation_type,is_fast_approx);
- }
- //! Blur image, with the joint bilateral filter.
- /**
- \param guide Image used to model the smoothing weights.
- \param sigma_x Amount of blur along the X-axis.
- \param sigma_y Amount of blur along the Y-axis.
- \param sigma_z Amount of blur along the Z-axis.
- \param sigma_r Amount of blur along the value axis.
- \param sampling_x Amount of downsampling along the X-axis used for the approximation.
- Defaults (0) to sigma_x.
- \param sampling_y Amount of downsampling along the Y-axis used for the approximation.
- Defaults (0) to sigma_y.
- \param sampling_z Amount of downsampling along the Z-axis used for the approximation.
- Defaults (0) to sigma_z.
- \param sampling_r Amount of downsampling along the value axis used for the approximation.
- Defaults (0) to sigma_r.
- \note This algorithm uses the optimisation technique proposed by S. Paris and F. Durand, in ECCV'2006
- (extended for 3D volumetric images).
- It is based on the reference implementation http://people.csail.mit.edu/jiawen/software/bilateralFilter.m
- **/
- template<typename t>
- CImg<T>& blur_bilateral(const CImg<t>& guide,
- const float sigma_x, const float sigma_y,
- const float sigma_z, const float sigma_r,
- const float sampling_x, const float sampling_y,
- const float sampling_z, const float sampling_r) {
- if (!is_sameXYZ(guide))
- throw CImgArgumentException(_cimg_instance
- "blur_bilateral(): Invalid size for specified guide image (%u,%u,%u,%u,%p).",
- cimg_instance,
- guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
- if (is_empty() || (!sigma_x && !sigma_y && !sigma_z)) return *this;
- T edge_min, edge_max = guide.max_min(edge_min);
- if (edge_min==edge_max) return blur(sigma_x,sigma_y,sigma_z);
- const float
- edge_delta = (float)(edge_max - edge_min),
- _sigma_x = sigma_x>=0?sigma_x:-sigma_x*_width/100,
- _sigma_y = sigma_y>=0?sigma_y:-sigma_y*_height/100,
- _sigma_z = sigma_z>=0?sigma_z:-sigma_z*_depth/100,
- _sigma_r = sigma_r>=0?sigma_r:-sigma_r*edge_delta/100,
- _sampling_x = sampling_x?sampling_x:std::max(_sigma_x,1.f),
- _sampling_y = sampling_y?sampling_y:std::max(_sigma_y,1.f),
- _sampling_z = sampling_z?sampling_z:std::max(_sigma_z,1.f),
- _sampling_r = sampling_r?sampling_r:std::max(_sigma_r,edge_delta/256),
- derived_sigma_x = _sigma_x / _sampling_x,
- derived_sigma_y = _sigma_y / _sampling_y,
- derived_sigma_z = _sigma_z / _sampling_z,
- derived_sigma_r = _sigma_r / _sampling_r;
- const int
- padding_x = (int)(2*derived_sigma_x) + 1,
- padding_y = (int)(2*derived_sigma_y) + 1,
- padding_z = (int)(2*derived_sigma_z) + 1,
- padding_r = (int)(2*derived_sigma_r) + 1;
- const unsigned int
- bx = (unsigned int)((_width - 1)/_sampling_x + 1 + 2*padding_x),
- by = (unsigned int)((_height - 1)/_sampling_y + 1 + 2*padding_y),
- bz = (unsigned int)((_depth - 1)/_sampling_z + 1 + 2*padding_z),
- br = (unsigned int)(edge_delta/_sampling_r + 1 + 2*padding_r);
- if (bx>0 || by>0 || bz>0 || br>0) {
- const bool is_3d = (_depth>1);
- if (is_3d) { // 3D version of the algorithm
- CImg<floatT> bgrid(bx,by,bz,br), bgridw(bx,by,bz,br);
- cimg_forC(*this,c) {
- const CImg<t> _guide = guide.get_shared_channel(c%guide._spectrum);
- bgrid.fill(0); bgridw.fill(0);
- cimg_forXYZ(*this,x,y,z) {
- const T val = (*this)(x,y,z,c);
- const float edge = (float)_guide(x,y,z);
- const int
- X = (int)cimg::round(x/_sampling_x) + padding_x,
- Y = (int)cimg::round(y/_sampling_y) + padding_y,
- Z = (int)cimg::round(z/_sampling_z) + padding_z,
- R = (int)cimg::round((edge - edge_min)/_sampling_r) + padding_r;
- bgrid(X,Y,Z,R)+=(float)val;
- bgridw(X,Y,Z,R)+=1;
- }
- bgrid.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false);
- bgridw.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false);
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),4096))
- cimg_forXYZ(*this,x,y,z) {
- const float edge = (float)_guide(x,y,z);
- const float
- X = x/_sampling_x + padding_x,
- Y = y/_sampling_y + padding_y,
- Z = z/_sampling_z + padding_z,
- R = (edge - edge_min)/_sampling_r + padding_r;
- const float bval0 = bgrid._linear_atXYZC(X,Y,Z,R), bval1 = bgridw._linear_atXYZC(X,Y,Z,R);
- (*this)(x,y,z,c) = (T)(bval0/bval1);
- }
- }
- } else { // 2D version of the algorithm
- CImg<floatT> bgrid(bx,by,br,2);
- cimg_forC(*this,c) {
- const CImg<t> _guide = guide.get_shared_channel(c%guide._spectrum);
- bgrid.fill(0);
- cimg_forXY(*this,x,y) {
- const T val = (*this)(x,y,c);
- const float edge = (float)_guide(x,y);
- const int
- X = (int)cimg::round(x/_sampling_x) + padding_x,
- Y = (int)cimg::round(y/_sampling_y) + padding_y,
- R = (int)cimg::round((edge - edge_min)/_sampling_r) + padding_r;
- bgrid(X,Y,R,0)+=(float)val;
- bgrid(X,Y,R,1)+=1;
- }
- bgrid.blur(derived_sigma_x,derived_sigma_y,0,true).blur(0,0,derived_sigma_r,false);
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(size(),4096))
- cimg_forXY(*this,x,y) {
- const float edge = (float)_guide(x,y);
- const float
- X = x/_sampling_x + padding_x,
- Y = y/_sampling_y + padding_y,
- R = (edge - edge_min)/_sampling_r + padding_r;
- const float bval0 = bgrid._linear_atXYZ(X,Y,R,0), bval1 = bgrid._linear_atXYZ(X,Y,R,1);
- (*this)(x,y,c) = (T)(bval0/bval1);
- }
- }
- }
- }
- return *this;
- }
- //! Blur image, with the joint bilateral filter \newinstance.
- template<typename t>
- CImg<Tfloat> get_blur_bilateral(const CImg<t>& guide,
- const float sigma_x, const float sigma_y,
- const float sigma_z, const float sigma_r,
- const float sampling_x, const float sampling_y,
- const float sampling_z, const float sampling_r) const {
- return CImg<Tfloat>(*this,false).blur_bilateral(guide,sigma_x,sigma_y,sigma_z,sigma_r,
- sampling_x,sampling_y,sampling_z,sampling_r);
- }
- //! Blur image using the joint bilateral filter.
- /**
- \param guide Image used to model the smoothing weights.
- \param sigma_s Amount of blur along the XYZ-axes.
- \param sigma_r Amount of blur along the value axis.
- \param sampling_s Amount of downsampling along the XYZ-axes used for the approximation. Defaults to sigma_s.
- \param sampling_r Amount of downsampling along the value axis used for the approximation. Defaults to sigma_r.
- **/
- template<typename t>
- CImg<T>& blur_bilateral(const CImg<t>& guide,
- const float sigma_s, const float sigma_r,
- const float sampling_s=0, const float sampling_r=0) {
- const float _sigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100;
- return blur_bilateral(guide,_sigma_s,_sigma_s,_sigma_s,sigma_r,sampling_s,sampling_s,sampling_s,sampling_r);
- }
- //! Blur image using the bilateral filter \newinstance.
- template<typename t>
- CImg<Tfloat> get_blur_bilateral(const CImg<t>& guide,
- const float sigma_s, const float sigma_r,
- const float sampling_s=0, const float sampling_r=0) const {
- return CImg<Tfloat>(*this,false).blur_bilateral(guide,sigma_s,sigma_r,sampling_s,sampling_r);
- }
- // [internal] Apply a box filter (used by CImg<T>::boxfilter() and CImg<T>::blur_box()).
- /*
- \param ptr the pointer of the data
- \param N size of the data
- \param boxsize Size of the box filter (can be subpixel).
- \param off the offset between two data point
- \param order the order of the filter 0 (smoothing), 1st derivative and 2nd derivative.
- \param boundary_conditions Boundary conditions.
- Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
- */
- static void _cimg_blur_box_apply(T *ptr, const float boxsize, const int N, const ulongT off,
- const int order, const unsigned int boundary_conditions,
- const unsigned int nb_iter) {
- const int nboundary_conditions = boundary_conditions>1 && boxsize<=3?1:boundary_conditions;
- // Smooth.
- if (boxsize>1 && nb_iter) {
- const int w2 = (int)(boxsize - 1)/2;
- const unsigned int winsize = 2*w2 + 1U;
- const double frac = (boxsize - winsize)/2.;
- CImg<T> win(winsize);
- for (unsigned int iter = 0; iter<nb_iter; ++iter) {
- Tdouble sum = 0; // window sum
- for (int x = -w2; x<=w2; ++x) {
- win[x + w2] = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,x);
- sum+=win[x + w2];
- }
- int ifirst = 0, ilast = 2*w2;
- T
- prev = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,-w2 - 1),
- next = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,w2 + 1);
- for (int x = 0; x < N - 1; ++x) {
- const double sum2 = sum + frac * (prev + next);
- ptr[x*off] = (T)(sum2/boxsize);
- prev = win[ifirst];
- sum-=prev;
- ifirst = (int)((ifirst + 1)%winsize);
- ilast = (int)((ilast + 1)%winsize);
- win[ilast] = next;
- sum+=next;
- next = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,x + w2 + 2);
- }
- const double sum2 = sum + frac * (prev + next);
- ptr[(N - 1)*off] = (T)(sum2/boxsize);
- }
- }
- // Derive.
- switch (order) {
- case 0 :
- break;
- case 1 : {
- Tfloat
- p = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,-1),
- c = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,0),
- n = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,1);
- for (int x = 0; x<N - 1; ++x) {
- ptr[x*off] = (T)((n-p)/2.);
- p = c;
- c = n;
- n = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,x + 2);
- }
- ptr[(N - 1)*off] = (T)((n-p)/2.);
- } break;
- case 2: {
- Tfloat
- p = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,-1),
- c = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,0),
- n = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,1);
- for (int x = 0; x<N - 1; ++x) {
- ptr[x*off] = (T)(n - 2*c + p);
- p = c;
- c = n;
- n = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,x + 2);
- }
- ptr[(N - 1)*off] = (T)(n - 2*c + p);
- } break;
- }
- }
- static T __cimg_blur_box_apply(T *ptr, const int N, const ulongT off,
- const unsigned int boundary_conditions, const int x) {
- switch (boundary_conditions) {
- case 0 : // Dirichlet
- return x<0 || x>=N?(T)0:ptr[x*off];
- case 1 : { // Neumann
- const int nx = x<0?0:x>=N?N - 1:x;
- return ptr[nx*off];
- }
- case 2 : { // Periodic
- const int nx = cimg::mod(x,N);
- return ptr[nx*off];
- }
- default : { // Mirror
- const int
- N2 = 2*N,
- tx = cimg::mod(x,N2),
- nx = tx<N?tx:N2 - tx - 1;
- return ptr[nx*off];
- }
- }
- return (T)0;
- }
- // Apply box filter of order 0,1,2.
- /**
- \param boxsize Size of the box window (can be subpixel)
- \param order the order of the filter 0,1 or 2.
- \param axis Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
- \param boundary_conditions Boundary conditions.
- Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
- \param nb_iter Number of filter iterations.
- **/
- CImg<T>& boxfilter(const float boxsize, const int order, const char axis='x',
- const unsigned int boundary_conditions=1,
- const unsigned int nb_iter=1) {
- const char naxis = cimg::lowercase(axis);
- const float nboxsize = boxsize>=0?boxsize:-boxsize*
- (naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100;
- if (is_empty() || !nboxsize || (nboxsize<=1 && !order)) return *this;
- switch (naxis) {
- case 'x' : {
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
- _height*_depth*_spectrum>=16))
- cimg_forYZC(*this,y,z,c)
- _cimg_blur_box_apply(data(0,y,z,c),nboxsize,_width,1U,order,boundary_conditions,nb_iter);
- } break;
- case 'y' : {
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
- _height*_depth*_spectrum>=16))
- cimg_forXZC(*this,x,z,c)
- _cimg_blur_box_apply(data(x,0,z,c),nboxsize,_height,(ulongT)_width,order,boundary_conditions,nb_iter);
- } break;
- case 'z' : {
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
- _height*_depth*_spectrum>=16))
- cimg_forXYC(*this,x,y,c)
- _cimg_blur_box_apply(data(x,y,0,c),nboxsize,_depth,(ulongT)_width*_height,order,boundary_conditions,nb_iter);
- } break;
- default : {
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
- _height*_depth*_spectrum>=16))
- cimg_forXYZ(*this,x,y,z)
- _cimg_blur_box_apply(data(x,y,z,0),nboxsize,_spectrum,(ulongT)_width*_height*_depth,
- order,boundary_conditions,nb_iter);
- }
- }
- return *this;
- }
- // Apply box filter of order 0,1 or 2 \newinstance.
- CImg<Tfloat> get_boxfilter(const float boxsize, const int order, const char axis='x',
- const unsigned int boundary_conditions=1,
- const unsigned int nb_iter=1) const {
- return CImg<Tfloat>(*this,false).boxfilter(boxsize,order,axis,boundary_conditions,nb_iter);
- }
- //! Blur image with a box filter.
- /**
- \param boxsize_x Size of the box window, along the X-axis (can be subpixel).
- \param boxsize_y Size of the box window, along the Y-axis (can be subpixel).
- \param boxsize_z Size of the box window, along the Z-axis (can be subpixel).
- \param boundary_conditions Boundary conditions.
- Can be <tt>{ false=dirichlet | true=neumann | 2=periodic | 3=mirror }</tt>.
- \param nb_iter Number of filter iterations.
- \note
- - This is a recursive algorithm, not depending on the values of the box kernel size.
- \see blur().
- **/
- CImg<T>& blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z,
- const unsigned int boundary_conditions=1,
- const unsigned int nb_iter=1) {
- if (is_empty()) return *this;
- if (_width>1) boxfilter(boxsize_x,0,'x',boundary_conditions,nb_iter);
- if (_height>1) boxfilter(boxsize_y,0,'y',boundary_conditions,nb_iter);
- if (_depth>1) boxfilter(boxsize_z,0,'z',boundary_conditions,nb_iter);
- return *this;
- }
- //! Blur image with a box filter \newinstance.
- CImg<Tfloat> get_blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z,
- const unsigned int boundary_conditions=1) const {
- return CImg<Tfloat>(*this,false).blur_box(boxsize_x,boxsize_y,boxsize_z,boundary_conditions);
- }
- //! Blur image with a box filter.
- /**
- \param boxsize Size of the box window (can be subpixel).
- \param boundary_conditions Boundary conditions.
- Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.a
- \see deriche(), vanvliet().
- **/
- CImg<T>& blur_box(const float boxsize, const unsigned int boundary_conditions=1) {
- const float nboxsize = boxsize>=0?boxsize:-boxsize*cimg::max(_width,_height,_depth)/100;
- return blur_box(nboxsize,nboxsize,nboxsize,boundary_conditions);
- }
- //! Blur image with a box filter \newinstance.
- CImg<Tfloat> get_blur_box(const float boxsize, const unsigned int boundary_conditions=1) const {
- return CImg<Tfloat>(*this,false).blur_box(boxsize,boundary_conditions);
- }
- //! Blur image, with the image guided filter.
- /**
- \param guide Image used to guide the smoothing process.
- \param radius Spatial radius. If negative, it is expressed as a percentage of the largest image size.
- \param regularization Regularization parameter.
- If negative, it is expressed as a percentage of the guide value range.
- \note This method implements the filtering algorithm described in:
- He, Kaiming; Sun, Jian; Tang, Xiaoou, "Guided Image Filtering," Pattern Analysis and Machine Intelligence,
- IEEE Transactions on , vol.35, no.6, pp.1397,1409, June 2013
- **/
- template<typename t>
- CImg<T>& blur_guided(const CImg<t>& guide, const float radius, const float regularization) {
- return get_blur_guided(guide,radius,regularization).move_to(*this);
- }
- //! Blur image, with the image guided filter \newinstance.
- template<typename t>
- CImg<Tfloat> get_blur_guided(const CImg<t>& guide, const float radius, const float regularization) const {
- if (!is_sameXYZ(guide))
- throw CImgArgumentException(_cimg_instance
- "blur_guided(): Invalid size for specified guide image (%u,%u,%u,%u,%p).",
- cimg_instance,
- guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
- if (is_empty() || !radius) return *this;
- const int _radius = radius>=0?(int)radius:(int)(-radius*cimg::max(_width,_height,_depth)/100);
- float _regularization = regularization;
- if (regularization<0) {
- T edge_min, edge_max = guide.max_min(edge_min);
- if (edge_min==edge_max) return *this;
- _regularization = -regularization*(edge_max - edge_min)/100;
- }
- _regularization = std::max(_regularization,0.01f);
- const unsigned int psize = (unsigned int)(1 + 2*_radius);
- CImg<Tfloat>
- mean_p = get_blur_box(psize,true),
- mean_I = guide.get_blur_box(psize,true).resize(mean_p),
- cov_Ip = get_mul(guide).blur_box(psize,true)-=mean_p.get_mul(mean_I),
- var_I = guide.get_sqr().blur_box(psize,true)-=mean_I.get_sqr(),
- &a = cov_Ip.div(var_I+=_regularization),
- &b = mean_p-=a.get_mul(mean_I);
- a.blur_box(psize,true);
- b.blur_box(psize,true);
- return a.mul(guide)+=b;
- }
- //! Blur image using patch-based space.
- /**
- \param guide Image used to model the smoothing weights.
- \param sigma_s Amount of blur along the XYZ-axes.
- \param sigma_r Amount of blur along the value axis.
- \param patch_size Size of the patches.
- \param lookup_size Size of the window to search similar patches.
- \param smoothness Smoothness for the patch comparison.
- \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
- **/
- template<typename t>
- CImg<T>& blur_patch(const CImg<t>& guide,
- const float sigma_s, const float sigma_r, const unsigned int patch_size=3,
- const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) {
- if (is_empty() || !patch_size || !lookup_size) return *this;
- return get_blur_patch(guide,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx).move_to(*this);
- }
- //! Blur image using patch-based space \newinstance.
- template<typename t>
- CImg<Tfloat> get_blur_patch(const CImg<t>& guide,
- const float sigma_s, const float sigma_r, const unsigned int patch_size=3,
- const unsigned int lookup_size=4, const float smoothness=0,
- const bool is_fast_approx=true) const {
- #define _cimg_blur_patch3d_fast(N) { \
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) \
- cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) \
- firstprivate(P,Q)) \
- cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp2 { \
- cimg_abort_test2; \
- cimg_def##N##x##N##x##N(res,x,y,z); \
- tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,x,y,z,c,pP,tfloat); pP+=N3; } \
- const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \
- x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \
- tfloat sum_weights = 0; \
- cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) \
- if (cimg::abs(_guide(x,y,z,0) - _guide(p,q,r,0))<sigma_r3) { \
- tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,p,q,r,c,pQ,tfloat); pQ+=N3; } \
- tfloat distance2 = 0; \
- pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \
- distance2/=Pnorm; \
- const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, dz = (tfloat)r - z, \
- alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = alldist>3?0:1; \
- sum_weights+=weight; \
- cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c); \
- } \
- if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; \
- else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \
- } _cimg_abort_catch_openmp2 }
- #define _cimg_blur_patch3d(N) { \
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) \
- cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) \
- firstprivate(P,Q)) \
- cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp2 { \
- cimg_abort_test2; \
- cimg_def##N##x##N##x##N(res,x,y,z); \
- tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,x,y,z,c,pP,tfloat); pP+=N3; } \
- const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \
- x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \
- tfloat sum_weights = 0, weight_max = 0; \
- cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) { \
- tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,p,q,r,c,pQ,tfloat); pQ+=N3; } \
- tfloat distance2 = 0; \
- pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \
- distance2/=Pnorm; \
- const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, dz = (tfloat)r - z, \
- alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = std::exp(-alldist); \
- if (weight>weight_max) weight_max = weight; \
- sum_weights+=weight; \
- cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c); \
- } \
- sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight_max*(*this)(x,y,z,c); \
- if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; \
- else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \
- } _cimg_abort_catch_openmp2 }
- #define _cimg_blur_patch2d_fast(N) { \
- cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) \
- firstprivate(P,Q)) \
- cimg_forXY(res,x,y) _cimg_abort_try_openmp2 { \
- cimg_abort_test2; \
- cimg_def##N##x##N(res,x,y); \
- tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,x,y,0,c,pP,tfloat); pP+=N2; } \
- const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \
- tfloat sum_weights = 0; \
- cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) \
- if (cimg::abs(_guide(x,y,0,0) - _guide(p,q,0,0))<sigma_r3) { \
- tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,p,q,0,c,pQ,tfloat); pQ+=N2; } \
- tfloat distance2 = 0; \
- pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \
- distance2/=Pnorm; \
- const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, \
- alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = alldist>3?0:1; \
- sum_weights+=weight; \
- cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c); \
- } \
- if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; \
- else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \
- } _cimg_abort_catch_openmp2 }
- #define _cimg_blur_patch2d(N) { \
- cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) \
- firstprivate(P,Q)) \
- cimg_forXY(res,x,y) _cimg_abort_try_openmp2 { \
- cimg_abort_test2; \
- cimg_def##N##x##N(res,x,y); \
- tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,x,y,0,c,pP,tfloat); pP+=N2; } \
- const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \
- tfloat sum_weights = 0, weight_max = 0; \
- cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) { \
- tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,p,q,0,c,pQ,tfloat); pQ+=N2; } \
- tfloat distance2 = 0; \
- pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \
- distance2/=Pnorm; \
- const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, \
- alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = std::exp(-alldist); \
- if (weight>weight_max) weight_max = weight; \
- sum_weights+=weight; \
- cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c); \
- } \
- sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight_max*(*this)(x,y,c); \
- if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; \
- else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \
- } _cimg_abort_catch_openmp2 }
- typedef _cimg_tfloat tfloat;
- if (!is_sameXYZ(guide))
- throw CImgArgumentException(_cimg_instance
- "blur_patch(): Invalid size for specified guide image (%u,%u,%u,%u,%p).",
- cimg_instance,
- guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
- if (is_empty() || !patch_size || !lookup_size) return +*this;
- Tfloat val_min, val_max = (Tfloat)max_min(val_min);
- _cimg_abort_init_openmp;
- cimg_abort_init;
- CImg<Tfloat> res(_width,_height,_depth,_spectrum,0);
- const CImg<tfloat>
- __guide = guide?CImg<tfloat>(guide,guide.pixel_type()==cimg::type<tfloat>::string()):
- CImg<tfloat>(*this,pixel_type()==cimg::type<tfloat>::string()),
- _guide = smoothness>0?__guide.get_blur(smoothness):__guide.get_shared();
- CImg<tfloat> P(_guide._spectrum*patch_size*patch_size*(_depth>1?patch_size:1)), Q(P);
- t guide_min = (t)0, guide_max = (t)0;
- if (sigma_r<0) guide_max = guide.max_min(guide_min);
- const float
- guide_delta = (float)(guide_max - guide_min),
- _sigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100,
- _sigma_r = sigma_r>=0?sigma_r:-sigma_r*guide_delta/100,
- sigma_s2 = _sigma_s*_sigma_s,
- sigma_r2 = _sigma_r*_sigma_r,
- sigma_r3 = 3*_sigma_r,
- Pnorm = P.size()*sigma_r2;
- const int rsize2 = (int)lookup_size/2, rsize1 = (int)lookup_size - rsize2 - 1;
- const unsigned int N2 = patch_size*patch_size, N3 = N2*patch_size;
- cimg::unused(N2,N3);
- if (_depth>1) switch (patch_size) { // 3D
- case 2 : if (is_fast_approx) _cimg_blur_patch3d_fast(2) else _cimg_blur_patch3d(2) break;
- case 3 : if (is_fast_approx) _cimg_blur_patch3d_fast(3) else _cimg_blur_patch3d(3) break;
- default : {
- const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1;
- if (is_fast_approx) {
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
- cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4)
- firstprivate(P,Q))
- cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp2 { // Fast
- cimg_abort_test2;
- P = _guide.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true);
- const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1,
- x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2;
- tfloat sum_weights = 0;
- cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r)
- if (cimg::abs(_guide(x,y,z,0) - _guide(p,q,r,0))<sigma_r3) {
- (Q = _guide.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P;
- const tfloat
- dx = (tfloat)x - p, dy = (tfloat)y - q, dz = (tfloat)z - r,
- distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2),
- weight = distance2>3?0:1;
- sum_weights+=weight;
- cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c);
- }
- if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights;
- else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c));
- } _cimg_abort_catch_openmp2
- } else {
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
- cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4)
- firstprivate(P,Q))
- cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp2 { // Exact
- cimg_abort_test2;
- P = _guide.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true);
- const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1,
- x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2;
- tfloat sum_weights = 0, weight_max = 0;
- cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) {
- (Q = _guide.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P;
- const tfloat
- dx = (tfloat)x - p, dy = (tfloat)y - q, dz = (tfloat)z - r,
- distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2),
- weight = std::exp(-distance2);
- if (weight>weight_max) weight_max = weight;
- sum_weights+=weight;
- cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c);
- }
- sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight_max*(*this)(x,y,z,c);
- if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights;
- else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c));
- } _cimg_abort_catch_openmp2
- }
- }
- } else switch (patch_size) { // 2D
- case 2 : if (is_fast_approx) _cimg_blur_patch2d_fast(2) else _cimg_blur_patch2d(2) break;
- case 3 : if (is_fast_approx) _cimg_blur_patch2d_fast(3) else _cimg_blur_patch2d(3) break;
- case 4 : if (is_fast_approx) _cimg_blur_patch2d_fast(4) else _cimg_blur_patch2d(4) break;
- case 5 : if (is_fast_approx) _cimg_blur_patch2d_fast(5) else _cimg_blur_patch2d(5) break;
- case 6 : if (is_fast_approx) _cimg_blur_patch2d_fast(6) else _cimg_blur_patch2d(6) break;
- case 7 : if (is_fast_approx) _cimg_blur_patch2d_fast(7) else _cimg_blur_patch2d(7) break;
- case 8 : if (is_fast_approx) _cimg_blur_patch2d_fast(8) else _cimg_blur_patch2d(8) break;
- case 9 : if (is_fast_approx) _cimg_blur_patch2d_fast(9) else _cimg_blur_patch2d(9) break;
- default : { // Fast
- const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1;
- if (is_fast_approx) {
- cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4)
- firstprivate(P,Q))
- cimg_forXY(res,x,y) _cimg_abort_try_openmp2 { // Fast
- cimg_abort_test2;
- P = _guide.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true);
- const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2;
- tfloat sum_weights = 0;
- cimg_for_inXY(res,x0,y0,x1,y1,p,q)
- if (cimg::abs(_guide(x,y,0) - _guide(p,q,0))<sigma_r3) {
- (Q = _guide.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P;
- const tfloat
- dx = (tfloat)x - p, dy = (tfloat)y - q,
- distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2),
- weight = distance2>3?0:1;
- sum_weights+=weight;
- cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c);
- }
- if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights;
- else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c));
- } _cimg_abort_catch_openmp2
- } else {
- cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4)
- firstprivate(P,Q))
- cimg_forXY(res,x,y) _cimg_abort_try_openmp2 { // Exact
- cimg_abort_test2;
- P = _guide.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true);
- const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2;
- tfloat sum_weights = 0, weight_max = 0;
- cimg_for_inXY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) {
- (Q = _guide.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P;
- const tfloat
- dx = (tfloat)x - p, dy = (tfloat)y - q,
- distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2),
- weight = std::exp(-distance2);
- if (weight>weight_max) weight_max = weight;
- sum_weights+=weight;
- cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c);
- }
- sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight_max*(*this)(x,y,c);
- if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights;
- else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c));
- } _cimg_abort_catch_openmp2
- }
- }
- }
- cimg_abort_test;
- return res.cut(val_min,val_max);
- }
- //! Blur image using patch-based space \simplification.
- CImg<T>& blur_patch(const float sigma_s, const float sigma_r, const unsigned int patch_size=3,
- const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) {
- return blur_patch(*this,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx);
- }
- //! Blur image using patch-based space \simplification \newinstance.
- CImg<Tfloat> get_blur_patch(const float sigma_s, const float sigma_r, const unsigned int patch_size=3,
- const unsigned int lookup_size=4, const float smoothness=0,
- const bool is_fast_approx=true) const {
- return get_blur_patch(*this,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx);
- }
- //! Blur image with the median filter.
- /**
- \param n Size of the median filter.
- \param threshold Threshold used to discard pixels too far from the current pixel value in the median computation.
- **/
- CImg<T>& blur_median(const unsigned int n, const float threshold=0) {
- if (!n) return *this;
- return get_blur_median(n,threshold).move_to(*this);
- }
- //! Blur image with the median filter \newinstance.
- CImg<T> get_blur_median(const unsigned int n, const float threshold=0) const {
- if (is_empty() || n<=1) return +*this;
- CImg<T> res(_width,_height,_depth,_spectrum);
- T *ptrd = res._data;
- cimg::unused(ptrd);
- const int hr = (int)n/2, hl = n - hr - 1;
- if (res._depth!=1) { // 3D
- if (threshold>0)
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
- _height*_depth*_spectrum>=4))
- cimg_forXYZC(*this,x,y,z,c) { // With threshold
- const int
- x0 = x - hl, y0 = y - hl, z0 = z - hl, x1 = x + hr, y1 = y + hr, z1 = z + hr,
- nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0,
- nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1, nz1 = z1>=depth()?depth() - 1:z1;
- const Tfloat val0 = (Tfloat)(*this)(x,y,z,c);
- CImg<T> values(n*n*n);
- unsigned int nb_values = 0;
- T *_ptrd = values.data();
- cimg_for_inXYZ(*this,nx0,ny0,nz0,nx1,ny1,nz1,p,q,r)
- if (cimg::abs((*this)(p,q,r,c) - val0)<=threshold) { *(_ptrd++) = (*this)(p,q,r,c); ++nb_values; }
- res(x,y,z,c) = nb_values?values.get_shared_points(0,nb_values - 1).median():(*this)(x,y,z,c);
- }
- else
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
- _height*_depth*_spectrum>=4))
- cimg_forXYZC(*this,x,y,z,c) { // Without threshold
- const int
- x0 = x - hl, y0 = y - hl, z0 = z - hl, x1 = x + hr, y1 = y + hr, z1 = z + hr,
- nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0,
- nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1, nz1 = z1>=depth()?depth() - 1:z1;
- res(x,y,z,c) = get_crop(nx0,ny0,nz0,c,nx1,ny1,nz1,c).median();
- }
- } else {
- if (threshold>0)
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
- _height*_spectrum>=4))
- cimg_forXYC(*this,x,y,c) { // With threshold
- const int
- x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr,
- nx0 = x0<0?0:x0, ny0 = y0<0?0:y0,
- nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1;
- const Tfloat val0 = (Tfloat)(*this)(x,y,c);
- CImg<T> values(n*n);
- unsigned int nb_values = 0;
- T *_ptrd = values.data();
- cimg_for_inXY(*this,nx0,ny0,nx1,ny1,p,q)
- if (cimg::abs((*this)(p,q,c) - val0)<=threshold) { *(_ptrd++) = (*this)(p,q,c); ++nb_values; }
- res(x,y,c) = nb_values?values.get_shared_points(0,nb_values - 1).median():(*this)(x,y,c);
- }
- else {
- const int
- w1 = width() - 1, h1 = height() - 1,
- w2 = width() - 2, h2 = height() - 2,
- w3 = width() - 3, h3 = height() - 3,
- w4 = width() - 4, h4 = height() - 4;
- switch (n) { // Without threshold
- case 3 : {
- cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
- cimg_forC(*this,c) {
- CImg<T> I(9);
- cimg_for_in3x3(*this,1,1,w2,h2,x,y,0,c,I,T)
- res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6],I[7],I[8]);
- cimg_for_borderXY(*this,x,y,1)
- res(x,y,c) = get_crop(std::max(0,x - 1),std::max(0,y - 1),0,c,
- std::min(w1,x + 1),std::min(h1,y + 1),0,c).median();
- }
- } break;
- case 5 : {
- cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
- cimg_forC(*this,c) {
- CImg<T> I(25);
- cimg_for_in5x5(*this,2,2,w3,h3,x,y,0,c,I,T)
- res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],
- I[5],I[6],I[7],I[8],I[9],
- I[10],I[11],I[12],I[13],I[14],
- I[15],I[16],I[17],I[18],I[19],
- I[20],I[21],I[22],I[23],I[24]);
- cimg_for_borderXY(*this,x,y,2)
- res(x,y,c) = get_crop(std::max(0,x - 2),std::max(0,y - 2),0,c,
- std::min(w1,x + 2),std::min(h1,y + 2),0,c).median();
- }
- } break;
- case 7 : {
- cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
- cimg_forC(*this,c) {
- CImg<T> I(49);
- cimg_for_in7x7(*this,3,3,w4,h4,x,y,0,c,I,T)
- res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6],
- I[7],I[8],I[9],I[10],I[11],I[12],I[13],
- I[14],I[15],I[16],I[17],I[18],I[19],I[20],
- I[21],I[22],I[23],I[24],I[25],I[26],I[27],
- I[28],I[29],I[30],I[31],I[32],I[33],I[34],
- I[35],I[36],I[37],I[38],I[39],I[40],I[41],
- I[42],I[43],I[44],I[45],I[46],I[47],I[48]);
- cimg_for_borderXY(*this,x,y,3)
- res(x,y,c) = get_crop(std::max(0,x - 3),std::max(0,y - 3),0,c,
- std::min(w1,x + 3),std::min(h1,y + 3),0,c).median();
- }
- } break;
- default : {
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
- cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && _height*_spectrum>=4))
- cimg_forXYC(*this,x,y,c) {
- const int
- x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr,
- nx0 = x0<0?0:x0, ny0 = y0<0?0:y0,
- nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1;
- res(x,y,c) = get_crop(nx0,ny0,0,c,nx1,ny1,0,c).median();
- }
- }
- }
- }
- }
- return res;
- }
- //! Sharpen image.
- /**
- \param amplitude Sharpening amplitude
- \param sharpen_type Select sharpening method. Can be <tt>{ false=inverse diffusion | true=shock filters }</tt>.
- \param edge Edge threshold (shock filters only).
- \param alpha Gradient smoothness (shock filters only).
- \param sigma Tensor smoothness (shock filters only).
- **/
- CImg<T>& sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1,
- const float alpha=0, const float sigma=0) {
- if (is_empty()) return *this;
- T val_min, val_max = max_min(val_min);
- const float nedge = edge/2;
- CImg<Tfloat> velocity(_width,_height,_depth,_spectrum), _veloc_max(_spectrum);
- if (_depth>1) { // 3D
- if (sharpen_type) { // Shock filters
- CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors());
- if (sigma>0) G.blur(sigma);
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*32 &&
- _height*_depth>=16))
- cimg_forYZ(G,y,z) {
- Tfloat *ptrG0 = G.data(0,y,z,0), *ptrG1 = G.data(0,y,z,1),
- *ptrG2 = G.data(0,y,z,2), *ptrG3 = G.data(0,y,z,3);
- CImg<Tfloat> val, vec;
- cimg_forX(G,x) {
- G.get_tensor_at(x,y,z).symmetric_eigen(val,vec);
- if (val[0]<0) val[0] = 0;
- if (val[1]<0) val[1] = 0;
- if (val[2]<0) val[2] = 0;
- *(ptrG0++) = vec(0,0);
- *(ptrG1++) = vec(0,1);
- *(ptrG2++) = vec(0,2);
- *(ptrG3++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1] + val[2],-(Tfloat)nedge);
- }
- }
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*512 &&
- _spectrum>=2))
- cimg_forC(*this,c) {
- Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
- CImg_3x3x3(I,Tfloat);
- cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
- const Tfloat
- u = G(x,y,z,0),
- v = G(x,y,z,1),
- w = G(x,y,z,2),
- amp = G(x,y,z,3),
- ixx = Incc + Ipcc - 2*Iccc,
- ixy = (Innc + Ippc - Inpc - Ipnc)/4,
- ixz = (Incn + Ipcp - Incp - Ipcn)/4,
- iyy = Icnc + Icpc - 2*Iccc,
- iyz = (Icnn + Icpp - Icnp - Icpn)/4,
- izz = Iccn + Iccp - 2*Iccc,
- ixf = Incc - Iccc,
- ixb = Iccc - Ipcc,
- iyf = Icnc - Iccc,
- iyb = Iccc - Icpc,
- izf = Iccn - Iccc,
- izb = Iccc - Iccp,
- itt = u*u*ixx + v*v*iyy + w*w*izz + 2*u*v*ixy + 2*u*w*ixz + 2*v*w*iyz,
- it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb) + w*cimg::minmod(izf,izb),
- veloc = -amp*cimg::sign(itt)*cimg::abs(it);
- *(ptrd++) = veloc;
- if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
- }
- _veloc_max[c] = veloc_max;
- }
- } else // Inverse diffusion
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*512 &&
- _spectrum>=2))
- cimg_forC(*this,c) {
- Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
- CImg_3x3x3(I,Tfloat);
- cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
- const Tfloat veloc = -Ipcc - Incc - Icpc - Icnc - Iccp - Iccn + 6*Iccc;
- *(ptrd++) = veloc;
- if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
- }
- _veloc_max[c] = veloc_max;
- }
- } else { // 2D
- if (sharpen_type) { // Shock filters
- CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors());
- if (sigma>0) G.blur(sigma);
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*32 &&
- _height>=(cimg_openmp_sizefactor)*16))
- cimg_forY(G,y) {
- CImg<Tfloat> val, vec;
- Tfloat *ptrG0 = G.data(0,y,0,0), *ptrG1 = G.data(0,y,0,1), *ptrG2 = G.data(0,y,0,2);
- cimg_forX(G,x) {
- G.get_tensor_at(x,y).symmetric_eigen(val,vec);
- if (val[0]<0) val[0] = 0;
- if (val[1]<0) val[1] = 0;
- *(ptrG0++) = vec(0,0);
- *(ptrG1++) = vec(0,1);
- *(ptrG2++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1],-(Tfloat)nedge);
- }
- }
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*512 &&
- _spectrum>=2))
- cimg_forC(*this,c) {
- Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
- CImg_3x3(I,Tfloat);
- cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
- const Tfloat
- u = G(x,y,0),
- v = G(x,y,1),
- amp = G(x,y,2),
- ixx = Inc + Ipc - 2*Icc,
- ixy = (Inn + Ipp - Inp - Ipn)/4,
- iyy = Icn + Icp - 2*Icc,
- ixf = Inc - Icc,
- ixb = Icc - Ipc,
- iyf = Icn - Icc,
- iyb = Icc - Icp,
- itt = u*u*ixx + v*v*iyy + 2*u*v*ixy,
- it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb),
- veloc = -amp*cimg::sign(itt)*cimg::abs(it);
- *(ptrd++) = veloc;
- if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
- }
- _veloc_max[c] = veloc_max;
- }
- } else // Inverse diffusion
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*512 &&
- _spectrum>=2))
- cimg_forC(*this,c) {
- Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
- CImg_3x3(I,Tfloat);
- cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
- const Tfloat veloc = -Ipc - Inc - Icp - Icn + 4*Icc;
- *(ptrd++) = veloc;
- if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
- }
- _veloc_max[c] = veloc_max;
- }
- }
- const Tfloat veloc_max = _veloc_max.max();
- if (veloc_max<=0) return *this;
- return ((velocity*=amplitude/veloc_max)+=*this).cut(val_min,val_max).move_to(*this);
- }
- //! Sharpen image \newinstance.
- CImg<T> get_sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1,
- const float alpha=0, const float sigma=0) const {
- return (+*this).sharpen(amplitude,sharpen_type,edge,alpha,sigma);
- }
- //! Return image gradient.
- /**
- \param axes Axes considered for the gradient computation, as a C-string (e.g "xy").
- \param scheme = Numerical scheme used for the gradient computation:
- - -1 = Backward finite differences
- - 0 = Centered finite differences (default)
- - 1 = Forward finite differences
- - 2 = Using Sobel kernels
- - 3 = Using rotation invariant kernels
- - 4 = Using Deriche recursive filter.
- - 5 = Using Van Vliet recursive filter.
- **/
- CImgList<Tfloat> get_gradient(const char *const axes=0, const int scheme=0) const {
- CImgList<Tfloat> res;
- char __axes[4] = {};
- const char *_axes = axes?axes:__axes;
- if (!axes) {
- unsigned int k = 0;
- if (_width>1) __axes[k++] = 'x';
- if (_height>1) __axes[k++] = 'y';
- if (_depth>1) __axes[k++] = 'z';
- }
- CImg<Tfloat> grad;
- while (*_axes) {
- const char axis = cimg::lowercase(*(_axes++));
- if (axis!='x' && axis!='y' && axis!='z')
- throw CImgArgumentException(_cimg_instance
- "get_gradient(): Invalid specified axes '%s'.",
- cimg_instance,
- axes);
- const longT off = axis=='x'?1:axis=='y'?_width:_width*_height;
- if ((axis=='x' && _width==1) || (axis=='y' && _height==1) || (axis=='z' && _depth==1)) {
- grad.assign(_width,_height,_depth,_spectrum,0).move_to(res);
- continue;
- }
- const int _scheme = axis=='z' && (scheme==2 || scheme==3)?0:scheme;
- switch (_scheme) {
- case -1 : { // Backward finite differences
- grad.assign(_width,_height,_depth,_spectrum);
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384))
- cimg_forXYZC(*this,x,y,z,c) {
- const ulongT pos = offset(x,y,z,c);
- if ((axis=='x' && !x) || (axis=='y' && !y) || (axis=='z' && !z))
- grad[pos] = 0;
- else
- grad[pos] = (Tfloat)_data[pos] - _data[pos - off];
- }
- grad.move_to(res);
- } break;
- case 1 : { // Forward finite differences
- grad.assign(_width,_height,_depth,_spectrum);
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384))
- cimg_forXYZC(*this,x,y,z,c) {
- const ulongT pos = offset(x,y,z,c);
- if ((axis=='x' && x==width() - 1) || (axis=='y' && y==height() - 1) || (axis=='z' && z==depth() - 1))
- grad[pos] = 0;
- else
- grad[pos] = (Tfloat)_data[pos + off] - _data[pos];
- }
- grad.move_to(res);
- } break;
- case 2 : { // Sobel scheme
- grad.assign(_width,_height,_depth,_spectrum);
- if (axis=='x') // X-axis
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
- cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
- _depth*_spectrum>=2))
- cimg_forZC(*this,z,c) {
- CImg_3x3(I,Tfloat);
- cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = - Ipp + Inp - 2*Ipc + 2*Inc - Ipn + Inn;
- }
- else // Y-axis
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
- cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
- _depth*_spectrum>=2))
- cimg_forZC(*this,z,c) {
- CImg_3x3(I,Tfloat);
- cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = - Ipp - 2*Icp - Inp + Ipn + 2*Icn + Inn;
- }
- grad.move_to(res);
- } break;
- case 3 : { // Rotation invariant scheme
- const Tfloat a = (Tfloat)(0.25f*(2 - std::sqrt(2.f))), b = (Tfloat)(0.5f*(std::sqrt(2.f) - 1));
- grad.assign(_width,_height,_depth,_spectrum);
- if (axis=='x') // X-axis
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
- cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
- _depth*_spectrum>=2))
- cimg_forZC(*this,z,c) {
- CImg_3x3(I,Tfloat);
- cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = -a*Ipp - b*Ipc - a*Ipn + a*Inp + b*Inc + a*Inn;
- }
- else // Y-axis
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
- cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
- _depth*_spectrum>=2))
- cimg_forZC(*this,z,c) {
- CImg_3x3(I,Tfloat);
- cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = -a*Ipp - b*Icp - a*Inp + a*Ipn + b*Icn + a*Inn;
- }
- grad.move_to(res);
- } break;
- case 4 : // Deriche filter
- get_deriche(0,1,axis).move_to(res);
- break;
- case 5 : // Van Vliet filter
- get_vanvliet(0,1,axis).move_to(res);
- break;
- default : { // Central finite differences
- grad.assign(_width,_height,_depth,_spectrum);
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384))
- cimg_forXYZC(*this,x,y,z,c) {
- const ulongT pos = offset(x,y,z,c);
- if ((axis=='x' && !x) || (axis=='y' && !y) || (axis=='z' && !z))
- grad[pos] = ((Tfloat)_data[pos + off] - _data[pos])/2;
- else if ((axis=='x' && x==width() - 1) || (axis=='y' && y==height() - 1) || (axis=='z' && z==depth() - 1))
- grad[pos] = ((Tfloat)_data[pos] - _data[pos - off])/2;
- else
- grad[pos] = ((Tfloat)_data[pos + off] - _data[pos - off])/2;
- }
- grad.move_to(res);
- } break;
- }
- }
- return res;
- }
- //! Return image hessian.
- /**
- \param axes Axes considered for the hessian computation, as a C-string (e.g "xy").
- **/
- CImgList<Tfloat> get_hessian(const char *const axes=0) const {
- CImgList<Tfloat> res;
- char __axes[12] = {};
- const char *_axes = axes?axes:__axes;
- if (!axes) {
- unsigned int k = 0;
- if (_width>1) { __axes[k++] = 'x'; __axes[k++] = 'x'; }
- if (_width>1 && _height>1) { __axes[k++] = 'x'; __axes[k++] = 'y'; }
- if (_width>1 && _depth>1) { __axes[k++] = 'x'; __axes[k++] = 'z'; }
- if (_height>1) { __axes[k++] = 'y'; __axes[k++] = 'y'; }
- if (_height>1 && _depth>1) { __axes[k++] = 'y'; __axes[k++] = 'z'; }
- if (_depth>1) { __axes[k++] = 'z'; __axes[k++] = 'z'; }
- }
- const unsigned int len = (unsigned int)std::strlen(_axes);
- if (len%2)
- throw CImgArgumentException(_cimg_instance
- "get_hessian(): Invalid specified axes '%s'.",
- cimg_instance,
- axes);
- CImg<Tfloat> hess;
- for (unsigned int k = 0; k<len; k+=2) {
- const char
- _axis1 = cimg::lowercase(_axes[k]),
- _axis2 = cimg::lowercase(_axes[k + 1]),
- axis1 = std::min(_axis1,_axis2),
- axis2 = std::max(_axis2,_axis2);
- if (axis1!='x' && axis1!='y' && axis1!='z' &&
- axis2!='x' && axis2!='y' && axis2!='z')
- throw CImgArgumentException(_cimg_instance
- "get_hessian(): Invalid specified axes '%s'.",
- cimg_instance,
- axes);
- const longT off = axis1=='x'?1:axis1=='y'?_width:_width*_height;
- hess.assign(_width,_height,_depth,_spectrum);
- if (((axis1=='x' || axis2=='x') && _width==1) ||
- ((axis1=='y' || axis2=='y') && _height==1) ||
- ((axis1=='z' || axis2=='z') && _depth==1)) {
- hess.fill(0).move_to(res);
- continue;
- }
- if (axis1==axis2) // Ixx, Iyy, Izz
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384))
- cimg_forXYZC(*this,x,y,z,c) {
- const ulongT pos = offset(x,y,z,c);
- if ((axis1=='x' && !x) || (axis1=='y' && !y) || (axis1=='z' && !z))
- hess[pos] = (Tfloat)_data[pos + off] - _data[pos];
- else if ((axis1=='x' && x==width() - 1) ||
- (axis1=='y' && y==height() - 1) ||
- (axis1=='z' && z==depth() - 1))
- hess[pos] = (Tfloat)_data[pos - off] - _data[pos];
- else
- hess[pos] = (Tfloat)_data[pos + off] + _data[pos - off] - 2*_data[pos];
- }
- else if (axis1=='x' && axis2=='y') // Ixy
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
- cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
- _depth*_spectrum>=2))
- cimg_forZC(*this,z,c) {
- CImg_3x3(I,Tfloat);
- cimg_for3x3(*this,x,y,z,c,I,Tfloat) hess(x,y,z,c) = (Inn + Ipp - Inp - Ipn)/4;
- }
- else if (axis1=='x' && axis2=='z') // Ixz
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*16384 &&
- _spectrum>=2))
- cimg_forC(*this,c) {
- CImg_3x3x3(I,Tfloat);
- cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) hess(x,y,z,c) = (Incn + Ipcp - Incp - Ipcn)/4;
- }
- else // Iyz
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*16384 &&
- _spectrum>=2))
- cimg_forC(*this,c) {
- CImg_3x3x3(I,Tfloat);
- cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) hess(x,y,z,c) = (Icnn + Icpp - Icnp - Icpn)/4;
- }
- hess.move_to(res);
- }
- return res;
- }
- //! Compute image Laplacian.
- CImg<T>& laplacian() {
- return get_laplacian().move_to(*this);
- }
- //! Compute image Laplacian \newinstance.
- CImg<Tfloat> get_laplacian() const {
- if (is_empty()) return CImg<Tfloat>();
- CImg<Tfloat> res(_width,_height,_depth,_spectrum);
- if (_depth>1) { // 3D
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 &&
- _spectrum>=2))
- cimg_forC(*this,c) {
- Tfloat *ptrd = res.data(0,0,0,c);
- CImg_3x3x3(I,Tfloat);
- cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Incc + Ipcc + Icnc + Icpc + Iccn + Iccp - 6*Iccc;
- }
- } else if (_height>1) { // 2D
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 &&
- _depth*_spectrum>=2))
- cimg_forC(*this,c) {
- Tfloat *ptrd = res.data(0,0,0,c);
- CImg_3x3(I,Tfloat);
- cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc + Icn + Icp - 4*Icc;
- }
- } else { // 1D
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*1048576 &&
- _height*_depth*_spectrum>=2))
- cimg_forC(*this,c) {
- Tfloat *ptrd = res.data(0,0,0,c);
- CImg_3x3(I,Tfloat);
- cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc - 2*Icc;
- }
- }
- return res;
- }
- //! Compute the structure tensor field of an image.
- /**
- \param is_fwbw_scheme scheme. Can be <tt>{ false=centered | true=forward-backward }</tt>
- **/
- CImg<T>& structure_tensors(const bool is_fwbw_scheme=false) {
- return get_structure_tensors(is_fwbw_scheme).move_to(*this);
- }
- //! Compute the structure tensor field of an image \newinstance.
- CImg<Tfloat> get_structure_tensors(const bool is_fwbw_scheme=false) const {
- if (is_empty()) return *this;
- CImg<Tfloat> res;
- if (_depth>1) { // 3D
- res.assign(_width,_height,_depth,6,0);
- if (!is_fwbw_scheme) { // Classical central finite differences
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 &&
- _spectrum>=2))
- cimg_forC(*this,c) {
- Tfloat
- *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
- *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
- CImg_3x3x3(I,Tfloat);
- cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
- const Tfloat
- ix = (Incc - Ipcc)/2,
- iy = (Icnc - Icpc)/2,
- iz = (Iccn - Iccp)/2;
- cimg_pragma_openmp(atomic) *(ptrd0++)+=ix*ix;
- cimg_pragma_openmp(atomic) *(ptrd1++)+=ix*iy;
- cimg_pragma_openmp(atomic) *(ptrd2++)+=ix*iz;
- cimg_pragma_openmp(atomic) *(ptrd3++)+=iy*iy;
- cimg_pragma_openmp(atomic) *(ptrd4++)+=iy*iz;
- cimg_pragma_openmp(atomic) *(ptrd5++)+=iz*iz;
- }
- }
- } else { // Forward/backward finite differences
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 &&
- _spectrum>=2))
- cimg_forC(*this,c) {
- Tfloat
- *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
- *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
- CImg_3x3x3(I,Tfloat);
- cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
- const Tfloat
- ixf = Incc - Iccc, ixb = Iccc - Ipcc, ixc = (Incc - Ipcc)/2,
- iyf = Icnc - Iccc, iyb = Iccc - Icpc, iyc = (Icnc - Icpc)/2,
- izf = Iccn - Iccc, izb = Iccc - Iccp, izc = (Iccn - Iccp)/2;
- cimg_pragma_openmp(atomic) *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2;
- cimg_pragma_openmp(atomic) *(ptrd1++)+=ixc*iyc;
- cimg_pragma_openmp(atomic) *(ptrd2++)+=ixc*izc;
- cimg_pragma_openmp(atomic) *(ptrd3++)+=(iyf*iyf + iyb*iyb)/2;
- cimg_pragma_openmp(atomic) *(ptrd4++)+=iyc*izc;
- cimg_pragma_openmp(atomic) *(ptrd5++)+=(izf*izf + izb*izb)/2;
- }
- }
- }
- } else { // 2D
- res.assign(_width,_height,_depth,3,0);
- if (!is_fwbw_scheme) { // Classical central finite differences
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 &&
- _depth*_spectrum>=2))
- cimg_forC(*this,c) {
- Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
- CImg_3x3(I,Tfloat);
- cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
- const Tfloat
- ix = (Inc - Ipc)/2,
- iy = (Icn - Icp)/2;
- cimg_pragma_openmp(atomic) *(ptrd0++)+=ix*ix;
- cimg_pragma_openmp(atomic) *(ptrd1++)+=ix*iy;
- cimg_pragma_openmp(atomic) *(ptrd2++)+=iy*iy;
- }
- }
- } else { // Forward/backward finite differences (version 2)
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 &&
- _depth*_spectrum>=2))
- cimg_forC(*this,c) {
- Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
- CImg_3x3(I,Tfloat);
- cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
- const Tfloat
- ixf = Inc - Icc, ixb = Icc - Ipc, ixc = (Inc - Ipc)/2,
- iyf = Icn - Icc, iyb = Icc - Icp, iyc = (Icn - Icp)/2;
- cimg_pragma_openmp(atomic) *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2;
- cimg_pragma_openmp(atomic) *(ptrd1++)+=ixc*iyc;
- cimg_pragma_openmp(atomic) *(ptrd2++)+=(iyf*iyf + iyb*iyb)/2;
- }
- }
- }
- }
- return res;
- }
- //! Compute field of diffusion tensors for edge-preserving smoothing.
- /**
- \param sharpness Sharpness
- \param anisotropy Anisotropy
- \param alpha Standard deviation of the gradient blur.
- \param sigma Standard deviation of the structure tensor blur.
- \param is_sqrt Tells if the square root of the tensor field is computed instead.
- **/
- CImg<T>& diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f,
- const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) {
- CImg<Tfloat> res;
- const float
- nsharpness = std::max(sharpness,1e-5f),
- power1 = (is_sqrt?0.5f:1)*nsharpness,
- power2 = power1/(1e-7f + 1 - anisotropy);
- blur(alpha).normalize(0,(T)255);
- if (_depth>1) { // 3D
- get_structure_tensors().move_to(res).blur(sigma);
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
- _height*_depth>=(cimg_openmp_sizefactor)*256))
- cimg_forYZ(*this,y,z) {
- Tfloat
- *ptrd0 = res.data(0,y,z,0), *ptrd1 = res.data(0,y,z,1), *ptrd2 = res.data(0,y,z,2),
- *ptrd3 = res.data(0,y,z,3), *ptrd4 = res.data(0,y,z,4), *ptrd5 = res.data(0,y,z,5);
- CImg<floatT> val(3), vec(3,3);
- cimg_forX(*this,x) {
- res.get_tensor_at(x,y,z).symmetric_eigen(val,vec);
- const float
- _l1 = val[2], _l2 = val[1], _l3 = val[0],
- l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0, l3 = _l3>0?_l3:0,
- ux = vec(0,0), uy = vec(0,1), uz = vec(0,2),
- vx = vec(1,0), vy = vec(1,1), vz = vec(1,2),
- wx = vec(2,0), wy = vec(2,1), wz = vec(2,2),
- n1 = (float)std::pow(1 + l1 + l2 + l3,-power1),
- n2 = (float)std::pow(1 + l1 + l2 + l3,-power2);
- *(ptrd0++) = n1*(ux*ux + vx*vx) + n2*wx*wx;
- *(ptrd1++) = n1*(ux*uy + vx*vy) + n2*wx*wy;
- *(ptrd2++) = n1*(ux*uz + vx*vz) + n2*wx*wz;
- *(ptrd3++) = n1*(uy*uy + vy*vy) + n2*wy*wy;
- *(ptrd4++) = n1*(uy*uz + vy*vz) + n2*wy*wz;
- *(ptrd5++) = n1*(uz*uz + vz*vz) + n2*wz*wz;
- }
- }
- } else { // for 2D images
- get_structure_tensors().move_to(res).blur(sigma);
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
- _height>=(cimg_openmp_sizefactor)*256))
- cimg_forY(*this,y) {
- Tfloat *ptrd0 = res.data(0,y,0,0), *ptrd1 = res.data(0,y,0,1), *ptrd2 = res.data(0,y,0,2);
- CImg<floatT> val(2), vec(2,2);
- cimg_forX(*this,x) {
- res.get_tensor_at(x,y).symmetric_eigen(val,vec);
- const float
- _l1 = val[1], _l2 = val[0],
- l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0,
- ux = vec(1,0), uy = vec(1,1),
- vx = vec(0,0), vy = vec(0,1),
- n1 = (float)std::pow(1 + l1 + l2,-power1),
- n2 = (float)std::pow(1 + l1 + l2,-power2);
- *(ptrd0++) = n1*ux*ux + n2*vx*vx;
- *(ptrd1++) = n1*ux*uy + n2*vx*vy;
- *(ptrd2++) = n1*uy*uy + n2*vy*vy;
- }
- }
- }
- return res.move_to(*this);
- }
- //! Compute field of diffusion tensors for edge-preserving smoothing \newinstance.
- CImg<Tfloat> get_diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f,
- const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) const {
- return CImg<Tfloat>(*this,false).diffusion_tensors(sharpness,anisotropy,alpha,sigma,is_sqrt);
- }
- //! Estimate displacement field between two images.
- /**
- \param source Reference image.
- \param smoothness Smoothness of estimated displacement field.
- \param precision Precision required for algorithm convergence.
- \param nb_scales Number of scales used to estimate the displacement field.
- \param iteration_max Maximum number of iterations allowed for one scale.
- \param is_backward If false, match I2(X + U(X)) = I1(X), else match I2(X) = I1(X - U(X)).
- \param guide Image used as the initial correspondence estimate for the algorithm.
- 'guide' may have a last channel with boolean values (0=false | other=true) that
- tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask).
- **/
- CImg<T>& displacement(const CImg<T>& source, const float smoothness=0.1f, const float precision=5.f,
- const unsigned int nb_scales=0, const unsigned int iteration_max=10000,
- const bool is_backward=false,
- const CImg<floatT>& guide=CImg<floatT>::const_empty()) {
- return get_displacement(source,smoothness,precision,nb_scales,iteration_max,is_backward,guide).
- move_to(*this);
- }
- //! Estimate displacement field between two images \newinstance.
- CImg<floatT> get_displacement(const CImg<T>& source,
- const float smoothness=0.1f, const float precision=5.f,
- const unsigned int nb_scales=0, const unsigned int iteration_max=10000,
- const bool is_backward=false,
- const CImg<floatT>& guide=CImg<floatT>::const_empty()) const {
- if (is_empty() || !source) return +*this;
- if (!is_sameXYZC(source))
- throw CImgArgumentException(_cimg_instance
- "displacement(): Instance and source image (%u,%u,%u,%u,%p) have "
- "different dimensions.",
- cimg_instance,
- source._width,source._height,source._depth,source._spectrum,source._data);
- if (precision<0)
- throw CImgArgumentException(_cimg_instance
- "displacement(): Invalid specified precision %g "
- "(should be >=0)",
- cimg_instance,
- precision);
- const bool is_3d = source._depth>1;
- const unsigned int constraint = is_3d?3:2;
- if (guide &&
- (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum<constraint))
- throw CImgArgumentException(_cimg_instance
- "displacement(): Specified guide (%u,%u,%u,%u,%p) "
- "has invalid dimensions.",
- cimg_instance,
- guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
- const unsigned int
- mins = is_3d?cimg::min(_width,_height,_depth):std::min(_width,_height),
- _nb_scales = nb_scales>0?nb_scales:
- (unsigned int)cimg::round(std::log(mins/8.)/std::log(1.5),1,1);
- const float _precision = (float)std::pow(10.,-(double)precision);
- float sm, sM = source.max_min(sm), tm, tM = max_min(tm);
- const float sdelta = sm==sM?1:(sM - sm), tdelta = tm==tM?1:(tM - tm);
- CImg<floatT> U, V;
- floatT bound = 0;
- for (int scale = (int)_nb_scales - 1; scale>=0; --scale) {
- const float factor = (float)std::pow(1.5,(double)scale);
- const unsigned int
- _sw = (unsigned int)(_width/factor), sw = _sw?_sw:1,
- _sh = (unsigned int)(_height/factor), sh = _sh?_sh:1,
- _sd = (unsigned int)(_depth/factor), sd = _sd?_sd:1;
- if (sw<5 && sh<5 && (!is_3d || sd<5)) continue; // Skip too small scales
- const CImg<Tfloat>
- I1 = (source.get_resize(sw,sh,sd,-100,2)-=sm)/=sdelta,
- I2 = (get_resize(I1,2)-=tm)/=tdelta;
- if (guide._spectrum>constraint) guide.get_resize(I2._width,I2._height,I2._depth,-100,1).move_to(V);
- if (U) (U*=1.5f).resize(I2._width,I2._height,I2._depth,-100,3);
- else {
- if (guide)
- guide.get_shared_channels(0,is_3d?2:1).get_resize(I2._width,I2._height,I2._depth,-100,2).move_to(U);
- else U.assign(I2._width,I2._height,I2._depth,is_3d?3:2,0);
- }
- float dt = 2, energy = cimg::type<float>::max();
- const CImgList<Tfloat> dI = is_backward?I1.get_gradient():I2.get_gradient();
- cimg_abort_init;
- for (unsigned int iteration = 0; iteration<iteration_max; ++iteration) {
- cimg_abort_test;
- float _energy = 0;
- if (is_3d) { // 3D version
- if (smoothness>=0) // Isotropic regularization
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
- cimg_openmp_if(_height*_depth>=(cimg_openmp_sizefactor)*8 &&
- _width>=(cimg_openmp_sizefactor)*16)
- reduction(+:_energy))
- cimg_forYZ(U,y,z) {
- const int
- _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y,
- _p1z = z?z - 1:0, _n1z = z<U.depth() - 1?z + 1:z;
- cimg_for3X(U,x) {
- const float
- X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0),
- Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1),
- Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2);
- float delta_I = 0, _energy_regul = 0;
- if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c));
- else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,z,c) - I2._linear_atXYZ(X,Y,Z,c));
- cimg_forC(U,c) {
- const float
- Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)),
- Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)),
- Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)),
- Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c),
- Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c),
- Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c);
- U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(delta_I*dI[c]._linear_atXYZ(X,Y,Z) +
- smoothness* ( Uxx + Uyy + Uzz)))/(1 + 6*smoothness*dt);
- _energy_regul+=Ux*Ux + Uy*Uy + Uz*Uz;
- }
- if (is_backward) { // Constraint displacement vectors to stay in image
- if (U(x,y,z,0)>x) U(x,y,z,0) = (float)x;
- if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y;
- if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z;
- bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound;
- bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound;
- bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound;
- } else {
- if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x;
- if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y;
- if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z;
- bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound;
- bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound;
- bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound;
- }
- _energy+=delta_I*delta_I + smoothness*_energy_regul;
- }
- if (V) cimg_forXYZ(V,_x,_y,_z) if (V(_x,_y,_z,3)) { // Apply constraints
- U(_x,_y,_z,0) = V(_x,_y,_z,0)/factor;
- U(_x,_y,_z,1) = V(_x,_y,_z,1)/factor;
- U(_x,_y,_z,2) = V(_x,_y,_z,2)/factor;
- }
- } else { // Anisotropic regularization
- const float nsmoothness = -smoothness;
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
- cimg_openmp_if(_height*_depth>=(cimg_openmp_sizefactor)*8 &&
- _width>=(cimg_openmp_sizefactor)*16)
- reduction(+:_energy))
- cimg_forYZ(U,y,z) {
- const int
- _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y,
- _p1z = z?z - 1:0, _n1z = z<U.depth() - 1?z + 1:z;
- cimg_for3X(U,x) {
- const float
- X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0),
- Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1),
- Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2);
- float delta_I = 0, _energy_regul = 0;
- if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c));
- else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,z,c) - I2._linear_atXYZ(X,Y,Z,c));
- cimg_forC(U,c) {
- const float
- Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)),
- Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)),
- Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)),
- N2 = Ux*Ux + Uy*Uy + Uz*Uz,
- N = std::sqrt(N2),
- N3 = 1e-5f + N2*N,
- coef_a = (1 - Ux*Ux/N2)/N,
- coef_b = -2*Ux*Uy/N3,
- coef_c = -2*Ux*Uz/N3,
- coef_d = (1 - Uy*Uy/N2)/N,
- coef_e = -2*Uy*Uz/N3,
- coef_f = (1 - Uz*Uz/N2)/N,
- Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c),
- Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c),
- Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c),
- Uxy = 0.25f*(U(_n1x,_n1y,z,c) + U(_p1x,_p1y,z,c) - U(_n1x,_p1y,z,c) - U(_n1x,_p1y,z,c)),
- Uxz = 0.25f*(U(_n1x,y,_n1z,c) + U(_p1x,y,_p1z,c) - U(_n1x,y,_p1z,c) - U(_n1x,y,_p1z,c)),
- Uyz = 0.25f*(U(x,_n1y,_n1z,c) + U(x,_p1y,_p1z,c) - U(x,_n1y,_p1z,c) - U(x,_n1y,_p1z,c));
- U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(delta_I*dI[c]._linear_atXYZ(X,Y,Z) +
- nsmoothness* ( coef_a*Uxx + coef_b*Uxy +
- coef_c*Uxz + coef_d*Uyy +
- coef_e*Uyz + coef_f*Uzz ))
- )/(1 + 2*(coef_a + coef_d + coef_f)*nsmoothness*dt);
- _energy_regul+=N;
- }
- if (is_backward) { // Constraint displacement vectors to stay in image
- if (U(x,y,z,0)>x) U(x,y,z,0) = (float)x;
- if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y;
- if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z;
- bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound;
- bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound;
- bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound;
- } else {
- if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x;
- if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y;
- if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z;
- bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound;
- bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound;
- bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound;
- }
- _energy+=delta_I*delta_I + nsmoothness*_energy_regul;
- }
- if (V) cimg_forXYZ(V,_x,_y,_z) if (V(_x,_y,_z,3)) { // Apply constraints
- U(_x,_y,_z,0) = V(_x,_y,_z,0)/factor;
- U(_x,_y,_z,1) = V(_x,_y,_z,1)/factor;
- U(_x,_y,_z,2) = V(_x,_y,_z,2)/factor;
- }
- }
- }
- } else { // 2D version
- if (smoothness>=0) // Isotropic regularization
- cimg_pragma_openmp(parallel for cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*8 &&
- _width>=(cimg_openmp_sizefactor)*16) reduction(+:_energy))
- cimg_forY(U,y) {
- const int _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y;
- cimg_for3X(U,x) {
- const float
- X = is_backward?x - U(x,y,0):x + U(x,y,0),
- Y = is_backward?y - U(x,y,1):y + U(x,y,1);
- float delta_I = 0, _energy_regul = 0;
- if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXY(X,Y,c) - I2(x,y,c));
- else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,c) - I2._linear_atXY(X,Y,c));
- cimg_forC(U,c) {
- const float
- Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)),
- Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)),
- Uxx = U(_n1x,y,c) + U(_p1x,y,c),
- Uyy = U(x,_n1y,c) + U(x,_p1y,c);
- U(x,y,c) = (float)(U(x,y,c) + dt*(delta_I*dI[c]._linear_atXY(X,Y) +
- smoothness*( Uxx + Uyy )))/(1 + 4*smoothness*dt);
- _energy_regul+=Ux*Ux + Uy*Uy;
- }
- if (is_backward) { // Constraint displacement vectors to stay in image
- if (U(x,y,0)>x) U(x,y,0) = (float)x;
- if (U(x,y,1)>y) U(x,y,1) = (float)y;
- bound = (float)x - _width; if (U(x,y,0)<=bound) U(x,y,0) = bound;
- bound = (float)y - _height; if (U(x,y,1)<=bound) U(x,y,1) = bound;
- } else {
- if (U(x,y,0)<-x) U(x,y,0) = -(float)x;
- if (U(x,y,1)<-y) U(x,y,1) = -(float)y;
- bound = (float)_width - x; if (U(x,y,0)>=bound) U(x,y,0) = bound;
- bound = (float)_height - y; if (U(x,y,1)>=bound) U(x,y,1) = bound;
- }
- _energy+=delta_I*delta_I + smoothness*_energy_regul;
- }
- if (V) cimg_forXY(V,_x,_y) if (V(_x,_y,2)) { // Apply constraints
- U(_x,_y,0) = V(_x,_y,0)/factor;
- U(_x,_y,1) = V(_x,_y,1)/factor;
- }
- } else { // Anisotropic regularization
- const float nsmoothness = -smoothness;
- cimg_pragma_openmp(parallel for cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*8 &&
- _width>=(cimg_openmp_sizefactor)*16) reduction(+:_energy))
- cimg_forY(U,y) {
- const int _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y;
- cimg_for3X(U,x) {
- const float
- X = is_backward?x - U(x,y,0):x + U(x,y,0),
- Y = is_backward?y - U(x,y,1):y + U(x,y,1);
- float delta_I = 0, _energy_regul = 0;
- if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXY(X,Y,c) - I2(x,y,c));
- else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,c) - I2._linear_atXY(X,Y,c));
- cimg_forC(U,c) {
- const float
- Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)),
- Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)),
- N2 = Ux*Ux + Uy*Uy,
- N = std::sqrt(N2),
- N3 = 1e-5f + N2*N,
- coef_a = Uy*Uy/N3,
- coef_b = -2*Ux*Uy/N3,
- coef_c = Ux*Ux/N3,
- Uxx = U(_n1x,y,c) + U(_p1x,y,c),
- Uyy = U(x,_n1y,c) + U(x,_p1y,c),
- Uxy = 0.25f*(U(_n1x,_n1y,c) + U(_p1x,_p1y,c) - U(_n1x,_p1y,c) - U(_n1x,_p1y,c));
- U(x,y,c) = (float)(U(x,y,c) + dt*(delta_I*dI[c]._linear_atXY(X,Y) +
- nsmoothness*( coef_a*Uxx + coef_b*Uxy + coef_c*Uyy )))/
- (1 + 2*(coef_a + coef_c)*nsmoothness*dt);
- _energy_regul+=N;
- }
- if (is_backward) { // Constraint displacement vectors to stay in image
- if (U(x,y,0)>x) U(x,y,0) = (float)x;
- if (U(x,y,1)>y) U(x,y,1) = (float)y;
- bound = (float)x - _width; if (U(x,y,0)<=bound) U(x,y,0) = bound;
- bound = (float)y - _height; if (U(x,y,1)<=bound) U(x,y,1) = bound;
- } else {
- if (U(x,y,0)<-x) U(x,y,0) = -(float)x;
- if (U(x,y,1)<-y) U(x,y,1) = -(float)y;
- bound = (float)_width - x; if (U(x,y,0)>=bound) U(x,y,0) = bound;
- bound = (float)_height - y; if (U(x,y,1)>=bound) U(x,y,1) = bound;
- }
- _energy+=delta_I*delta_I + nsmoothness*_energy_regul;
- }
- if (V) cimg_forXY(V,_x,_y) if (V(_x,_y,2)) { // Apply constraints
- U(_x,_y,0) = V(_x,_y,0)/factor;
- U(_x,_y,1) = V(_x,_y,1)/factor;
- }
- }
- }
- }
- const float d_energy = (_energy - energy)/(sw*sh*sd);
- if (d_energy<=0 && -d_energy<_precision) break;
- if (d_energy>0) dt*=0.5f;
- energy = _energy;
- }
- }
- return U;
- }
- //! Compute correspondence map between two images, using a patch-matching algorithm.
- /**
- \param patch_image The image containing the reference patches to match with the instance image.
- \param patch_width Width of the patch used for matching.
- \param patch_height Height of the patch used for matching.
- \param patch_depth Depth of the patch used for matching.
- \param nb_iterations Number of patch-match iterations.
- \param nb_randoms Number of randomization attempts (per pixel).
- \param patch_penalization Penalization factor in score related patch occurrences.
- if negative, also tells that identity result is not avoided.
- \param guide Image used as the initial correspondence estimate for the algorithm.
- 'guide' may have a last channel with boolean values (0=false | other=true) that
- tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask).
- \param[out] matching_score Returned as the image of matching scores.
- **/
- template<typename t1, typename t2>
- CImg<T>& matchpatch(const CImg<T>& patch_image,
- const unsigned int patch_width,
- const unsigned int patch_height,
- const unsigned int patch_depth,
- const unsigned int nb_iterations,
- const unsigned int nb_randoms,
- const float patch_penalization,
- const CImg<t1> &guide,
- CImg<t2> &matching_score) {
- return get_matchpatch(patch_image,patch_width,patch_height,patch_depth,
- nb_iterations,nb_randoms,patch_penalization,guide,matching_score).move_to(*this);
- }
- //! Compute correspondence map between two images, using the patch-match algorithm \newinstance.
- template<typename t1, typename t2>
- CImg<intT> get_matchpatch(const CImg<T>& patch_image,
- const unsigned int patch_width,
- const unsigned int patch_height,
- const unsigned int patch_depth,
- const unsigned int nb_iterations,
- const unsigned int nb_randoms,
- const float patch_penalization,
- const CImg<t1> &guide,
- CImg<t2> &matching_score) const {
- return _matchpatch(patch_image,patch_width,patch_height,patch_depth,
- nb_iterations,nb_randoms,patch_penalization,
- guide,true,matching_score);
- }
- //! Compute correspondence map between two images, using the patch-match algorithm \overloading.
- template<typename t>
- CImg<T>& matchpatch(const CImg<T>& patch_image,
- const unsigned int patch_width,
- const unsigned int patch_height,
- const unsigned int patch_depth,
- const unsigned int nb_iterations=5,
- const unsigned int nb_randoms=5,
- const float patch_penalization=0,
- const CImg<t> &guide=CImg<t>::const_empty()) {
- return get_matchpatch(patch_image,patch_width,patch_height,patch_depth,
- nb_iterations,nb_randoms,patch_penalization,guide).move_to(*this);
- }
- //! Compute correspondence map between two images, using the patch-match algorithm \overloading.
- template<typename t>
- CImg<intT> get_matchpatch(const CImg<T>& patch_image,
- const unsigned int patch_width,
- const unsigned int patch_height,
- const unsigned int patch_depth,
- const unsigned int nb_iterations=5,
- const unsigned int nb_randoms=5,
- const float patch_penalization=0,
- const CImg<t> &guide=CImg<t>::const_empty()) const {
- CImg<T> matching_score;
- return _matchpatch(patch_image,patch_width,patch_height,patch_depth,
- nb_iterations,nb_randoms,patch_penalization,guide,false,matching_score);
- }
- template<typename t1, typename t2>
- CImg<intT> _matchpatch(const CImg<T>& patch_image,
- const unsigned int patch_width,
- const unsigned int patch_height,
- const unsigned int patch_depth,
- const unsigned int nb_iterations,
- const unsigned int nb_randoms,
- const float patch_penalization,
- const CImg<t1> &guide,
- const bool is_matching_score,
- CImg<t2> &matching_score) const {
- if (is_empty()) return CImg<intT>::const_empty();
- if (patch_image._spectrum!=_spectrum)
- throw CImgArgumentException(_cimg_instance
- "matchpatch(): Instance image and specified patch image (%u,%u,%u,%u,%p) "
- "have different spectrums.",
- cimg_instance,
- patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum,
- patch_image._data);
- if (patch_width>_width || patch_height>_height || patch_depth>_depth)
- throw CImgArgumentException(_cimg_instance
- "matchpatch(): Specified patch size %ux%ux%u is bigger than the dimensions "
- "of the instance image.",
- cimg_instance,patch_width,patch_height,patch_depth);
- if (patch_width>patch_image._width || patch_height>patch_image._height || patch_depth>patch_image._depth)
- throw CImgArgumentException(_cimg_instance
- "matchpatch(): Specified patch size %ux%ux%u is bigger than the dimensions "
- "of the patch image image (%u,%u,%u,%u,%p).",
- cimg_instance,patch_width,patch_height,patch_depth,
- patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum,
- patch_image._data);
- const unsigned int
- _constraint = patch_image._depth>1?3:2,
- constraint = guide._spectrum>_constraint?_constraint:0;
- if (guide &&
- (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum<_constraint))
- throw CImgArgumentException(_cimg_instance
- "matchpatch(): Specified guide (%u,%u,%u,%u,%p) has invalid dimensions "
- "considering instance and patch image (%u,%u,%u,%u,%p).",
- cimg_instance,
- guide._width,guide._height,guide._depth,guide._spectrum,guide._data,
- patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum,
- patch_image._data);
- CImg<intT> a_map(_width,_height,_depth,patch_image._depth>1?3:2);
- CImg<ucharT> is_updated(_width,_height,_depth,1,3);
- CImg<floatT> score(_width,_height,_depth), penalty;
- const float _patch_penalization = cimg::abs(patch_penalization);
- const bool allow_identity = patch_penalization>=0;
- if (_patch_penalization!=0)
- penalty.assign(patch_image._width,patch_image._height,patch_image._depth,1,0);
- const int
- psizew = (int)patch_width, psizew1 = psizew/2, psizew2 = psizew - psizew1 - 1,
- psizeh = (int)patch_height, psizeh1 = psizeh/2, psizeh2 = psizeh - psizeh1 - 1,
- psized = (int)patch_depth, psized1 = psized/2, psized2 = psized - psized1 - 1;
- // Interleave image buffers to speed up patch comparison (more cache-friendly).
- CImg<T> in_this = get_permute_axes("cxyz");
- in_this._width = _width*_spectrum;
- in_this._height = _height;
- in_this._depth = _depth;
- in_this._spectrum = 1;
- CImg<T> in_patch = patch_image.get_permute_axes("cxyz");
- in_patch._width = patch_image._width*patch_image._spectrum;
- in_patch._height = patch_image._height;
- in_patch._depth = patch_image._depth;
- in_patch._spectrum = 1;
- if (_depth>1 || patch_image._depth>1) { // 3D version
- // Initialize correspondence map.
- if (guide)
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(_width,64))
- cimg_forXYZ(*this,x,y,z) { // User-defined initialization
- const int
- cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1,
- cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
- cz1 = z<=psized1?z:(z<depth() - psized2?psized1:psized + z - depth()), cz2 = psized - cz1 - 1,
- u = cimg::cut((int)guide(x,y,z,0),cx1,patch_image.width() - 1 - cx2),
- v = cimg::cut((int)guide(x,y,z,1),cy1,patch_image.height() - 1 - cy2),
- w = cimg::cut((int)guide(x,y,z,2),cz1,patch_image.depth() - 1 - cz2);
- a_map(x,y,z,0) = u;
- a_map(x,y,z,1) = v;
- a_map(x,y,z,2) = w;
- score(x,y,z) = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
- x - cx1,y - cy1,z - cz1,
- u - cx1,v - cy1,w - cz1,
- u,v,w,0,allow_identity,cimg::type<float>::inf());
- } else cimg_pragma_openmp(parallel cimg_openmp_if_size(_width,64)) {
- cimg_uint64 rng = (cimg::_rand(),cimg::rng());
- #if cimg_use_openmp!=0
- rng+=omp_get_thread_num();
- #endif
- cimg_pragma_openmp(for cimg_openmp_collapse(2))
- cimg_forXYZ(*this,x,y,z) { // Random initialization
- const int
- cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1,
- cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
- cz1 = z<=psized1?z:(z<depth() - psized2?psized1:psized + z - depth()), cz2 = psized - cz1 - 1,
- u = (int)cimg::round(cimg::rand(cx1,patch_image.width() - 1 - cx2,&rng)),
- v = (int)cimg::round(cimg::rand(cy1,patch_image.height() - 1 - cy2,&rng)),
- w = (int)cimg::round(cimg::rand(cz1,patch_image.depth() - 1 - cz2,&rng));
- a_map(x,y,z,0) = u;
- a_map(x,y,z,1) = v;
- a_map(x,y,z,2) = w;
- score(x,y,z) = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
- x - cx1,y - cy1,z - cz1,
- u - cx1,v - cy1,w - cz1,
- u,v,w,0,allow_identity,cimg::type<float>::inf());
- }
- cimg::srand(rng);
- }
- // Start iteration loop.
- cimg_abort_init;
- for (unsigned int iter = 0; iter<nb_iterations; ++iter) {
- cimg_abort_test;
- const bool is_backward = iter&1, is_forward = !is_backward;
- const unsigned int cmask = is_backward?1:2, nmask = 3 - cmask;
- cimg_pragma_openmp(parallel cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64)) {
- cimg_uint64 rng = (cimg::_rand(),cimg::rng());
- #if cimg_use_openmp!=0
- rng+=omp_get_thread_num();
- #endif
- cimg_pragma_openmp(for cimg_openmp_collapse(2))
- cimg_forXYZ(*this,X,Y,Z) {
- const int
- x = is_backward?width() - 1 - X:X,
- y = is_backward?height() - 1 - Y:Y,
- z = is_backward?depth() - 1 - Z:Z;
- if (score(x,y,z)<=1e-5 || (constraint && guide(x,y,z,constraint)!=0)) continue;
- const int
- cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1,
- cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
- cz1 = z<=psized1?z:(z<depth() - psized2?psized1:psized + z - depth()), cz2 = psized - cz1 - 1,
- xp = x - cx1,
- yp = y - cy1,
- zp = z - cz1;
- int best_u = a_map(x,y,z,0), best_v = a_map(x,y,z,1), best_w = a_map(x,y,z,2), u, v, w;
- const float best_score0 = score(x,y,z);
- float best_score = best_score0, s;
- if (is_forward && x>0 && (is_updated(x - 1,y,z)&cmask)) { // Compare with left neighbor
- u = a_map(x - 1,y,z,0);
- v = a_map(x - 1,y,z,1);
- w = a_map(x - 1,y,z,2);
- if (u>=cx1 - 1 && u<patch_image.width() - 1 - cx2 &&
- v>=cy1 && v<patch_image.height() - cy2 &&
- w>=cz1 && w<patch_image.depth() - cz2) {
- s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
- xp,yp,zp,u + 1 - cx1,v - cy1,w - cz1,
- u,v,w,_patch_penalization,allow_identity,best_score);
- if (s<best_score) { best_u = u + 1; best_v = v; best_w = w; best_score = s; }
- }
- }
- if (is_forward && y>0 && (is_updated(x,y - 1,z)&cmask)) { // Compare with up neighbor
- u = a_map(x,y - 1,z,0);
- v = a_map(x,y - 1,z,1);
- w = a_map(x,y - 1,z,2);
- if (u>=cx1 && u<patch_image.width() - cx2 &&
- v>=cy1 - 1 && v<patch_image.height() - 1 - cy2 &&
- w>=cz1 && w<patch_image.depth() - cz2) {
- s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
- xp,yp,zp,u - cx1,v + 1 - cy1,w - cz1,
- u,v,w,_patch_penalization,allow_identity,best_score);
- if (s<best_score) { best_u = u; best_v = v + 1; best_w = w; best_score = s; }
- }
- }
- if (is_forward && z>0 && (is_updated(x,y,z - 1)&cmask)) { // Compare with backward neighbor
- u = a_map(x,y,z - 1,0);
- v = a_map(x,y,z - 1,1);
- w = a_map(x,y,z - 1,2);
- if (u>=cx1 && u<patch_image.width() - cx2 &&
- v>=cy1 && v<patch_image.height() - cy2 &&
- w>=cz1 - 1 && w<patch_image.depth() - 1 - cz2) {
- s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
- xp,yp,zp,u - cx1,v - cy1,w + 1 - cz1,
- u,v,w,_patch_penalization,allow_identity,best_score);
- if (s<best_score) { best_u = u; best_v = v; best_w = w + 1; best_score = s; }
- }
- }
- if (is_backward && x<width() - 1 && (is_updated(x + 1,y,z)&cmask)) { // Compare with right neighbor
- u = a_map(x + 1,y,z,0);
- v = a_map(x + 1,y,z,1);
- w = a_map(x + 1,y,z,2);
- if (u>=cx1 + 1 && u<patch_image.width() + 1 - cx2 &&
- v>=cy1 && v<patch_image.height() - cy2 &&
- w>=cz1 && w<patch_image.depth() - cz2) {
- s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
- xp,yp,zp,u - 1 - cx1,v - cy1,w - cz1,
- u,v,w,_patch_penalization,allow_identity,best_score);
- if (s<best_score) { best_u = u - 1; best_v = v; best_w = w; best_score = s; }
- }
- }
- if (is_backward && y<height() - 1 && (is_updated(x,y + 1,z)&cmask)) { // Compare with bottom neighbor
- u = a_map(x,y + 1,z,0);
- v = a_map(x,y + 1,z,1);
- w = a_map(x,y + 1,z,2);
- if (u>=cx1 && u<patch_image.width() - cx2 &&
- v>=cy1 + 1 && v<patch_image.height() + 1 - cy2 &&
- w>=cz1 && w<patch_image.depth() - cz2) {
- s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
- xp,yp,zp,u - cx1,v - 1 - cy1,w - cz1,
- u,v,w,_patch_penalization,allow_identity,best_score);
- if (s<best_score) { best_u = u; best_v = v - 1; best_w = w; best_score = s; }
- }
- }
- if (is_backward && z<depth() - 1 && (is_updated(x,y,z + 1)&cmask)) { // Compare with forward neighbor
- u = a_map(x,y,z + 1,0);
- v = a_map(x,y,z + 1,1);
- w = a_map(x,y,z + 1,2);
- if (u>=cx1 && u<patch_image.width() - cx2 &&
- v>=cy1 && v<patch_image.height() - cy2 &&
- w>=cz1 + 1 && w<patch_image.depth() + 1 - cz2) {
- s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
- xp,yp,zp,u - cx1,v - cy1,w - 1 - cz1,
- u,v,w,_patch_penalization,allow_identity,best_score);
- if (s<best_score) { best_u = u; best_v = v; best_w = w - 1; best_score = s; }
- }
- }
- float
- dw = (float)patch_image.width(),
- dh = (float)patch_image.height(),
- dd = (float)patch_image.depth();
- for (unsigned int i = 0; i<nb_randoms; ++i) {
- u = (int)cimg::round(cimg::rand(std::max((float)cx1,best_u - dw),
- std::min(patch_image.width() - 1.f - cx2,best_u + dw),&rng));
- v = (int)cimg::round(cimg::rand(std::max((float)cy1,best_v - dh),
- std::min(patch_image.height() - 1.f - cy2,best_v + dh),&rng));
- w = (int)cimg::round(cimg::rand(std::max((float)cz1,best_w - dd),
- std::min(patch_image.depth() - 1.f - cz2,best_w + dd),&rng));
- if (u!=best_u || v!=best_v || w!=best_w) {
- s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
- xp,yp,zp,u - cx1,v - cy1,w - cz1,
- u,v,w,_patch_penalization,allow_identity,best_score);
- if (s<best_score) { best_u = u; best_v = v; best_w = w; best_score = s; }
- dw = std::max(5.f,dw*0.5f); dh = std::max(5.f,dh*0.5f); dd = std::max(5.f,dd*0.5f);
- }
- }
- if (best_score<best_score0) {
- if (_patch_penalization!=0) {
- float &p_penalty = penalty(a_map(x,y,z,0),a_map(x,y,z,1),a_map(x,y,z,2));
- if (p_penalty) cimg_pragma_openmp(atomic) --p_penalty;
- }
- a_map(x,y,z,0) = best_u;
- a_map(x,y,z,1) = best_v;
- a_map(x,y,z,2) = best_w;
- score(x,y,z) = best_score;
- is_updated(x,y,z) = 3;
- } else is_updated(x,y,z)&=~nmask;
- if (_patch_penalization!=0) cimg_pragma_openmp(atomic) ++penalty(best_u,best_v,best_w);
- }
- cimg::srand(rng);
- }
- // Update score according to new penalties.
- if (penalty)
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64))
- cimg_forXYZ(score,x,y,z) {
- const float p_score = score(x,y,z);
- const int
- cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()),
- cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()),
- cz1 = z<=psized1?z:(z<depth() - psized2?psized1:psized + z - depth()),
- xp = x - cx1,
- yp = y - cy1,
- zp = z - cz1,
- u = a_map(x,y,z,0),
- v = a_map(x,y,z,1),
- w = a_map(x,y,z,2);
- const float n_score = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
- xp,yp,zp,u - cx1,v - cy1,w - cz1,
- u,v,w,_patch_penalization,allow_identity,cimg::type<float>::inf());
- if (n_score!=p_score) { score(x,y,z) = n_score; is_updated(x,y) = 3; }
- }
- }
- } else { // 2D version
- // Initialize correspondence map.
- if (guide)
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width,64))
- cimg_forXY(*this,x,y) { // User-defined initialization
- const int
- cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1,
- cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
- u = cimg::cut((int)guide(x,y,0),cx1,patch_image.width() - 1 - cx2),
- v = cimg::cut((int)guide(x,y,1),cy1,patch_image.height() - 1 - cy2);
- a_map(x,y,0) = u;
- a_map(x,y,1) = v;
- score(x,y) = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum,
- x - cx1,y - cy1,u - cx1,v - cy1,
- u,v,0,allow_identity,cimg::type<float>::inf());
- } else cimg_pragma_openmp(parallel cimg_openmp_if_size(_width,64)) {
- cimg_uint64 rng = (cimg::_rand(),cimg::rng());
- #if cimg_use_openmp!=0
- rng+=omp_get_thread_num();
- #endif
- cimg_pragma_openmp(for)
- cimg_forXY(*this,x,y) { // Random initialization
- const int
- cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1,
- cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
- u = (int)cimg::round(cimg::rand(cx1,patch_image.width() - 1 - cx2,&rng)),
- v = (int)cimg::round(cimg::rand(cy1,patch_image.height() - 1 - cy2,&rng));
- a_map(x,y,0) = u;
- a_map(x,y,1) = v;
- score(x,y) = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum,
- x - cx1,y - cy1,u - cx1,v - cy1,
- u,v,0,allow_identity,cimg::type<float>::inf());
- }
- cimg::srand(rng);
- }
- // Start iteration loop.
- cimg_abort_init;
- for (unsigned int iter = 0; iter<nb_iterations; ++iter) {
- cimg_abort_test;
- const bool is_backward = iter&1, is_forward = !is_backward;
- const unsigned int cmask = is_backward?1:2, nmask = 3 - cmask;
- cimg_pragma_openmp(parallel cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64)) {
- cimg_uint64 rng = (cimg::_rand(),cimg::rng());
- #if cimg_use_openmp!=0
- rng+=omp_get_thread_num();
- #endif
- cimg_pragma_openmp(for)
- cimg_forXY(*this,X,Y) {
- const int
- x = is_backward?width() - 1 - X:X,
- y = is_backward?height() - 1 - Y:Y;
- if (score(x,y)<=1e-5 || (constraint && guide(x,y,constraint)!=0)) continue;
- const int
- cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1,
- cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
- xp = x - cx1,
- yp = y - cy1;
- int best_u = a_map(x,y,0), best_v = a_map(x,y,1), u, v;
- const float best_score0 = score(x,y);
- float best_score = best_score0, s;
- if (is_forward && x>0 && (is_updated(x - 1,y)&cmask)) { // Compare with left neighbor
- u = a_map(x - 1,y,0);
- v = a_map(x - 1,y,1);
- if (u>=cx1 - 1 && u<patch_image.width() - 1 - cx2 &&
- v>=cy1 && v<patch_image.height() - cy2) {
- s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum,
- xp,yp,u + 1 - cx1,v - cy1,
- u,v,_patch_penalization,allow_identity,best_score);
- if (s<best_score) { best_u = u + 1; best_v = v; best_score = s; }
- }
- }
- if (is_forward && y>0 && (is_updated(x,y - 1)&cmask)) { // Compare with up neighbor
- u = a_map(x,y - 1,0);
- v = a_map(x,y - 1,1);
- if (u>=cx1 && u<patch_image.width() - cx2 &&
- v>=cy1 - 1 && v<patch_image.height() - 1 - cy2) {
- s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum,
- xp,yp,u - cx1,v + 1 - cy1,
- u,v,_patch_penalization,allow_identity,best_score);
- if (s<best_score) { best_u = u; best_v = v + 1; best_score = s; }
- }
- }
- if (is_backward && x<width() - 1 && (is_updated(x + 1,y)&cmask)) { // Compare with right neighbor
- u = a_map(x + 1,y,0);
- v = a_map(x + 1,y,1);
- if (u>=cx1 + 1 && u<patch_image.width() + 1 - cx2 &&
- v>=cy1 && v<patch_image.height() - cy2) {
- s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum,
- xp,yp,u - 1 - cx1,v - cy1,
- u,v,_patch_penalization,allow_identity,best_score);
- if (s<best_score) { best_u = u - 1; best_v = v; best_score = s; }
- }
- }
- if (is_backward && y<height() - 1 && (is_updated(x,y + 1)&cmask)) { // Compare with bottom neighbor
- u = a_map(x,y + 1,0);
- v = a_map(x,y + 1,1);
- if (u>=cx1 && u<patch_image.width() - cx2 &&
- v>=cy1 + 1 && v<patch_image.height() + 1 - cy2) {
- s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum,
- xp,yp,u - cx1,v - 1 - cy1,
- u,v,_patch_penalization,allow_identity,best_score);
- if (s<best_score) { best_u = u; best_v = v - 1; best_score = s; }
- }
- }
- float
- dw = (float)patch_image.width(),
- dh = (float)patch_image.height();
- for (unsigned int i = 0; i<nb_randoms; ++i) {
- u = (int)cimg::round(cimg::rand(std::max((float)cx1,best_u - dw),
- std::min(patch_image.width() - 1.f - cx2,best_u + dw),&rng));
- v = (int)cimg::round(cimg::rand(std::max((float)cy1,best_v - dh),
- std::min(patch_image.height() - 1.f - cy2,best_v + dh),&rng));
- if (u!=best_u || v!=best_v) {
- s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum,
- xp,yp,u - cx1,v - cy1,
- u,v,_patch_penalization,allow_identity,best_score);
- if (s<best_score) { best_u = u; best_v = v; best_score = s; }
- dw = std::max(5.f,dw*0.5f); dh = std::max(5.f,dh*0.5f);
- }
- }
- if (best_score<best_score0) {
- if (_patch_penalization!=0) {
- float &p_penalty = penalty(a_map(x,y,0),a_map(x,y,1));
- if (p_penalty) cimg_pragma_openmp(atomic) --p_penalty;
- }
- a_map(x,y,0) = best_u;
- a_map(x,y,1) = best_v;
- score(x,y) = best_score;
- is_updated(x,y) = 3;
- } else is_updated(x,y)&=~nmask;
- if (_patch_penalization!=0) cimg_pragma_openmp(atomic) ++penalty(best_u,best_v);
- }
- cimg::srand(rng);
- }
- // Update score according to new penalties.
- if (penalty)
- cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64))
- cimg_forXY(score,x,y) {
- const float p_score = score(x,y);
- const int
- cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()),
- cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()),
- xp = x - cx1,
- yp = y - cy1,
- u = a_map(x,y,0),
- v = a_map(x,y,1);
- const float n_score = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum,
- xp,yp,u - cx1,v - cy1,
- u,v,_patch_penalization,allow_identity,cimg::type<float>::inf());
- if (n_score!=p_score) { score(x,y) = n_score; is_updated(x,y) = 3; }
- }
- }
- }
- if (is_matching_score) score.move_to(matching_score);
- return a_map;
- }
- // Compute SSD between two patches in different images.
- static float _matchpatch(const CImg<T>& img1, const CImg<T>& img2, const CImg<floatT>& penalty,
- const unsigned int psizew, const unsigned int psizeh,
- const unsigned int psized, const unsigned int psizec,
- const int x1, const int y1, const int z1,
- const int x2, const int y2, const int z2,
- const int xc, const int yc, const int zc,
- const float patch_penalization,
- const bool allow_identity,
- const float max_score) { // 3D version
- if (!allow_identity && cimg::hypot((float)x1 - x2,(float)y1 - y2,(float)z1 - z2)<patch_penalization)
- return cimg::type<float>::inf();
- const T *p1 = img1.data(x1*psizec,y1,z1), *p2 = img2.data(x2*psizec,y2,z2);
- const unsigned int psizewc = psizew*psizec;
- const ulongT
- offx1 = (ulongT)img1._width - psizewc,
- offx2 = (ulongT)img2._width - psizewc,
- offy1 = (ulongT)img1._width*img1._height - (ulongT)psizeh*img1._width,
- offy2 = (ulongT)img2._width*img2._height - (ulongT)psizeh*img2._width;
- float ssd = 0;
- for (unsigned int k = 0; k<psized; ++k) {
- for (unsigned int j = 0; j<psizeh; ++j) {
- for (unsigned int i = 0; i<psizewc; ++i)
- ssd += cimg::sqr((Tfloat)*(p1++) - *(p2++));
- if (ssd>max_score) return max_score;
- p1+=offx1; p2+=offx2;
- }
- p1+=offy1; p2+=offy2;
- }
- return patch_penalization==0?ssd:cimg::sqr(std::sqrt(ssd) +
- patch_penalization*psizewc*psizeh*psized*penalty(xc,yc,zc)/100);
- }
- static float _matchpatch(const CImg<T>& img1, const CImg<T>& img2, const CImg<floatT>& penalty,
- const unsigned int psizew, const unsigned int psizeh, const unsigned int psizec,
- const int x1, const int y1,
- const int x2, const int y2,
- const int xc, const int yc,
- const float patch_penalization,
- const bool allow_identity,
- const float max_score) { // 2D version
- if (!allow_identity && cimg::hypot((float)x1 - x2,(float)y1 - y2)<patch_penalization)
- return cimg::type<float>::inf();
- const T *p1 = img1.data(x1*psizec,y1), *p2 = img2.data(x2*psizec,y2);
- const unsigned int psizewc = psizew*psizec;
- const ulongT
- offx1 = (ulongT)img1._width - psizewc,
- offx2 = (ulongT)img2._width - psizewc;
- float ssd = 0;
- for (unsigned int j = 0; j<psizeh; ++j) {
- for (unsigned int i = 0; i<psizewc; ++i)
- ssd += cimg::sqr((Tfloat)*(p1++) - *(p2++));
- if (ssd>max_score) return max_score;
- p1+=offx1; p2+=offx2;
- }
- return patch_penalization==0?ssd:cimg::sqr(std::sqrt(ssd) +
- patch_penalization*psizewc*psizeh*penalty(xc,yc)/100);
- }
- //! Compute Euclidean distance function to a specified value.
- /**
- \param value Reference value.
- \param metric Type of metric. Can be <tt>{ 0=Chebyshev | 1=Manhattan | 2=Euclidean | 3=Squared-euclidean }</tt>.
- \note
- The distance transform implementation has been submitted by A. Meijster, and implements
- the article 'W.H. Hesselink, A. Meijster, J.B.T.M. Roerdink,
- "A general algorithm for computing distance transforms in linear time.",
- In: Mathematical Morphology and its Applications to Image and Signal Processing,
- J. Goutsias, L. Vincent, and D.S. Bloomberg (eds.), Kluwer, 2000, pp. 331-340.'
- The submitted code has then been modified to fit CImg coding style and constraints.
- **/
- CImg<T>& distance(const T& value, const unsigned int metric=2) {
- if (is_empty()) return *this;
- if (cimg::type<Tint>::string()!=pixel_type()) // For datatype < int
- return CImg<Tint>(*this,false).distance((Tint)value,metric).
- cut((Tint)cimg::type<T>::min(),(Tint)cimg::type<T>::max()).move_to(*this);
- bool is_value = false;
- cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,(T)0:(T)std::max(0,99999999); // (avoid VC++ warning)
- if (!is_value) return fill(cimg::type<T>::max());
- switch (metric) {
- case 0 : return _distance_core(_distance_sep_cdt,_distance_dist_cdt); // Chebyshev
- case 1 : return _distance_core(_distance_sep_mdt,_distance_dist_mdt); // Manhattan
- case 3 : return _distance_core(_distance_sep_edt,_distance_dist_edt); // Squared Euclidean
- default : return _distance_core(_distance_sep_edt,_distance_dist_edt).sqrt(); // Euclidean
- }
- return *this;
- }
- //! Compute distance to a specified value \newinstance.
- CImg<Tfloat> get_distance(const T& value, const unsigned int metric=2) const {
- return CImg<Tfloat>(*this,false).distance((Tfloat)value,metric);
- }
- static longT _distance_sep_edt(const longT i, const longT u, const longT *const g) {
- return (u*u - i*i + g[u] - g[i])/(2*(u - i));
- }
- static longT _distance_dist_edt(const longT x, const longT i, const longT *const g) {
- return (x - i)*(x - i) + g[i];
- }
- static longT _distance_sep_mdt(const longT i, const longT u, const longT *const g) {
- return (u - i<=g[u] - g[i]?999999999:(g[u] - g[i] + u + i)/2);
- }
- static longT _distance_dist_mdt(const longT x, const longT i, const longT *const g) {
- return (x<i?i - x:x - i) + g[i];
- }
- static longT _distance_sep_cdt(const longT i, const longT u, const longT *const g) {
- const longT h = (i + u)/2;
- if (g[i]<=g[u]) { return h<i + g[u]?i + g[u]:h; }
- return h<u - g[i]?h:u - g[i];
- }
- static longT _distance_dist_cdt(const longT x, const longT i, const longT *const g) {
- const longT d = x<i?i - x:x - i;
- return d<g[i]?g[i]:d;
- }
- static void _distance_scan(const unsigned int len,
- const longT *const g,
- longT (*const sep)(const longT, const longT, const longT *const),
- longT (*const f)(const longT, const longT, const longT *const),
- longT *const s,
- longT *const t,
- longT *const dt) {
- longT q = s[0] = t[0] = 0;
- for (int u = 1; u<(int)len; ++u) { // Forward scan
- while ((q>=0) && f(t[q],s[q],g)>f(t[q],u,g)) { --q; }
- if (q<0) { q = 0; s[0] = u; }
- else { const longT w = 1 + sep(s[q], u, g); if (w<(longT)len) { ++q; s[q] = u; t[q] = w; }}
- }
- for (int u = (int)len - 1; u>=0; --u) { dt[u] = f(u,s[q],g); if (u==t[q]) --q; } // Backward scan
- }
- CImg<T>& _distance_core(longT (*const sep)(const longT, const longT, const longT *const),
- longT (*const f)(const longT, const longT, const longT *const)) {
- // Check for g++ 4.9.X, as OpenMP seems to crash for this particular function. I have no clues why.
- #define cimg_is_gcc49x (__GNUC__==4 && __GNUC_MINOR__==9)
- const ulongT wh = (ulongT)_width*_height;
- #if cimg_use_openmp!=0 && !cimg_is_gcc49x
- cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
- #endif
- cimg_forC(*this,c) {
- CImg<longT> g(_width), dt(_width), s(_width), t(_width);
- CImg<T> img = get_shared_channel(c);
- #if cimg_use_openmp!=0 && !cimg_is_gcc49x
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
- _height*_depth>=16)
- firstprivate(g,dt,s,t))
- #endif
- cimg_forYZ(*this,y,z) { // Over X-direction
- cimg_forX(*this,x) g[x] = (longT)img(x,y,z,0,wh);
- _distance_scan(_width,g,sep,f,s,t,dt);
- cimg_forX(*this,x) img(x,y,z,0,wh) = (T)dt[x];
- }
- if (_height>1) {
- g.assign(_height); dt.assign(_height); s.assign(_height); t.assign(_height);
- #if cimg_use_openmp!=0 && !cimg_is_gcc49x
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
- cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*512 && _width*_depth>=16)
- firstprivate(g,dt,s,t))
- #endif
- cimg_forXZ(*this,x,z) { // Over Y-direction
- cimg_forY(*this,y) g[y] = (longT)img(x,y,z,0,wh);
- _distance_scan(_height,g,sep,f,s,t,dt);
- cimg_forY(*this,y) img(x,y,z,0,wh) = (T)dt[y];
- }
- }
- if (_depth>1) {
- g.assign(_depth); dt.assign(_depth); s.assign(_depth); t.assign(_depth);
- #if cimg_use_openmp!=0 && !cimg_is_gcc49x
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
- cimg_openmp_if(_depth>=(cimg_openmp_sizefactor)*512 && _width*_height>=16)
- firstprivate(g,dt,s,t))
- #endif
- cimg_forXY(*this,x,y) { // Over Z-direction
- cimg_forZ(*this,z) g[z] = (longT)img(x,y,z,0,wh);
- _distance_scan(_depth,g,sep,f,s,t,dt);
- cimg_forZ(*this,z) img(x,y,z,0,wh) = (T)dt[z];
- }
- }
- }
- return *this;
- }
- //! Compute chamfer distance to a specified value, with a custom metric.
- /**
- \param value Reference value.
- \param metric_mask Metric mask.
- \note The algorithm code has been initially proposed by A. Meijster, and modified by D. Tschumperlé.
- **/
- template<typename t>
- CImg<T>& distance(const T& value, const CImg<t>& metric_mask) {
- if (is_empty()) return *this;
- bool is_value = false;
- cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,0:(T)999999999;
- if (!is_value) return fill(cimg::type<T>::max());
- const ulongT wh = (ulongT)_width*_height;
- cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
- cimg_forC(*this,c) {
- CImg<T> img = get_shared_channel(c);
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
- cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1024))
- cimg_forXYZ(metric_mask,dx,dy,dz) {
- const t weight = metric_mask(dx,dy,dz);
- if (weight) {
- for (int z = dz, nz = 0; z<depth(); ++z,++nz) { // Forward scan
- for (int y = dy , ny = 0; y<height(); ++y,++ny) {
- for (int x = dx, nx = 0; x<width(); ++x,++nx) {
- const T dd = img(nx,ny,nz,0,wh) + weight;
- if (dd<img(x,y,z,0,wh)) img(x,y,z,0,wh) = dd;
- }
- }
- }
- for (int z = depth() - 1 - dz, nz = depth() - 1; z>=0; --z,--nz) { // Backward scan
- for (int y = height() - 1 - dy, ny = height() - 1; y>=0; --y,--ny) {
- for (int x = width() - 1 - dx, nx = width() - 1; x>=0; --x,--nx) {
- const T dd = img(nx,ny,nz,0,wh) + weight;
- if (dd<img(x,y,z,0,wh)) img(x,y,z,0,wh) = dd;
- }
- }
- }
- }
- }
- }
- return *this;
- }
- //! Compute chamfer distance to a specified value, with a custom metric \newinstance.
- template<typename t>
- CImg<Tfloat> get_distance(const T& value, const CImg<t>& metric_mask) const {
- return CImg<Tfloat>(*this,false).distance(value,metric_mask);
- }
- //! Compute distance to a specified value, according to a custom metric (use dijkstra algorithm).
- /**
- \param value Reference value.
- \param metric Field of distance potentials.
- \param is_high_connectivity Tells if the algorithm uses low or high connectivity.
- \param[out] return_path An image containing the nodes of the minimal path.
- **/
- template<typename t, typename to>
- CImg<T>& distance_dijkstra(const T& value, const CImg<t>& metric, const bool is_high_connectivity,
- CImg<to>& return_path) {
- return get_distance_dijkstra(value,metric,is_high_connectivity,return_path).move_to(*this);
- }
- //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm) \newinstance.
- template<typename t, typename to>
- CImg<typename cimg::superset<t,long>::type>
- get_distance_dijkstra(const T& value, const CImg<t>& metric, const bool is_high_connectivity,
- CImg<to>& return_path) const {
- if (is_empty()) return return_path.assign();
- if (!is_sameXYZ(metric))
- throw CImgArgumentException(_cimg_instance
- "distance_dijkstra(): image instance and metric map (%u,%u,%u,%u) "
- "have incompatible dimensions.",
- cimg_instance,
- metric._width,metric._height,metric._depth,metric._spectrum);
- typedef typename cimg::superset<t,long>::type td; // Type used for computing cumulative distances
- CImg<td> result(_width,_height,_depth,_spectrum), Q;
- CImg<boolT> is_queued(_width,_height,_depth,1);
- if (return_path) return_path.assign(_width,_height,_depth,_spectrum);
- cimg_forC(*this,c) {
- const CImg<T> img = get_shared_channel(c);
- const CImg<t> met = metric.get_shared_channel(c%metric._spectrum);
- CImg<td> res = result.get_shared_channel(c);
- CImg<to> path = return_path?return_path.get_shared_channel(c):CImg<to>();
- unsigned int sizeQ = 0;
- // Detect initial seeds.
- is_queued.fill(0);
- cimg_forXYZ(img,x,y,z) if (img(x,y,z)==value) {
- Q._priority_queue_insert(is_queued,sizeQ,0,x,y,z);
- res(x,y,z) = 0;
- if (path) path(x,y,z) = (to)0;
- }
- // Start distance propagation.
- while (sizeQ) {
- // Get and remove point with minimal potential from the queue.
- const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
- const td P = (td)-Q(0,0);
- Q._priority_queue_remove(sizeQ);
- // Update neighbors.
- td npot = 0;
- if (x - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x - 1,y,z) + P),x - 1,y,z)) {
- res(x - 1,y,z) = npot; if (path) path(x - 1,y,z) = (to)2;
- }
- if (x + 1<width() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x + 1,y,z) + P),x + 1,y,z)) {
- res(x + 1,y,z) = npot; if (path) path(x + 1,y,z) = (to)1;
- }
- if (y - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y - 1,z) + P),x,y - 1,z)) {
- res(x,y - 1,z) = npot; if (path) path(x,y - 1,z) = (to)8;
- }
- if (y + 1<height() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y + 1,z) + P),x,y + 1,z)) {
- res(x,y + 1,z) = npot; if (path) path(x,y + 1,z) = (to)4;
- }
- if (z - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y,z - 1) + P),x,y,z - 1)) {
- res(x,y,z - 1) = npot; if (path) path(x,y,z - 1) = (to)32;
- }
- if (z + 1<depth() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y,z + 1) + P),x,y,z + 1)) {
- res(x,y,z + 1) = npot; if (path) path(x,y,z + 1) = (to)16;
- }
- if (is_high_connectivity) {
- const float sqrt2 = std::sqrt(2.f), sqrt3 = std::sqrt(3.f);
- // Diagonal neighbors on slice z.
- if (x - 1>=0 && y - 1>=0 &&
- Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y - 1,z) + P)),x - 1,y - 1,z)) {
- res(x - 1,y - 1,z) = npot; if (path) path(x - 1,y - 1,z) = (to)10;
- }
- if (x + 1<width() && y - 1>=0 &&
- Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y - 1,z) + P)),x + 1,y - 1,z)) {
- res(x + 1,y - 1,z) = npot; if (path) path(x + 1,y - 1,z) = (to)9;
- }
- if (x - 1>=0 && y + 1<height() &&
- Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y + 1,z) + P)),x - 1,y + 1,z)) {
- res(x - 1,y + 1,z) = npot; if (path) path(x - 1,y + 1,z) = (to)6;
- }
- if (x + 1<width() && y + 1<height() &&
- Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y + 1,z) + P)),x + 1,y + 1,z)) {
- res(x + 1,y + 1,z) = npot; if (path) path(x + 1,y + 1,z) = (to)5;
- }
- if (z - 1>=0) { // Diagonal neighbors on slice z - 1
- if (x - 1>=0 &&
- Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y,z - 1) + P)),x - 1,y,z - 1)) {
- res(x - 1,y,z - 1) = npot; if (path) path(x - 1,y,z - 1) = (to)34;
- }
- if (x + 1<width() &&
- Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y,z - 1) + P)),x + 1,y,z - 1)) {
- res(x + 1,y,z - 1) = npot; if (path) path(x + 1,y,z - 1) = (to)33;
- }
- if (y - 1>=0 &&
- Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y - 1,z - 1) + P)),x,y - 1,z - 1)) {
- res(x,y - 1,z - 1) = npot; if (path) path(x,y - 1,z - 1) = (to)40;
- }
- if (y + 1<height() &&
- Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y + 1,z - 1) + P)),x,y + 1,z - 1)) {
- res(x,y + 1,z - 1) = npot; if (path) path(x,y + 1,z - 1) = (to)36;
- }
- if (x - 1>=0 && y - 1>=0 &&
- Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y - 1,z - 1) + P)),
- x - 1,y - 1,z - 1)) {
- res(x - 1,y - 1,z - 1) = npot; if (path) path(x - 1,y - 1,z - 1) = (to)42;
- }
- if (x + 1<width() && y - 1>=0 &&
- Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y - 1,z - 1) + P)),
- x + 1,y - 1,z - 1)) {
- res(x + 1,y - 1,z - 1) = npot; if (path) path(x + 1,y - 1,z - 1) = (to)41;
- }
- if (x - 1>=0 && y + 1<height() &&
- Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y + 1,z - 1) + P)),
- x - 1,y + 1,z - 1)) {
- res(x - 1,y + 1,z - 1) = npot; if (path) path(x - 1,y + 1,z - 1) = (to)38;
- }
- if (x + 1<width() && y + 1<height() &&
- Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y + 1,z - 1) + P)),
- x + 1,y + 1,z - 1)) {
- res(x + 1,y + 1,z - 1) = npot; if (path) path(x + 1,y + 1,z - 1) = (to)37;
- }
- }
- if (z + 1<depth()) { // Diagonal neighbors on slice z + 1
- if (x - 1>=0 &&
- Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y,z + 1) + P)),x - 1,y,z + 1)) {
- res(x - 1,y,z + 1) = npot; if (path) path(x - 1,y,z + 1) = (to)18;
- }
- if (x + 1<width() &&
- Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y,z + 1) + P)),x + 1,y,z + 1)) {
- res(x + 1,y,z + 1) = npot; if (path) path(x + 1,y,z + 1) = (to)17;
- }
- if (y - 1>=0 &&
- Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y - 1,z + 1) + P)),x,y - 1,z + 1)) {
- res(x,y - 1,z + 1) = npot; if (path) path(x,y - 1,z + 1) = (to)24;
- }
- if (y + 1<height() &&
- Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y + 1,z + 1) + P)),x,y + 1,z + 1)) {
- res(x,y + 1,z + 1) = npot; if (path) path(x,y + 1,z + 1) = (to)20;
- }
- if (x - 1>=0 && y - 1>=0 &&
- Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y - 1,z + 1) + P)),
- x - 1,y - 1,z + 1)) {
- res(x - 1,y - 1,z + 1) = npot; if (path) path(x - 1,y - 1,z + 1) = (to)26;
- }
- if (x + 1<width() && y - 1>=0 &&
- Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y - 1,z + 1) + P)),
- x + 1,y - 1,z + 1)) {
- res(x + 1,y - 1,z + 1) = npot; if (path) path(x + 1,y - 1,z + 1) = (to)25;
- }
- if (x - 1>=0 && y + 1<height() &&
- Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y + 1,z + 1) + P)),
- x - 1,y + 1,z + 1)) {
- res(x - 1,y + 1,z + 1) = npot; if (path) path(x - 1,y + 1,z + 1) = (to)22;
- }
- if (x + 1<width() && y + 1<height() &&
- Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y + 1,z + 1) + P)),
- x + 1,y + 1,z + 1)) {
- res(x + 1,y + 1,z + 1) = npot; if (path) path(x + 1,y + 1,z + 1) = (to)21;
- }
- }
- }
- }
- }
- return result;
- }
- //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm). \overloading.
- template<typename t>
- CImg<T>& distance_dijkstra(const T& value, const CImg<t>& metric,
- const bool is_high_connectivity=false) {
- return get_distance_dijkstra(value,metric,is_high_connectivity).move_to(*this);
- }
- //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm). \newinstance.
- template<typename t>
- CImg<Tfloat> get_distance_dijkstra(const T& value, const CImg<t>& metric,
- const bool is_high_connectivity=false) const {
- CImg<T> return_path;
- return get_distance_dijkstra(value,metric,is_high_connectivity,return_path);
- }
- //! Compute distance map to one source point, according to a custom metric (use fast marching algorithm).
- /**
- \param value Reference value.
- \param metric Field of distance potentials.
- **/
- template<typename t>
- CImg<T>& distance_eikonal(const T& value, const CImg<t>& metric) {
- return get_distance_eikonal(value,metric).move_to(*this);
- }
- //! Compute distance map to one source point, according to a custom metric (use fast marching algorithm).
- template<typename t>
- CImg<Tfloat> get_distance_eikonal(const T& value, const CImg<t>& metric) const {
- if (is_empty()) return *this;
- if (!is_sameXYZ(metric))
- throw CImgArgumentException(_cimg_instance
- "distance_eikonal(): image instance and metric map (%u,%u,%u,%u) have "
- "incompatible dimensions.",
- cimg_instance,
- metric._width,metric._height,metric._depth,metric._spectrum);
- CImg<Tfloat> result(_width,_height,_depth,_spectrum,cimg::type<Tfloat>::max()), Q;
- CImg<charT> state(_width,_height,_depth); // -1=far away, 0=narrow, 1=frozen
- cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2) firstprivate(Q,state))
- cimg_forC(*this,c) {
- const CImg<T> img = get_shared_channel(c);
- const CImg<t> met = metric.get_shared_channel(c%metric._spectrum);
- CImg<Tfloat> res = result.get_shared_channel(c);
- unsigned int sizeQ = 0;
- state.fill(-1);
- // Detect initial seeds.
- Tfloat *ptr1 = res._data; char *ptr2 = state._data;
- cimg_for(img,ptr0,T) { if (*ptr0==value) { *ptr1 = 0; *ptr2 = 1; } ++ptr1; ++ptr2; }
- // Initialize seeds neighbors.
- ptr2 = state._data;
- cimg_forXYZ(img,x,y,z) if (*(ptr2++)==1) {
- if (x - 1>=0 && state(x - 1,y,z)==-1) {
- const Tfloat dist = res(x - 1,y,z) = __distance_eikonal(res,met(x - 1,y,z),x - 1,y,z);
- Q._eik_priority_queue_insert(state,sizeQ,-dist,x - 1,y,z);
- }
- if (x + 1<width() && state(x + 1,y,z)==-1) {
- const Tfloat dist = res(x + 1,y,z) = __distance_eikonal(res,met(x + 1,y,z),x + 1,y,z);
- Q._eik_priority_queue_insert(state,sizeQ,-dist,x + 1,y,z);
- }
- if (y - 1>=0 && state(x,y - 1,z)==-1) {
- const Tfloat dist = res(x,y - 1,z) = __distance_eikonal(res,met(x,y - 1,z),x,y - 1,z);
- Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y - 1,z);
- }
- if (y + 1<height() && state(x,y + 1,z)==-1) {
- const Tfloat dist = res(x,y + 1,z) = __distance_eikonal(res,met(x,y + 1,z),x,y + 1,z);
- Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y + 1,z);
- }
- if (z - 1>=0 && state(x,y,z - 1)==-1) {
- const Tfloat dist = res(x,y,z - 1) = __distance_eikonal(res,met(x,y,z - 1),x,y,z - 1);
- Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z - 1);
- }
- if (z + 1<depth() && state(x,y,z + 1)==-1) {
- const Tfloat dist = res(x,y,z + 1) = __distance_eikonal(res,met(x,y,z + 1),x,y,z + 1);
- Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z + 1);
- }
- }
- // Propagate front.
- while (sizeQ) {
- int x = -1, y = -1, z = -1;
- while (sizeQ && x<0) {
- x = (int)Q(0,1); y = (int)Q(0,2); z = (int)Q(0,3);
- Q._priority_queue_remove(sizeQ);
- if (state(x,y,z)==1) x = -1; else state(x,y,z) = 1;
- }
- if (x>=0) {
- if (x - 1>=0 && state(x - 1,y,z)!=1) {
- const Tfloat dist = __distance_eikonal(res,met(x - 1,y,z),x - 1,y,z);
- if (dist<res(x - 1,y,z)) {
- res(x - 1,y,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x - 1,y,z);
- }
- }
- if (x + 1<width() && state(x + 1,y,z)!=1) {
- const Tfloat dist = __distance_eikonal(res,met(x + 1,y,z),x + 1,y,z);
- if (dist<res(x + 1,y,z)) {
- res(x + 1,y,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x + 1,y,z);
- }
- }
- if (y - 1>=0 && state(x,y - 1,z)!=1) {
- const Tfloat dist = __distance_eikonal(res,met(x,y - 1,z),x,y - 1,z);
- if (dist<res(x,y - 1,z)) {
- res(x,y - 1,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y - 1,z);
- }
- }
- if (y + 1<height() && state(x,y + 1,z)!=1) {
- const Tfloat dist = __distance_eikonal(res,met(x,y + 1,z),x,y + 1,z);
- if (dist<res(x,y + 1,z)) {
- res(x,y + 1,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y + 1,z);
- }
- }
- if (z - 1>=0 && state(x,y,z - 1)!=1) {
- const Tfloat dist = __distance_eikonal(res,met(x,y,z - 1),x,y,z - 1);
- if (dist<res(x,y,z - 1)) {
- res(x,y,z - 1) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z - 1);
- }
- }
- if (z + 1<depth() && state(x,y,z + 1)!=1) {
- const Tfloat dist = __distance_eikonal(res,met(x,y,z + 1),x,y,z + 1);
- if (dist<res(x,y,z + 1)) {
- res(x,y,z + 1) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z + 1);
- }
- }
- }
- }
- }
- return result;
- }
- // Locally solve eikonal equation.
- Tfloat __distance_eikonal(const CImg<Tfloat>& res, const Tfloat P,
- const int x=0, const int y=0, const int z=0) const {
- const Tfloat M = (Tfloat)cimg::type<T>::max();
- T T1 = (T)std::min(x - 1>=0?res(x - 1,y,z):M,x + 1<width()?res(x + 1,y,z):M);
- Tfloat root = 0;
- if (_depth>1) { // 3D
- T
- T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1<height()?res(x,y + 1,z):M),
- T3 = (T)std::min(z - 1>=0?res(x,y,z - 1):M,z + 1<depth()?res(x,y,z + 1):M);
- if (T1>T2) cimg::swap(T1,T2);
- if (T2>T3) cimg::swap(T2,T3);
- if (T1>T2) cimg::swap(T1,T2);
- if (P<=0) return (Tfloat)T1;
- if (T3<M && ___distance_eikonal(3,-2*(T1 + T2 + T3),T1*T1 + T2*T2 + T3*T3 - P*P,root))
- return std::max((Tfloat)T3,root);
- if (T2<M && ___distance_eikonal(2,-2*(T1 + T2),T1*T1 + T2*T2 - P*P,root))
- return std::max((Tfloat)T2,root);
- return P + T1;
- } else if (_height>1) { // 2D
- T T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1<height()?res(x,y + 1,z):M);
- if (T1>T2) cimg::swap(T1,T2);
- if (P<=0) return (Tfloat)T1;
- if (T2<M && ___distance_eikonal(2,-2*(T1 + T2),T1*T1 + T2*T2 - P*P,root))
- return std::max((Tfloat)T2,root);
- return P + T1;
- } else { // 1D
- if (P<=0) return (Tfloat)T1;
- return P + T1;
- }
- return 0;
- }
- // Find max root of a 2nd-order polynomial.
- static bool ___distance_eikonal(const Tfloat a, const Tfloat b, const Tfloat c, Tfloat &root) {
- const Tfloat delta = b*b - 4*a*c;
- if (delta<0) return false;
- root = 0.5f*(-b + std::sqrt(delta))/a;
- return true;
- }
- // Insert new point in heap.
- template<typename t>
- void _eik_priority_queue_insert(CImg<charT>& state, unsigned int& siz, const t value,
- const unsigned int x, const unsigned int y, const unsigned int z) {
- if (state(x,y,z)>0) return;
- state(x,y,z) = 0;
- if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); }
- (*this)(siz - 1,0) = (T)value; (*this)(siz - 1,1) = (T)x; (*this)(siz - 1,2) = (T)y; (*this)(siz - 1,3) = (T)z;
- for (unsigned int pos = siz - 1, par = 0; pos && value>(t)(*this)(par=(pos + 1)/2 - 1,0); pos = par) {
- cimg::swap((*this)(pos,0),(*this)(par,0)); cimg::swap((*this)(pos,1),(*this)(par,1));
- cimg::swap((*this)(pos,2),(*this)(par,2)); cimg::swap((*this)(pos,3),(*this)(par,3));
- }
- }
- //! Compute distance function to 0-valued isophotes, using the Eikonal PDE.
- /**
- \param nb_iterations Number of PDE iterations.
- \param band_size Size of the narrow band.
- \param time_step Time step of the PDE iterations.
- **/
- CImg<T>& distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) {
- if (is_empty()) return *this;
- CImg<Tfloat> velocity(*this,false);
- for (unsigned int iteration = 0; iteration<nb_iterations; ++iteration) {
- Tfloat *ptrd = velocity._data, veloc_max = 0;
- if (_depth>1) { // 3D
- CImg_3x3x3(I,Tfloat);
- cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) if (band_size<=0 || cimg::abs(Iccc)<band_size) {
- const Tfloat
- gx = (Incc - Ipcc)/2,
- gy = (Icnc - Icpc)/2,
- gz = (Iccn - Iccp)/2,
- sgn = -cimg::sign(Iccc),
- ix = gx*sgn>0?(Incc - Iccc):(Iccc - Ipcc),
- iy = gy*sgn>0?(Icnc - Iccc):(Iccc - Icpc),
- iz = gz*sgn>0?(Iccn - Iccc):(Iccc - Iccp),
- ng = 1e-5f + cimg::hypot(gx,gy,gz),
- ngx = gx/ng,
- ngy = gy/ng,
- ngz = gz/ng,
- veloc = sgn*(ngx*ix + ngy*iy + ngz*iz - 1);
- *(ptrd++) = veloc;
- if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
- } else *(ptrd++) = 0;
- } else { // 2D version
- CImg_3x3(I,Tfloat);
- cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) if (band_size<=0 || cimg::abs(Icc)<band_size) {
- const Tfloat
- gx = (Inc - Ipc)/2,
- gy = (Icn - Icp)/2,
- sgn = -cimg::sign(Icc),
- ix = gx*sgn>0?(Inc - Icc):(Icc - Ipc),
- iy = gy*sgn>0?(Icn - Icc):(Icc - Icp),
- ng = std::max((Tfloat)1e-5,cimg::hypot(gx,gy)),
- ngx = gx/ng,
- ngy = gy/ng,
- veloc = sgn*(ngx*ix + ngy*iy - 1);
- *(ptrd++) = veloc;
- if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
- } else *(ptrd++) = 0;
- }
- if (veloc_max>0) *this+=(velocity*=time_step/veloc_max);
- }
- return *this;
- }
- //! Compute distance function to 0-valued isophotes, using the Eikonal PDE \newinstance.
- CImg<Tfloat> get_distance_eikonal(const unsigned int nb_iterations, const float band_size=0,
- const float time_step=0.5f) const {
- return CImg<Tfloat>(*this,false).distance_eikonal(nb_iterations,band_size,time_step);
- }
- //! Compute Haar multiscale wavelet transform.
- /**
- \param axis Axis considered for the transform.
- \param invert Set inverse of direct transform.
- \param nb_scales Number of scales used for the transform.
- **/
- CImg<T>& haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) {
- return get_haar(axis,invert,nb_scales).move_to(*this);
- }
- //! Compute Haar multiscale wavelet transform \newinstance.
- CImg<Tfloat> get_haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) const {
- if (is_empty() || !nb_scales) return +*this;
- CImg<Tfloat> res;
- const Tfloat sqrt2 = std::sqrt(2.f);
- if (nb_scales==1) {
- switch (cimg::lowercase(axis)) { // Single scale transform
- case 'x' : {
- const unsigned int w = _width/2;
- if (w) {
- if ((w%2) && w!=1)
- throw CImgInstanceException(_cimg_instance
- "haar(): Sub-image width %u is not even.",
- cimg_instance,
- w);
- res.assign(_width,_height,_depth,_spectrum);
- if (invert) cimg_forYZC(*this,y,z,c) { // Inverse transform along X
- for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) {
- const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(xw,y,z,c);
- res(x2++,y,z,c) = (val0 - val1)/sqrt2;
- res(x2++,y,z,c) = (val0 + val1)/sqrt2;
- }
- } else cimg_forYZC(*this,y,z,c) { // Direct transform along X
- for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) {
- const Tfloat val0 = (Tfloat)(*this)(x2++,y,z,c), val1 = (Tfloat)(*this)(x2++,y,z,c);
- res(x,y,z,c) = (val0 + val1)/sqrt2;
- res(xw,y,z,c) = (val1 - val0)/sqrt2;
- }
- }
- } else return *this;
- } break;
- case 'y' : {
- const unsigned int h = _height/2;
- if (h) {
- if ((h%2) && h!=1)
- throw CImgInstanceException(_cimg_instance
- "haar(): Sub-image height %u is not even.",
- cimg_instance,
- h);
- res.assign(_width,_height,_depth,_spectrum);
- if (invert) cimg_forXZC(*this,x,z,c) { // Inverse transform along Y
- for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) {
- const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,yh,z,c);
- res(x,y2++,z,c) = (val0 - val1)/sqrt2;
- res(x,y2++,z,c) = (val0 + val1)/sqrt2;
- }
- } else cimg_forXZC(*this,x,z,c) {
- for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) { // Direct transform along Y
- const Tfloat val0 = (Tfloat)(*this)(x,y2++,z,c), val1 = (Tfloat)(*this)(x,y2++,z,c);
- res(x,y,z,c) = (val0 + val1)/sqrt2;
- res(x,yh,z,c) = (val1 - val0)/sqrt2;
- }
- }
- } else return *this;
- } break;
- case 'z' : {
- const unsigned int d = _depth/2;
- if (d) {
- if ((d%2) && d!=1)
- throw CImgInstanceException(_cimg_instance
- "haar(): Sub-image depth %u is not even.",
- cimg_instance,
- d);
- res.assign(_width,_height,_depth,_spectrum);
- if (invert) cimg_forXYC(*this,x,y,c) { // Inverse transform along Z
- for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) {
- const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,y,zd,c);
- res(x,y,z2++,c) = (val0 - val1)/sqrt2;
- res(x,y,z2++,c) = (val0 + val1)/sqrt2;
- }
- } else cimg_forXYC(*this,x,y,c) {
- for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) { // Direct transform along Z
- const Tfloat val0 = (Tfloat)(*this)(x,y,z2++,c), val1 = (Tfloat)(*this)(x,y,z2++,c);
- res(x,y,z,c) = (val0 + val1)/sqrt2;
- res(x,y,zd,c) = (val1 - val0)/sqrt2;
- }
- }
- } else return *this;
- } break;
- default :
- throw CImgArgumentException(_cimg_instance
- "haar(): Invalid specified axis '%c' "
- "(should be { x | y | z }).",
- cimg_instance,
- axis);
- }
- } else { // Multi-scale version
- if (invert) {
- res.assign(*this,false);
- switch (cimg::lowercase(axis)) {
- case 'x' : {
- unsigned int w = _width;
- for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2;
- for (w = w?w:1; w<=_width; w*=2) res.draw_image(res.get_crop(0,w - 1).get_haar('x',true,1));
- } break;
- case 'y' : {
- unsigned int h = _width;
- for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2;
- for (h = h?h:1; h<=_height; h*=2) res.draw_image(res.get_crop(0,0,_width - 1,h - 1).get_haar('y',true,1));
- } break;
- case 'z' : {
- unsigned int d = _depth;
- for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2;
- for (d = d?d:1; d<=_depth; d*=2)
- res.draw_image(res.get_crop(0,0,0,_width - 1,_height - 1,d - 1).get_haar('z',true,1));
- } break;
- default :
- throw CImgArgumentException(_cimg_instance
- "haar(): Invalid specified axis '%c' "
- "(should be { x | y | z }).",
- cimg_instance,
- axis);
- }
- } else { // Direct transform
- res = get_haar(axis,false,1);
- switch (cimg::lowercase(axis)) {
- case 'x' : {
- for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2)
- res.draw_image(res.get_crop(0,w - 1).get_haar('x',false,1));
- } break;
- case 'y' : {
- for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2)
- res.draw_image(res.get_crop(0,0,_width - 1,h - 1).get_haar('y',false,1));
- } break;
- case 'z' : {
- for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2)
- res.draw_image(res.get_crop(0,0,0,_width - 1,_height - 1,d - 1).get_haar('z',false,1));
- } break;
- default :
- throw CImgArgumentException(_cimg_instance
- "haar(): Invalid specified axis '%c' "
- "(should be { x | y | z }).",
- cimg_instance,
- axis);
- }
- }
- }
- return res;
- }
- //! Compute Haar multiscale wavelet transform \overloading.
- /**
- \param invert Set inverse of direct transform.
- \param nb_scales Number of scales used for the transform.
- **/
- CImg<T>& haar(const bool invert=false, const unsigned int nb_scales=1) {
- return get_haar(invert,nb_scales).move_to(*this);
- }
- //! Compute Haar multiscale wavelet transform \newinstance.
- CImg<Tfloat> get_haar(const bool invert=false, const unsigned int nb_scales=1) const {
- CImg<Tfloat> res;
- if (nb_scales==1) { // Single scale transform
- if (_width>1) get_haar('x',invert,1).move_to(res);
- if (_height>1) { if (res) res.haar('y',invert,1); else get_haar('y',invert,1).move_to(res); }
- if (_depth>1) { if (res) res.haar('z',invert,1); else get_haar('z',invert,1).move_to(res); }
- if (res) return res;
- } else { // Multi-scale transform
- if (invert) { // Inverse transform
- res.assign(*this,false);
- if (_width>1) {
- if (_height>1) {
- if (_depth>1) {
- unsigned int w = _width, h = _height, d = _depth;
- for (unsigned int s = 1; w && h && d && s<nb_scales; ++s) { w/=2; h/=2; d/=2; }
- for (w = w?w:1, h = h?h:1, d = d?d:1; w<=_width && h<=_height && d<=_depth; w*=2, h*=2, d*=2)
- res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,d - 1).get_haar(true,1));
- } else {
- unsigned int w = _width, h = _height;
- for (unsigned int s = 1; w && h && s<nb_scales; ++s) { w/=2; h/=2; }
- for (w = w?w:1, h = h?h:1; w<=_width && h<=_height; w*=2, h*=2)
- res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,0).get_haar(true,1));
- }
- } else {
- if (_depth>1) {
- unsigned int w = _width, d = _depth;
- for (unsigned int s = 1; w && d && s<nb_scales; ++s) { w/=2; d/=2; }
- for (w = w?w:1, d = d?d:1; w<=_width && d<=_depth; w*=2, d*=2)
- res.draw_image(res.get_crop(0,0,0,w - 1,0,d - 1).get_haar(true,1));
- } else {
- unsigned int w = _width;
- for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2;
- for (w = w?w:1; w<=_width; w*=2)
- res.draw_image(res.get_crop(0,0,0,w - 1,0,0).get_haar(true,1));
- }
- }
- } else {
- if (_height>1) {
- if (_depth>1) {
- unsigned int h = _height, d = _depth;
- for (unsigned int s = 1; h && d && s<nb_scales; ++s) { h/=2; d/=2; }
- for (h = h?h:1, d = d?d:1; h<=_height && d<=_depth; h*=2, d*=2)
- res.draw_image(res.get_crop(0,0,0,0,h - 1,d - 1).get_haar(true,1));
- } else {
- unsigned int h = _height;
- for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2;
- for (h = h?h:1; h<=_height; h*=2)
- res.draw_image(res.get_crop(0,0,0,0,h - 1,0).get_haar(true,1));
- }
- } else {
- if (_depth>1) {
- unsigned int d = _depth;
- for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2;
- for (d = d?d:1; d<=_depth; d*=2)
- res.draw_image(res.get_crop(0,0,0,0,0,d - 1).get_haar(true,1));
- } else return *this;
- }
- }
- } else { // Direct transform
- res = get_haar(false,1);
- if (_width>1) {
- if (_height>1) {
- if (_depth>1)
- for (unsigned int s = 1, w = _width/2, h = _height/2, d = _depth/2; w && h && d && s<nb_scales;
- ++s, w/=2, h/=2, d/=2)
- res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,d - 1).haar(false,1));
- else for (unsigned int s = 1, w = _width/2, h = _height/2; w && h && s<nb_scales; ++s, w/=2, h/=2)
- res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,0).haar(false,1));
- } else {
- if (_depth>1) for (unsigned int s = 1, w = _width/2, d = _depth/2; w && d && s<nb_scales; ++s, w/=2, d/=2)
- res.draw_image(res.get_crop(0,0,0,w - 1,0,d - 1).haar(false,1));
- else for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2)
- res.draw_image(res.get_crop(0,0,0,w - 1,0,0).haar(false,1));
- }
- } else {
- if (_height>1) {
- if (_depth>1)
- for (unsigned int s = 1, h = _height/2, d = _depth/2; h && d && s<nb_scales; ++s, h/=2, d/=2)
- res.draw_image(res.get_crop(0,0,0,0,h - 1,d - 1).haar(false,1));
- else for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2)
- res.draw_image(res.get_crop(0,0,0,0,h - 1,0).haar(false,1));
- } else {
- if (_depth>1) for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2)
- res.draw_image(res.get_crop(0,0,0,0,0,d - 1).haar(false,1));
- else return *this;
- }
- }
- }
- return res;
- }
- return *this;
- }
- //! Compute 1D Fast Fourier Transform, along a specified axis.
- /**
- \param axis Axis along which the FFT is computed.
- \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed.
- **/
- CImgList<Tfloat> get_FFT(const char axis, const bool is_inverse=false) const {
- CImgList<Tfloat> res(*this,CImg<Tfloat>());
- CImg<Tfloat>::FFT(res[0],res[1],axis,is_inverse);
- return res;
- }
- //! Compute n-D Fast Fourier Transform.
- /*
- \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed.
- **/
- CImgList<Tfloat> get_FFT(const bool is_inverse=false) const {
- CImgList<Tfloat> res(*this,CImg<Tfloat>());
- CImg<Tfloat>::FFT(res[0],res[1],is_inverse);
- return res;
- }
- //! Compute 1D Fast Fourier Transform, along a specified axis.
- /**
- \param[in,out] real Real part of the pixel values.
- \param[in,out] imag Imaginary part of the pixel values.
- \param axis Axis along which the FFT is computed.
- \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed.
- **/
- static void FFT(CImg<T>& real, CImg<T>& imag, const char axis, const bool is_inverse=false,
- const unsigned int nb_threads=0) {
- if (!real)
- throw CImgInstanceException("CImg<%s>::FFT(): Specified real part is empty.",
- pixel_type());
- if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,(T)0);
- if (!real.is_sameXYZC(imag))
- throw CImgInstanceException("CImg<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and "
- "imaginary part (%u,%u,%u,%u,%p) have different dimensions.",
- pixel_type(),
- real._width,real._height,real._depth,real._spectrum,real._data,
- imag._width,imag._height,imag._depth,imag._spectrum,imag._data);
- const char _axis = cimg::lowercase(axis);
- if (_axis!='x' && _axis!='y' && _axis!='z')
- throw CImgArgumentException("CImgList<%s>::FFT(): Invalid specified axis '%c' for real and imaginary parts "
- "(%u,%u,%u,%u) "
- "(should be { x | y | z }).",
- pixel_type(),axis,
- real._width,real._height,real._depth,real._spectrum);
- cimg::unused(nb_threads);
- #ifdef cimg_use_fftw3
- cimg::mutex(12);
- #ifndef cimg_use_fftw3_singlethread
- fftw_plan_with_nthreads(nb_threads?nb_threads:cimg::nb_cpus());
- #endif
- fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth);
- if (!data_in)
- throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) "
- "for computing FFT of image (%u,%u,%u,%u) along the X-axis.",
- pixel_type(),
- cimg::strbuffersize(sizeof(fftw_complex)*real._width*real._height*real._depth),
- real._width,real._height,real._depth,real._spectrum);
- double *const ptrf = (double*)data_in;
- fftw_plan data_plan =
- _axis=='x'?fftw_plan_many_dft(1,(int*)&real._width,real.height()*real.depth(),
- data_in,0,1,real.width(),
- data_in,0,1,real.width(),
- is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE):
- _axis=='y'?fftw_plan_many_dft(1,(int*)&real._height,real.width()*real.depth(),
- data_in,0,1,real.height(),
- data_in,0,1,real.height(),
- is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE):
- fftw_plan_many_dft(1,(int*)&real._depth,real.width()*real.height(),
- data_in,0,1,real.depth(),
- data_in,0,1,real.depth(),
- is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
- cimg_forC(real,c) {
- CImg<T> realc = real.get_shared_channel(c), imagc = imag.get_shared_channel(c);
- switch (_axis) {
- case 'x' :
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
- cimg_forXYZ(realc,x,y,z) {
- const ulongT
- i = realc.offset(x,y,z),
- j = 2*(x + (ulongT)y*realc._width + (ulongT)z*realc._width*realc._height);
- ptrf[j] = (double)realc[i];
- ptrf[j + 1] = (double)imagc[i];
- }
- break;
- case 'y' :
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
- cimg_forXYZ(realc,x,y,z) {
- const ulongT
- i = realc.offset(x,y,z),
- j = 2*(y + (ulongT)x*realc._height + (ulongT)z*realc._width*realc._height);
- ptrf[j] = (double)realc[i];
- ptrf[j + 1] = (double)imagc[i];
- }
- break;
- default :
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
- cimg_forXYZ(realc,x,y,z) {
- const ulongT
- i = realc.offset(x,y,z),
- j = 2*(z + (ulongT)x*realc._depth + (ulongT)y*realc._width*realc._depth);
- ptrf[j] = (double)realc[i];
- ptrf[j + 1] = (double)imagc[i];
- }
- }
- fftw_execute(data_plan);
- const double a = is_inverse?1.0/(_axis=='x'?real.width():_axis=='y'?real.height():real.depth()):1.0;
- switch (_axis) {
- case 'x' :
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
- cimg_forXYZ(realc,x,y,z) {
- const ulongT
- i = 2*(x + (ulongT)y*realc._width + (ulongT)z*realc._width*realc._height),
- j = realc.offset(x,y,z);
- realc[j] = (T)(a*ptrf[i]);
- imagc[j] = (T)(a*ptrf[i + 1]);
- }
- break;
- case 'y' :
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
- cimg_forXYZ(realc,x,y,z) {
- const ulongT
- i = 2*(y + (ulongT)x*realc._height + (ulongT)z*realc._width*realc._height),
- j = realc.offset(x,y,z);
- realc[j] = (T)(a*ptrf[i]);
- imagc[j] = (T)(a*ptrf[i + 1]);
- }
- break;
- default :
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
- cimg_forXYZ(realc,x,y,z) {
- const ulongT
- i = 2*(z + (ulongT)x*realc._depth + (ulongT)y*realc._width*realc._depth),
- j = realc.offset(x,y,z);
- realc[j] = (T)(a*ptrf[i]);
- imagc[j] = (T)(a*ptrf[i + 1]);
- }
- }
- }
- fftw_destroy_plan(data_plan);
- fftw_free(data_in);
- #ifndef cimg_use_fftw3_singlethread
- fftw_cleanup_threads();
- #endif
- cimg::mutex(12,0);
- #else
- switch (_axis) {
- case 'x' : { // Fourier along X, using built-in functions
- const unsigned int N = real._width, N2 = N>>1;
- if (((N - 1)&N) && N!=1)
- throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) "
- "have non 2^N dimension along the X-axis.",
- pixel_type(),
- real._width,real._height,real._depth,real._spectrum);
- for (unsigned int i = 0, j = 0; i<N2; ++i) {
- if (j>i) cimg_forYZC(real,y,z,c) {
- cimg::swap(real(i,y,z,c),real(j,y,z,c));
- cimg::swap(imag(i,y,z,c),imag(j,y,z,c));
- if (j<N2) {
- const unsigned int ri = N - 1 - i, rj = N - 1 - j;
- cimg::swap(real(ri,y,z,c),real(rj,y,z,c));
- cimg::swap(imag(ri,y,z,c),imag(rj,y,z,c));
- }
- }
- for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
- }
- for (unsigned int delta = 2; delta<=N; delta<<=1) {
- const unsigned int delta2 = delta>>1;
- for (unsigned int i = 0; i<N; i+=delta) {
- float wr = 1, wi = 0;
- const float
- angle = (float)((is_inverse?+1:-1)*2*cimg::PI/delta),
- ca = (float)std::cos(angle),
- sa = (float)std::sin(angle);
- for (unsigned int k = 0; k<delta2; ++k) {
- const unsigned int j = i + k, nj = j + delta2;
- cimg_forYZC(real,y,z,c) {
- T &ir = real(j,y,z,c), &ii = imag(j,y,z,c), &nir = real(nj,y,z,c), &nii = imag(nj,y,z,c);
- const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
- nir = (T)(ir - tmpr);
- nii = (T)(ii - tmpi);
- ir+=(T)tmpr;
- ii+=(T)tmpi;
- }
- const float nwr = wr*ca-wi*sa;
- wi = wi*ca + wr*sa;
- wr = nwr;
- }
- }
- }
- if (is_inverse) { real/=N; imag/=N; }
- } break;
- case 'y' : { // Fourier along Y, using built-in functions
- const unsigned int N = real._height, N2 = N>>1;
- if (((N - 1)&N) && N!=1)
- throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) "
- "have non 2^N dimension along the Y-axis.",
- pixel_type(),
- real._width,real._height,real._depth,real._spectrum);
- for (unsigned int i = 0, j = 0; i<N2; ++i) {
- if (j>i) cimg_forXZC(real,x,z,c) {
- cimg::swap(real(x,i,z,c),real(x,j,z,c));
- cimg::swap(imag(x,i,z,c),imag(x,j,z,c));
- if (j<N2) {
- const unsigned int ri = N - 1 - i, rj = N - 1 - j;
- cimg::swap(real(x,ri,z,c),real(x,rj,z,c));
- cimg::swap(imag(x,ri,z,c),imag(x,rj,z,c));
- }
- }
- for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
- }
- for (unsigned int delta = 2; delta<=N; delta<<=1) {
- const unsigned int delta2 = (delta>>1);
- for (unsigned int i = 0; i<N; i+=delta) {
- float wr = 1, wi = 0;
- const float
- angle = (float)((is_inverse?+1:-1)*2*cimg::PI/delta),
- ca = (float)std::cos(angle),
- sa = (float)std::sin(angle);
- for (unsigned int k = 0; k<delta2; ++k) {
- const unsigned int j = i + k, nj = j + delta2;
- cimg_forXZC(real,x,z,c) {
- T &ir = real(x,j,z,c), &ii = imag(x,j,z,c), &nir = real(x,nj,z,c), &nii = imag(x,nj,z,c);
- const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
- nir = (T)(ir - tmpr);
- nii = (T)(ii - tmpi);
- ir+=(T)tmpr;
- ii+=(T)tmpi;
- }
- const float nwr = wr*ca-wi*sa;
- wi = wi*ca + wr*sa;
- wr = nwr;
- }
- }
- }
- if (is_inverse) { real/=N; imag/=N; }
- } break;
- default : { // Fourier along Z, using built-in functions
- const unsigned int N = real._depth, N2 = N>>1;
- if (((N - 1)&N) && N!=1)
- throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) "
- "have non 2^N dimension along the Z-axis.",
- pixel_type(),
- real._width,real._height,real._depth,real._spectrum);
- for (unsigned int i = 0, j = 0; i<N2; ++i) {
- if (j>i) cimg_forXYC(real,x,y,c) {
- cimg::swap(real(x,y,i,c),real(x,y,j,c));
- cimg::swap(imag(x,y,i,c),imag(x,y,j,c));
- if (j<N2) {
- const unsigned int ri = N - 1 - i, rj = N - 1 - j;
- cimg::swap(real(x,y,ri,c),real(x,y,rj,c));
- cimg::swap(imag(x,y,ri,c),imag(x,y,rj,c));
- }
- }
- for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
- }
- for (unsigned int delta = 2; delta<=N; delta<<=1) {
- const unsigned int delta2 = (delta>>1);
- for (unsigned int i = 0; i<N; i+=delta) {
- float wr = 1, wi = 0;
- const float
- angle = (float)((is_inverse?+1:-1)*2*cimg::PI/delta),
- ca = (float)std::cos(angle),
- sa = (float)std::sin(angle);
- for (unsigned int k = 0; k<delta2; ++k) {
- const unsigned int j = i + k, nj = j + delta2;
- cimg_forXYC(real,x,y,c) {
- T &ir = real(x,y,j,c), &ii = imag(x,y,j,c), &nir = real(x,y,nj,c), &nii = imag(x,y,nj,c);
- const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
- nir = (T)(ir - tmpr);
- nii = (T)(ii - tmpi);
- ir+=(T)tmpr;
- ii+=(T)tmpi;
- }
- const float nwr = wr*ca-wi*sa;
- wi = wi*ca + wr*sa;
- wr = nwr;
- }
- }
- }
- if (is_inverse) { real/=N; imag/=N; }
- } break;
- }
- #endif
- }
- //! Compute n-D Fast Fourier Transform.
- /**
- \param[in,out] real Real part of the pixel values.
- \param[in,out] imag Imaginary part of the pixel values.
- \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed.
- \param nb_threads Number of parallel threads used for the computation.
- Use \c 0 to set this to the number of available cpus.
- **/
- static void FFT(CImg<T>& real, CImg<T>& imag, const bool is_inverse=false,
- const unsigned int nb_threads=0) {
- if (!real)
- throw CImgInstanceException("CImgList<%s>::FFT(): Empty specified real part.",
- pixel_type());
- if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,(T)0);
- if (!real.is_sameXYZC(imag))
- throw CImgInstanceException("CImgList<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and "
- "imaginary part (%u,%u,%u,%u,%p) have different dimensions.",
- pixel_type(),
- real._width,real._height,real._depth,real._spectrum,real._data,
- imag._width,imag._height,imag._depth,imag._spectrum,imag._data);
- cimg::unused(nb_threads);
- #ifdef cimg_use_fftw3
- cimg::mutex(12);
- #ifndef cimg_use_fftw3_singlethread
- fftw_plan_with_nthreads(nb_threads?nb_threads:cimg::nb_cpus());
- #endif
- fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth);
- if (!data_in)
- throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) "
- "for computing FFT of image (%u,%u,%u,%u).",
- pixel_type(),
- cimg::strbuffersize(sizeof(fftw_complex)*real._width*
- real._height*real._depth*real._spectrum),
- real._width,real._height,real._depth,real._spectrum);
- double *const ptrf = (double*)data_in;
- fftw_plan data_plan =
- real._depth>1?fftw_plan_dft_3d(real._depth,real._height,real._width,data_in,data_in,
- is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE):
- real._height>1?fftw_plan_dft_2d(real._height,real._width,data_in,data_in,
- is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE):
- fftw_plan_dft_1d(real._width,data_in,data_in,
- is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
- cimg_forC(real,c) {
- CImg<T> realc = real.get_shared_channel(c), imagc = imag.get_shared_channel(c);
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
- cimg_rofoff(realc,i) { const ulongT i2 = 2*i; ptrf[i2] = (double)realc[i]; ptrf[i2 + 1] = (double)imagc[i]; }
- fftw_execute(data_plan);
- if (is_inverse) {
- const double a = 1.0/(real.width()*real.height()*real.depth());
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
- cimg_rofoff(realc,i) { const ulongT i2 = 2*i; realc[i] = (T)(a*ptrf[i2]); imagc[i] = (T)(a*ptrf[i2 + 1]); }
- } else
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
- cimg_rofoff(realc,i) { const ulongT i2 = 2*i; realc[i] = (T)ptrf[i2]; imagc[i] = (T)ptrf[i2 + 1]; }
- }
- fftw_destroy_plan(data_plan);
- fftw_free(data_in);
- #ifndef cimg_use_fftw3_singlethread
- fftw_cleanup_threads();
- #endif
- cimg::mutex(12,0);
- #else
- if (real._depth>1) FFT(real,imag,'z',is_inverse);
- if (real._height>1) FFT(real,imag,'y',is_inverse);
- if (real._width>1) FFT(real,imag,'x',is_inverse);
- #endif
- }
- //@}
- //-------------------------------------
- //
- //! \name 3D Objects Management
- //@{
- //-------------------------------------
- //! Rotate 3D object's vertices.
- /**
- \param x X-coordinate of the rotation axis, or first quaternion coordinate.
- \param y Y-coordinate of the rotation axis, or second quaternion coordinate.
- \param z Z-coordinate of the rotation axis, or second quaternion coordinate.
- \param w Angle of the rotation axis (in degree), or fourth quaternion coordinate.
- \param is_quaternion Tell is the four arguments denotes a set { axis + angle } or a quaternion (x,y,z,w).
- **/
- CImg<T>& rotate_object3d(const float x, const float y, const float z, const float w,
- const bool is_quaternion=false) {
- return get_rotate_object3d(x,y,z,w,is_quaternion).move_to(*this);
- }
- CImg<Tfloat> get_rotate_object3d(const float x, const float y, const float z, const float w,
- const bool is_quaternion=false) const {
- if (_height!=3 || _depth>1 || _spectrum>1)
- throw CImgInstanceException(_cimg_instance
- "rotate_object3d(): Instance is not a set of 3D vertices.",
- cimg_instance);
- return CImg<Tfloat>::rotation_matrix(x,y,z,w,is_quaternion)**this;
- }
- //! Shift 3D object's vertices.
- /**
- \param tx X-coordinate of the 3D displacement vector.
- \param ty Y-coordinate of the 3D displacement vector.
- \param tz Z-coordinate of the 3D displacement vector.
- **/
- CImg<T>& shift_object3d(const float tx, const float ty=0, const float tz=0) {
- if (_height!=3 || _depth>1 || _spectrum>1)
- throw CImgInstanceException(_cimg_instance
- "shift_object3d(): Instance is not a set of 3D vertices.",
- cimg_instance);
- get_shared_row(0)+=tx; get_shared_row(1)+=ty; get_shared_row(2)+=tz;
- return *this;
- }
- //! Shift 3D object's vertices \newinstance.
- CImg<Tfloat> get_shift_object3d(const float tx, const float ty=0, const float tz=0) const {
- return CImg<Tfloat>(*this,false).shift_object3d(tx,ty,tz);
- }
- //! Shift 3D object's vertices, so that it becomes centered.
- /**
- \note The object center is computed as its barycenter.
- **/
- CImg<T>& shift_object3d() {
- if (_height!=3 || _depth>1 || _spectrum>1)
- throw CImgInstanceException(_cimg_instance
- "shift_object3d(): Instance is not a set of 3D vertices.",
- cimg_instance);
- CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
- float
- xm, xM = (float)xcoords.max_min(xm),
- ym, yM = (float)ycoords.max_min(ym),
- zm, zM = (float)zcoords.max_min(zm);
- xcoords-=(xm + xM)/2; ycoords-=(ym + yM)/2; zcoords-=(zm + zM)/2;
- return *this;
- }
- //! Shift 3D object's vertices, so that it becomes centered \newinstance.
- CImg<Tfloat> get_shift_object3d() const {
- return CImg<Tfloat>(*this,false).shift_object3d();
- }
- //! Resize 3D object.
- /**
- \param sx Width of the 3D object's bounding box.
- \param sy Height of the 3D object's bounding box.
- \param sz Depth of the 3D object's bounding box.
- **/
- CImg<T>& resize_object3d(const float sx, const float sy=-100, const float sz=-100) {
- if (_height!=3 || _depth>1 || _spectrum>1)
- throw CImgInstanceException(_cimg_instance
- "resize_object3d(): Instance is not a set of 3D vertices.",
- cimg_instance);
- CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
- float
- xm, xM = (float)xcoords.max_min(xm),
- ym, yM = (float)ycoords.max_min(ym),
- zm, zM = (float)zcoords.max_min(zm);
- if (xm<xM) { if (sx>0) xcoords*=sx/(xM-xm); else xcoords*=-sx/100; }
- if (ym<yM) { if (sy>0) ycoords*=sy/(yM-ym); else ycoords*=-sy/100; }
- if (zm<zM) { if (sz>0) zcoords*=sz/(zM-zm); else zcoords*=-sz/100; }
- return *this;
- }
- //! Resize 3D object \newinstance.
- CImg<Tfloat> get_resize_object3d(const float sx, const float sy=-100, const float sz=-100) const {
- return CImg<Tfloat>(*this,false).resize_object3d(sx,sy,sz);
- }
- //! Resize 3D object to unit size.
- CImg<T> resize_object3d() {
- if (_height!=3 || _depth>1 || _spectrum>1)
- throw CImgInstanceException(_cimg_instance
- "resize_object3d(): Instance is not a set of 3D vertices.",
- cimg_instance);
- CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
- float
- xm, xM = (float)xcoords.max_min(xm),
- ym, yM = (float)ycoords.max_min(ym),
- zm, zM = (float)zcoords.max_min(zm);
- const float dx = xM - xm, dy = yM - ym, dz = zM - zm, dmax = cimg::max(dx,dy,dz);
- if (dmax>0) { xcoords/=dmax; ycoords/=dmax; zcoords/=dmax; }
- return *this;
- }
- //! Resize 3D object to unit size \newinstance.
- CImg<Tfloat> get_resize_object3d() const {
- return CImg<Tfloat>(*this,false).resize_object3d();
- }
- //! Merge two 3D objects together.
- /**
- \param[in,out] primitives Primitives data of the current 3D object.
- \param obj_vertices Vertices data of the additional 3D object.
- \param obj_primitives Primitives data of the additional 3D object.
- **/
- template<typename tf, typename tp, typename tff>
- CImg<T>& append_object3d(CImgList<tf>& primitives, const CImg<tp>& obj_vertices,
- const CImgList<tff>& obj_primitives) {
- if (!obj_vertices || !obj_primitives) return *this;
- if (obj_vertices._height!=3 || obj_vertices._depth>1 || obj_vertices._spectrum>1)
- throw CImgInstanceException(_cimg_instance
- "append_object3d(): Specified vertice image (%u,%u,%u,%u,%p) is not a "
- "set of 3D vertices.",
- cimg_instance,
- obj_vertices._width,obj_vertices._height,
- obj_vertices._depth,obj_vertices._spectrum,obj_vertices._data);
- if (is_empty()) { primitives.assign(obj_primitives); return assign(obj_vertices); }
- if (_height!=3 || _depth>1 || _spectrum>1)
- throw CImgInstanceException(_cimg_instance
- "append_object3d(): Instance is not a set of 3D vertices.",
- cimg_instance);
- const unsigned int P = _width;
- append(obj_vertices,'x');
- const unsigned int N = primitives._width;
- primitives.insert(obj_primitives);
- for (unsigned int i = N; i<primitives._width; ++i) {
- CImg<tf> &p = primitives[i];
- switch (p.size()) {
- case 1 : p[0]+=P; break; // Point
- case 5 : p[0]+=P; p[1]+=P; break; // Sphere
- case 2 : case 6 : p[0]+=P; p[1]+=P; break; // Segment
- case 3 : case 9 : p[0]+=P; p[1]+=P; p[2]+=P; break; // Triangle
- case 4 : case 12 : p[0]+=P; p[1]+=P; p[2]+=P; p[3]+=P; break; // Rectangle
- }
- }
- return *this;
- }
- //! Texturize primitives of a 3D object.
- /**
- \param[in,out] primitives Primitives data of the 3D object.
- \param[in,out] colors Colors data of the 3D object.
- \param texture Texture image to map to 3D object.
- \param coords Texture-mapping coordinates.
- **/
- template<typename tp, typename tc, typename tt, typename tx>
- const CImg<T>& texturize_object3d(CImgList<tp>& primitives, CImgList<tc>& colors,
- const CImg<tt>& texture, const CImg<tx>& coords=CImg<tx>::const_empty()) const {
- if (is_empty()) return *this;
- if (_height!=3)
- throw CImgInstanceException(_cimg_instance
- "texturize_object3d(): image instance is not a set of 3D points.",
- cimg_instance);
- if (coords && (coords._width!=_width || coords._height!=2))
- throw CImgArgumentException(_cimg_instance
- "texturize_object3d(): Invalid specified texture coordinates (%u,%u,%u,%u,%p).",
- cimg_instance,
- coords._width,coords._height,coords._depth,coords._spectrum,coords._data);
- CImg<intT> _coords;
- if (!coords) { // If no texture coordinates specified, do a default XY-projection
- _coords.assign(_width,2);
- float
- xmin, xmax = (float)get_shared_row(0).max_min(xmin),
- ymin, ymax = (float)get_shared_row(1).max_min(ymin),
- dx = xmax>xmin?xmax-xmin:1,
- dy = ymax>ymin?ymax-ymin:1;
- cimg_forX(*this,p) {
- _coords(p,0) = (int)(((*this)(p,0) - xmin)*texture._width/dx);
- _coords(p,1) = (int)(((*this)(p,1) - ymin)*texture._height/dy);
- }
- } else _coords = coords;
- int texture_ind = -1;
- cimglist_for(primitives,l) {
- CImg<tp> &p = primitives[l];
- const unsigned int siz = p.size();
- switch (siz) {
- case 1 : { // Point
- const unsigned int i0 = (unsigned int)p[0];
- const int x0 = _coords(i0,0), y0 = _coords(i0,1);
- texture.get_vector_at(x0<=0?0:x0>=texture.width()?texture.width() - 1:x0,
- y0<=0?0:y0>=texture.height()?texture.height() - 1:y0).move_to(colors[l]);
- } break;
- case 2 : case 6 : { // Line
- const unsigned int i0 = (unsigned int)p[0], i1 = (unsigned int)p[1];
- const int
- x0 = _coords(i0,0), y0 = _coords(i0,1),
- x1 = _coords(i1,0), y1 = _coords(i1,1);
- if (texture_ind<0) colors[texture_ind=l].assign(texture,false);
- else colors[l].assign(colors[texture_ind],true);
- CImg<tp>::vector(i0,i1,x0,y0,x1,y1).move_to(p);
- } break;
- case 3 : case 9 : { // Triangle
- const unsigned int i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2];
- const int
- x0 = _coords(i0,0), y0 = _coords(i0,1),
- x1 = _coords(i1,0), y1 = _coords(i1,1),
- x2 = _coords(i2,0), y2 = _coords(i2,1);
- if (texture_ind<0) colors[texture_ind=l].assign(texture,false);
- else colors[l].assign(colors[texture_ind],true);
- CImg<tp>::vector(i0,i1,i2,x0,y0,x1,y1,x2,y2).move_to(p);
- } break;
- case 4 : case 12 : { // Quadrangle
- const unsigned int
- i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2], i3 = (unsigned int)p[3];
- const int
- x0 = _coords(i0,0), y0 = _coords(i0,1),
- x1 = _coords(i1,0), y1 = _coords(i1,1),
- x2 = _coords(i2,0), y2 = _coords(i2,1),
- x3 = _coords(i3,0), y3 = _coords(i3,1);
- if (texture_ind<0) colors[texture_ind=l].assign(texture,false);
- else colors[l].assign(colors[texture_ind],true);
- CImg<tp>::vector(i0,i1,i2,i3,x0,y0,x1,y1,x2,y2,x3,y3).move_to(p);
- } break;
- }
- }
- return *this;
- }
- //! Generate a 3D elevation of the image instance.
- /**
- \param[out] primitives The returned list of the 3D object primitives
- (template type \e tf should be at least \e unsigned \e int).
- \param[out] colors The returned list of the 3D object colors.
- \param elevation The input elevation map.
- \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
- \par Example
- \code
- const CImg<float> img("reference.jpg");
- CImgList<unsigned int> faces3d;
- CImgList<unsigned char> colors3d;
- const CImg<float> points3d = img.get_elevation3d(faces3d,colors3d,img.get_norm()*0.2);
- CImg<unsigned char>().display_object3d("Elevation3d",points3d,faces3d,colors3d);
- \endcode
- \image html ref_elevation3d.jpg
- **/
- template<typename tf, typename tc, typename te>
- CImg<floatT> get_elevation3d(CImgList<tf>& primitives, CImgList<tc>& colors, const CImg<te>& elevation) const {
- if (!is_sameXY(elevation) || elevation._depth>1 || elevation._spectrum>1)
- throw CImgArgumentException(_cimg_instance
- "get_elevation3d(): Instance and specified elevation (%u,%u,%u,%u,%p) "
- "have incompatible dimensions.",
- cimg_instance,
- elevation._width,elevation._height,elevation._depth,
- elevation._spectrum,elevation._data);
- if (is_empty()) return *this;
- float m, M = (float)max_min(m);
- if (M==m) ++M;
- colors.assign();
- const unsigned int size_x1 = _width - 1, size_y1 = _height - 1;
- for (unsigned int y = 0; y<size_y1; ++y)
- for (unsigned int x = 0; x<size_x1; ++x) {
- const unsigned char
- r = (unsigned char)(((*this)(x,y,0) - m)*255/(M-m)),
- g = (unsigned char)(_spectrum>1?((*this)(x,y,1) - m)*255/(M-m):r),
- b = (unsigned char)(_spectrum>2?((*this)(x,y,2) - m)*255/(M-m):_spectrum>1?0:r);
- CImg<tc>::vector((tc)r,(tc)g,(tc)b).move_to(colors);
- }
- const typename CImg<te>::_functor2d_int func(elevation);
- return elevation3d(primitives,func,0,0,_width - 1.f,_height - 1.f,_width,_height);
- }
- //! Generate the 3D projection planes of the image instance.
- /**
- \param[out] primitives Primitives data of the returned 3D object.
- \param[out] colors Colors data of the returned 3D object.
- \param x0 X-coordinate of the projection point.
- \param y0 Y-coordinate of the projection point.
- \param z0 Z-coordinate of the projection point.
- \param normalize_colors Tells if the created textures have normalized colors.
- **/
- template<typename tf, typename tc>
- CImg<floatT> get_projections3d(CImgList<tf>& primitives, CImgList<tc>& colors,
- const unsigned int x0, const unsigned int y0, const unsigned int z0,
- const bool normalize_colors=false) const {
- float m = 0, M = 0, delta = 1;
- if (normalize_colors) { m = (float)min_max(M); delta = 255/(m==M?1:M-m); }
- const unsigned int
- _x0 = (x0>=_width)?_width - 1:x0,
- _y0 = (y0>=_height)?_height - 1:y0,
- _z0 = (z0>=_depth)?_depth - 1:z0;
- CImg<tc> img_xy, img_xz, img_yz;
- if (normalize_colors) {
- ((get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1)-=m)*=delta).move_to(img_xy);
- ((get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1)-=m)*=delta).resize(_width,_depth,1,-100,-1).
- move_to(img_xz);
- ((get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1)-=m)*=delta).resize(_height,_depth,1,-100,-1).
- move_to(img_yz);
- } else {
- get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1).move_to(img_xy);
- get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1).resize(_width,_depth,1,-100,-1).move_to(img_xz);
- get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1).resize(_height,_depth,1,-100,-1).move_to(img_yz);
- }
- CImg<floatT> points(12,3,1,1,
- 0,_width - 1,_width - 1,0, 0,_width - 1,_width - 1,0, _x0,_x0,_x0,_x0,
- 0,0,_height - 1,_height - 1, _y0,_y0,_y0,_y0, 0,_height - 1,_height - 1,0,
- _z0,_z0,_z0,_z0, 0,0,_depth - 1,_depth - 1, 0,0,_depth - 1,_depth - 1);
- primitives.assign();
- CImg<tf>::vector(0,1,2,3,0,0,img_xy._width - 1,0,img_xy._width - 1,img_xy._height - 1,0,img_xy._height - 1).
- move_to(primitives);
- CImg<tf>::vector(4,5,6,7,0,0,img_xz._width - 1,0,img_xz._width - 1,img_xz._height - 1,0,img_xz._height - 1).
- move_to(primitives);
- CImg<tf>::vector(8,9,10,11,0,0,img_yz._width - 1,0,img_yz._width - 1,img_yz._height - 1,0,img_yz._height - 1).
- move_to(primitives);
- colors.assign();
- img_xy.move_to(colors);
- img_xz.move_to(colors);
- img_yz.move_to(colors);
- return points;
- }
- //! Generate a isoline of the image instance as a 3D object.
- /**
- \param[out] primitives The returned list of the 3D object primitives
- (template type \e tf should be at least \e unsigned \e int).
- \param isovalue The returned list of the 3D object colors.
- \param size_x The number of subdivisions along the X-axis.
- \param size_y The number of subdisivions along the Y-axis.
- \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
- \par Example
- \code
- const CImg<float> img("reference.jpg");
- CImgList<unsigned int> faces3d;
- const CImg<float> points3d = img.get_isoline3d(faces3d,100);
- CImg<unsigned char>().display_object3d("Isoline3d",points3d,faces3d,colors3d);
- \endcode
- \image html ref_isoline3d.jpg
- **/
- template<typename tf>
- CImg<floatT> get_isoline3d(CImgList<tf>& primitives, const float isovalue,
- const int size_x=-100, const int size_y=-100) const {
- if (_spectrum>1)
- throw CImgInstanceException(_cimg_instance
- "get_isoline3d(): Instance is not a scalar image.",
- cimg_instance);
- if (_depth>1)
- throw CImgInstanceException(_cimg_instance
- "get_isoline3d(): Instance is not a 2D image.",
- cimg_instance);
- primitives.assign();
- if (is_empty()) return *this;
- CImg<floatT> vertices;
- if ((size_x==-100 && size_y==-100) || (size_x==width() && size_y==height())) {
- const _functor2d_int func(*this);
- vertices = isoline3d(primitives,func,isovalue,0,0,width() - 1.f,height() - 1.f,width(),height());
- } else {
- const _functor2d_float func(*this);
- vertices = isoline3d(primitives,func,isovalue,0,0,width() - 1.f,height() - 1.f,size_x,size_y);
- }
- return vertices;
- }
- //! Compute isolines of a function, as a 3D object.
- /**
- \param[out] primitives Primitives data of the resulting 3D object.
- \param func Elevation functor. Must have <tt>operator()(x,y)</tt> defined.
- \param isovalue Isovalue to extract from function.
- \param x0 X-coordinate of the starting point.
- \param y0 Y-coordinate of the starting point.
- \param x1 X-coordinate of the ending point.
- \param y1 Y-coordinate of the ending point.
- \param size_x Resolution of the function along the X-axis.
- \param size_y Resolution of the function along the Y-axis.
- \note Use the marching squares algorithm for extracting the isolines.
- **/
- template<typename tf, typename tfunc>
- static CImg<floatT> isoline3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue,
- const float x0, const float y0, const float x1, const float y1,
- const int size_x=256, const int size_y=256) {
- CImgList<floatT> vertices;
- primitives.assign();
- typename CImg<floatT>::_functor_isoline3d add_vertex(vertices);
- typename CImg<tf>::_functor_isoline3d add_segment(primitives);
- isoline3d(add_vertex,add_segment,func,isovalue,x0,y0,x1,y1,size_x,size_y);
- return vertices>'x';
- }
- //! Compute isolines of a function, as a 3D object.
- /**
- \param[out] add_vertex : Functor with operator()(x,y,z) defined for adding new vertex.
- \param[out] add_segment : Functor with operator()(i,j) defined for adding new segment.
- \param func Elevation function. Is of type <tt>float (*func)(const float x,const float y)</tt>.
- \param isovalue Isovalue to extract from function.
- \param x0 X-coordinate of the starting point.
- \param y0 Y-coordinate of the starting point.
- \param x1 X-coordinate of the ending point.
- \param y1 Y-coordinate of the ending point.
- \param size_x Resolution of the function along the X-axis.
- \param size_y Resolution of the function along the Y-axis.
- \note Use the marching squares algorithm for extracting the isolines.
- **/
- template<typename tv, typename tf, typename tfunc>
- static void isoline3d(tv& add_vertex, tf& add_segment, const tfunc& func, const float isovalue,
- const float x0, const float y0, const float x1, const float y1,
- const int size_x, const int size_y) {
- static const unsigned int edges[16] = { 0x0, 0x9, 0x3, 0xa, 0x6, 0xf, 0x5, 0xc, 0xc,
- 0x5, 0xf, 0x6, 0xa, 0x3, 0x9, 0x0 };
- static const int segments[16][4] = { { -1,-1,-1,-1 }, { 0,3,-1,-1 }, { 0,1,-1,-1 }, { 1,3,-1,-1 },
- { 1,2,-1,-1 }, { 0,1,2,3 }, { 0,2,-1,-1 }, { 2,3,-1,-1 },
- { 2,3,-1,-1 }, { 0,2,-1,-1}, { 0,3,1,2 }, { 1,2,-1,-1 },
- { 1,3,-1,-1 }, { 0,1,-1,-1}, { 0,3,-1,-1}, { -1,-1,-1,-1 } };
- const unsigned int
- _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)),
- _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)),
- nx = _nx?_nx:1,
- ny = _ny?_ny:1,
- nxm1 = nx - 1,
- nym1 = ny - 1;
- if (!nxm1 || !nym1) return;
- const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1;
- CImg<intT> indices1(nx,1,1,2,-1), indices2(nx,1,1,2);
- CImg<floatT> values1(nx), values2(nx);
- float X = x0, Y = y0, nX = X + dx, nY = Y + dy;
- int nb_vertices = 0;
- // Fill first line with values
- cimg_forX(values1,x) { values1(x) = (float)func(X,Y); X+=dx; }
- // Run the marching squares algorithm
- for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y=nY, nY+=dy) {
- X = x0; nX = X + dx;
- indices2.fill(-1);
- values2(0) = (float)func(X,nY);
- for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X=nX, nX+=dx) {
- // Determine square configuration
- const float
- val0 = values1(xi),
- val1 = values1(nxi),
- val2 = values2(nxi) = (float)func(nX,nY),
- val3 = values2(xi);
- const unsigned int
- configuration = (val0<isovalue?1U:0U) | (val1<isovalue?2U:0U) |
- (val2<isovalue?4U:0U) | (val3<isovalue?8U:0U),
- edge = edges[configuration];
- // Compute intersection vertices
- if (edge) {
- if ((edge&1) && indices1(xi,0)<0) {
- const float Xi = X + (isovalue-val0)*dx/(val1-val0);
- indices1(xi,0) = nb_vertices++;
- add_vertex(Xi,Y,0.0f);
- }
- if ((edge&2) && indices1(nxi,1)<0) {
- const float Yi = Y + (isovalue-val1)*dy/(val2-val1);
- indices1(nxi,1) = nb_vertices++;
- add_vertex(nX,Yi,0.0f);
- }
- if ((edge&4) && indices2(xi,0)<0) {
- const float Xi = X + (isovalue-val3)*dx/(val2-val3);
- indices2(xi,0) = nb_vertices++;
- add_vertex(Xi,nY,0.0f);
- }
- if ((edge&8) && indices1(xi,1)<0) {
- const float Yi = Y + (isovalue-val0)*dy/(val3-val0);
- indices1(xi,1) = nb_vertices++;
- add_vertex(X,Yi,0.0f);
- }
- // Create segments
- for (const int *segment = segments[configuration]; *segment!=-1; ) {
- const unsigned int p0 = (unsigned int)*(segment++), p1 = (unsigned int)*(segment++);
- const int
- i0 = _isoline3d_index(p0,indices1,indices2,xi,nxi),
- i1 = _isoline3d_index(p1,indices1,indices2,xi,nxi);
- add_segment(i0,i1);
- }
- }
- }
- values1.swap(values2);
- indices1.swap(indices2);
- }
- }
- //! Compute isolines of a function, as a 3D object \overloading.
- template<typename tf>
- static CImg<floatT> isoline3d(CImgList<tf>& primitives, const char *const expression, const float isovalue,
- const float x0, const float y0, const float x1, const float y1,
- const int size_x=256, const int size_y=256) {
- const _functor2d_expr func(expression);
- return isoline3d(primitives,func,isovalue,x0,y0,x1,y1,size_x,size_y);
- }
- template<typename t>
- static int _isoline3d_index(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2,
- const unsigned int x, const unsigned int nx) {
- switch (edge) {
- case 0 : return (int)indices1(x,0);
- case 1 : return (int)indices1(nx,1);
- case 2 : return (int)indices2(x,0);
- case 3 : return (int)indices1(x,1);
- }
- return 0;
- }
- //! Generate an isosurface of the image instance as a 3D object.
- /**
- \param[out] primitives The returned list of the 3D object primitives
- (template type \e tf should be at least \e unsigned \e int).
- \param isovalue The returned list of the 3D object colors.
- \param size_x Number of subdivisions along the X-axis.
- \param size_y Number of subdisivions along the Y-axis.
- \param size_z Number of subdisivions along the Z-axis.
- \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
- \par Example
- \code
- const CImg<float> img = CImg<unsigned char>("reference.jpg").resize(-100,-100,20);
- CImgList<unsigned int> faces3d;
- const CImg<float> points3d = img.get_isosurface3d(faces3d,100);
- CImg<unsigned char>().display_object3d("Isosurface3d",points3d,faces3d,colors3d);
- \endcode
- \image html ref_isosurface3d.jpg
- **/
- template<typename tf>
- CImg<floatT> get_isosurface3d(CImgList<tf>& primitives, const float isovalue,
- const int size_x=-100, const int size_y=-100, const int size_z=-100) const {
- if (_spectrum>1)
- throw CImgInstanceException(_cimg_instance
- "get_isosurface3d(): Instance is not a scalar image.",
- cimg_instance);
- primitives.assign();
- if (is_empty()) return *this;
- CImg<floatT> vertices;
- if ((size_x==-100 && size_y==-100 && size_z==-100) || (size_x==width() && size_y==height() && size_z==depth())) {
- const _functor3d_int func(*this);
- vertices = isosurface3d(primitives,func,isovalue,0,0,0,width() - 1.f,height() - 1.f,depth() - 1.f,
- width(),height(),depth());
- } else {
- const _functor3d_float func(*this);
- vertices = isosurface3d(primitives,func,isovalue,0,0,0,width() - 1.f,height() - 1.f,depth() - 1.f,
- size_x,size_y,size_z);
- }
- return vertices;
- }
- //! Compute isosurface of a function, as a 3D object.
- /**
- \param[out] primitives Primitives data of the resulting 3D object.
- \param func Implicit function. Is of type <tt>float (*func)(const float x, const float y, const float z)</tt>.
- \param isovalue Isovalue to extract.
- \param x0 X-coordinate of the starting point.
- \param y0 Y-coordinate of the starting point.
- \param z0 Z-coordinate of the starting point.
- \param x1 X-coordinate of the ending point.
- \param y1 Y-coordinate of the ending point.
- \param z1 Z-coordinate of the ending point.
- \param size_x Resolution of the elevation function along the X-axis.
- \param size_y Resolution of the elevation function along the Y-axis.
- \param size_z Resolution of the elevation function along the Z-axis.
- \note Use the marching cubes algorithm for extracting the isosurface.
- **/
- template<typename tf, typename tfunc>
- static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue,
- const float x0, const float y0, const float z0,
- const float x1, const float y1, const float z1,
- const int size_x=32, const int size_y=32, const int size_z=32) {
- CImgList<floatT> vertices;
- primitives.assign();
- typename CImg<floatT>::_functor_isosurface3d add_vertex(vertices);
- typename CImg<tf>::_functor_isosurface3d add_triangle(primitives);
- isosurface3d(add_vertex,add_triangle,func,isovalue,x0,y0,z0,x1,y1,z1,size_x,size_y,size_z);
- return vertices>'x';
- }
- //! Compute isosurface of a function, as a 3D object.
- /**
- \param[out] add_vertex : Functor with operator()(x,y,z) defined for adding new vertex.
- \param[out] add_triangle : Functor with operator()(i,j) defined for adding new segment.
- \param func Implicit function. Is of type <tt>float (*func)(const float x, const float y, const float z)</tt>.
- \param isovalue Isovalue to extract.
- \param x0 X-coordinate of the starting point.
- \param y0 Y-coordinate of the starting point.
- \param z0 Z-coordinate of the starting point.
- \param x1 X-coordinate of the ending point.
- \param y1 Y-coordinate of the ending point.
- \param z1 Z-coordinate of the ending point.
- \param size_x Resolution of the elevation function along the X-axis.
- \param size_y Resolution of the elevation function along the Y-axis.
- \param size_z Resolution of the elevation function along the Z-axis.
- \note Use the marching cubes algorithm for extracting the isosurface.
- **/
- template<typename tv, typename tf, typename tfunc>
- static void isosurface3d(tv& add_vertex, tf& add_triangle, const tfunc& func, const float isovalue,
- const float x0, const float y0, const float z0,
- const float x1, const float y1, const float z1,
- const int size_x, const int size_y, const int size_z) {
- static const unsigned int edges[256] = {
- 0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
- 0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
- 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
- 0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
- 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
- 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
- 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
- 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
- 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
- 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
- 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
- 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460,
- 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0,
- 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230,
- 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190,
- 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000
- };
- static const int triangles[256][16] = {
- { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 },
- { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1 },
- { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 },
- { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1 },
- { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 },
- { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1 },
- { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 },
- { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 },
- { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 },
- { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 },
- { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1 },
- { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1 },
- { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1 },
- { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1 },
- { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 },
- { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 },
- { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 },
- { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 },
- { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 },
- { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 },
- { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1 },
- { 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1 },
- { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1 },
- { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1 },
- { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1 },
- { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 },
- { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1 },
- { 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1 },
- { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1 },
- { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 },
- { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1 },
- { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1 },
- { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1 },
- { 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 },
- { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1 },
- { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1 },
- { 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1 },
- { 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 },
- { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1 },
- { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 },
- { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 },
- { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 },
- { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1 },
- { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1 },
- { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 },
- { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 },
- { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1 },
- { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 },
- { 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1 },
- { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1 },
- { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1 },
- { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1 },
- { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1 },
- { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 },
- { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1 },
- { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1 },
- { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1 },
- { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1 },
- { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1 },
- { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1 },
- { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1 },
- { 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1 },
- { 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 },
- { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1 },
- { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 },
- { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1 },
- { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 },
- { 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1 },
- { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1 },
- { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1 },
- { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1 },
- { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1 },
- { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 },
- { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1 },
- { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 },
- { 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 },
- { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 },
- { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1 },
- { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 },
- { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1 },
- { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1 },
- { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1 },
- { 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1 },
- { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1 },
- { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1 },
- { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 },
- { 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 },
- { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 },
- { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1 },
- { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 },
- { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1 },
- { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 },
- { 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 },
- { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 },
- { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1 },
- { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1 },
- { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 },
- { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1 },
- { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1 },
- { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1 },
- { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1 },
- { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1 },
- { 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1 },
- { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1 },
- { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 },
- { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1 },
- { 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 },
- { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1 },
- { 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 },
- { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 },
- { 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1 },
- { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 },
- { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1 },
- { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1 },
- { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1 },
- { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1 },
- { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1 },
- { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1 },
- { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1 },
- { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1 },
- { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1 },
- { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1 },
- { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1 },
- { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 },
- { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 },
- { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1 },
- { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 },
- { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1 },
- { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1 },
- { 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1 },
- { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1 },
- { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 },
- { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 },
- { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1 },
- { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1 },
- { 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1 },
- { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1 },
- { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1 },
- { 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1 },
- { 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1 },
- { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1 },
- { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1 },
- { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 },
- { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1 },
- { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1 },
- { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1 },
- { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 },
- { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 },
- { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1 },
- { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1 },
- { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1 },
- { 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1 },
- { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1 },
- { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1 },
- { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1 },
- { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 },
- { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 },
- { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1 },
- { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1 },
- { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1 },
- { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1 },
- { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1 },
- { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1 },
- { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1 },
- { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1 },
- { 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 },
- { 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1 },
- { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 },
- { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1 },
- { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1 },
- { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 },
- { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1 },
- { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 },
- { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1 },
- { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1 },
- { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 },
- { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1 },
- { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
- { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }
- };
- const unsigned int
- _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)),
- _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)),
- _nz = (unsigned int)(size_z>=0?size_z:cimg::round((z1-z0)*-size_z/100 + 1)),
- nx = _nx?_nx:1,
- ny = _ny?_ny:1,
- nz = _nz?_nz:1,
- nxm1 = nx - 1,
- nym1 = ny - 1,
- nzm1 = nz - 1;
- if (!nxm1 || !nym1 || !nzm1) return;
- const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1, dz = (z1 - z0)/nzm1;
- CImg<intT> indices1(nx,ny,1,3,-1), indices2(indices1);
- CImg<floatT> values1(nx,ny), values2(nx,ny);
- float X = 0, Y = 0, Z = 0, nX = 0, nY = 0, nZ = 0;
- int nb_vertices = 0;
- // Fill the first plane with function values
- Y = y0;
- cimg_forY(values1,y) {
- X = x0;
- cimg_forX(values1,x) { values1(x,y) = (float)func(X,Y,z0); X+=dx; }
- Y+=dy;
- }
- // Run Marching Cubes algorithm
- Z = z0; nZ = Z + dz;
- for (unsigned int zi = 0; zi<nzm1; ++zi, Z = nZ, nZ+=dz) {
- Y = y0; nY = Y + dy;
- indices2.fill(-1);
- X = x0; for (unsigned int xi = 0; xi<nx; ++xi) { values2(xi,0) = (float)func(X,Y,nZ); X += dx; }
- for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y = nY, nY+=dy) {
- X = x0; nX = X + dx;
- values2(0,nyi) = (float)func(X,nY,nZ);
- for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X = nX, nX+=dx) {
- // Determine cube configuration
- const float
- val0 = values1(xi,yi),
- val1 = values1(nxi,yi),
- val2 = values1(nxi,nyi),
- val3 = values1(xi,nyi),
- val4 = values2(xi,yi),
- val5 = values2(nxi,yi),
- val6 = values2(nxi,nyi) = (float)func(nX,nY,nZ),
- val7 = values2(xi,nyi);
- const unsigned int configuration =
- (val0<isovalue?1U:0U) | (val1<isovalue?2U:0U) | (val2<isovalue?4U:0U) | (val3<isovalue?8U:0U) |
- (val4<isovalue?16U:0U) | (val5<isovalue?32U:0U) | (val6<isovalue?64U:0U) | (val7<isovalue?128U:0U),
- edge = edges[configuration];
- // Compute intersection vertices
- if (edge) {
- if ((edge&1) && indices1(xi,yi,0)<0) {
- const float Xi = X + (isovalue-val0)*dx/(val1-val0);
- indices1(xi,yi,0) = nb_vertices++;
- add_vertex(Xi,Y,Z);
- }
- if ((edge&2) && indices1(nxi,yi,1)<0) {
- const float Yi = Y + (isovalue-val1)*dy/(val2-val1);
- indices1(nxi,yi,1) = nb_vertices++;
- add_vertex(nX,Yi,Z);
- }
- if ((edge&4) && indices1(xi,nyi,0)<0) {
- const float Xi = X + (isovalue-val3)*dx/(val2-val3);
- indices1(xi,nyi,0) = nb_vertices++;
- add_vertex(Xi,nY,Z);
- }
- if ((edge&8) && indices1(xi,yi,1)<0) {
- const float Yi = Y + (isovalue-val0)*dy/(val3-val0);
- indices1(xi,yi,1) = nb_vertices++;
- add_vertex(X,Yi,Z);
- }
- if ((edge&16) && indices2(xi,yi,0)<0) {
- const float Xi = X + (isovalue-val4)*dx/(val5-val4);
- indices2(xi,yi,0) = nb_vertices++;
- add_vertex(Xi,Y,nZ);
- }
- if ((edge&32) && indices2(nxi,yi,1)<0) {
- const float Yi = Y + (isovalue-val5)*dy/(val6-val5);
- indices2(nxi,yi,1) = nb_vertices++;
- add_vertex(nX,Yi,nZ);
- }
- if ((edge&64) && indices2(xi,nyi,0)<0) {
- const float Xi = X + (isovalue-val7)*dx/(val6-val7);
- indices2(xi,nyi,0) = nb_vertices++;
- add_vertex(Xi,nY,nZ);
- }
- if ((edge&128) && indices2(xi,yi,1)<0) {
- const float Yi = Y + (isovalue-val4)*dy/(val7-val4);
- indices2(xi,yi,1) = nb_vertices++;
- add_vertex(X,Yi,nZ);
- }
- if ((edge&256) && indices1(xi,yi,2)<0) {
- const float Zi = Z+ (isovalue-val0)*dz/(val4-val0);
- indices1(xi,yi,2) = nb_vertices++;
- add_vertex(X,Y,Zi);
- }
- if ((edge&512) && indices1(nxi,yi,2)<0) {
- const float Zi = Z + (isovalue-val1)*dz/(val5-val1);
- indices1(nxi,yi,2) = nb_vertices++;
- add_vertex(nX,Y,Zi);
- }
- if ((edge&1024) && indices1(nxi,nyi,2)<0) {
- const float Zi = Z + (isovalue-val2)*dz/(val6-val2);
- indices1(nxi,nyi,2) = nb_vertices++;
- add_vertex(nX,nY,Zi);
- }
- if ((edge&2048) && indices1(xi,nyi,2)<0) {
- const float Zi = Z + (isovalue-val3)*dz/(val7-val3);
- indices1(xi,nyi,2) = nb_vertices++;
- add_vertex(X,nY,Zi);
- }
- // Create triangles
- for (const int *triangle = triangles[configuration]; *triangle!=-1; ) {
- const unsigned int
- p0 = (unsigned int)*(triangle++),
- p1 = (unsigned int)*(triangle++),
- p2 = (unsigned int)*(triangle++);
- const int
- i0 = _isosurface3d_index(p0,indices1,indices2,xi,yi,nxi,nyi),
- i1 = _isosurface3d_index(p1,indices1,indices2,xi,yi,nxi,nyi),
- i2 = _isosurface3d_index(p2,indices1,indices2,xi,yi,nxi,nyi);
- add_triangle(i0,i2,i1);
- }
- }
- }
- }
- cimg::swap(values1,values2);
- cimg::swap(indices1,indices2);
- }
- }
- //! Compute isosurface of a function, as a 3D object \overloading.
- template<typename tf>
- static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const char *const expression, const float isovalue,
- const float x0, const float y0, const float z0,
- const float x1, const float y1, const float z1,
- const int dx=32, const int dy=32, const int dz=32) {
- const _functor3d_expr func(expression);
- return isosurface3d(primitives,func,isovalue,x0,y0,z0,x1,y1,z1,dx,dy,dz);
- }
- template<typename t>
- static int _isosurface3d_index(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2,
- const unsigned int x, const unsigned int y,
- const unsigned int nx, const unsigned int ny) {
- switch (edge) {
- case 0 : return indices1(x,y,0);
- case 1 : return indices1(nx,y,1);
- case 2 : return indices1(x,ny,0);
- case 3 : return indices1(x,y,1);
- case 4 : return indices2(x,y,0);
- case 5 : return indices2(nx,y,1);
- case 6 : return indices2(x,ny,0);
- case 7 : return indices2(x,y,1);
- case 8 : return indices1(x,y,2);
- case 9 : return indices1(nx,y,2);
- case 10 : return indices1(nx,ny,2);
- case 11 : return indices1(x,ny,2);
- }
- return 0;
- }
- // Define functors for accessing image values (used in previous functions).
- struct _functor2d_int {
- const CImg<T>& ref;
- _functor2d_int(const CImg<T>& pref):ref(pref) {}
- float operator()(const float x, const float y) const {
- return (float)ref((int)x,(int)y);
- }
- };
- struct _functor2d_float {
- const CImg<T>& ref;
- _functor2d_float(const CImg<T>& pref):ref(pref) {}
- float operator()(const float x, const float y) const {
- return (float)ref._linear_atXY(x,y);
- }
- };
- struct _functor2d_expr {
- _cimg_math_parser *mp;
- ~_functor2d_expr() { mp->end(); delete mp; }
- _functor2d_expr(const char *const expr):mp(0) {
- mp = new _cimg_math_parser(expr,0,CImg<T>::const_empty(),0);
- }
- float operator()(const float x, const float y) const {
- return (float)(*mp)(x,y,0,0);
- }
- };
- struct _functor3d_int {
- const CImg<T>& ref;
- _functor3d_int(const CImg<T>& pref):ref(pref) {}
- float operator()(const float x, const float y, const float z) const {
- return (float)ref((int)x,(int)y,(int)z);
- }
- };
- struct _functor3d_float {
- const CImg<T>& ref;
- _functor3d_float(const CImg<T>& pref):ref(pref) {}
- float operator()(const float x, const float y, const float z) const {
- return (float)ref._linear_atXYZ(x,y,z);
- }
- };
- struct _functor3d_expr {
- _cimg_math_parser *mp;
- ~_functor3d_expr() { mp->end(); delete mp; }
- _functor3d_expr(const char *const expr):mp(0) {
- mp = new _cimg_math_parser(expr,0,CImg<T>::const_empty(),0);
- }
- float operator()(const float x, const float y, const float z) const {
- return (float)(*mp)(x,y,z,0);
- }
- };
- struct _functor4d_int {
- const CImg<T>& ref;
- _functor4d_int(const CImg<T>& pref):ref(pref) {}
- float operator()(const float x, const float y, const float z, const unsigned int c) const {
- return (float)ref((int)x,(int)y,(int)z,c);
- }
- };
- struct _functor_isoline3d {
- CImgList<T>& list;
- _functor_isoline3d(CImgList<T>& _list):list(_list) {}
- template<typename t>
- void operator()(const t x, const t y, const t z) { CImg<T>::vector((T)x,(T)y,(T)z).move_to(list); }
- template<typename t>
- void operator()(const t i, const t j) { CImg<T>::vector((T)i,(T)j).move_to(list); }
- };
- struct _functor_isosurface3d {
- CImgList<T>& list;
- _functor_isosurface3d(CImgList<T>& _list):list(_list) {}
- template<typename t>
- void operator()(const t x, const t y, const t z) { CImg<T>::vector((T)x,(T)y,(T)z).move_to(list); }
- };
- //! Compute 3D elevation of a function as a 3D object.
- /**
- \param[out] primitives Primitives data of the resulting 3D object.
- \param func Elevation function. Is of type <tt>float (*func)(const float x,const float y)</tt>.
- \param x0 X-coordinate of the starting point.
- \param y0 Y-coordinate of the starting point.
- \param x1 X-coordinate of the ending point.
- \param y1 Y-coordinate of the ending point.
- \param size_x Resolution of the function along the X-axis.
- \param size_y Resolution of the function along the Y-axis.
- **/
- template<typename tf, typename tfunc>
- static CImg<floatT> elevation3d(CImgList<tf>& primitives, const tfunc& func,
- const float x0, const float y0, const float x1, const float y1,
- const int size_x=256, const int size_y=256) {
- const float
- nx0 = x0<x1?x0:x1, ny0 = y0<y1?y0:y1,
- nx1 = x0<x1?x1:x0, ny1 = y0<y1?y1:y0;
- const unsigned int
- _nsize_x = (unsigned int)(size_x>=0?size_x:(nx1-nx0)*-size_x/100),
- nsize_x = _nsize_x?_nsize_x:1, nsize_x1 = nsize_x - 1,
- _nsize_y = (unsigned int)(size_y>=0?size_y:(ny1-ny0)*-size_y/100),
- nsize_y = _nsize_y?_nsize_y:1, nsize_y1 = nsize_y - 1;
- if (nsize_x<2 || nsize_y<2)
- throw CImgArgumentException("CImg<%s>::elevation3d(): Invalid specified size (%d,%d).",
- pixel_type(),
- nsize_x,nsize_y);
- CImg<floatT> vertices(nsize_x*nsize_y,3);
- floatT *ptr_x = vertices.data(0,0), *ptr_y = vertices.data(0,1), *ptr_z = vertices.data(0,2);
- for (unsigned int y = 0; y<nsize_y; ++y) {
- const float Y = ny0 + y*(ny1-ny0)/nsize_y1;
- for (unsigned int x = 0; x<nsize_x; ++x) {
- const float X = nx0 + x*(nx1-nx0)/nsize_x1;
- *(ptr_x++) = (float)x;
- *(ptr_y++) = (float)y;
- *(ptr_z++) = (float)func(X,Y);
- }
- }
- primitives.assign(nsize_x1*nsize_y1,1,4);
- for (unsigned int p = 0, y = 0; y<nsize_y1; ++y) {
- const unsigned int yw = y*nsize_x;
- for (unsigned int x = 0; x<nsize_x1; ++x) {
- const unsigned int xpyw = x + yw, xpyww = xpyw + nsize_x;
- primitives[p++].fill(xpyw,xpyww,xpyww + 1,xpyw + 1);
- }
- }
- return vertices;
- }
- //! Compute 3D elevation of a function, as a 3D object \overloading.
- template<typename tf>
- static CImg<floatT> elevation3d(CImgList<tf>& primitives, const char *const expression,
- const float x0, const float y0, const float x1, const float y1,
- const int size_x=256, const int size_y=256) {
- const _functor2d_expr func(expression);
- return elevation3d(primitives,func,x0,y0,x1,y1,size_x,size_y);
- }
- //! Generate a 3D box object.
- /**
- \param[out] primitives The returned list of the 3D object primitives
- (template type \e tf should be at least \e unsigned \e int).
- \param size_x The width of the box (dimension along the X-axis).
- \param size_y The height of the box (dimension along the Y-axis).
- \param size_z The depth of the box (dimension along the Z-axis).
- \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
- \par Example
- \code
- CImgList<unsigned int> faces3d;
- const CImg<float> points3d = CImg<float>::box3d(faces3d,10,20,30);
- CImg<unsigned char>().display_object3d("Box3d",points3d,faces3d);
- \endcode
- \image html ref_box3d.jpg
- **/
- template<typename tf>
- static CImg<floatT> box3d(CImgList<tf>& primitives,
- const float size_x=200, const float size_y=100, const float size_z=100) {
- primitives.assign(6,1,4,1,1, 0,3,2,1, 4,5,6,7, 0,1,5,4, 3,7,6,2, 0,4,7,3, 1,2,6,5);
- return CImg<floatT>(8,3,1,1,
- 0.,size_x,size_x, 0., 0.,size_x,size_x, 0.,
- 0., 0.,size_y,size_y, 0., 0.,size_y,size_y,
- 0., 0., 0., 0.,size_z,size_z,size_z,size_z);
- }
- //! Generate a 3D cone.
- /**
- \param[out] primitives The returned list of the 3D object primitives
- (template type \e tf should be at least \e unsigned \e int).
- \param radius The radius of the cone basis.
- \param size_z The cone's height.
- \param subdivisions The number of basis angular subdivisions.
- \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
- \par Example
- \code
- CImgList<unsigned int> faces3d;
- const CImg<float> points3d = CImg<float>::cone3d(faces3d,50);
- CImg<unsigned char>().display_object3d("Cone3d",points3d,faces3d);
- \endcode
- \image html ref_cone3d.jpg
- **/
- template<typename tf>
- static CImg<floatT> cone3d(CImgList<tf>& primitives,
- const float radius=50, const float size_z=100, const unsigned int subdivisions=24) {
- primitives.assign();
- if (!subdivisions) return CImg<floatT>();
- CImgList<floatT> vertices(2,1,3,1,1,
- 0.,0.,size_z,
- 0.,0.,0.);
- for (float delta = 360.f/subdivisions, angle = 0; angle<360; angle+=delta) {
- const float a = (float)(angle*cimg::PI/180);
- CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0).move_to(vertices);
- }
- const unsigned int nbr = vertices._width - 2;
- for (unsigned int p = 0; p<nbr; ++p) {
- const unsigned int curr = 2 + p, next = 2 + ((p + 1)%nbr);
- CImg<tf>::vector(1,next,curr).move_to(primitives);
- CImg<tf>::vector(0,curr,next).move_to(primitives);
- }
- return vertices>'x';
- }
- //! Generate a 3D cylinder.
- /**
- \param[out] primitives The returned list of the 3D object primitives
- (template type \e tf should be at least \e unsigned \e int).
- \param radius The radius of the cylinder basis.
- \param size_z The cylinder's height.
- \param subdivisions The number of basis angular subdivisions.
- \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
- \par Example
- \code
- CImgList<unsigned int> faces3d;
- const CImg<float> points3d = CImg<float>::cylinder3d(faces3d,50);
- CImg<unsigned char>().display_object3d("Cylinder3d",points3d,faces3d);
- \endcode
- \image html ref_cylinder3d.jpg
- **/
- template<typename tf>
- static CImg<floatT> cylinder3d(CImgList<tf>& primitives,
- const float radius=50, const float size_z=100, const unsigned int subdivisions=24) {
- primitives.assign();
- if (!subdivisions) return CImg<floatT>();
- CImgList<floatT> vertices(2,1,3,1,1,
- 0.,0.,0.,
- 0.,0.,size_z);
- for (float delta = 360.f/subdivisions, angle = 0; angle<360; angle+=delta) {
- const float a = (float)(angle*cimg::PI/180);
- CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0.f).move_to(vertices);
- CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),size_z).move_to(vertices);
- }
- const unsigned int nbr = (vertices._width - 2)/2;
- for (unsigned int p = 0; p<nbr; ++p) {
- const unsigned int curr = 2 + 2*p, next = 2 + (2*((p + 1)%nbr));
- CImg<tf>::vector(0,next,curr).move_to(primitives);
- CImg<tf>::vector(1,curr + 1,next + 1).move_to(primitives);
- CImg<tf>::vector(curr,next,next + 1,curr + 1).move_to(primitives);
- }
- return vertices>'x';
- }
- //! Generate a 3D torus.
- /**
- \param[out] primitives The returned list of the 3D object primitives
- (template type \e tf should be at least \e unsigned \e int).
- \param radius1 The large radius.
- \param radius2 The small radius.
- \param subdivisions1 The number of angular subdivisions for the large radius.
- \param subdivisions2 The number of angular subdivisions for the small radius.
- \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
- \par Example
- \code
- CImgList<unsigned int> faces3d;
- const CImg<float> points3d = CImg<float>::torus3d(faces3d,20,4);
- CImg<unsigned char>().display_object3d("Torus3d",points3d,faces3d);
- \endcode
- \image html ref_torus3d.jpg
- **/
- template<typename tf>
- static CImg<floatT> torus3d(CImgList<tf>& primitives,
- const float radius1=100, const float radius2=30,
- const unsigned int subdivisions1=24, const unsigned int subdivisions2=12) {
- primitives.assign();
- if (!subdivisions1 || !subdivisions2) return CImg<floatT>();
- CImgList<floatT> vertices;
- for (unsigned int v = 0; v<subdivisions1; ++v) {
- const float
- beta = (float)(v*2*cimg::PI/subdivisions1),
- xc = radius1*(float)std::cos(beta),
- yc = radius1*(float)std::sin(beta);
- for (unsigned int u = 0; u<subdivisions2; ++u) {
- const float
- alpha = (float)(u*2*cimg::PI/subdivisions2),
- x = xc + radius2*(float)(std::cos(alpha)*std::cos(beta)),
- y = yc + radius2*(float)(std::cos(alpha)*std::sin(beta)),
- z = radius2*(float)std::sin(alpha);
- CImg<floatT>::vector(x,y,z).move_to(vertices);
- }
- }
- for (unsigned int vv = 0; vv<subdivisions1; ++vv) {
- const unsigned int nv = (vv + 1)%subdivisions1;
- for (unsigned int uu = 0; uu<subdivisions2; ++uu) {
- const unsigned int nu = (uu + 1)%subdivisions2, svv = subdivisions2*vv, snv = subdivisions2*nv;
- CImg<tf>::vector(svv + nu,svv + uu,snv + uu,snv + nu).move_to(primitives);
- }
- }
- return vertices>'x';
- }
- //! Generate a 3D XY-plane.
- /**
- \param[out] primitives The returned list of the 3D object primitives
- (template type \e tf should be at least \e unsigned \e int).
- \param size_x The width of the plane (dimension along the X-axis).
- \param size_y The height of the plane (dimensions along the Y-axis).
- \param subdivisions_x The number of planar subdivisions along the X-axis.
- \param subdivisions_y The number of planar subdivisions along the Y-axis.
- \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
- \par Example
- \code
- CImgList<unsigned int> faces3d;
- const CImg<float> points3d = CImg<float>::plane3d(faces3d,100,50);
- CImg<unsigned char>().display_object3d("Plane3d",points3d,faces3d);
- \endcode
- \image html ref_plane3d.jpg
- **/
- template<typename tf>
- static CImg<floatT> plane3d(CImgList<tf>& primitives,
- const float size_x=100, const float size_y=100,
- const unsigned int subdivisions_x=10, const unsigned int subdivisions_y=10) {
- primitives.assign();
- if (!subdivisions_x || !subdivisions_y) return CImg<floatT>();
- CImgList<floatT> vertices;
- const unsigned int w = subdivisions_x + 1, h = subdivisions_y + 1;
- const float fx = (float)size_x/w, fy = (float)size_y/h;
- for (unsigned int y = 0; y<h; ++y) for (unsigned int x = 0; x<w; ++x)
- CImg<floatT>::vector(fx*x,fy*y,0).move_to(vertices);
- for (unsigned int y = 0; y<subdivisions_y; ++y) for (unsigned int x = 0; x<subdivisions_x; ++x) {
- const int off1 = x + y*w, off2 = x + 1 + y*w, off3 = x + 1 + (y + 1)*w, off4 = x + (y + 1)*w;
- CImg<tf>::vector(off1,off4,off3,off2).move_to(primitives);
- }
- return vertices>'x';
- }
- //! Generate a 3D sphere.
- /**
- \param[out] primitives The returned list of the 3D object primitives
- (template type \e tf should be at least \e unsigned \e int).
- \param radius The radius of the sphere (dimension along the X-axis).
- \param subdivisions The number of recursive subdivisions from an initial icosahedron.
- \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
- \par Example
- \code
- CImgList<unsigned int> faces3d;
- const CImg<float> points3d = CImg<float>::sphere3d(faces3d,100,4);
- CImg<unsigned char>().display_object3d("Sphere3d",points3d,faces3d);
- \endcode
- \image html ref_sphere3d.jpg
- **/
- template<typename tf>
- static CImg<floatT> sphere3d(CImgList<tf>& primitives,
- const float radius=50, const unsigned int subdivisions=3) {
- // Create initial icosahedron
- primitives.assign();
- const double tmp = (1 + std::sqrt(5.f))/2, a = 1./std::sqrt(1 + tmp*tmp), b = tmp*a;
- CImgList<floatT> vertices(12,1,3,1,1, b,a,0., -b,a,0., -b,-a,0., b,-a,0., a,0.,b, a,0.,-b,
- -a,0.,-b, -a,0.,b, 0.,b,a, 0.,-b,a, 0.,-b,-a, 0.,b,-a);
- primitives.assign(20,1,3,1,1, 4,8,7, 4,7,9, 5,6,11, 5,10,6, 0,4,3, 0,3,5, 2,7,1, 2,1,6,
- 8,0,11, 8,11,1, 9,10,3, 9,2,10, 8,4,0, 11,0,5, 4,9,3,
- 5,3,10, 7,8,1, 6,1,11, 7,2,9, 6,10,2);
- // edge - length/2
- float he = (float)a;
- // Recurse subdivisions
- for (unsigned int i = 0; i<subdivisions; ++i) {
- const unsigned int L = primitives._width;
- he/=2;
- const float he2 = he*he;
- for (unsigned int l = 0; l<L; ++l) {
- const unsigned int
- p0 = (unsigned int)primitives(0,0), p1 = (unsigned int)primitives(0,1), p2 = (unsigned int)primitives(0,2);
- const float
- x0 = vertices(p0,0), y0 = vertices(p0,1), z0 = vertices(p0,2),
- x1 = vertices(p1,0), y1 = vertices(p1,1), z1 = vertices(p1,2),
- x2 = vertices(p2,0), y2 = vertices(p2,1), z2 = vertices(p2,2),
- tnx0 = (x0 + x1)/2, tny0 = (y0 + y1)/2, tnz0 = (z0 + z1)/2,
- nn0 = cimg::hypot(tnx0,tny0,tnz0),
- tnx1 = (x0 + x2)/2, tny1 = (y0 + y2)/2, tnz1 = (z0 + z2)/2,
- nn1 = cimg::hypot(tnx1,tny1,tnz1),
- tnx2 = (x1 + x2)/2, tny2 = (y1 + y2)/2, tnz2 = (z1 + z2)/2,
- nn2 = cimg::hypot(tnx2,tny2,tnz2),
- nx0 = tnx0/nn0, ny0 = tny0/nn0, nz0 = tnz0/nn0,
- nx1 = tnx1/nn1, ny1 = tny1/nn1, nz1 = tnz1/nn1,
- nx2 = tnx2/nn2, ny2 = tny2/nn2, nz2 = tnz2/nn2;
- int i0 = -1, i1 = -1, i2 = -1;
- cimglist_for(vertices,p) {
- const float x = (float)vertices(p,0), y = (float)vertices(p,1), z = (float)vertices(p,2);
- if (cimg::sqr(x-nx0) + cimg::sqr(y-ny0) + cimg::sqr(z-nz0)<he2) i0 = p;
- if (cimg::sqr(x-nx1) + cimg::sqr(y-ny1) + cimg::sqr(z-nz1)<he2) i1 = p;
- if (cimg::sqr(x-nx2) + cimg::sqr(y-ny2) + cimg::sqr(z-nz2)<he2) i2 = p;
- }
- if (i0<0) { CImg<floatT>::vector(nx0,ny0,nz0).move_to(vertices); i0 = vertices.width() - 1; }
- if (i1<0) { CImg<floatT>::vector(nx1,ny1,nz1).move_to(vertices); i1 = vertices.width() - 1; }
- if (i2<0) { CImg<floatT>::vector(nx2,ny2,nz2).move_to(vertices); i2 = vertices.width() - 1; }
- primitives.remove(0);
- CImg<tf>::vector(p0,i0,i1).move_to(primitives);
- CImg<tf>::vector((tf)i0,(tf)p1,(tf)i2).move_to(primitives);
- CImg<tf>::vector((tf)i1,(tf)i2,(tf)p2).move_to(primitives);
- CImg<tf>::vector((tf)i1,(tf)i0,(tf)i2).move_to(primitives);
- }
- }
- return (vertices>'x')*=radius;
- }
- //! Generate a 3D ellipsoid.
- /**
- \param[out] primitives The returned list of the 3D object primitives
- (template type \e tf should be at least \e unsigned \e int).
- \param tensor The tensor which gives the shape and size of the ellipsoid.
- \param subdivisions The number of recursive subdivisions from an initial stretched icosahedron.
- \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
- \par Example
- \code
- CImgList<unsigned int> faces3d;
- const CImg<float> tensor = CImg<float>::diagonal(10,7,3),
- points3d = CImg<float>::ellipsoid3d(faces3d,tensor,4);
- CImg<unsigned char>().display_object3d("Ellipsoid3d",points3d,faces3d);
- \endcode
- \image html ref_ellipsoid3d.jpg
- **/
- template<typename tf, typename t>
- static CImg<floatT> ellipsoid3d(CImgList<tf>& primitives,
- const CImg<t>& tensor, const unsigned int subdivisions=3) {
- primitives.assign();
- if (!subdivisions) return CImg<floatT>();
- CImg<floatT> S, V;
- tensor.symmetric_eigen(S,V);
- const float orient =
- (V(0,1)*V(1,2) - V(0,2)*V(1,1))*V(2,0) +
- (V(0,2)*V(1,0) - V(0,0)*V(1,2))*V(2,1) +
- (V(0,0)*V(1,1) - V(0,1)*V(1,0))*V(2,2);
- if (orient<0) { V(2,0) = -V(2,0); V(2,1) = -V(2,1); V(2,2) = -V(2,2); }
- const float l0 = S[0], l1 = S[1], l2 = S[2];
- CImg<floatT> vertices = sphere3d(primitives,1.,subdivisions);
- vertices.get_shared_row(0)*=l0;
- vertices.get_shared_row(1)*=l1;
- vertices.get_shared_row(2)*=l2;
- return V*vertices;
- }
- //! Convert 3D object into a CImg3d representation.
- /**
- \param primitives Primitives data of the 3D object.
- \param colors Colors data of the 3D object.
- \param opacities Opacities data of the 3D object.
- \param full_check Tells if full checking of the 3D object must be performed.
- **/
- template<typename tp, typename tc, typename to>
- CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
- const CImgList<tc>& colors,
- const to& opacities,
- const bool full_check=true) {
- return get_object3dtoCImg3d(primitives,colors,opacities,full_check).move_to(*this);
- }
- //! Convert 3D object into a CImg3d representation \overloading.
- template<typename tp, typename tc>
- CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
- const CImgList<tc>& colors,
- const bool full_check=true) {
- return get_object3dtoCImg3d(primitives,colors,full_check).move_to(*this);
- }
- //! Convert 3D object into a CImg3d representation \overloading.
- template<typename tp>
- CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
- const bool full_check=true) {
- return get_object3dtoCImg3d(primitives,full_check).move_to(*this);
- }
- //! Convert 3D object into a CImg3d representation \overloading.
- CImg<T>& object3dtoCImg3d(const bool full_check=true) {
- return get_object3dtoCImg3d(full_check).move_to(*this);
- }
- //! Convert 3D object into a CImg3d representation \newinstance.
- template<typename tp, typename tc, typename to>
- CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
- const CImgList<tc>& colors,
- const to& opacities,
- const bool full_check=true) const {
- CImg<charT> error_message(1024);
- if (!is_object3d(primitives,colors,opacities,full_check,error_message))
- throw CImgInstanceException(_cimg_instance
- "object3dtoCImg3d(): Invalid specified 3D object (%u,%u) (%s).",
- cimg_instance,_width,primitives._width,error_message.data());
- CImg<floatT> res(1,_size_object3dtoCImg3d(primitives,colors,opacities));
- float *ptrd = res._data;
- // Put magick number.
- *(ptrd++) = 'C' + 0.5f; *(ptrd++) = 'I' + 0.5f; *(ptrd++) = 'm' + 0.5f;
- *(ptrd++) = 'g' + 0.5f; *(ptrd++) = '3' + 0.5f; *(ptrd++) = 'd' + 0.5f;
- // Put number of vertices and primitives.
- *(ptrd++) = cimg::uint2float(_width);
- *(ptrd++) = cimg::uint2float(primitives._width);
- // Put vertex data.
- if (is_empty() || !primitives) return res;
- const T *ptrx = data(0,0), *ptry = data(0,1), *ptrz = data(0,2);
- cimg_forX(*this,p) {
- *(ptrd++) = (float)*(ptrx++);
- *(ptrd++) = (float)*(ptry++);
- *(ptrd++) = (float)*(ptrz++);
- }
- // Put primitive data.
- cimglist_for(primitives,p) {
- *(ptrd++) = (float)primitives[p].size();
- const tp *ptrp = primitives[p]._data;
- cimg_foroff(primitives[p],i) *(ptrd++) = cimg::uint2float((unsigned int)*(ptrp++));
- }
- // Put color/texture data.
- const unsigned int csiz = std::min(colors._width,primitives._width);
- for (int c = 0; c<(int)csiz; ++c) {
- const CImg<tc>& color = colors[c];
- const tc *ptrc = color._data;
- if (color.size()==3) { *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*ptrc; }
- else {
- *(ptrd++) = -128.f;
- int shared_ind = -1;
- if (color.is_shared()) for (int i = 0; i<c; ++i) if (ptrc==colors[i]._data) { shared_ind = i; break; }
- if (shared_ind<0) {
- *(ptrd++) = (float)color._width;
- *(ptrd++) = (float)color._height;
- *(ptrd++) = (float)color._spectrum;
- cimg_foroff(color,l) *(ptrd++) = (float)*(ptrc++);
- } else {
- *(ptrd++) = (float)cimg::uint2float((unsigned int)shared_ind);
- *(ptrd++) = 0;
- *(ptrd++) = 0;
- }
- }
- }
- const int csiz2 = primitives.width() - colors.width();
- for (int c = 0; c<csiz2; ++c) { *(ptrd++) = 200.f; *(ptrd++) = 200.f; *(ptrd++) = 200.f; }
- // Put opacity data.
- ptrd = _object3dtoCImg3d(opacities,ptrd);
- const float *ptre = res.end();
- while (ptrd<ptre) *(ptrd++) = 1.f;
- return res;
- }
- template<typename to>
- float* _object3dtoCImg3d(const CImgList<to>& opacities, float *ptrd) const {
- cimglist_for(opacities,o) {
- const CImg<to>& opacity = opacities[o];
- const to *ptro = opacity._data;
- if (opacity.size()==1) *(ptrd++) = (float)*ptro;
- else {
- *(ptrd++) = -128.f;
- int shared_ind = -1;
- if (opacity.is_shared()) for (int i = 0; i<o; ++i) if (ptro==opacities[i]._data) { shared_ind = i; break; }
- if (shared_ind<0) {
- *(ptrd++) = (float)opacity._width;
- *(ptrd++) = (float)opacity._height;
- *(ptrd++) = (float)opacity._spectrum;
- cimg_foroff(opacity,l) *(ptrd++) = (float)*(ptro++);
- } else {
- *(ptrd++) = (float)cimg::uint2float((unsigned int)shared_ind);
- *(ptrd++) = 0;
- *(ptrd++) = 0;
- }
- }
- }
- return ptrd;
- }
- template<typename to>
- float* _object3dtoCImg3d(const CImg<to>& opacities, float *ptrd) const {
- const to *ptro = opacities._data;
- cimg_foroff(opacities,o) *(ptrd++) = (float)*(ptro++);
- return ptrd;
- }
- template<typename tp, typename tc, typename to>
- unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives,
- const CImgList<tc>& colors,
- const CImgList<to>& opacities) const {
- unsigned int siz = 8U + 3*_width;
- cimglist_for(primitives,p) siz+=primitives[p].size() + 1;
- for (int c = std::min(primitives.width(),colors.width()) - 1; c>=0; --c) {
- if (colors[c].is_shared()) siz+=4;
- else { const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4 + csiz:3; }
- }
- if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width);
- cimglist_for(opacities,o) {
- if (opacities[o].is_shared()) siz+=4;
- else { const unsigned int osiz = opacities[o].size(); siz+=(osiz!=1)?4 + osiz:1; }
- }
- siz+=primitives._width - opacities._width;
- return siz;
- }
- template<typename tp, typename tc, typename to>
- unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives,
- const CImgList<tc>& colors,
- const CImg<to>& opacities) const {
- unsigned int siz = 8U + 3*_width;
- cimglist_for(primitives,p) siz+=primitives[p].size() + 1;
- for (int c = std::min(primitives.width(),colors.width()) - 1; c>=0; --c) {
- const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4 + csiz:3;
- }
- if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width);
- siz+=primitives.size();
- cimg::unused(opacities);
- return siz;
- }
- //! Convert 3D object into a CImg3d representation \overloading.
- template<typename tp, typename tc>
- CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
- const CImgList<tc>& colors,
- const bool full_check=true) const {
- CImgList<T> opacities;
- return get_object3dtoCImg3d(primitives,colors,opacities,full_check);
- }
- //! Convert 3D object into a CImg3d representation \overloading.
- template<typename tp>
- CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
- const bool full_check=true) const {
- CImgList<T> colors, opacities;
- return get_object3dtoCImg3d(primitives,colors,opacities,full_check);
- }
- //! Convert 3D object into a CImg3d representation \overloading.
- CImg<floatT> get_object3dtoCImg3d(const bool full_check=true) const {
- CImgList<T> opacities, colors;
- CImgList<uintT> primitives(width(),1,1,1,1);
- cimglist_for(primitives,p) primitives(p,0) = p;
- return get_object3dtoCImg3d(primitives,colors,opacities,full_check);
- }
- //! Convert CImg3d representation into a 3D object.
- /**
- \param[out] primitives Primitives data of the 3D object.
- \param[out] colors Colors data of the 3D object.
- \param[out] opacities Opacities data of the 3D object.
- \param full_check Tells if full checking of the 3D object must be performed.
- **/
- template<typename tp, typename tc, typename to>
- CImg<T>& CImg3dtoobject3d(CImgList<tp>& primitives,
- CImgList<tc>& colors,
- CImgList<to>& opacities,
- const bool full_check=true) {
- return get_CImg3dtoobject3d(primitives,colors,opacities,full_check).move_to(*this);
- }
- //! Convert CImg3d representation into a 3D object \newinstance.
- template<typename tp, typename tc, typename to>
- CImg<T> get_CImg3dtoobject3d(CImgList<tp>& primitives,
- CImgList<tc>& colors,
- CImgList<to>& opacities,
- const bool full_check=true) const {
- CImg<charT> error_message(1024);
- if (!is_CImg3d(full_check,error_message))
- throw CImgInstanceException(_cimg_instance
- "CImg3dtoobject3d(): image instance is not a CImg3d (%s).",
- cimg_instance,error_message.data());
- const T *ptrs = _data + 6;
- const unsigned int
- nb_points = cimg::float2uint((float)*(ptrs++)),
- nb_primitives = cimg::float2uint((float)*(ptrs++));
- const CImg<T> points = CImg<T>(ptrs,3,nb_points,1,1,true).get_transpose();
- ptrs+=3*nb_points;
- primitives.assign(nb_primitives);
- cimglist_for(primitives,p) {
- const unsigned int nb_inds = (unsigned int)*(ptrs++);
- primitives[p].assign(1,nb_inds);
- tp *ptrp = primitives[p]._data;
- for (unsigned int i = 0; i<nb_inds; ++i)
- *(ptrp++) = (tp)cimg::float2uint((float)*(ptrs++));
- }
- colors.assign(nb_primitives);
- cimglist_for(colors,c) {
- if (*ptrs==(T)-128) {
- ++ptrs;
- const unsigned int
- w = (unsigned int)cimg::float2uint((float)*(ptrs++)),
- h = (unsigned int)*(ptrs++),
- s = (unsigned int)*(ptrs++);
- if (!h && !s) colors[c].assign(colors[w],true);
- else { colors[c].assign(ptrs,w,h,1,s,false); ptrs+=(ulongT)w*h*s; }
- } else { colors[c].assign(ptrs,1,1,1,3,false); ptrs+=3; }
- }
- opacities.assign(nb_primitives);
- cimglist_for(opacities,o) {
- if (*ptrs==(T)-128) {
- ++ptrs;
- const unsigned int
- w = (unsigned int)cimg::float2uint((float)*(ptrs++)),
- h = (unsigned int)*(ptrs++),
- s = (unsigned int)*(ptrs++);
- if (!h && !s) opacities[o].assign(opacities[w],true);
- else { opacities[o].assign(ptrs,w,h,1,s,false); ptrs+=(ulongT)w*h*s; }
- } else opacities[o].assign(1,1,1,1,*(ptrs++));
- }
- return points;
- }
- //@}
- //---------------------------
- //
- //! \name Drawing Functions
- //@{
- //---------------------------
- #define cimg_init_scanline(opacity) \
- static const T _sc_maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max()); \
- const float _sc_nopacity = cimg::abs((float)opacity), _sc_copacity = 1 - std::max((float)opacity,0.f); \
- const ulongT _sc_whd = (ulongT)_width*_height*_depth; \
- cimg::unused(_sc_maxval);
- #define cimg_draw_scanline(x0,x1,y,color,opacity,brightness) \
- _draw_scanline(x0,x1,y,color,opacity,brightness,_sc_nopacity,_sc_copacity,_sc_whd,_sc_maxval)
- // [internal] The following _draw_scanline() routines are *non user-friendly functions*,
- // used only for internal purpose.
- // Pre-requisites: x0<=x1, y-coordinate is valid, col is valid.
- template<typename tc>
- CImg<T>& _draw_scanline(const int x0, const int x1, const int y,
- const tc *const color, const float opacity,
- const float brightness,
- const float nopacity, const float copacity, const ulongT whd, const T _sc_maxval) {
- const int nx0 = x0>0?x0:0, nx1 = x1<width()?x1:width() - 1, dx = nx1 - nx0;
- if (dx>=0) {
- const tc *col = color;
- const ulongT off = whd - dx - 1;
- T *ptrd = data(nx0,y);
- if (opacity>=1) { // ** Opaque drawing **
- if (brightness==1) { // Brightness==1
- if (sizeof(T)!=1) cimg_forC(*this,c) {
- const T val = (T)*(col++);
- for (int x = dx; x>=0; --x) *(ptrd++) = val;
- ptrd+=off;
- } else cimg_forC(*this,c) {
- const T val = (T)*(col++);
- std::memset(ptrd,(int)val,dx + 1);
- ptrd+=whd;
- }
- } else if (brightness<1) { // Brightness<1
- if (sizeof(T)!=1) cimg_forC(*this,c) {
- const T val = (T)(*(col++)*brightness);
- for (int x = dx; x>=0; --x) *(ptrd++) = val;
- ptrd+=off;
- } else cimg_forC(*this,c) {
- const T val = (T)(*(col++)*brightness);
- std::memset(ptrd,(int)val,dx + 1);
- ptrd+=whd;
- }
- } else { // Brightness>1
- if (sizeof(T)!=1) cimg_forC(*this,c) {
- const T val = (T)((2-brightness)**(col++) + (brightness - 1)*_sc_maxval);
- for (int x = dx; x>=0; --x) *(ptrd++) = val;
- ptrd+=off;
- } else cimg_forC(*this,c) {
- const T val = (T)((2-brightness)**(col++) + (brightness - 1)*_sc_maxval);
- std::memset(ptrd,(int)val,dx + 1);
- ptrd+=whd;
- }
- }
- } else { // ** Transparent drawing **
- if (brightness==1) { // Brightness==1
- cimg_forC(*this,c) {
- const Tfloat val = *(col++)*nopacity;
- for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; }
- ptrd+=off;
- }
- } else if (brightness<=1) { // Brightness<1
- cimg_forC(*this,c) {
- const Tfloat val = *(col++)*brightness*nopacity;
- for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; }
- ptrd+=off;
- }
- } else { // Brightness>1
- cimg_forC(*this,c) {
- const Tfloat val = ((2-brightness)**(col++) + (brightness - 1)*_sc_maxval)*nopacity;
- for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; }
- ptrd+=off;
- }
- }
- }
- }
- return *this;
- }
- //! Draw a 3D point.
- /**
- \param x0 X-coordinate of the point.
- \param y0 Y-coordinate of the point.
- \param z0 Z-coordinate of the point.
- \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
- \param opacity Drawing opacity.
- \note
- - To set pixel values without clipping needs, you should use the faster CImg::operator()() function.
- \par Example:
- \code
- CImg<unsigned char> img(100,100,1,3,0);
- const unsigned char color[] = { 255,128,64 };
- img.draw_point(50,50,color);
- \endcode
- **/
- template<typename tc>
- CImg<T>& draw_point(const int x0, const int y0, const int z0,
- const tc *const color, const float opacity=1) {
- if (is_empty()) return *this;
- if (!color)
- throw CImgArgumentException(_cimg_instance
- "draw_point(): Specified color is (null).",
- cimg_instance);
- if (x0>=0 && y0>=0 && z0>=0 && x0<width() && y0<height() && z0<depth()) {
- const ulongT whd = (ulongT)_width*_height*_depth;
- const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
- T *ptrd = data(x0,y0,z0,0);
- const tc *col = color;
- if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
- else cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; }
- }
- return *this;
- }
- //! Draw a 2D point \simplification.
- template<typename tc>
- CImg<T>& draw_point(const int x0, const int y0,
- const tc *const color, const float opacity=1) {
- return draw_point(x0,y0,0,color,opacity);
- }
- // Draw a points cloud.
- /**
- \param points Image of vertices coordinates.
- \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
- \param opacity Drawing opacity.
- **/
- template<typename t, typename tc>
- CImg<T>& draw_point(const CImg<t>& points,
- const tc *const color, const float opacity=1) {
- if (is_empty() || !points) return *this;
- switch (points._height) {
- case 0 : case 1 :
- throw CImgArgumentException(_cimg_instance
- "draw_point(): Invalid specified point set (%u,%u,%u,%u,%p).",
- cimg_instance,
- points._width,points._height,points._depth,points._spectrum,points._data);
- case 2 : {
- cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),color,opacity);
- } break;
- default : {
- cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),(int)points(i,2),color,opacity);
- }
- }
- return *this;
- }
- //! Draw a 2D line.
- /**
- \param x0 X-coordinate of the starting line point.
- \param y0 Y-coordinate of the starting line point.
- \param x1 X-coordinate of the ending line point.
- \param y1 Y-coordinate of the ending line point.
- \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
- \param opacity Drawing opacity.
- \param pattern An integer whose bits describe the line pattern.
- \param init_hatch Tells if a reinitialization of the hash state must be done.
- \note
- - Line routine uses Bresenham's algorithm.
- - Set \p init_hatch = false to draw consecutive hatched segments without breaking the line pattern.
- \par Example:
- \code
- CImg<unsigned char> img(100,100,1,3,0);
- const unsigned char color[] = { 255,128,64 };
- img.draw_line(40,40,80,70,color);
- \endcode
- **/
- template<typename tc>
- CImg<T>& draw_line(int x0, int y0,
- int x1, int y1,
- const tc *const color, const float opacity=1,
- const unsigned int pattern=~0U, const bool init_hatch=true) {
- if (is_empty() || !opacity || !pattern ||
- std::min(y0,y1)>=height() || std::max(y0,y1)<0 ||
- std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
- int
- w1 = width() - 1, h1 = height() - 1,
- dx01 = x1 - x0, dy01 = y1 - y0;
- const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
- if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
- if (pattern==~0U && y0>y1) { cimg::swap(x0,x1,y0,y1); dx01*=-1; dy01*=-1; }
- static unsigned int hatch = ~0U - (~0U>>1);
- if (init_hatch) hatch = ~0U - (~0U>>1);
- cimg_init_scanline(opacity);
- const int
- step = y0<=y1?1:-1,
- hdy01 = dy01*cimg::sign(dx01)/2,
- cy0 = cimg::cut(y0,0,h1),
- cy1 = cimg::cut(y1,0,h1) + step;
- dy01+=dy01?0:1;
- for (int y = cy0; y!=cy1; y+=step) {
- const int
- yy0 = y - y0,
- x = x0 + (dx01*yy0 + hdy01)/dy01;
- if (x>=0 && x<=w1 && pattern&hatch) {
- T *const ptrd = is_horizontal?data(y,x):data(x,y);
- cimg_forC(*this,c) {
- const T val = color[c];
- ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
- }
- }
- if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
- }
- return *this;
- }
- //! Draw a 2D line, with z-buffering.
- /**
- \param zbuffer Zbuffer image.
- \param x0 X-coordinate of the starting point.
- \param y0 Y-coordinate of the starting point.
- \param z0 Z-coordinate of the starting point
- \param x1 X-coordinate of the ending point.
- \param y1 Y-coordinate of the ending point.
- \param z1 Z-coordinate of the ending point.
- \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
- \param opacity Drawing opacity.
- \param pattern An integer whose bits describe the line pattern.
- \param init_hatch Tells if a reinitialization of the hash state must be done.
- **/
- template<typename tz, typename tc>
- CImg<T>& draw_line(CImg<tz>& zbuffer,
- int x0, int y0, const float z0,
- int x1, int y1, const float z1,
- const tc *const color, const float opacity=1,
- const unsigned int pattern=~0U, const bool init_hatch=true) {
- if (is_empty() || z0<=0 || z1<=0 || !opacity || !pattern) return *this;
- if (!color)
- throw CImgArgumentException(_cimg_instance
- "draw_line(): Specified color is (null).",
- cimg_instance);
- if (!is_sameXY(zbuffer))
- throw CImgArgumentException(_cimg_instance
- "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
- "different dimensions.",
- cimg_instance,
- zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
- if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
- float iz0 = 1/z0, iz1 = 1/z1;
- int
- w1 = width() - 1, h1 = height() - 1,
- dx01 = x1 - x0, dy01 = y1 - y0;
- float diz01 = iz1 - iz0;
- const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
- if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
- if (pattern==~0U && y0>y1) {
- cimg::swap(x0,x1,y0,y1,iz0,iz1);
- dx01*=-1; dy01*=-1; diz01*=-1;
- }
- static unsigned int hatch = ~0U - (~0U>>1);
- if (init_hatch) hatch = ~0U - (~0U>>1);
- cimg_init_scanline(opacity);
- const int
- step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2,
- cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
- dy01+=dy01?0:1;
- for (int y = cy0; y!=cy1; y+=step) {
- const int
- yy0 = y - y0,
- x = x0 + (dx01*yy0 + hdy01)/dy01;
- const float iz = iz0 + diz01*yy0/dy01;
- tz *const ptrz = is_horizontal?zbuffer.data(y,x):zbuffer.data(x,y);
- if (x>=0 && x<=w1 && pattern&hatch && iz>=*ptrz) {
- *ptrz = (tz)iz;
- T *const ptrd = is_horizontal?data(y,x):data(x,y);
- cimg_forC(*this,c) {
- const T val = color[c];
- ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
- }
- }
- if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
- }
- return *this;
- }
- //! Draw a textured 2D line.
- /**
- \param x0 X-coordinate of the starting line point.
- \param y0 Y-coordinate of the starting line point.
- \param x1 X-coordinate of the ending line point.
- \param y1 Y-coordinate of the ending line point.
- \param texture Texture image defining the pixel colors.
- \param tx0 X-coordinate of the starting texture point.
- \param ty0 Y-coordinate of the starting texture point.
- \param tx1 X-coordinate of the ending texture point.
- \param ty1 Y-coordinate of the ending texture point.
- \param opacity Drawing opacity.
- \param pattern An integer whose bits describe the line pattern.
- \param init_hatch Tells if the hash variable must be reinitialized.
- \note
- - Line routine uses the well known Bresenham's algorithm.
- \par Example:
- \code
- CImg<unsigned char> img(100,100,1,3,0), texture("texture256x256.ppm");
- const unsigned char color[] = { 255,128,64 };
- img.draw_line(40,40,80,70,texture,0,0,255,255);
- \endcode
- **/
- template<typename tc>
- CImg<T>& draw_line(int x0, int y0,
- int x1, int y1,
- const CImg<tc>& texture,
- int tx0, int ty0,
- int tx1, int ty1,
- const float opacity=1,
- const unsigned int pattern=~0U, const bool init_hatch=true) {
- if (is_empty() || !opacity || !pattern) return *this;
- if (texture._depth>1 || texture._spectrum<_spectrum)
- throw CImgArgumentException(_cimg_instance
- "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
- cimg_instance,
- texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
- if (is_overlapped(texture)) return draw_line(x0,y0,x1,y1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
- if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
- int w1 = width() - 1, h1 = height() - 1;
- longT
- dx01 = (longT)x1 - x0, dy01 = (longT)y1 - y0,
- dtx01 = (longT)tx1 - tx0, dty01 = (longT)ty1 - ty0;
- const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
- if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
- if (pattern==~0U && y0>y1) {
- cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1);
- dx01*=-1; dy01*=-1; dtx01*=-1; dty01*=-1;
- }
- const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
- static unsigned int hatch = ~0U - (~0U>>1);
- if (init_hatch) hatch = ~0U - (~0U>>1);
- cimg_init_scanline(opacity);
- const int step = y0<=y1?1:-1, cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
- const longT
- hdy01 = dy01*cimg::sign(dx01)/2,
- hdy01tx = dy01*cimg::sign(dtx01)/2,
- hdy01ty = dy01*cimg::sign(dty01)/2;
- dy01+=dy01?0:1;
- for (int y = cy0; y!=cy1; y+=step) {
- const longT
- yy0 = (longT)y - y0,
- x = x0 + (dx01*yy0 + hdy01)/dy01,
- tx = tx0 + (dtx01*yy0 + hdy01tx)/dy01,
- ty = ty0 + (dty01*yy0 + hdy01ty)/dy01;
- if (x>=0 && x<=w1 && pattern&hatch) {
- T *const ptrd = is_horizontal?data(y,x):data(x,y);
- const tc *const color = &texture._atXY(tx,ty);
- cimg_forC(*this,c) {
- const T val = color[c*twhd];
- ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
- }
- }
- if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
- }
- return *this;
- }
- //! Draw a textured 2D line, with perspective correction.
- /**
- \param x0 X-coordinate of the starting point.
- \param y0 Y-coordinate of the starting point.
- \param z0 Z-coordinate of the starting point
- \param x1 X-coordinate of the ending point.
- \param y1 Y-coordinate of the ending point.
- \param z1 Z-coordinate of the ending point.
- \param texture Texture image defining the pixel colors.
- \param tx0 X-coordinate of the starting texture point.
- \param ty0 Y-coordinate of the starting texture point.
- \param tx1 X-coordinate of the ending texture point.
- \param ty1 Y-coordinate of the ending texture point.
- \param opacity Drawing opacity.
- \param pattern An integer whose bits describe the line pattern.
- \param init_hatch Tells if the hash variable must be reinitialized.
- **/
- template<typename tc>
- CImg<T>& draw_line(int x0, int y0, const float z0,
- int x1, int y1, const float z1,
- const CImg<tc>& texture,
- const int tx0, const int ty0,
- const int tx1, const int ty1,
- const float opacity=1,
- const unsigned int pattern=~0U, const bool init_hatch=true) {
- if (is_empty() || z0<=0 || z1<=0 || !opacity || !pattern) return *this;
- if (texture._depth>1 || texture._spectrum<_spectrum)
- throw CImgArgumentException(_cimg_instance
- "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
- cimg_instance,
- texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
- if (is_overlapped(texture))
- return draw_line(x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
- if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
- float iz0 = 1/z0, iz1 = 1/z1;
- int w1 = width() - 1, h1 = height() - 1;
- longT dx01 = (longT)x1 - x0, dy01 = (longT)y1 - y0;
- float
- diz01 = iz1 - iz0,
- txz0 = tx0*iz0, txz1 = tx1*iz1,
- tyz0 = ty0*iz0, tyz1 = ty1*iz1,
- dtxz01 = txz1 - txz0, dtyz01 = tyz1 - tyz0;
- const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
- if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
- if (pattern==~0U && y0>y1) {
- cimg::swap(x0,x1,y0,y1,iz0,iz1,txz0,txz1,tyz0,tyz1);
- dx01*=-1; dy01*=-1; diz01*=-1; dtxz01*=-1; dtyz01*=-1;
- }
- const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
- static unsigned int hatch = ~0U - (~0U>>1);
- if (init_hatch) hatch = ~0U - (~0U>>1);
- cimg_init_scanline(opacity);
- const int step = y0<=y1?1:-1, cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
- const longT hdy01 = dy01*cimg::sign(dx01)/2;
- dy01+=dy01?0:1;
- for (int y = cy0; y!=cy1; y+=step) {
- const longT
- yy0 = (longT)y - y0,
- x = x0 + (dx01*yy0 + hdy01)/dy01;
- const float
- iz = iz0 + diz01*yy0/dy01,
- txz = txz0 + dtxz01*yy0/dy01,
- tyz = tyz0 + dtyz01*yy0/dy01;
- if (x>=0 && x<=w1 && pattern&hatch) {
- const int
- tx = (int)cimg::round(txz/iz),
- ty = (int)cimg::round(tyz/iz);
- T *const ptrd = is_horizontal?data(y,x):data(x,y);
- const tc *const color = &texture._atXY(tx,ty);
- cimg_forC(*this,c) {
- const T val = color[c*twhd];
- ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
- }
- }
- if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
- }
- return *this;
- }
- //! Draw a textured 2D line, with perspective correction and z-buffering.
- /**
- \param zbuffer Z-buffer image.
- \param x0 X-coordinate of the starting point.
- \param y0 Y-coordinate of the starting point.
- \param z0 Z-coordinate of the starting point
- \param x1 X-coordinate of the ending point.
- \param y1 Y-coordinate of the ending point.
- \param z1 Z-coordinate of the ending point.
- \param texture Texture image defining the pixel colors.
- \param tx0 X-coordinate of the starting texture point.
- \param ty0 Y-coordinate of the starting texture point.
- \param tx1 X-coordinate of the ending texture point.
- \param ty1 Y-coordinate of the ending texture point.
- \param opacity Drawing opacity.
- \param pattern An integer whose bits describe the line pattern.
- \param init_hatch Tells if the hash variable must be reinitialized.
- **/
- template<typename tz, typename tc>
- CImg<T>& draw_line(CImg<tz>& zbuffer,
- int x0, int y0, const float z0,
- int x1, int y1, const float z1,
- const CImg<tc>& texture,
- const int tx0, const int ty0,
- const int tx1, const int ty1,
- const float opacity=1,
- const unsigned int pattern=~0U, const bool init_hatch=true) {
- if (is_empty() || z0<=0 || z1<=0 || !opacity || !pattern) return *this;
- if (!is_sameXY(zbuffer))
- throw CImgArgumentException(_cimg_instance
- "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
- "different dimensions.",
- cimg_instance,
- zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
- if (texture._depth>1 || texture._spectrum<_spectrum)
- throw CImgArgumentException(_cimg_instance
- "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
- cimg_instance,
- texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
- if (is_overlapped(texture))
- return draw_line(zbuffer,x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
- if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
- float iz0 = 1/z0, iz1 = 1/z1;
- int w1 = width() - 1, h1 = height() - 1;
- longT dx01 = (longT)x1 - x0, dy01 = (longT)y1 - y0;
- float
- diz01 = iz1 - iz0,
- txz0 = tx0*iz0, txz1 = tx1*iz1,
- tyz0 = ty0*iz0, tyz1 = ty1*iz1,
- dtxz01 = txz1 - txz0, dtyz01 = tyz1 - tyz0;
- const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
- if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
- if (pattern==~0U && y0>y1) {
- cimg::swap(x0,x1,y0,y1,iz0,iz1,txz0,txz1,tyz0,tyz1);
- dx01*=-1; dy01*=-1; diz01*=-1; dtxz01*=-1; dtyz01*=-1;
- }
- const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
- static unsigned int hatch = ~0U - (~0U>>1);
- if (init_hatch) hatch = ~0U - (~0U>>1);
- cimg_init_scanline(opacity);
- const int step = y0<=y1?1:-1, cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
- const longT hdy01 = dy01*cimg::sign(dx01)/2;
- dy01+=dy01?0:1;
- for (int y = cy0; y!=cy1; y+=step) {
- const longT
- yy0 = (longT)y - y0,
- x = x0 + (dx01*yy0 + hdy01)/dy01;
- const float
- iz = iz0 + diz01*yy0/dy01,
- txz = txz0 + dtxz01*yy0/dy01,
- tyz = tyz0 + dtyz01*yy0/dy01;
- tz *const ptrz = is_horizontal?zbuffer.data(y,x):zbuffer.data(x,y);
- if (x>=0 && x<=w1 && pattern&hatch && iz>=*ptrz) {
- *ptrz = (tz)iz;
- const int
- tx = (int)cimg::round(txz/iz),
- ty = (int)cimg::round(tyz/iz);
- T *const ptrd = is_horizontal?data(y,x):data(x,y);
- const tc *const color = &texture._atXY(tx,ty);
- cimg_forC(*this,c) {
- const T val = color[c*twhd];
- ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
- }
- }
- if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
- }
- return *this;
- }
- //! Draw a set of consecutive lines.
- /**
- \param points Coordinates of vertices, stored as a list of vectors.
- \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
- \param opacity Drawing opacity.
- \param pattern An integer whose bits describe the line pattern.
- \param init_hatch If set to true, init hatch motif.
- \note
- - This function uses several call to the single CImg::draw_line() procedure,
- depending on the vectors size in \p points.
- **/
- template<typename tp, typename tc>
- CImg<T>& draw_line(const CImg<tp>& points,
- const tc *const color, const float opacity=1,
- const unsigned int pattern=~0U, const bool init_hatch=true) {
- if (is_empty() || !points) return *this;
- if (!color)
- throw CImgArgumentException(_cimg_instance
- "draw_line(): Specified color is (null).",
- cimg_instance);
- if (points.height()!=2)
- throw CImgArgumentException(_cimg_instance
- "draw_line(): Invalid specified point set (%u,%u,%u,%u).",
- cimg_instance,
- points._width,points._height,points._depth,points._spectrum);
- CImg<intT> ipoints;
- if (cimg::type<tp>::is_float()) ipoints = points.get_round();
- else ipoints.assign(points,cimg::type<tp>::string()==cimg::type<int>::string());
- bool ninit_hatch = init_hatch;
- const int x0 = ipoints(0,0), y0 = ipoints(0,1);
- int ox = x0, oy = y0;
- for (unsigned int i = 1; i<ipoints._width; ++i) {
- const int x = ipoints(i,0), y = ipoints(i,1);
- draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
- ninit_hatch = false;
- ox = x; oy = y;
- }
- return *this;
- }
- //! Draw a 2D arrow.
- /**
- \param x0 X-coordinate of the starting arrow point (tail).
- \param y0 Y-coordinate of the starting arrow point (tail).
- \param x1 X-coordinate of the ending arrow point (head).
- \param y1 Y-coordinate of the ending arrow point (head).
- \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
- \param angle Aperture angle of the arrow head.
- \param length Length of the arrow head. If negative, describes a percentage of the arrow length.
- \param opacity Drawing opacity.
- \param pattern An integer whose bits describe the line pattern.
- **/
- template<typename tc>
- CImg<T>& draw_arrow(const int x0, const int y0,
- const int x1, const int y1,
- const tc *const color, const float opacity=1,
- const float angle=30, const float length=-10,
- const unsigned int pattern=~0U) {
- if (is_empty()) return *this;
- const float u = (float)(x0 - x1), v = (float)(y0 - y1), sq = u*u + v*v,
- deg = (float)(angle*cimg::PI/180), ang = (sq>0)?(float)std::atan2(v,u):0.f,
- l = (length>=0)?length:-length*(float)std::sqrt(sq)/100;
- if (sq>0) {
- const float
- cl = (float)std::cos(ang - deg), sl = (float)std::sin(ang - deg),
- cr = (float)std::cos(ang + deg), sr = (float)std::sin(ang + deg);
- const int
- xl = x1 + (int)(l*cl), yl = y1 + (int)(l*sl),
- xr = x1 + (int)(l*cr), yr = y1 + (int)(l*sr),
- xc = x1 + (int)((l + 1)*(cl + cr))/2, yc = y1 + (int)((l + 1)*(sl + sr))/2;
- draw_line(x0,y0,xc,yc,color,opacity,pattern).draw_triangle(x1,y1,xl,yl,xr,yr,color,opacity);
- } else draw_point(x0,y0,color,opacity);
- return *this;
- }
- //! Draw a 2D spline.
- /**
- \param x0 X-coordinate of the starting curve point
- \param y0 Y-coordinate of the starting curve point
- \param u0 X-coordinate of the starting velocity
- \param v0 Y-coordinate of the starting velocity
- \param x1 X-coordinate of the ending curve point
- \param y1 Y-coordinate of the ending curve point
- \param u1 X-coordinate of the ending velocity
- \param v1 Y-coordinate of the ending velocity
- \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
- \param precision Curve drawing precision.
- \param opacity Drawing opacity.
- \param pattern An integer whose bits describe the line pattern.
- \param init_hatch If \c true, init hatch motif.
- \note
- - The curve is a 2D cubic Bezier spline, from the set of specified starting/ending points
- and corresponding velocity vectors.
- - The spline is drawn as a sequence of connected segments. The \p precision parameter sets the
- average number of pixels in each drawn segment.
- - A cubic Bezier curve is sometimes defined by a set of 4 points { (\p x0,\p y0), (\p xa,\p ya),
- (\p xb,\p yb), (\p x1,\p y1) } where (\p x0,\p y0) is the starting point, (\p x1,\p y1) is the ending point
- and (\p xa,\p ya), (\p xb,\p yb) are two
- \e control points.
- The starting and ending velocities (\p u0,\p v0) and (\p u1,\p v1) can be deduced easily from
- the control points as
- \p u0 = (\p xa - \p x0), \p v0 = (\p ya - \p y0), \p u1 = (\p x1 - \p xb) and \p v1 = (\p y1 - \p yb).
- \par Example:
- \code
- CImg<unsigned char> img(100,100,1,3,0);
- const unsigned char color[] = { 255,255,255 };
- img.draw_spline(30,30,0,100,90,40,0,-100,color);
- \endcode
- **/
- template<typename tc>
- CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
- const int x1, const int y1, const float u1, const float v1,
- const tc *const color, const float opacity=1,
- const float precision=0.25, const unsigned int pattern=~0U,
- const bool init_hatch=true) {
- if (is_empty()) return *this;
- if (!color)
- throw CImgArgumentException(_cimg_instance
- "draw_spline(): Specified color is (null).",
- cimg_instance);
- if (x0==x1 && y0==y1) return draw_point(x0,y0,color,opacity);
- bool ninit_hatch = init_hatch;
- const float
- ax = u0 + u1 + 2*(x0 - x1),
- bx = 3*(x1 - x0) - 2*u0 - u1,
- ay = v0 + v1 + 2*(y0 - y1),
- by = 3*(y1 - y0) - 2*v0 - v1,
- _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1));
- int ox = x0, oy = y0;
- for (float t = 0; t<1; t+=_precision) {
- const float t2 = t*t, t3 = t2*t;
- const int
- nx = (int)(ax*t3 + bx*t2 + u0*t + x0),
- ny = (int)(ay*t3 + by*t2 + v0*t + y0);
- draw_line(ox,oy,nx,ny,color,opacity,pattern,ninit_hatch);
- ninit_hatch = false;
- ox = nx; oy = ny;
- }
- return draw_line(ox,oy,x1,y1,color,opacity,pattern,false);
- }
- //! Draw a textured 2D spline.
- /**
- \param x0 X-coordinate of the starting curve point
- \param y0 Y-coordinate of the starting curve point
- \param u0 X-coordinate of the starting velocity
- \param v0 Y-coordinate of the starting velocity
- \param x1 X-coordinate of the ending curve point
- \param y1 Y-coordinate of the ending curve point
- \param u1 X-coordinate of the ending velocity
- \param v1 Y-coordinate of the ending velocity
- \param texture Texture image defining line pixel colors.
- \param tx0 X-coordinate of the starting texture point.
- \param ty0 Y-coordinate of the starting texture point.
- \param tx1 X-coordinate of the ending texture point.
- \param ty1 Y-coordinate of the ending texture point.
- \param precision Curve drawing precision.
- \param opacity Drawing opacity.
- \param pattern An integer whose bits describe the line pattern.
- \param init_hatch if \c true, reinit hatch motif.
- **/
- template<typename t>
- CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
- const int x1, const int y1, const float u1, const float v1,
- const CImg<t>& texture,
- const int tx0, const int ty0, const int tx1, const int ty1,
- const float opacity=1,
- const float precision=4, const unsigned int pattern=~0U,
- const bool init_hatch=true) {
- if (texture._depth>1 || texture._spectrum<_spectrum)
- throw CImgArgumentException(_cimg_instance
- "draw_spline(): Invalid specified texture (%u,%u,%u,%u,%p).",
- cimg_instance,
- texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
- if (is_empty()) return *this;
- if (is_overlapped(texture))
- return draw_spline(x0,y0,u0,v0,x1,y1,u1,v1,+texture,tx0,ty0,tx1,ty1,precision,opacity,pattern,init_hatch);
- if (x0==x1 && y0==y1)
- return draw_point(x0,y0,texture.get_vector_at(x0<=0?0:x0>=texture.width()?texture.width() - 1:x0,
- y0<=0?0:y0>=texture.height()?texture.height() - 1:y0).data(),
- opacity);
- bool ninit_hatch = init_hatch;
- const float
- ax = u0 + u1 + 2*(x0 - x1),
- bx = 3*(x1 - x0) - 2*u0 - u1,
- ay = v0 + v1 + 2*(y0 - y1),
- by = 3*(y1 - y0) - 2*v0 - v1,
- _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1));
- int ox = x0, oy = y0, otx = tx0, oty = ty0;
- for (float t1 = 0; t1<1; t1+=_precision) {
- const float t2 = t1*t1, t3 = t2*t1;
- const int
- nx = (int)(ax*t3 + bx*t2 + u0*t1 + x0),
- ny = (int)(ay*t3 + by*t2 + v0*t1 + y0),
- ntx = tx0 + (int)((tx1 - tx0)*t1),
- nty = ty0 + (int)((ty1 - ty0)*t1);
- draw_line(ox,oy,nx,ny,texture,otx,oty,ntx,nty,opacity,pattern,ninit_hatch);
- ninit_hatch = false;
- ox = nx; oy = ny; otx = ntx; oty = nty;
- }
- return draw_line(ox,oy,x1,y1,texture,otx,oty,tx1,ty1,opacity,pattern,false);
- }
- //! Draw a set of consecutive splines.
- /**
- \param points Vertices data.
- \param tangents Tangents data.
- \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
- \param opacity Drawing opacity.
- \param is_closed_set Tells if the drawn spline set is closed.
- \param precision Precision of the drawing.
- \param pattern An integer whose bits describe the line pattern.
- \param init_hatch If \c true, init hatch motif.
- **/
- template<typename tp, typename tt, typename tc>
- CImg<T>& draw_spline(const CImg<tp>& points, const CImg<tt>& tangents,
- const tc *const color, const float opacity=1,
- const bool is_closed_set=false, const float precision=4,
- const unsigned int pattern=~0U, const bool init_hatch=true) {
- if (is_empty() || !points || !tangents || points._width<2 || tangents._width<2) return *this;
- bool ninit_hatch = init_hatch;
- switch (points._height) {
- case 0 : case 1 :
- throw CImgArgumentException(_cimg_instance
- "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).",
- cimg_instance,
- points._width,points._height,points._depth,points._spectrum,points._data);
- default : {
- const int x0 = (int)points(0,0), y0 = (int)points(0,1);
- const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1);
- int ox = x0, oy = y0;
- float ou = u0, ov = v0;
- for (unsigned int i = 1; i<points._width; ++i) {
- const int x = (int)points(i,0), y = (int)points(i,1);
- const float u = (float)tangents(i,0), v = (float)tangents(i,1);
- draw_spline(ox,oy,ou,ov,x,y,u,v,color,precision,opacity,pattern,ninit_hatch);
- ninit_hatch = false;
- ox = x; oy = y; ou = u; ov = v;
- }
- if (is_closed_set) draw_spline(ox,oy,ou,ov,x0,y0,u0,v0,color,precision,opacity,pattern,false);
- }
- }
- return *this;
- }
- //! Draw a set of consecutive splines \overloading.
- /**
- Similar to previous function, with the point tangents automatically estimated from the given points set.
- **/
- template<typename tp, typename tc>
- CImg<T>& draw_spline(const CImg<tp>& points,
- const tc *const color, const float opacity=1,
- const bool is_closed_set=false, const float precision=4,
- const unsigned int pattern=~0U, const bool init_hatch=true) {
- if (is_empty() || !points || points._width<2) return *this;
- CImg<Tfloat> tangents;
- switch (points._height) {
- case 0 : case 1 :
- throw CImgArgumentException(_cimg_instance
- "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).",
- cimg_instance,
- points._width,points._height,points._depth,points._spectrum,points._data);
- case 2 : {
- tangents.assign(points._width,points._height);
- cimg_forX(points,p) {
- const unsigned int
- p0 = is_closed_set?(p + points.width() - 1)%points.width():(p?p - 1:0),
- p1 = is_closed_set?(p + 1)%points.width():(p + 1<points.width()?p + 1:p);
- const float
- x = (float)points(p,0),
- y = (float)points(p,1),
- x0 = (float)points(p0,0),
- y0 = (float)points(p0,1),
- x1 = (float)points(p1,0),
- y1 = (float)points(p1,1),
- u0 = x - x0,
- v0 = y - y0,
- n0 = 1e-8f + cimg::hypot(u0,v0),
- u1 = x1 - x,
- v1 = y1 - y,
- n1 = 1e-8f + cimg::hypot(u1,v1),
- u = u0/n0 + u1/n1,
- v = v0/n0 + v1/n1,
- n = 1e-8f + cimg::hypot(u,v),
- fact = 0.5f*(n0 + n1);
- tangents(p,0) = (Tfloat)(fact*u/n);
- tangents(p,1) = (Tfloat)(fact*v/n);
- }
- } break;
- default : {
- tangents.assign(points._width,points._height);
- cimg_forX(points,p) {
- const unsigned int
- p0 = is_closed_set?(p + points.width() - 1)%points.width():(p?p - 1:0),
- p1 = is_closed_set?(p + 1)%points.width():(p + 1<points.width()?p + 1:p);
- const float
- x = (float)points(p,0),
- y = (float)points(p,1),
- z = (float)points(p,2),
- x0 = (float)points(p0,0),
- y0 = (float)points(p0,1),
- z0 = (float)points(p0,2),
- x1 = (float)points(p1,0),
- y1 = (float)points(p1,1),
- z1 = (float)points(p1,2),
- u0 = x - x0,
- v0 = y - y0,
- w0 = z - z0,
- n0 = 1e-8f + cimg::hypot(u0,v0,w0),
- u1 = x1 - x,
- v1 = y1 - y,
- w1 = z1 - z,
- n1 = 1e-8f + cimg::hypot(u1,v1,w1),
- u = u0/n0 + u1/n1,
- v = v0/n0 + v1/n1,
- w = w0/n0 + w1/n1,
- n = 1e-8f + cimg::hypot(u,v,w),
- fact = 0.5f*(n0 + n1);
- tangents(p,0) = (Tfloat)(fact*u/n);
- tangents(p,1) = (Tfloat)(fact*v/n);
- tangents(p,2) = (Tfloat)(fact*w/n);
- }
- }
- }
- return draw_spline(points,tangents,color,opacity,is_closed_set,precision,pattern,init_hatch);
- }
- // [internal] Draw a filled triangle.
- template<typename tc>
- CImg<T>& _draw_triangle(int x0, int y0,
- int x1, int y1,
- int x2, int y2,
- const tc *const color, const float opacity,
- const float brightness) {
- if (y0>y1) cimg::swap(x0,x1,y0,y1);
- if (y0>y2) cimg::swap(x0,x2,y0,y2);
- if (y1>y2) cimg::swap(x1,x2,y1,y2);
- if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
- const int
- h1 = height() - 1,
- cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1);
- const longT
- dx01 = (longT)x1 - x0, dx02 = (longT)x2 - x0, dx12 = (longT)x2 - x1,
- dy01 = std::max((longT)1,(longT)y1 - y0),
- dy02 = std::max((longT)1,(longT)y2 - y0),
- dy12 = std::max((longT)1,(longT)y2 - y1),
- hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
- const float cbs = cimg::cut(brightness,0,2);
- cimg_init_scanline(opacity);
- for (int y = cy0; y<=cy2; ++y) {
- const longT yy0 = (longT)y - y0, yy1 = (longT)y - y1;
- longT
- xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
- xM = x0 + (dx02*yy0 + hdy02)/dy02;
- if (xm>xM) cimg::swap(xm,xM);
- cimg_draw_scanline(xm,xM,y,color,opacity,cbs);
- }
- return *this;
- }
- //! Draw a filled 2D triangle.
- /**
- \param x0 X-coordinate of the first vertex.
- \param y0 Y-coordinate of the first vertex.
- \param x1 X-coordinate of the second vertex.
- \param y1 Y-coordinate of the second vertex.
- \param x2 X-coordinate of the third vertex.
- \param y2 Y-coordinate of the third vertex.
- \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
- \param opacity Drawing opacity.
- **/
- template<typename tc>
- CImg<T>& draw_triangle(const int x0, const int y0,
- const int x1, const int y1,
- const int x2, const int y2,
- const tc *const color, const float opacity=1) {
- if (is_empty()) return *this;
- if (!color)
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Specified color is (null).",
- cimg_instance);
- _draw_triangle(x0,y0,x1,y1,x2,y2,color,opacity,1);
- return *this;
- }
- //! Draw a outlined 2D triangle.
- /**
- \param x0 X-coordinate of the first vertex.
- \param y0 Y-coordinate of the first vertex.
- \param x1 X-coordinate of the second vertex.
- \param y1 Y-coordinate of the second vertex.
- \param x2 X-coordinate of the third vertex.
- \param y2 Y-coordinate of the third vertex.
- \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
- \param opacity Drawing opacity.
- \param pattern An integer whose bits describe the outline pattern.
- **/
- template<typename tc>
- CImg<T>& draw_triangle(const int x0, const int y0,
- const int x1, const int y1,
- const int x2, const int y2,
- const tc *const color, const float opacity,
- const unsigned int pattern) {
- if (is_empty()) return *this;
- if (!color)
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Specified color is (null).",
- cimg_instance);
- draw_line(x0,y0,x1,y1,color,opacity,pattern,true).
- draw_line(x1,y1,x2,y2,color,opacity,pattern,false).
- draw_line(x2,y2,x0,y0,color,opacity,pattern,false);
- return *this;
- }
- //! Draw a filled 2D triangle, with z-buffering.
- /**
- \param zbuffer Z-buffer image.
- \param x0 X-coordinate of the first vertex.
- \param y0 Y-coordinate of the first vertex.
- \param z0 Z-coordinate of the first vertex.
- \param x1 X-coordinate of the second vertex.
- \param y1 Y-coordinate of the second vertex.
- \param z1 Z-coordinate of the second vertex.
- \param x2 X-coordinate of the third vertex.
- \param y2 Y-coordinate of the third vertex.
- \param z2 Z-coordinate of the third vertex.
- \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
- \param opacity Drawing opacity.
- \param brightness Brightness factor.
- **/
- template<typename tz, typename tc>
- CImg<T>& draw_triangle(CImg<tz>& zbuffer,
- int x0, int y0, const float z0,
- int x1, int y1, const float z1,
- int x2, int y2, const float z2,
- const tc *const color, const float opacity=1,
- const float brightness=1) {
- if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
- if (!color)
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Specified color is (null).",
- cimg_instance);
- if (!is_sameXY(zbuffer))
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
- "different dimensions.",
- cimg_instance,
- zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
- float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
- if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1);
- if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2);
- if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2);
- if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
- const int w1 = width() - 1, h1 = height() - 1, cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1);
- const longT
- dx01 = (longT)x1 - x0, dx02 = (longT)x2 - x0, dx12 = (longT)x2 - x1,
- dy01 = std::max((longT)1,(longT)y1 - y0),
- dy02 = std::max((longT)1,(longT)y2 - y0),
- dy12 = std::max((longT)1,(longT)y2 - y1),
- hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
- const float diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1;
- const float cbs = cimg::cut(brightness,0,2);
- cimg_init_scanline(opacity);
- for (int y = cy0; y<=cy2; ++y) {
- const longT yy0 = (longT)y - y0, yy1 = (longT)y - y1;
- longT
- xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
- xM = x0 + (dx02*yy0 + hdy02)/dy02;
- float
- izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
- izM = iz0 + diz02*yy0/dy02;
- if (xm>xM) cimg::swap(xm,xM,izm,izM);
- if (xM>=0 && xm<=w1) {
- const int
- cxm = (int)cimg::cut(xm,(longT)0,(longT)w1),
- cxM = (int)cimg::cut(xM,(longT)0,(longT)w1);
- T *ptrd = data(cxm,y);
- tz *ptrz = zbuffer.data(cxm,y);
- const longT dxmM = std::max((longT)1,xM - xm);
- const float dizmM = izM - izm;
- for (int x = cxm; x<=cxM; ++x) {
- const longT xxm = x - xm;
- const float iz = izm + dizmM*xxm/dxmM;
- if (iz>=*ptrz) {
- *ptrz = (tz)iz;
- cimg_forC(*this,c) {
- const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval;
- ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
- }
- }
- ++ptrd; ++ptrz;
- }
- }
- }
- return *this;
- }
- //! Draw a Gouraud-shaded 2D triangle.
- /**
- \param x0 X-coordinate of the first vertex in the image instance.
- \param y0 Y-coordinate of the first vertex in the image instance.
- \param x1 X-coordinate of the second vertex in the image instance.
- \param y1 Y-coordinate of the second vertex in the image instance.
- \param x2 X-coordinate of the third vertex in the image instance.
- \param y2 Y-coordinate of the third vertex in the image instance.
- \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
- \param bs0 Brightness factor of the first vertex (in [0,2]).
- \param bs1 brightness factor of the second vertex (in [0,2]).
- \param bs2 brightness factor of the third vertex (in [0,2]).
- \param opacity Drawing opacity.
- **/
- template<typename tc>
- CImg<T>& draw_triangle(int x0, int y0,
- int x1, int y1,
- int x2, int y2,
- const tc *const color,
- float bs0,
- float bs1,
- float bs2,
- const float opacity=1) {
- if (is_empty()) return *this;
- if (!color)
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Specified color is (null).",
- cimg_instance);
- if (y0>y1) cimg::swap(x0,x1,y0,y1,bs0,bs1);
- if (y0>y2) cimg::swap(x0,x2,y0,y2,bs0,bs2);
- if (y1>y2) cimg::swap(x1,x2,y1,y2,bs1,bs2);
- if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
- const int w1 = width() - 1, h1 = height() - 1, cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1);
- const longT
- dx01 = (longT)x1 - x0, dx02 = (longT)x2 - x0, dx12 = (longT)x2 - x1,
- dy01 = std::max((longT)1,(longT)y1 - y0),
- dy02 = std::max((longT)1,(longT)y2 - y0),
- dy12 = std::max((longT)1,(longT)y2 - y1),
- hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
- const float dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
- cimg_init_scanline(opacity);
- for (int y = cy0; y<=cy2; ++y) {
- const longT yy0 = (longT)y - y0, yy1 = (longT)y - y1;
- longT
- xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
- xM = x0 + (dx02*yy0 + hdy02)/dy02;
- float
- bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
- bsM = bs0 + dbs02*yy0/dy02;
- if (xm>xM) cimg::swap(xm,xM,bsm,bsM);
- if (xM>=0 && xm<=w1) {
- const int
- cxm = (int)cimg::cut(xm,(longT)0,(longT)w1),
- cxM = (int)cimg::cut(xM,(longT)0,(longT)w1);
- T *ptrd = data(cxm,y);
- const longT dxmM = std::max((longT)1,xM - xm);
- const float dbsmM = bsM - bsm;
- for (int x = cxm; x<=cxM; ++x) {
- const longT xxm = (longT)x - xm;
- const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
- cimg_forC(*this,c) {
- const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval;
- ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
- }
- ++ptrd;
- }
- }
- }
- return *this;
- }
- //! Draw a Gouraud-shaded 2D triangle, with z-buffering \overloading.
- template<typename tz, typename tc>
- CImg<T>& draw_triangle(CImg<tz>& zbuffer,
- int x0, int y0, const float z0,
- int x1, int y1, const float z1,
- int x2, int y2, const float z2,
- const tc *const color,
- float bs0,
- float bs1,
- float bs2,
- float opacity=1) {
- if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
- if (!color)
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Specified color is (null).",
- cimg_instance);
- if (!is_sameXY(zbuffer))
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
- "different dimensions.",
- cimg_instance,
- zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
- float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
- if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,bs0,bs1);
- if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,bs0,bs2);
- if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,bs1,bs2);
- if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
- const int
- w1 = width() - 1, h1 = height() - 1,
- dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
- dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
- cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
- hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
- const float
- diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
- dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
- cimg_init_scanline(opacity);
- for (int y = cy0; y<=cy2; ++y) {
- const int yy0 = y - y0, yy1 = y - y1;
- int
- xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
- xM = x0 + (dx02*yy0 + hdy02)/dy02;
- float
- izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
- izM = iz0 + diz02*yy0/dy02,
- bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
- bsM = bs0 + dbs02*yy0/dy02;
- if (xm>xM) cimg::swap(xm,xM,izm,izM,bsm,bsM);
- if (xM>=0 && xm<=w1) {
- const int
- cxm = cimg::cut(xm,0,w1),
- cxM = cimg::cut(xM,0,w1);
- T *ptrd = data(cxm,y);
- tz *ptrz = zbuffer.data(cxm,y);
- const int dxmM = std::max(1,xM - xm);
- const float dizmM = izM - izm, dbsmM = bsM - bsm;
- for (int x = cxm; x<=cxM; ++x) {
- const int xxm = x - xm;
- const float iz = izm + dizmM*xxm/dxmM;
- if (iz>=*ptrz) {
- *ptrz = (tz)iz;
- const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
- cimg_forC(*this,c) {
- const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval;
- ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
- }
- }
- ++ptrd; ++ptrz;
- }
- }
- }
- return *this;
- }
- //! Draw a color-interpolated 2D triangle.
- /**
- \param x0 X-coordinate of the first vertex in the image instance.
- \param y0 Y-coordinate of the first vertex in the image instance.
- \param x1 X-coordinate of the second vertex in the image instance.
- \param y1 Y-coordinate of the second vertex in the image instance.
- \param x2 X-coordinate of the third vertex in the image instance.
- \param y2 Y-coordinate of the third vertex in the image instance.
- \param color1 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the first vertex.
- \param color2 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the second vertex.
- \param color3 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the third vertex.
- \param opacity Drawing opacity.
- **/
- template<typename tc>
- CImg<T>& draw_triangle(int x0, int y0,
- int x1, int y1,
- int x2, int y2,
- const tc *color0,
- const tc *color1,
- const tc *color2,
- const float opacity=1) {
- typedef typename cimg::superset<tc,int>::type stc;
- if (is_empty()) return *this;
- if (!color0 || !color1 || !color2)
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): One of the specified color is (null).",
- cimg_instance);
- if (y0>y1) cimg::swap(x0,x1,y0,y1,color0,color1);
- if (y0>y2) cimg::swap(x0,x2,y0,y2,color0,color2);
- if (y1>y2) cimg::swap(x1,x2,y1,y2,color1,color2);
- if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
- const int w1 = width() - 1, h1 = height() - 1, cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1);
- const longT
- dx01 = (longT)x1 - x0, dx02 = (longT)x2 - x0, dx12 = (longT)x2 - x1,
- dy01 = std::max((longT)1,(longT)y1 - y0),
- dy02 = std::max((longT)1,(longT)y2 - y0),
- dy12 = std::max((longT)1,(longT)y2 - y1),
- hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
- cimg_init_scanline(opacity);
- cimg_forC(*this,c) {
- const stc dcolor01 = color1[c] - color0[c], dcolor02 = color2[c] - color0[c], dcolor12 = color2[c] - color1[c];
- for (int y = cy0; y<=cy2; ++y) {
- const longT yy0 = (longT)y - y0, yy1 = (longT)y - y1;
- longT
- xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
- xM = x0 + (dx02*yy0 + hdy02)/dy02;
- stc
- colorm = y<y1?(color0[c] + dcolor01*yy0/dy01):(color1[c] + dcolor12*yy1/dy12),
- colorM = color0[c] + dcolor02*yy0/dy02;
- if (xm>xM) cimg::swap(xm,xM,colorm,colorM);
- if (xM>=0 && xm<=w1) {
- const int
- cxm = (int)cimg::cut(xm,(longT)0,(longT)w1),
- cxM = (int)cimg::cut(xM,(longT)0,(longT)w1);
- T *ptrd = data(cxm,y);
- const longT dxmM = std::max((longT)1,xM - xm);
- const stc dcolormM = colorM - colorm;
- for (int x = cxm; x<=cxM; ++x) {
- const longT xxm = (longT)x - xm;
- const stc col = colorm + dcolormM*xxm/dxmM;
- ptrd[c*_sc_whd] = (T)(opacity>=1?col:col*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
- ++ptrd;
- }
- }
- }
- }
- return *this;
- }
- //! Draw a textured 2D triangle.
- /**
- \param x0 X-coordinate of the first vertex in the image instance.
- \param y0 Y-coordinate of the first vertex in the image instance.
- \param x1 X-coordinate of the second vertex in the image instance.
- \param y1 Y-coordinate of the second vertex in the image instance.
- \param x2 X-coordinate of the third vertex in the image instance.
- \param y2 Y-coordinate of the third vertex in the image instance.
- \param texture Texture image used to fill the triangle.
- \param tx0 X-coordinate of the first vertex in the texture image.
- \param ty0 Y-coordinate of the first vertex in the texture image.
- \param tx1 X-coordinate of the second vertex in the texture image.
- \param ty1 Y-coordinate of the second vertex in the texture image.
- \param tx2 X-coordinate of the third vertex in the texture image.
- \param ty2 Y-coordinate of the third vertex in the texture image.
- \param opacity Drawing opacity.
- \param brightness Brightness factor of the drawing (in [0,2]).
- **/
- template<typename tc>
- CImg<T>& draw_triangle(int x0, int y0,
- int x1, int y1,
- int x2, int y2,
- const CImg<tc>& texture,
- int tx0, int ty0,
- int tx1, int ty1,
- int tx2, int ty2,
- const float opacity=1,
- const float brightness=1) {
- if (is_empty()) return *this;
- if (texture._depth>1 || texture._spectrum<_spectrum)
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
- cimg_instance,
- texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
- if (is_overlapped(texture))
- return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
- if (y0>y1) cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1);
- if (y0>y2) cimg::swap(x0,x2,y0,y2,tx0,tx2,ty0,ty2);
- if (y1>y2) cimg::swap(x1,x2,y1,y2,tx1,ty1,tx2,ty2);
- if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
- const int
- w1 = width() - 1, h1 = height() - 1,
- dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
- dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
- cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
- hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
- dtx01 = tx1 - tx0, dtx02 = tx2 - tx0, dtx12 = tx2 - tx1,
- dty01 = ty1 - ty0, dty02 = ty2 - ty0, dty12 = ty2 - ty1,
- hdy01tx = dy01*cimg::sign(dtx01)/2, hdy02tx = dy02*cimg::sign(dtx02)/2, hdy12tx = dy12*cimg::sign(dtx12)/2,
- hdy01ty = dy01*cimg::sign(dty01)/2, hdy02ty = dy02*cimg::sign(dty02)/2, hdy12ty = dy12*cimg::sign(dty12)/2;
- const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
- const float cbs = cimg::cut(brightness,0,2);
- cimg_init_scanline(opacity);
- for (int y = cy0; y<=cy2; ++y) {
- const int yy0 = y - y0, yy1 = y - y1;
- int
- xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
- xM = x0 + (dx02*yy0 + hdy02)/dy02,
- txm = y<y1?tx0 + (dtx01*yy0 + hdy01tx)/dy01:tx1 + (dtx12*yy1 + hdy12tx)/dy12,
- txM = tx0 + (dtx02*yy0 + hdy02tx)/dy02,
- tym = y<y1?ty0 + (dty01*yy0 + hdy01ty)/dy01:ty1 + (dty12*yy1 + hdy12ty)/dy12,
- tyM = ty0 + (dty02*yy0 + hdy02ty)/dy02;
- if (xm>xM) cimg::swap(xm,xM,txm,txM,tym,tyM);
- if (xM>=0 && xm<=w1) {
- const int
- cxm = cimg::cut(xm,0,w1),
- cxM = cimg::cut(xM,0,w1);
- T *ptrd = data(cxm,y);
- const int
- dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
- dtxmM = txM - txm, dtymM = tyM - tym;
- for (int x = cxm; x<=cxM; ++x) {
- const int
- xxm = x - xm,
- tx = (txm*dxmM + dtxmM*xxm + hdxmM)/dxmM,
- ty = (tym*dxmM + dtymM*xxm + hdxmM)/dxmM;
- const tc *const color = &texture._atXY(tx,ty);
- cimg_forC(*this,c) {
- const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval;
- ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
- }
- ++ptrd;
- }
- }
- }
- return *this;
- }
- //! Draw a 2D textured triangle, with perspective correction.
- template<typename tc>
- CImg<T>& draw_triangle(int x0, int y0, const float z0,
- int x1, int y1, const float z1,
- int x2, int y2, const float z2,
- const CImg<tc>& texture,
- int tx0, int ty0,
- int tx1, int ty1,
- int tx2, int ty2,
- const float opacity=1,
- const float brightness=1) {
- if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
- if (texture._depth>1 || texture._spectrum<_spectrum)
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
- cimg_instance,
- texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
- if (is_overlapped(texture))
- return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
- float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
- if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1);
- if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2);
- if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2);
- if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
- const int
- w1 = width() - 1, h1 = height() - 1,
- dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
- dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
- cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
- hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
- const float
- diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
- txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
- tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
- dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
- dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1;
- const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
- const float cbs = cimg::cut(brightness,0,2);
- cimg_init_scanline(opacity);
- for (int y = cy0; y<=cy2; ++y) {
- const int yy0 = y - y0, yy1 = y - y1;
- int
- xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
- xM = x0 + (dx02*yy0 + hdy02)/dy02;
- float
- izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
- izM = iz0 + diz02*yy0/dy02,
- txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
- txzM = txz0 + dtxz02*yy0/dy02,
- tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
- tyzM = tyz0 + dtyz02*yy0/dy02;
- if (xm>xM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM);
- if (xM>=0 && xm<=w1) {
- const int
- cxm = cimg::cut(xm,0,w1),
- cxM = cimg::cut(xM,0,w1);
- T *ptrd = data(cxm,y);
- const int dxmM = std::max(1,xM - xm);
- const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm;
- for (int x = cxm; x<=cxM; ++x) {
- const int xxm = x - xm;
- const float
- iz = izm + dizmM*xxm/dxmM,
- txz = txzm + dtxzmM*xxm/dxmM,
- tyz = tyzm + dtyzmM*xxm/dxmM;
- const int
- tx = (int)cimg::round(txz/iz),
- ty = (int)cimg::round(tyz/iz);
- const tc *const color = &texture._atXY(tx,ty);
- cimg_forC(*this,c) {
- const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval;
- ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
- }
- ++ptrd;
- }
- }
- }
- return *this;
- }
- //! Draw a textured 2D triangle, with perspective correction and z-buffering.
- template<typename tz, typename tc>
- CImg<T>& draw_triangle(CImg<tz>& zbuffer,
- int x0, int y0, const float z0,
- int x1, int y1, const float z1,
- int x2, int y2, const float z2,
- const CImg<tc>& texture,
- int tx0, int ty0,
- int tx1, int ty1,
- int tx2, int ty2,
- const float opacity=1,
- const float brightness=1) {
- if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
- if (!is_sameXY(zbuffer))
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
- "different dimensions.",
- cimg_instance,
- zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
- if (texture._depth>1 || texture._spectrum<_spectrum)
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
- cimg_instance,
- texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
- if (is_overlapped(texture))
- return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
- float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
- if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1);
- if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2);
- if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2);
- if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
- const int
- w1 = width() - 1, h1 = height() - 1,
- dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
- dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
- cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
- hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
- const float
- diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
- txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
- tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
- dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
- dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1;
- const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
- const float cbs = cimg::cut(brightness,0,2);
- cimg_init_scanline(opacity);
- for (int y = cy0; y<=cy2; ++y) {
- const int yy0 = y - y0, yy1 = y - y1;
- int
- xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
- xM = x0 + (dx02*yy0 + hdy02)/dy02;
- float
- izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
- izM = iz0 + diz02*yy0/dy02,
- txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
- txzM = txz0 + dtxz02*yy0/dy02,
- tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
- tyzM = tyz0 + dtyz02*yy0/dy02;
- if (xm>xM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM);
- if (xM>=0 && xm<=w1) {
- const int
- cxm = cimg::cut(xm,0,w1),
- cxM = cimg::cut(xM,0,w1);
- T *ptrd = data(cxm,y);
- tz *ptrz = zbuffer.data(cxm,y);
- const int dxmM = std::max(1,xM - xm);
- const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm;
- for (int x = cxm; x<=cxM; ++x) {
- const int xxm = x - xm;
- const float iz = izm + dizmM*xxm/dxmM;
- if (iz>=*ptrz) {
- *ptrz = (tz)iz;
- const float
- txz = txzm + dtxzmM*xxm/dxmM,
- tyz = tyzm + dtyzmM*xxm/dxmM;
- const int
- tx = (int)cimg::round(txz/iz),
- ty = (int)cimg::round(tyz/iz);
- const tc *const color = &texture._atXY(tx,ty);
- cimg_forC(*this,c) {
- const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval;
- ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
- }
- }
- ++ptrd; ++ptrz;
- }
- }
- }
- return *this;
- }
- //! Draw a Phong-shaded 2D triangle.
- /**
- \param x0 X-coordinate of the first vertex in the image instance.
- \param y0 Y-coordinate of the first vertex in the image instance.
- \param x1 X-coordinate of the second vertex in the image instance.
- \param y1 Y-coordinate of the second vertex in the image instance.
- \param x2 X-coordinate of the third vertex in the image instance.
- \param y2 Y-coordinate of the third vertex in the image instance.
- \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
- \param light Light image.
- \param lx0 X-coordinate of the first vertex in the light image.
- \param ly0 Y-coordinate of the first vertex in the light image.
- \param lx1 X-coordinate of the second vertex in the light image.
- \param ly1 Y-coordinate of the second vertex in the light image.
- \param lx2 X-coordinate of the third vertex in the light image.
- \param ly2 Y-coordinate of the third vertex in the light image.
- \param opacity Drawing opacity.
- **/
- template<typename tc, typename tl>
- CImg<T>& draw_triangle(int x0, int y0,
- int x1, int y1,
- int x2, int y2,
- const tc *const color,
- const CImg<tl>& light,
- int lx0, int ly0,
- int lx1, int ly1,
- int lx2, int ly2,
- const float opacity=1) {
- if (is_empty()) return *this;
- if (!color)
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Specified color is (null).",
- cimg_instance);
- if (light._depth>1 || light._spectrum<_spectrum)
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
- cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
- if (y0>y1) cimg::swap(x0,x1,y0,y1,lx0,lx1,ly0,ly1);
- if (y0>y2) cimg::swap(x0,x2,y0,y2,lx0,lx2,ly0,ly2);
- if (y1>y2) cimg::swap(x1,x2,y1,y2,lx1,lx2,ly1,ly2);
- if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
- const int
- w1 = width() - 1, h1 = height() - 1,
- dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
- dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
- cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
- hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
- dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1,
- dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1,
- hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2,
- hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2;
- const ulongT lwhd = (ulongT)light._width*light._height*light._depth;
- cimg_init_scanline(opacity);
- for (int y = cy0; y<=cy2; ++y) {
- const int yy0 = y - y0, yy1 = y - y1;
- int
- xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
- xM = x0 + (dx02*yy0 + hdy02)/dy02,
- lxm = y<y1?lx0 + (dlx01*yy0 + hdy01lx)/dy01:lx1 + (dlx12*yy1 + hdy12lx)/dy12,
- lxM = lx0 + (dlx02*yy0 + hdy02lx)/dy02,
- lym = y<y1?ly0 + (dly01*yy0 + hdy01ly)/dy01:ly1 + (dly12*yy1 + hdy12ly)/dy12,
- lyM = ly0 + (dly02*yy0 + hdy02ly)/dy02;
- if (xm>xM) cimg::swap(xm,xM,lxm,lxM,lym,lyM);
- if (xM>=0 && xm<=w1) {
- const int
- cxm = cimg::cut(xm,0,w1),
- cxM = cimg::cut(xM,0,w1);
- T *ptrd = data(cxm,y);
- const int
- dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
- dlxmM = lxM - lxm, dlymM = lyM - lym;
- for (int x = cxm; x<=cxM; ++x) {
- const int
- xxm = x - xm,
- lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM,
- ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM;
- const tl *const lig = &light._atXY(lx,ly);
- cimg_forC(*this,c) {
- const tc col = color[c];
- const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
- const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
- ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
- }
- ++ptrd;
- }
- }
- }
- return *this;
- }
- //! Draw a Phong-shaded 2D triangle, with z-buffering.
- template<typename tz, typename tc, typename tl>
- CImg<T>& draw_triangle(CImg<tz>& zbuffer,
- int x0, int y0, const float z0,
- int x1, int y1, const float z1,
- int x2, int y2, const float z2,
- const tc *const color,
- const CImg<tl>& light,
- int lx0, int ly0,
- int lx1, int ly1,
- int lx2, int ly2,
- const float opacity=1) {
- if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
- if (!color)
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Specified color is (null).",
- cimg_instance);
- if (light._depth>1 || light._spectrum<_spectrum)
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
- cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
- if (!is_sameXY(zbuffer))
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
- "different dimensions.",
- cimg_instance,
- zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
- if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,
- +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
- float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
- if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,lx0,lx1,ly0,ly1);
- if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,lx0,lx2,ly0,ly2);
- if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,lx1,lx2,ly1,ly2);
- if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
- const int
- w1 = width() - 1, h1 = height() - 1,
- dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
- dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
- cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
- hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
- dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1,
- dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1,
- hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2,
- hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2;
- const float diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1;
- const ulongT lwhd = (ulongT)light._width*light._height*light._depth;
- cimg_init_scanline(opacity);
- for (int y = cy0; y<=cy2; ++y) {
- const int yy0 = y - y0, yy1 = y - y1;
- int
- xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
- xM = x0 + (dx02*yy0 + hdy02)/dy02,
- lxm = y<y1?lx0 + (dlx01*yy0 + hdy01lx)/dy01:lx1 + (dlx12*yy1 + hdy12lx)/dy12,
- lxM = lx0 + (dlx02*yy0 + hdy02lx)/dy02,
- lym = y<y1?ly0 + (dly01*yy0 + hdy01ly)/dy01:ly1 + (dly12*yy1 + hdy12ly)/dy12,
- lyM = ly0 + (dly02*yy0 + hdy02ly)/dy02;
- float
- izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
- izM = iz0 + diz02*yy0/dy02;
- if (xm>xM) cimg::swap(xm,xM,lxm,lxM,lym,lyM,izm,izM);
- if (xM>=0 && xm<=w1) {
- const int
- cxm = cimg::cut(xm,0,w1),
- cxM = cimg::cut(xM,0,w1);
- T *ptrd = data(cxm,y);
- tz *ptrz = zbuffer.data(cxm,y);
- const int
- dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
- dlxmM = lxM - lxm, dlymM = lyM - lym;
- const float dizmM = izM - izm;
- for (int x = cxm; x<=cxM; ++x) {
- const int xxm = x - xm;
- const float iz = izm + dizmM*xxm/dxmM;
- if (iz>=*ptrz) {
- *ptrz = (tz)iz;
- const int
- lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM,
- ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM;
- const tl *const lig = &light._atXY(lx,ly);
- cimg_forC(*this,c) {
- const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
- const tc col = color[c];
- const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
- ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
- }
- }
- ++ptrd; ++ptrz;
- }
- }
- }
- return *this;
- }
- //! Draw a textured Gouraud-shaded 2D triangle.
- /**
- \param x0 X-coordinate of the first vertex in the image instance.
- \param y0 Y-coordinate of the first vertex in the image instance.
- \param x1 X-coordinate of the second vertex in the image instance.
- \param y1 Y-coordinate of the second vertex in the image instance.
- \param x2 X-coordinate of the third vertex in the image instance.
- \param y2 Y-coordinate of the third vertex in the image instance.
- \param texture Texture image used to fill the triangle.
- \param tx0 X-coordinate of the first vertex in the texture image.
- \param ty0 Y-coordinate of the first vertex in the texture image.
- \param tx1 X-coordinate of the second vertex in the texture image.
- \param ty1 Y-coordinate of the second vertex in the texture image.
- \param tx2 X-coordinate of the third vertex in the texture image.
- \param ty2 Y-coordinate of the third vertex in the texture image.
- \param bs0 Brightness factor of the first vertex.
- \param bs1 Brightness factor of the second vertex.
- \param bs2 Brightness factor of the third vertex.
- \param opacity Drawing opacity.
- **/
- template<typename tc>
- CImg<T>& draw_triangle(int x0, int y0,
- int x1, int y1,
- int x2, int y2,
- const CImg<tc>& texture,
- int tx0, int ty0,
- int tx1, int ty1,
- int tx2, int ty2,
- float bs0,
- float bs1,
- float bs2,
- const float opacity=1) {
- if (is_empty()) return *this;
- if (texture._depth>1 || texture._spectrum<_spectrum)
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
- cimg_instance,
- texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
- if (is_overlapped(texture))
- return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
- bs0,bs1,bs2,opacity);
- if (y0>y1) cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1,bs0,bs1);
- if (y0>y2) cimg::swap(x0,x2,y0,y2,tx0,tx2,ty0,ty2,bs0,bs2);
- if (y1>y2) cimg::swap(x1,x2,y1,y2,tx1,tx2,ty1,ty2,bs1,bs2);
- if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
- const int
- w1 = width() - 1, h1 = height() - 1,
- dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
- dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
- cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
- hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
- dtx01 = tx1 - tx0, dtx02 = tx2 - tx0, dtx12 = tx2 - tx1,
- dty01 = ty1 - ty0, dty02 = ty2 - ty0, dty12 = ty2 - ty1,
- hdy01tx = dy01*cimg::sign(dtx01)/2, hdy02tx = dy02*cimg::sign(dtx02)/2, hdy12tx = dy12*cimg::sign(dtx12)/2,
- hdy01ty = dy01*cimg::sign(dty01)/2, hdy02ty = dy02*cimg::sign(dty02)/2, hdy12ty = dy12*cimg::sign(dty12)/2;
- const float dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
- const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
- cimg_init_scanline(opacity);
- for (int y = cy0; y<=cy2; ++y) {
- const int yy0 = y - y0, yy1 = y - y1;
- int
- xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
- xM = x0 + (dx02*yy0 + hdy02)/dy02,
- txm = y<y1?tx0 + (dtx01*yy0 + hdy01tx)/dy01:tx1 + (dtx12*yy1 + hdy12tx)/dy12,
- txM = tx0 + (dtx02*yy0 + hdy02tx)/dy02,
- tym = y<y1?ty0 + (dty01*yy0 + hdy01ty)/dy01:ty1 + (dty12*yy1 + hdy12ty)/dy12,
- tyM = ty0 + (dty02*yy0 + hdy02ty)/dy02;
- float
- bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
- bsM = bs0 + dbs02*yy0/dy02;
- if (xm>xM) cimg::swap(xm,xM,txm,txM,tym,tyM,bsm,bsM);
- if (xM>=0 && xm<=w1) {
- const int
- cxm = cimg::cut(xm,0,w1),
- cxM = cimg::cut(xM,0,w1);
- T *ptrd = data(cxm,y);
- const int
- dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
- dtxmM = txM - txm, dtymM = tyM - tym;
- const float dbsmM = bsM - bsm;
- for (int x = cxm; x<=cxM; ++x) {
- const int
- xxm = x - xm,
- tx = (txm*dxmM + dtxmM*xxm + hdxmM)/dxmM,
- ty = (tym*dxmM + dtymM*xxm + hdxmM)/dxmM;
- const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
- const tc *const color = &texture._atXY(tx,ty);
- cimg_forC(*this,c) {
- const tc col = color[c*twhd];
- const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
- ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
- }
- ++ptrd;
- }
- }
- }
- return *this;
- }
- //! Draw a textured Gouraud-shaded 2D triangle, with perspective correction \overloading.
- template<typename tc>
- CImg<T>& draw_triangle(int x0, int y0, const float z0,
- int x1, int y1, const float z1,
- int x2, int y2, const float z2,
- const CImg<tc>& texture,
- int tx0, int ty0,
- int tx1, int ty1,
- int tx2, int ty2,
- float bs0,
- float bs1,
- float bs2,
- const float opacity=1) {
- if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
- if (texture._depth>1 || texture._spectrum<_spectrum)
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
- cimg_instance,
- texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
- if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
- bs0,bs1,bs2,opacity);
- float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
- if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,bs0,bs1);
- if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,bs0,bs2);
- if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,bs1,bs2);
- if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
- const int
- w1 = width() - 1, h1 = height() - 1,
- dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
- dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
- cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
- hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
- const float
- diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
- txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
- tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
- dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
- dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1,
- dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
- const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
- cimg_init_scanline(opacity);
- for (int y = cy0; y<=cy2; ++y) {
- const int yy0 = y - y0, yy1 = y - y1;
- int
- xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
- xM = x0 + (dx02*yy0 + hdy02)/dy02;
- float
- izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
- izM = iz0 + diz02*yy0/dy02,
- txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
- txzM = txz0 + dtxz02*yy0/dy02,
- tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
- tyzM = tyz0 + dtyz02*yy0/dy02,
- bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
- bsM = bs0 + dbs02*yy0/dy02;
- if (xm>xM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM,bsm,bsM);
- if (xM>=0 && xm<=w1) {
- const int
- cxm = cimg::cut(xm,0,w1),
- cxM = cimg::cut(xM,0,w1);
- T *ptrd = data(cxm,y);
- const int dxmM = std::max(1,xM - xm);
- const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm, dbsmM = bsM - bsm;
- for (int x = cxm; x<=cxM; ++x) {
- const int xxm = x - xm;
- const float
- iz = izm + dizmM*xxm/dxmM,
- txz = txzm + dtxzmM*xxm/dxmM,
- tyz = tyzm + dtyzmM*xxm/dxmM,
- cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
- const int
- tx = (int)cimg::round(txz/iz),
- ty = (int)cimg::round(tyz/iz);
- const tc *const color = &texture._atXY(tx,ty);
- cimg_forC(*this,c) {
- const tc col = color[c*twhd];
- const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
- ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
- }
- ++ptrd;
- }
- }
- }
- return *this;
- }
- //! Draw a textured Gouraud-shaded 2D triangle, with perspective correction and z-buffering \overloading.
- template<typename tz, typename tc>
- CImg<T>& draw_triangle(CImg<tz>& zbuffer,
- int x0, int y0, const float z0,
- int x1, int y1, const float z1,
- int x2, int y2, const float z2,
- const CImg<tc>& texture,
- int tx0, int ty0,
- int tx1, int ty1,
- int tx2, int ty2,
- float bs0,
- float bs1,
- float bs2,
- const float opacity=1) {
- if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
- if (!is_sameXY(zbuffer))
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
- "different dimensions.",
- cimg_instance,
- zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
- if (texture._depth>1 || texture._spectrum<_spectrum)
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
- cimg_instance,
- texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
- if (is_overlapped(texture))
- return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,bs0,bs1,bs2,opacity);
- float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
- if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,bs0,bs1);
- if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,bs0,bs2);
- if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,bs1,bs2);
- if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
- const int
- w1 = width() - 1, h1 = height() - 1,
- dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
- dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
- cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
- hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
- const float
- diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
- txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
- tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
- dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
- dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1,
- dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
- const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
- cimg_init_scanline(opacity);
- for (int y = cy0; y<=cy2; ++y) {
- const int yy0 = y - y0, yy1 = y - y1;
- int
- xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
- xM = x0 + (dx02*yy0 + hdy02)/dy02;
- float
- izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
- izM = iz0 + diz02*yy0/dy02,
- txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
- txzM = txz0 + dtxz02*yy0/dy02,
- tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
- tyzM = tyz0 + dtyz02*yy0/dy02,
- bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
- bsM = bs0 + dbs02*yy0/dy02;
- if (xm>xM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM,bsm,bsM);
- if (xM>=0 && xm<=w1) {
- const int
- cxm = cimg::cut(xm,0,w1),
- cxM = cimg::cut(xM,0,w1);
- T *ptrd = data(cxm,y);
- tz *ptrz = zbuffer.data(cxm,y);
- const int dxmM = std::max(1,xM - xm);
- const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm, dbsmM = bsM - bsm;
- for (int x = cxm; x<=cxM; ++x) {
- const int xxm = x - xm;
- const float iz = izm + dizmM*xxm/dxmM;
- if (iz>=*ptrz) {
- *ptrz = (tz)iz;
- const float
- txz = txzm + dtxzmM*xxm/dxmM,
- tyz = tyzm + dtyzmM*xxm/dxmM,
- cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
- const int
- tx = (int)cimg::round(txz/iz),
- ty = (int)cimg::round(tyz/iz);
- const tc *const color = &texture._atXY(tx,ty);
- cimg_forC(*this,c) {
- const tc col = color[c*twhd];
- const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
- ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
- }
- }
- ++ptrd; ++ptrz;
- }
- }
- }
- return *this;
- }
- //! Draw a textured Phong-shaded 2D triangle.
- /**
- \param x0 X-coordinate of the first vertex in the image instance.
- \param y0 Y-coordinate of the first vertex in the image instance.
- \param x1 X-coordinate of the second vertex in the image instance.
- \param y1 Y-coordinate of the second vertex in the image instance.
- \param x2 X-coordinate of the third vertex in the image instance.
- \param y2 Y-coordinate of the third vertex in the image instance.
- \param texture Texture image used to fill the triangle.
- \param tx0 X-coordinate of the first vertex in the texture image.
- \param ty0 Y-coordinate of the first vertex in the texture image.
- \param tx1 X-coordinate of the second vertex in the texture image.
- \param ty1 Y-coordinate of the second vertex in the texture image.
- \param tx2 X-coordinate of the third vertex in the texture image.
- \param ty2 Y-coordinate of the third vertex in the texture image.
- \param light Light image.
- \param lx0 X-coordinate of the first vertex in the light image.
- \param ly0 Y-coordinate of the first vertex in the light image.
- \param lx1 X-coordinate of the second vertex in the light image.
- \param ly1 Y-coordinate of the second vertex in the light image.
- \param lx2 X-coordinate of the third vertex in the light image.
- \param ly2 Y-coordinate of the third vertex in the light image.
- \param opacity Drawing opacity.
- **/
- template<typename tc, typename tl>
- CImg<T>& draw_triangle(int x0, int y0,
- int x1, int y1,
- int x2, int y2,
- const CImg<tc>& texture,
- int tx0, int ty0,
- int tx1, int ty1,
- int tx2, int ty2,
- const CImg<tl>& light,
- int lx0, int ly0,
- int lx1, int ly1,
- int lx2, int ly2,
- const float opacity=1) {
- if (is_empty()) return *this;
- if (texture._depth>1 || texture._spectrum<_spectrum)
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
- cimg_instance,
- texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
- if (light._depth>1 || light._spectrum<_spectrum)
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
- cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
- if (is_overlapped(texture))
- return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
- if (is_overlapped(light))
- return draw_triangle(x0,y0,x1,y1,x2,y2,texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
- if (y0>y1) cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1,lx0,lx1,ly0,ly1);
- if (y0>y2) cimg::swap(x0,x2,y0,y2,tx0,tx2,ty0,ty2,lx0,lx2,ly0,ly2);
- if (y1>y2) cimg::swap(x1,x2,y1,y2,tx1,tx2,ty1,ty2,lx1,lx2,ly1,ly2);
- if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
- const int
- w1 = width() - 1, h1 = height() - 1,
- dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
- dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
- cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
- hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
- dtx01 = tx1 - tx0, dtx02 = tx2 - tx0, dtx12 = tx2 - tx1,
- dty01 = ty1 - ty0, dty02 = ty2 - ty0, dty12 = ty2 - ty1,
- hdy01tx = dy01*cimg::sign(dtx01)/2, hdy02tx = dy02*cimg::sign(dtx02)/2, hdy12tx = dy12*cimg::sign(dtx12)/2,
- hdy01ty = dy01*cimg::sign(dty01)/2, hdy02ty = dy02*cimg::sign(dty02)/2, hdy12ty = dy12*cimg::sign(dty12)/2,
- dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1,
- dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1,
- hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2,
- hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2;
- const ulongT
- twhd = (ulongT)texture._width*texture._height*texture._depth,
- lwhd = (ulongT)light._width*light._height*light._depth;
- cimg_init_scanline(opacity);
- for (int y = cy0; y<=cy2; ++y) {
- const int yy0 = y - y0, yy1 = y - y1;
- int
- xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
- xM = x0 + (dx02*yy0 + hdy02)/dy02,
- txm = y<y1?tx0 + (dtx01*yy0 + hdy01tx)/dy01:tx1 + (dtx12*yy1 + hdy12tx)/dy12,
- txM = tx0 + (dtx02*yy0 + hdy02tx)/dy02,
- tym = y<y1?ty0 + (dty01*yy0 + hdy01ty)/dy01:ty1 + (dty12*yy1 + hdy12ty)/dy12,
- tyM = ty0 + (dty02*yy0 + hdy02ty)/dy02,
- lxm = y<y1?lx0 + (dlx01*yy0 + hdy01lx)/dy01:lx1 + (dlx12*yy1 + hdy12lx)/dy12,
- lxM = lx0 + (dlx02*yy0 + hdy02lx)/dy02,
- lym = y<y1?ly0 + (dly01*yy0 + hdy01ly)/dy01:ly1 + (dly12*yy1 + hdy12ly)/dy12,
- lyM = ly0 + (dly02*yy0 + hdy02ly)/dy02;
- if (xm>xM) cimg::swap(xm,xM,txm,txM,tym,tyM,lxm,lxM,lym,lyM);
- if (xM>=0 && xm<=w1) {
- const int
- cxm = cimg::cut(xm,0,w1),
- cxM = cimg::cut(xM,0,w1);
- T *ptrd = data(cxm,y);
- const int
- dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
- dtxmM = txM - txm, dtymM = tyM - tym,
- dlxmM = lxM - lxm, dlymM = lyM - lym;
- for (int x = cxm; x<=cxM; ++x) {
- const int
- xxm = x - xm,
- tx = (txm*dxmM + dtxmM*xxm + hdxmM)/dxmM,
- ty = (tym*dxmM + dtymM*xxm + hdxmM)/dxmM,
- lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM,
- ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM;
- const tc *const color = &texture._atXY(tx,ty);
- const tl *const lig = &light._atXY(lx,ly);
- cimg_forC(*this,c) {
- const tc col = color[c*twhd];
- const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
- const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
- ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
- }
- ++ptrd;
- }
- }
- }
- return *this;
- }
- //! Draw a textured Phong-shaded 2D triangle, with perspective correction.
- template<typename tc, typename tl>
- CImg<T>& draw_triangle(int x0, int y0, const float z0,
- int x1, int y1, const float z1,
- int x2, int y2, const float z2,
- const CImg<tc>& texture,
- int tx0, int ty0,
- int tx1, int ty1,
- int tx2, int ty2,
- const CImg<tl>& light,
- int lx0, int ly0,
- int lx1, int ly1,
- int lx2, int ly2,
- const float opacity=1) {
- if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
- if (texture._depth>1 || texture._spectrum<_spectrum)
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
- cimg_instance,
- texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
- if (light._depth>1 || light._spectrum<_spectrum)
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
- cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
- if (is_overlapped(texture))
- return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
- light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
- if (is_overlapped(light))
- return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,texture,tx0,ty0,tx1,ty1,tx2,ty2,
- +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
- float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
- if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,lx0,lx1,ly0,ly1);
- if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,lx0,lx2,ly0,ly2);
- if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,lx1,lx2,ly1,ly2);
- if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
- const int
- w1 = width() - 1, h1 = height() - 1,
- dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
- dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
- cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
- hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
- const float
- diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
- txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
- tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
- dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
- dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1,
- lxz0 = lx0*iz0, lxz1 = lx1*iz1, lxz2 = lx2*iz2,
- lyz0 = ly0*iz0, lyz1 = ly1*iz1, lyz2 = ly2*iz2,
- dlxz01 = lxz1 - lxz0, dlxz02 = lxz2 - lxz0, dlxz12 = lxz2 - lxz1,
- dlyz01 = lyz1 - lyz0, dlyz02 = lyz2 - lyz0, dlyz12 = lyz2 - lyz1;
- const ulongT
- twhd = (ulongT)texture._width*texture._height*texture._depth,
- lwhd = (ulongT)light._width*light._height*light._depth;
- cimg_init_scanline(opacity);
- for (int y = cy0; y<=cy2; ++y) {
- const int yy0 = y - y0, yy1 = y - y1;
- int
- xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
- xM = x0 + (dx02*yy0 + hdy02)/dy02;
- float
- izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
- izM = iz0 + diz02*yy0/dy02,
- txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
- txzM = txz0 + dtxz02*yy0/dy02,
- tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
- tyzM = tyz0 + dtyz02*yy0/dy02,
- lxzm = y<y1?(lxz0 + dlxz01*yy0/dy01):(lxz1 + dlxz12*yy1/dy12),
- lxzM = lxz0 + dlxz02*yy0/dy02,
- lyzm = y<y1?(lyz0 + dlyz01*yy0/dy01):(lyz1 + dlyz12*yy1/dy12),
- lyzM = lyz0 + dlyz02*yy0/dy02;
- if (xm>xM) cimg::swap(xm,xM,izm,izM,txzm,txzM,tyzm,tyzM,lxzm,lxzM,lyzm,lyzM);
- if (xM>=0 && xm<=w1) {
- const int
- cxm = cimg::cut(xm,0,w1),
- cxM = cimg::cut(xM,0,w1);
- T *ptrd = data(cxm,y);
- const int dxmM = std::max(1,xM - xm);
- const float
- dizmM = izM - izm,
- dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm,
- dlxzmM = lxzM - lxzm, dlyzmM = lyzM - lyzm;
- for (int x = cxm; x<=cxM; ++x) {
- const int xxm = x - xm;
- const float
- iz = izm + dizmM*xxm/dxmM,
- txz = txzm + dtxzmM*xxm/dxmM,
- tyz = tyzm + dtyzmM*xxm/dxmM,
- lxz = lxzm + dlxzmM*xxm/dxmM,
- lyz = lyzm + dlyzmM*xxm/dxmM;
- const int
- tx = (int)cimg::round(txz/iz),
- ty = (int)cimg::round(tyz/iz),
- lx = (int)cimg::round(lxz/iz),
- ly = (int)cimg::round(lyz/iz);
- const tc *const color = &texture._atXY(tx,ty);
- const tl *const lig = &light._atXY(lx,ly);
- cimg_forC(*this,c) {
- const tc col = color[c*twhd];
- const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
- const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
- ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
- }
- ++ptrd;
- }
- }
- }
- return *this;
- }
- //! Draw a textured Phong-shaded 2D triangle, with perspective correction and z-buffering.
- template<typename tz, typename tc, typename tl>
- CImg<T>& draw_triangle(CImg<tz>& zbuffer,
- int x0, int y0, const float z0,
- int x1, int y1, const float z1,
- int x2, int y2, const float z2,
- const CImg<tc>& texture,
- int tx0, int ty0,
- int tx1, int ty1,
- int tx2, int ty2,
- const CImg<tl>& light,
- int lx0, int ly0,
- int lx1, int ly1,
- int lx2, int ly2,
- const float opacity=1) {
- if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
- if (!is_sameXY(zbuffer))
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
- "different dimensions.",
- cimg_instance,
- zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
- if (texture._depth>1 || texture._spectrum<_spectrum)
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
- cimg_instance,
- texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
- if (light._depth>1 || light._spectrum<_spectrum)
- throw CImgArgumentException(_cimg_instance
- "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
- cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
- if (is_overlapped(texture))
- return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,
- +texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
- if (is_overlapped(light))
- return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,
- texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
- float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
- if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,lx0,lx1,ly0,ly1);
- if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,lx0,lx2,ly0,ly2);
- if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,lx1,lx2,ly1,ly2);
- if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
- const int
- w1 = width() - 1, h1 = height() - 1,
- dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
- dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
- cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
- hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
- const float
- diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
- txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
- tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
- dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
- dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1,
- lxz0 = lx0*iz0, lxz1 = lx1*iz1, lxz2 = lx2*iz2,
- lyz0 = ly0*iz0, lyz1 = ly1*iz1, lyz2 = ly2*iz2,
- dlxz01 = lxz1 - lxz0, dlxz02 = lxz2 - lxz0, dlxz12 = lxz2 - lxz1,
- dlyz01 = lyz1 - lyz0, dlyz02 = lyz2 - lyz0, dlyz12 = lyz2 - lyz1;
- const ulongT
- twhd = (ulongT)texture._width*texture._height*texture._depth,
- lwhd = (ulongT)light._width*light._height*light._depth;
- cimg_init_scanline(opacity);
- for (int y = cy0; y<=cy2; ++y) {
- const int yy0 = y - y0, yy1 = y - y1;
- int
- xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
- xM = x0 + (dx02*yy0 + hdy02)/dy02;
- float
- izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
- izM = iz0 + diz02*yy0/dy02,
- txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
- txzM = txz0 + dtxz02*yy0/dy02,
- tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
- tyzM = tyz0 + dtyz02*yy0/dy02,
- lxzm = y<y1?(lxz0 + dlxz01*yy0/dy01):(lxz1 + dlxz12*yy1/dy12),
- lxzM = lxz0 + dlxz02*yy0/dy02,
- lyzm = y<y1?(lyz0 + dlyz01*yy0/dy01):(lyz1 + dlyz12*yy1/dy12),
- lyzM = lyz0 + dlyz02*yy0/dy02;
- if (xm>xM) cimg::swap(xm,xM,izm,izM,txzm,txzM,tyzm,tyzM,lxzm,lxzM,lyzm,lyzM);
- if (xM>=0 && xm<=w1) {
- const int
- cxm = cimg::cut(xm,0,w1),
- cxM = cimg::cut(xM,0,w1);
- T *ptrd = data(cxm,y);
- tz *ptrz = zbuffer.data(cxm,y);
- const int dxmM = std::max(1,xM - xm);
- const float
- dizmM = izM - izm,
- dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm,
- dlxzmM = lxzM - lxzm, dlyzmM = lyzM - lyzm;
- for (int x = cxm; x<=cxM; ++x) {
- const int xxm = x - xm;
- const float iz = izm + dizmM*xxm/dxmM;
- if (iz>=*ptrz) {
- *ptrz = (tz)iz;
- const float
- txz = txzm + dtxzmM*xxm/dxmM,
- tyz = tyzm + dtyzmM*xxm/dxmM,
- lxz = lxzm + dlxzmM*xxm/dxmM,
- lyz = lyzm + dlyzmM*xxm/dxmM;
- const int
- tx = (int)cimg::round(txz/iz),
- ty = (int)cimg::round(tyz/iz),
- lx = (int)cimg::round(lxz/iz),
- ly = (int)cimg::round(lyz/iz);
- const tc *const color = &texture._atXY(tx,ty);
- const tl *const lig = &light._atXY(lx,ly);
- cimg_forC(*this,c) {
- const tc col = color[c*twhd];
- const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
- const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
- ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
- }
- }
- ++ptrd; ++ptrz;
- }
- }
- }
- return *this;
- }
- //! Draw a filled 4D rectangle.
- /**
- \param x0 X-coordinate of the upper-left rectangle corner.
- \param y0 Y-coordinate of the upper-left rectangle corner.
- \param z0 Z-coordinate of the upper-left rectangle corner.
- \param c0 C-coordinate of the upper-left rectangle corner.
- \param x1 X-coordinate of the lower-right rectangle corner.
- \param y1 Y-coordinate of the lower-right rectangle corner.
- \param z1 Z-coordinate of the lower-right rectangle corner.
- \param c1 C-coordinate of the lower-right rectangle corner.
- \param val Scalar value used to fill the rectangle area.
- \param opacity Drawing opacity.
- **/
- CImg<T>& draw_rectangle(const int x0, const int y0, const int z0, const int c0,
- const int x1, const int y1, const int z1, const int c1,
- const T val, const float opacity=1) {
- if (is_empty()) return *this;
- const int
- nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
- ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0,
- nz0 = z0<z1?z0:z1, nz1 = z0^z1^nz0,
- nc0 = c0<c1?c0:c1, nc1 = c0^c1^nc0;
- const int
- lx = (1 + nx1 - nx0) + (nx1>=width()?width() - 1 - nx1:0) + (nx0<0?nx0:0),
- ly = (1 + ny1 - ny0) + (ny1>=height()?height() - 1 - ny1:0) + (ny0<0?ny0:0),
- lz = (1 + nz1 - nz0) + (nz1>=depth()?depth() - 1 - nz1:0) + (nz0<0?nz0:0),
- lc = (1 + nc1 - nc0) + (nc1>=spectrum()?spectrum() - 1 - nc1:0) + (nc0<0?nc0:0);
- const ulongT
- offX = (ulongT)_width - lx,
- offY = (ulongT)_width*(_height - ly),
- offZ = (ulongT)_width*_height*(_depth - lz);
- const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
- T *ptrd = data(nx0<0?0:nx0,ny0<0?0:ny0,nz0<0?0:nz0,nc0<0?0:nc0);
- if (lx>0 && ly>0 && lz>0 && lc>0)
- for (int v = 0; v<lc; ++v) {
- for (int z = 0; z<lz; ++z) {
- for (int y = 0; y<ly; ++y) {
- if (opacity>=1) {
- if (sizeof(T)!=1) { for (int x = 0; x<lx; ++x) *(ptrd++) = val; ptrd+=offX; }
- else { std::memset(ptrd,(int)val,lx); ptrd+=_width; }
- } else { for (int x = 0; x<lx; ++x) { *ptrd = (T)(nopacity*val + *ptrd*copacity); ++ptrd; } ptrd+=offX; }
- }
- ptrd+=offY;
- }
- ptrd+=offZ;
- }
- return *this;
- }
- //! Draw a filled 3D rectangle.
- /**
- \param x0 X-coordinate of the upper-left rectangle corner.
- \param y0 Y-coordinate of the upper-left rectangle corner.
- \param z0 Z-coordinate of the upper-left rectangle corner.
- \param x1 X-coordinate of the lower-right rectangle corner.
- \param y1 Y-coordinate of the lower-right rectangle corner.
- \param z1 Z-coordinate of the lower-right rectangle corner.
- \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
- \param opacity Drawing opacity.
- **/
- template<typename tc>
- CImg<T>& draw_rectangle(const int x0, const int y0, const int z0,
- const int x1, const int y1, const int z1,
- const tc *const color, const float opacity=1) {
- if (is_empty()) return *this;
- if (!color)
- throw CImgArgumentException(_cimg_instance
- "draw_rectangle(): Specified color is (null).",
- cimg_instance);
- cimg_forC(*this,c) draw_rectangle(x0,y0,z0,c,x1,y1,z1,c,(T)color[c],opacity);
- return *this;
- }
- //! Draw a filled 2D rectangle.
- /**
- \param x0 X-coordinate of the upper-left rectangle corner.
- \param y0 Y-coordinate of the upper-left rectangle corner.
- \param x1 X-coordinate of the lower-right rectangle corner.
- \param y1 Y-coordinate of the lower-right rectangle corner.
- \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
- \param opacity Drawing opacity.
- **/
- template<typename tc>
- CImg<T>& draw_rectangle(const int x0, const int y0,
- const int x1, const int y1,
- const tc *const color, const float opacity=1) {
- return draw_rectangle(x0,y0,0,x1,y1,_depth - 1,color,opacity);
- }
- //! Draw a outlined 2D rectangle \overloading.
- template<typename tc>
- CImg<T>& draw_rectangle(const int x0, const int y0,
- const int x1, const int y1,
- const tc *const color, const float opacity,
- const unsigned int pattern) {
- if (is_empty()) return *this;
- if (y0==y1) return draw_line(x0,y0,x1,y0,color,opacity,pattern,true);
- if (x0==x1) return draw_line(x0,y0,x0,y1,color,opacity,pattern,true);
- const int
- nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
- ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0;
- if (ny1==ny0 + 1) return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
- draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false);
- return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
- draw_line(nx1,ny0 + 1,nx1,ny1 - 1,color,opacity,pattern,false).
- draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false).
- draw_line(nx0,ny1 - 1,nx0,ny0 + 1,color,opacity,pattern,false);
- }
- //! Draw a filled 2D polygon.
- /**
- \param points Set of polygon vertices.
- \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
- \param opacity Drawing opacity.
- **/
- template<typename tp, typename tc>
- CImg<T>& draw_polygon(const CImg<tp>& points,
- const tc *const color, const float opacity=1) {
- if (is_empty() || !points) return *this;
- if (!color)
- throw CImgArgumentException(_cimg_instance
- "draw_polygon(): Specified color is (null).",
- cimg_instance);
- if (points.height()!=2)
- throw CImgArgumentException(_cimg_instance
- "draw_polygon(): Invalid specified point set (%u,%u,%u,%u).",
- cimg_instance,
- points._width,points._height,points._depth,points._spectrum);
- CImg<intT> ipoints;
- if (cimg::type<tp>::is_float()) ipoints = points.get_round();
- else ipoints.assign(points,cimg::type<tp>::string()==cimg::type<int>::string());
- if (ipoints._width==1) return draw_point(ipoints(0,0),ipoints(0,1),color,opacity);
- if (ipoints._width==2) return draw_line(ipoints(0,0),ipoints(0,1),ipoints(1,0),ipoints(1,1),color,opacity);
- if (ipoints._width==3) return draw_triangle(ipoints(0,0),ipoints(0,1),ipoints(1,0),ipoints(1,1),
- ipoints(2,0),ipoints(2,1),color,opacity);
- cimg_init_scanline(opacity);
- int
- xmin = 0, ymin = 0,
- xmax = ipoints.get_shared_row(0).max_min(xmin),
- ymax = ipoints.get_shared_row(1).max_min(ymin);
- if (xmax<0 || xmin>=width() || ymax<0 || ymin>=height()) return *this;
- if (ymin==ymax) return draw_line(xmin,ymin,xmax,ymax,color,opacity);
- ymin = std::max(0,ymin);
- ymax = std::min(height() - 1,ymax);
- CImg<intT> Xs(ipoints._width,ymax - ymin + 1);
- CImg<uintT> count(Xs._height,1,1,1,0);
- unsigned int n = 0, nn = 1;
- bool go_on = true;
- while (go_on) {
- unsigned int an = (nn + 1)%ipoints._width;
- const int x0 = ipoints(n,0), y0 = ipoints(n,1);
- if (ipoints(nn,1)==y0) while (ipoints(an,1)==y0) { nn = an; (an+=1)%=ipoints._width; }
- const int x1 = ipoints(nn,0), y1 = ipoints(nn,1);
- unsigned int tn = an;
- while (ipoints(tn,1)==y1) (tn+=1)%=ipoints._width;
- if (y0!=y1) {
- const int
- y2 = ipoints(tn,1),
- x01 = x1 - x0, y01 = y1 - y0, y12 = y2 - y1,
- step = cimg::sign(y01),
- tmax = std::max(1,cimg::abs(y01)),
- htmax = tmax*cimg::sign(x01)/2,
- tend = tmax - (step==cimg::sign(y12));
- unsigned int y = (unsigned int)y0 - ymin;
- for (int t = 0; t<=tend; ++t, y+=step)
- if (y<Xs._height) Xs(count[y]++,y) = x0 + (t*x01 + htmax)/tmax;
- }
- go_on = nn>n;
- n = nn;
- nn = an;
- }
- cimg_pragma_openmp(parallel for cimg_openmp_if(Xs._height>=(cimg_openmp_sizefactor)*512))
- cimg_forY(Xs,y) {
- const CImg<intT> Xsy = Xs.get_shared_points(0,count[y] - 1,y).sort();
- int px = width();
- for (unsigned int k = 0; k<Xsy._width; k+=2) {
- int x0 = Xsy[k];
- const int x1 = Xsy[k + 1];
- x0+=x0==px;
- cimg_draw_scanline(x0,x1,y + ymin,color,opacity,1);
- px = x1;
- }
- }
- return *this;
- }
- //! Draw a outlined 2D or 3D polygon \overloading.
- template<typename tp, typename tc>
- CImg<T>& draw_polygon(const CImg<tp>& points,
- const tc *const color, const float opacity, const unsigned int pattern) {
- if (is_empty() || !points) return *this;
- if (!color)
- throw CImgArgumentException(_cimg_instance
- "draw_polygon(): Specified color is (null).",
- cimg_instance);
- if (points.height()!=2)
- throw CImgArgumentException(_cimg_instance
- "draw_polygon(): Invalid specified point set (%u,%u,%u,%u).",
- cimg_instance,
- points._width,points._height,points._depth,points._spectrum);
- CImg<intT> ipoints;
- if (cimg::type<tp>::is_float()) ipoints = points.get_round();
- else ipoints.assign(points,cimg::type<tp>::string()==cimg::type<int>::string());
- if (ipoints._width==1) return draw_point(ipoints(0,0),ipoints(0,1),color,opacity);
- if (ipoints._width==2) return draw_line(ipoints(0,0),ipoints(0,1),ipoints(1,0),ipoints(1,1),
- color,opacity,pattern);
- if (ipoints._width==3) return draw_triangle(ipoints(0,0),ipoints(0,1),ipoints(1,0),ipoints(1,1),
- ipoints(2,0),ipoints(2,1),color,opacity,pattern);
- bool ninit_hatch = true;
- const int x0 = ipoints(0,0), y0 = ipoints(0,1);
- int ox = x0, oy = y0;
- for (unsigned int i = 1; i<ipoints._width; ++i) {
- const int x = ipoints(i,0), y = ipoints(i,1);
- draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
- ninit_hatch = false;
- ox = x; oy = y;
- }
- draw_line(ox,oy,x0,y0,color,opacity,pattern,false);
- return *this;
- }
- //! Draw a filled 2D ellipse.
- /**
- \param x0 X-coordinate of the ellipse center.
- \param y0 Y-coordinate of the ellipse center.
- \param r1 First radius of the ellipse.
- \param r2 Second radius of the ellipse.
- \param angle Angle of the first radius.
- \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
- \param opacity Drawing opacity.
- **/
- template<typename tc>
- CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
- const tc *const color, const float opacity=1) {
- return _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,0U,true);
- }
- //! Draw a filled 2D ellipse \overloading.
- /**
- \param x0 X-coordinate of the ellipse center.
- \param y0 Y-coordinate of the ellipse center.
- \param tensor Diffusion tensor describing the ellipse.
- \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
- \param opacity Drawing opacity.
- **/
- template<typename t, typename tc>
- CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
- const tc *const color, const float opacity=1) {
- CImgList<t> eig = tensor.get_symmetric_eigen();
- const CImg<t> &val = eig[0], &vec = eig[1];
- return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)),
- std::atan2(vec(0,1),vec(0,0))*180/cimg::PI,
- color,opacity);
- }
- //! Draw an outlined 2D ellipse.
- /**
- \param x0 X-coordinate of the ellipse center.
- \param y0 Y-coordinate of the ellipse center.
- \param r1 First radius of the ellipse.
- \param r2 Second radius of the ellipse.
- \param angle Angle of the first radius.
- \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
- \param opacity Drawing opacity.
- \param pattern An integer whose bits describe the outline pattern.
- **/
- template<typename tc>
- CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
- const tc *const color, const float opacity, const unsigned int pattern) {
- if (pattern) _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,pattern,false);
- return *this;
- }
- //! Draw an outlined 2D ellipse \overloading.
- /**
- \param x0 X-coordinate of the ellipse center.
- \param y0 Y-coordinate of the ellipse center.
- \param tensor Diffusion tensor describing the ellipse.
- \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
- \param opacity Drawing opacity.
- \param pattern An integer whose bits describe the outline pattern.
- **/
- template<typename t, typename tc>
- CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
- const tc *const color, const float opacity,
- const unsigned int pattern) {
- CImgList<t> eig = tensor.get_symmetric_eigen();
- const CImg<t> &val = eig[0], &vec = eig[1];
- return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)),
- std::atan2(vec(0,1),vec(0,0))*180/cimg::PI,
- color,opacity,pattern);
- }
- template<typename tc>
- CImg<T>& _draw_ellipse(const int x0, const int y0, const float radius1, const float radius2, const float angle,
- const tc *const color, const float opacity,
- const unsigned int pattern, const bool is_filled) {
- if (is_empty() || (!is_filled && !pattern)) return *this;
- const float radiusM = std::max(radius1,radius2);
- if (radius1<0 || radius2<0 || x0 - radiusM>=width() || y0 + radiusM<0 || y0 - radiusM>=height()) return *this;
- if (!color)
- throw CImgArgumentException(_cimg_instance
- "draw_ellipse(): Specified color is (null).",
- cimg_instance);
- const int iradius1 = (int)cimg::round(radius1), iradius2 = (int)cimg::round(radius2);
- if (!iradius1 && !iradius2) return draw_point(x0,y0,color,opacity);
- if (iradius1==iradius2) {
- if (is_filled) return draw_circle(x0,y0,iradius1,color,opacity);
- else if (pattern==~0U) return draw_circle(x0,y0,iradius1,color,opacity,pattern);
- }
- const float ang = (float)(angle*cimg::PI/180);
- if (!is_filled) { // Outlined
- const float ca = std::cos(ang), sa = std::sin(ang);
- CImg<int> points((unsigned int)cimg::round(6*radiusM),2);
- cimg_forX(points,k) {
- const float
- _ang = (float)(2*cimg::PI*k/points._width),
- X = (float)(radius1*std::cos(_ang)),
- Y = (float)(radius2*std::sin(_ang));
- points(k,0) = (int)cimg::round(x0 + (X*ca - Y*sa));
- points(k,1) = (int)cimg::round(y0 + (X*sa + Y*ca));
- }
- draw_polygon(points,color,opacity,pattern);
- } else { // Filled
- cimg_init_scanline(opacity);
- const float
- ca = std::cos(ang),
- sa = -std::sin(ang),
- ca2 = ca*ca,
- sa2 = sa*sa,
- casa = ca*sa,
- i1 = 1/cimg::sqr(radius1),
- i2 = 1/cimg::sqr(radius2),
- t1 = i1*ca2 + i2*sa2,
- t2 = (i2 - i1)*casa,
- t3 = i2*ca2 + i1*sa2,
- t12 = t1*2;
- const int
- _ymin = (int)std::floor(y0 - radiusM),
- _ymax = (int)std::ceil(y0 + radiusM),
- ymin = _ymin<0?0:_ymin,
- ymax = _ymax>=height()?height() - 1:_ymax;
- for (int y = ymin; y<=ymax; ++y) {
- const float
- Y = y - y0 + 0.5f,
- B = 2*t2*Y,
- C = t3*Y*Y - 1,
- D = B*B - 4*t1*C;
- if (D>=0) {
- const float sD = std::sqrt(D);
- const int
- xmin = (int)(x0 + cimg::round((-B - sD)/t12)),
- xmax = (int)(x0 + cimg::round((-B + sD)/t12));
- cimg_draw_scanline(xmin,xmax,y,color,opacity,1);
- }
- }
- }
- return *this;
- }
- //! Draw a filled 2D circle.
- /**
- \param x0 X-coordinate of the circle center.
- \param y0 Y-coordinate of the circle center.
- \param radius Circle radius.
- \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
- \param opacity Drawing opacity.
- \note
- - Circle version of the Bresenham's algorithm is used.
- **/
- template<typename tc>
- CImg<T>& draw_circle(const int x0, const int y0, int radius,
- const tc *const color, const float opacity=1) {
- if (is_empty()) return *this;
- if (radius<0 || x0 - radius>=width() || y0 + radius<0 || y0 - radius>=height()) return *this;
- if (!color)
- throw CImgArgumentException(_cimg_instance
- "draw_circle(): Specified color is (null).",
- cimg_instance);
- if (!radius) return draw_point(x0,y0,color,opacity);
- cimg_init_scanline(opacity);
- if (y0>=0 && y0<height()) cimg_draw_scanline(x0 - radius,x0 + radius,y0,color,opacity,1);
- for (int f = 1 - radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
- if (f>=0) {
- const int x1 = x0 - x, x2 = x0 + x, y1 = y0 - y, y2 = y0 + y;
- if (y1>=0 && y1<height()) cimg_draw_scanline(x1,x2,y1,color,opacity,1);
- if (y2>=0 && y2<height()) cimg_draw_scanline(x1,x2,y2,color,opacity,1);
- f+=(ddFy+=2); --y;
- }
- const bool no_diag = y!=(x++);
- ++(f+=(ddFx+=2));
- const int x1 = x0 - y, x2 = x0 + y, y1 = y0 - x, y2 = y0 + x;
- if (no_diag) {
- if (y1>=0 && y1<height()) cimg_draw_scanline(x1,x2,y1,color,opacity,1);
- if (y2>=0 && y2<height()) cimg_draw_scanline(x1,x2,y2,color,opacity,1);
- }
- }
- return *this;
- }
- //! Draw an outlined 2D circle.
- /**
- \param x0 X-coordinate of the circle center.
- \param y0 Y-coordinate of the circle center.
- \param radius Circle radius.
- \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
- \param opacity Drawing opacity.
- \param pattern An integer whose bits describe the outline pattern.
- **/
- template<typename tc>
- CImg<T>& draw_circle(const int x0, const int y0, int radius,
- const tc *const color, const float opacity,
- const unsigned int pattern) {
- if (pattern!=~0U) return draw_ellipse(x0,y0,radius,radius,0,color,opacity,pattern);
- if (is_empty()) return *this;
- if (!color)
- throw CImgArgumentException(_cimg_instance
- "draw_circle(): Specified color is (null).",
- cimg_instance);
- if (radius<0 || x0 - radius>=width() || y0 + radius<0 || y0 - radius>=height()) return *this;
- if (!radius) return draw_point(x0,y0,color,opacity);
- draw_point(x0 - radius,y0,color,opacity).draw_point(x0 + radius,y0,color,opacity).
- draw_point(x0,y0 - radius,color,opacity).draw_point(x0,y0 + radius,color,opacity);
- if (radius==1) return *this;
- for (int f = 1 - radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
- if (f>=0) { f+=(ddFy+=2); --y; }
- ++x; ++(f+=(ddFx+=2));
- if (x!=y + 1) {
- const int x1 = x0 - y, x2 = x0 + y, y1 = y0 - x, y2 = y0 + x,
- x3 = x0 - x, x4 = x0 + x, y3 = y0 - y, y4 = y0 + y;
- draw_point(x1,y1,color,opacity).draw_point(x1,y2,color,opacity).
- draw_point(x2,y1,color,opacity).draw_point(x2,y2,color,opacity);
- if (x!=y)
- draw_point(x3,y3,color,opacity).draw_point(x4,y4,color,opacity).
- draw_point(x4,y3,color,opacity).draw_point(x3,y4,color,opacity);
- }
- }
- return *this;
- }
- //! Draw an image.
- /**
- \param sprite Sprite image.
- \param x0 X-coordinate of the sprite position.
- \param y0 Y-coordinate of the sprite position.
- \param z0 Z-coordinate of the sprite position.
- \param c0 C-coordinate of the sprite position.
- \param opacity Drawing opacity.
- **/
- template<typename t>
- CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
- const CImg<t>& sprite, const float opacity=1) {
- if (is_empty() || !sprite) return *this;
- if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
- if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared())
- return assign(sprite,false);
- const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0;
- const int
- dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0,
- sx0 = dx0 - x0, sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0,
- lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0),
- ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0),
- lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0),
- lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0);
- const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
- if (lx>0 && ly>0 && lz>0 && lc>0) {
- for (int c = 0; c<lc; ++c)
- for (int z = 0; z<lz; ++z)
- for (int y = 0; y<ly; ++y) {
- T *ptrd = data(dx0,dy0 + y,dz0 + z,dc0 + c);
- const t *ptrs = sprite.data(sx0,sy0 + y,sz0 + z,sc0 + c);
- if (opacity>=1) for (int x = 0; x<lx; ++x) *(ptrd++) = (T)*(ptrs++);
- else for (int x = 0; x<lx; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
- }
- }
- return *this;
- }
- //! Draw an image \specialization.
- CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
- const CImg<T>& sprite, const float opacity=1) {
- if (is_empty() || !sprite) return *this;
- if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
- if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared())
- return assign(sprite,false);
- const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0;
- const int
- dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0,
- sx0 = dx0 - x0, sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0,
- lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0),
- ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0),
- lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0),
- lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0);
- const ulongT slx = lx*sizeof(T);
- const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
- if (lx>0 && ly>0 && lz>0 && lc>0) {
- for (int c = 0; c<lc; ++c)
- for (int z = 0; z<lz; ++z)
- for (int y = 0; y<ly; ++y) {
- T *ptrd = data(dx0,dy0 + y,dz0 + z,dc0 + c);
- const T *ptrs = sprite.data(sx0,sy0 + y,sz0 + z,sc0 + c);
- if (opacity>=1) std::memcpy(ptrd,ptrs,slx);
- else for (int x = 0; x<lx; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
- }
- }
- return *this;
- }
- //! Draw an image \overloading.
- template<typename t>
- CImg<T>& draw_image(const int x0, const int y0, const int z0,
- const CImg<t>& sprite, const float opacity=1) {
- return draw_image(x0,y0,z0,0,sprite,opacity);
- }
- //! Draw an image \overloading.
- template<typename t>
- CImg<T>& draw_image(const int x0, const int y0,
- const CImg<t>& sprite, const float opacity=1) {
- return draw_image(x0,y0,0,sprite,opacity);
- }
- //! Draw an image \overloading.
- template<typename t>
- CImg<T>& draw_image(const int x0,
- const CImg<t>& sprite, const float opacity=1) {
- return draw_image(x0,0,sprite,opacity);
- }
- //! Draw an image \overloading.
- template<typename t>
- CImg<T>& draw_image(const CImg<t>& sprite, const float opacity=1) {
- return draw_image(0,sprite,opacity);
- }
- //! Draw a masked image.
- /**
- \param sprite Sprite image.
- \param mask Mask image.
- \param x0 X-coordinate of the sprite position in the image instance.
- \param y0 Y-coordinate of the sprite position in the image instance.
- \param z0 Z-coordinate of the sprite position in the image instance.
- \param c0 C-coordinate of the sprite position in the image instance.
- \param mask_max_value Maximum pixel value of the mask image \c mask.
- \param opacity Drawing opacity.
- \note
- - Pixel values of \c mask set the opacity of the corresponding pixels in \c sprite.
- - Dimensions along x,y and z of \p sprite and \p mask must be the same.
- **/
- template<typename ti, typename tm>
- CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
- const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
- const float mask_max_value=1) {
- if (is_empty() || !sprite || !mask) return *this;
- if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,mask,opacity,mask_max_value);
- if (is_overlapped(mask)) return draw_image(x0,y0,z0,c0,sprite,+mask,opacity,mask_max_value);
- if (mask._width!=sprite._width || mask._height!=sprite._height || mask._depth!=sprite._depth)
- throw CImgArgumentException(_cimg_instance
- "draw_image(): Sprite (%u,%u,%u,%u,%p) and mask (%u,%u,%u,%u,%p) have "
- "incompatible dimensions.",
- cimg_instance,
- sprite._width,sprite._height,sprite._depth,sprite._spectrum,sprite._data,
- mask._width,mask._height,mask._depth,mask._spectrum,mask._data);
- const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0;
- const int
- dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0,
- sx0 = dx0 - x0, sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0,
- lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0),
- ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0),
- lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0),
- lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0);
- const ulongT msize = mask.size();
- if (lx>0 && ly>0 && lz>0 && lc>0) {
- for (int c = 0; c<lc; ++c)
- for (int z = 0; z<lz; ++z)
- for (int y = 0; y<ly; ++y) {
- T *ptrd = data(dx0,dy0 + y,dz0 + z,dc0 + c);
- const ti *ptrs = sprite.data(sx0,sy0 + y,sz0 + z,sc0 + c);
- const tm *ptrm = mask._data + (mask.offset(sx0,sy0 + y,sz0 + z,sc0 + c)%msize);
- for (int x = 0; x<lx; ++x) {
- const float mopacity = (float)(*(ptrm++)*opacity),
- nopacity = cimg::abs(mopacity), copacity = mask_max_value - std::max(mopacity,0.f);
- *ptrd = (T)((nopacity*(*(ptrs++)) + *ptrd*copacity)/mask_max_value);
- ++ptrd;
- }
- }
- }
- return *this;
- }
- //! Draw a masked image \overloading.
- template<typename ti, typename tm>
- CImg<T>& draw_image(const int x0, const int y0, const int z0,
- const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
- const float mask_max_value=1) {
- return draw_image(x0,y0,z0,0,sprite,mask,opacity,mask_max_value);
- }
- //! Draw a image \overloading.
- template<typename ti, typename tm>
- CImg<T>& draw_image(const int x0, const int y0,
- const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
- const float mask_max_value=1) {
- return draw_image(x0,y0,0,sprite,mask,opacity,mask_max_value);
- }
- //! Draw a image \overloading.
- template<typename ti, typename tm>
- CImg<T>& draw_image(const int x0,
- const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
- const float mask_max_value=1) {
- return draw_image(x0,0,sprite,mask,opacity,mask_max_value);
- }
- //! Draw an image.
- template<typename ti, typename tm>
- CImg<T>& draw_image(const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
- const float mask_max_value=1) {
- return draw_image(0,sprite,mask,opacity,mask_max_value);
- }
- //! Draw a text string.
- /**
- \param x0 X-coordinate of the text in the image instance.
- \param y0 Y-coordinate of the text in the image instance.
- \param text Format of the text ('printf'-style format string).
- \param foreground_color Pointer to \c spectrum() consecutive values, defining the foreground drawing color.
- \param background_color Pointer to \c spectrum() consecutive values, defining the background drawing color.
- \param opacity Drawing opacity.
- \param font Font used for drawing text.
- **/
- template<typename tc1, typename tc2, typename t>
- CImg<T>& draw_text(const int x0, const int y0,
- const char *const text,
- const tc1 *const foreground_color, const tc2 *const background_color,
- const float opacity, const CImgList<t>* const font, ...) {
- if (!font || !*font) return *this;
- CImg<charT> tmp(2048);
- std::va_list ap; va_start(ap,font); cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
- return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,*font,false);
- }
- //! Draw a text string \overloading.
- /**
- \note A transparent background is used for the text.
- **/
- template<typename tc, typename t>
- CImg<T>& draw_text(const int x0, const int y0,
- const char *const text,
- const tc *const foreground_color, const int,
- const float opacity, const CImgList<t>* const font, ...) {
- if (!font || !*font) return *this;
- CImg<charT> tmp(2048);
- std::va_list ap; va_start(ap,font); cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
- return _draw_text(x0,y0,tmp,foreground_color,(tc*)0,opacity,*font,false);
- }
- //! Draw a text string \overloading.
- /**
- \note A transparent foreground is used for the text.
- **/
- template<typename tc, typename t>
- CImg<T>& draw_text(const int x0, const int y0,
- const char *const text,
- const int, const tc *const background_color,
- const float opacity, const CImgList<t>* const font, ...) {
- if (!font || !*font) return *this;
- CImg<charT> tmp(2048);
- std::va_list ap; va_start(ap,font); cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
- return _draw_text(x0,y0,tmp,(tc*)0,background_color,opacity,*font,false);
- }
- //! Draw a text string \overloading.
- /**
- \param x0 X-coordinate of the text in the image instance.
- \param y0 Y-coordinate of the text in the image instance.
- \param text Format of the text ('printf'-style format string).
- \param foreground_color Array of spectrum() values of type \c T,
- defining the foreground color (0 means 'transparent').
- \param background_color Array of spectrum() values of type \c T,
- defining the background color (0 means 'transparent').
- \param opacity Drawing opacity.
- \param font_height Height of the text font (exact match for 13,32,64,128, interpolated otherwise).
- **/
- template<typename tc1, typename tc2>
- CImg<T>& draw_text(const int x0, const int y0,
- const char *const text,
- const tc1 *const foreground_color, const tc2 *const background_color,
- const float opacity=1, const unsigned int font_height=13, ...) {
- if (!font_height) return *this;
- CImg<charT> tmp(2048);
- std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
- const CImgList<ucharT>& font = CImgList<ucharT>::font(font_height,true);
- _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,true);
- return *this;
- }
- //! Draw a text string \overloading.
- template<typename tc>
- CImg<T>& draw_text(const int x0, const int y0,
- const char *const text,
- const tc *const foreground_color, const int background_color=0,
- const float opacity=1, const unsigned int font_height=13, ...) {
- if (!font_height) return *this;
- cimg::unused(background_color);
- CImg<charT> tmp(2048);
- std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
- return draw_text(x0,y0,"%s",foreground_color,(const tc*)0,opacity,font_height,tmp._data);
- }
- //! Draw a text string \overloading.
- template<typename tc>
- CImg<T>& draw_text(const int x0, const int y0,
- const char *const text,
- const int, const tc *const background_color,
- const float opacity=1, const unsigned int font_height=13, ...) {
- if (!font_height) return *this;
- CImg<charT> tmp(2048);
- std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
- return draw_text(x0,y0,"%s",(tc*)0,background_color,opacity,font_height,tmp._data);
- }
- template<typename tc1, typename tc2, typename t>
- CImg<T>& _draw_text(const int x0, const int y0,
- const char *const text,
- const tc1 *const foreground_color, const tc2 *const background_color,
- const float opacity, const CImgList<t>& font,
- const bool is_native_font) {
- if (!text || !font) return *this;
- const unsigned int text_length = (unsigned int)std::strlen(text);
- const int padding_x = font[0]._height<48?1:font[0]._height<128?(int)std::ceil(font[0]._height/51.0f + 0.745f):4;
- unsigned char o_ch, ch = 0;
- int x, y, w;
- CImg<intT> left_paddings(text_length,1,1,1,0);
- const CImg<t> empty = CImg<t>::empty();
- if (is_empty() || is_native_font) {
- // Pre-compute necessary size of the image as well as left paddings of each character.
- x = y = w = 0;
- o_ch = 0;
- for (unsigned int i = 0; i<text_length; ++i) {
- ch = (unsigned char)text[i];
- switch (ch) {
- case '\n' :
- if (font._width>10) y+=font[10]._height; else y+=font[0]._height;
- if (x>w) w = x;
- x = 0;
- break;
- case '\t' :
- if (font._width>32) x+=4*font[32]._width; else x+=4*font[0]._width;
- break;
- case ' ' :
- if (font._width>32) x+=font[32]._width; else x+=font[0]._width;
- break;
- default : if (ch<font._width) {
- int left_padding = 0;
- if (is_native_font && font[0]._height<128) { /// Determine left padding for native fonts.
- if (ch==':' || ch=='!' || ch=='.' || ch==';')
- left_padding = 2*padding_x;
- else if (o_ch==',' || (o_ch=='.' && (ch<'0' || ch>'9')) || o_ch==';' || o_ch==':' || o_ch=='!')
- left_padding = 4*padding_x;
- else if (((o_ch=='i' || o_ch=='l' || o_ch=='I' || o_ch=='J' || o_ch=='M' || o_ch=='N') &&
- ((ch>='0' && ch<='9') ||
- (ch>='a' && ch<='z' && ch!='v' && ch!='x' && ch!='y') ||
- (ch>='B' && ch<='Z' && ch!='J' && ch!='T' && ch!='V' && ch!='X' && ch!='Y'))) ||
- o_ch=='.' || o_ch=='\'' || ch=='\'')
- left_padding = padding_x;
- else if ((o_ch<'0' || o_ch>'9') && ch!='-') {
- const CImg<t> &mask = ch + 256U<font._width?font[ch + 256]:empty;
- if (o_ch && ch>' ' && o_ch>' ' && mask._height>13) {
- const CImg<t> &o_mask = o_ch + 256U<font._width?font[o_ch + 256]:empty;
- if (o_mask._height>13) {
- const int w1 = mask.width()>0?o_mask.width() - 1:0, w2 = w1>1?w1 - 1:0, w3 = w2>1?w2 - 1:0;
- left_padding = -10;
- cimg_forY(mask,k) {
- const int
- lpad = o_mask(w1,k)>=8?0:
- o_mask._width<=2 || o_mask(w2,k)>=8?-1:
- o_mask._width<=3 || o_mask(w3,k)>=8?-2:-3,
- rpad = mask(0,k)>=8?0:
- mask._width<=2 || mask(1,k)>=8?-1:
- mask._width<=3 || mask(2,k)>=8?-2:-3;
- left_padding = std::max(left_padding,lpad + rpad);
- }
- }
- }
- }
- left_paddings[i] = left_padding;
- }
- x+=left_padding + font[ch]._width + padding_x;
- o_ch = ch;
- }
- }
- }
- if (x!=0 || ch=='\n') { if (x>w) w = x; y+=font[0]._height; }
- if (is_empty()) assign(x0 + w,y0 + y,1,is_native_font?1:font[0]._spectrum,(T)0);
- }
- // Draw font characters on image.
- x = x0; y = y0;
- for (unsigned int i = 0; i<text_length; ++i) {
- ch = (unsigned char)text[i];
- switch (ch) {
- case '\n' :
- if (font._width>10) y+=font[10]._height; else y+=font[0]._height;
- x = x0;
- break;
- case '\t' :
- case ' ' : {
- const unsigned int
- lw = (ch=='\t'?4:1)*font[font._width>32?32:0]._width,
- lh = font[font._width>32?32:0]._height;
- if (background_color) draw_rectangle(x,y,x + lw - 1,y + lh - 1,background_color,opacity);
- x+=lw;
- } break;
- default : if (ch<font._width) {
- if (font[ch]) {
- CImg<T> letter = font[ch];
- const CImg<t> &mask = ch + 256U<font._width?font[ch + 256]:empty;
- const int posx = x + left_paddings[i] + padding_x;
- for (unsigned int c = 0; c<_spectrum; c+=letter._spectrum) {
- if (c) letter = font[ch];
- const unsigned int cmin = std::min(_spectrum - c,letter._spectrum);
- for (unsigned int d = 0; d<cmin; ++d)
- if (foreground_color[c + d]!=255)
- letter.get_shared_channel(d)*=foreground_color[c + d]/255.0f;
- if (mask) { // Letter with alpha
- if (background_color)
- for (unsigned int d = 0; d<cmin; ++d)
- draw_rectangle(x,y,0,c + d,posx + letter._width - 1,y + letter._height - 1,0,c + d,
- background_color[c + d],opacity);
- draw_image(posx,y,0,c,letter,mask,opacity,255.f);
- } else // Letter without alpha
- draw_image(posx,y,0,c,letter,opacity);
- }
- x = posx + letter._width;
- }
- }
- }
- }
- return *this;
- }
- // [internal] Version used to display text in interactive viewers.
- CImg<T>& __draw_text(const char *const text, unsigned int &font_size, const int is_down, ...) {
- CImg<charT> tmp(2048);
- std::va_list ap;
- va_start(ap,is_down);
- cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
- CImg<ucharT> a_label, a_labelmask;
- const unsigned char a_labelcolor = 255;
- unsigned int ofs = font_size, fs = ofs;
- do { // Determine best font size
- a_label.assign().draw_text(0,0,"%s",&a_labelcolor,0,1,fs,tmp._data);
- if (a_label._width<7*_width/10 && a_label._height>_height/20 && a_label._height<_height/5) {
- font_size = fs; break;
- } else if ((a_label._width>7*_width/10 || a_label._height>_height/5) && fs>13 && ofs>=fs) {
- ofs = fs; fs = std::max(13U,(unsigned int)cimg::round(fs/1.25f));
- } else if (a_label._width<3*_width/10 && a_label._height<_height/20 && fs<64 && ofs<=fs) {
- ofs = fs; fs = std::min(64U,(unsigned int)cimg::round(fs*1.25f));
- } else { font_size = fs; break; }
- } while (true);
- a_label.normalize(0,255);
- a_label+=(255 - a_label.get_dilate(3)).normalize(0,80);
- a_label.resize(-100,-100,1,3,1);
- return draw_image(0,is_down?height() - a_label.height():0,a_label,0.85f);
- }
- //! Draw a 2D vector field.
- /**
- \param flow Image of 2D vectors used as input data.
- \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
- \param opacity Drawing opacity.
- \param sampling Length (in pixels) between each arrow.
- \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length).
- \param is_arrow Tells if arrows must be drawn, instead of oriented segments.
- \param pattern Used pattern to draw lines.
- \note Clipping is supported.
- **/
- template<typename t1, typename t2>
- CImg<T>& draw_quiver(const CImg<t1>& flow,
- const t2 *const color, const float opacity=1,
- const unsigned int sampling=25, const float factor=-20,
- const bool is_arrow=true, const unsigned int pattern=~0U) {
- return draw_quiver(flow,CImg<t2>(color,_spectrum,1,1,1,true),opacity,sampling,factor,is_arrow,pattern);
- }
- //! Draw a 2D vector field, using a field of colors.
- /**
- \param flow Image of 2D vectors used as input data.
- \param color Image of spectrum()-D vectors corresponding to the color of each arrow.
- \param opacity Opacity of the drawing.
- \param sampling Length (in pixels) between each arrow.
- \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length).
- \param is_arrow Tells if arrows must be drawn, instead of oriented segments.
- \param pattern Used pattern to draw lines.
- \note Clipping is supported.
- **/
- template<typename t1, typename t2>
- CImg<T>& draw_quiver(const CImg<t1>& flow,
- const CImg<t2>& color, const float opacity=1,
- const unsigned int sampling=25, const float factor=-20,
- const bool is_arrow=true, const unsigned int pattern=~0U) {
- if (is_empty()) return *this;
- if (!flow || flow._spectrum!=2)
- throw CImgArgumentException(_cimg_instance
- "draw_quiver(): Invalid dimensions of specified flow (%u,%u,%u,%u,%p).",
- cimg_instance,
- flow._width,flow._height,flow._depth,flow._spectrum,flow._data);
- if (sampling<=0)
- throw CImgArgumentException(_cimg_instance
- "draw_quiver(): Invalid sampling value %g "
- "(should be >0)",
- cimg_instance,
- sampling);
- const bool colorfield = (color._width==flow._width && color._height==flow._height &&
- color._depth==1 && color._spectrum==_spectrum);
- if (is_overlapped(flow)) return draw_quiver(+flow,color,opacity,sampling,factor,is_arrow,pattern);
- float vmax,fact;
- if (factor<=0) {
- float m, M = (float)flow.get_norm(2).max_min(m);
- vmax = (float)std::max(cimg::abs(m),cimg::abs(M));
- if (!vmax) vmax = 1;
- fact = -factor;
- } else { fact = factor; vmax = 1; }
- for (unsigned int y = sampling/2; y<_height; y+=sampling)
- for (unsigned int x = sampling/2; x<_width; x+=sampling) {
- const unsigned int X = x*flow._width/_width, Y = y*flow._height/_height;
- float u = (float)flow(X,Y,0,0)*fact/vmax, v = (float)flow(X,Y,0,1)*fact/vmax;
- if (is_arrow) {
- const int xx = (int)(x + u), yy = (int)(y + v);
- if (colorfield) draw_arrow(x,y,xx,yy,color.get_vector_at(X,Y)._data,opacity,45,sampling/5.f,pattern);
- else draw_arrow(x,y,xx,yy,color._data,opacity,45,sampling/5.f,pattern);
- } else {
- if (colorfield)
- draw_line((int)(x - 0.5*u),(int)(y - 0.5*v),(int)(x + 0.5*u),(int)(y + 0.5*v),
- color.get_vector_at(X,Y)._data,opacity,pattern);
- else draw_line((int)(x - 0.5*u),(int)(y - 0.5*v),(int)(x + 0.5*u),(int)(y + 0.5*v),
- color._data,opacity,pattern);
- }
- }
- return *this;
- }
- //! Draw a labeled horizontal axis.
- /**
- \param values_x Values along the horizontal axis.
- \param y Y-coordinate of the horizontal axis in the image instance.
- \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
- \param opacity Drawing opacity.
- \param pattern Drawing pattern.
- \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise).
- \param allow_zero Enable/disable the drawing of label '0' if found.
- **/
- template<typename t, typename tc>
- CImg<T>& draw_axis(const CImg<t>& values_x, const int y,
- const tc *const color, const float opacity=1,
- const unsigned int pattern=~0U, const unsigned int font_height=13,
- const bool allow_zero=true, const float round_x=0) {
- if (is_empty()) return *this;
- const int yt = (y + 3 + font_height)<_height?y + 3:y - 2 - (int)font_height;
- const int siz = (int)values_x.size() - 1;
- CImg<charT> txt(32);
- CImg<T> a_label;
- if (siz<=0) { // Degenerated case
- draw_line(0,y,_width - 1,y,color,opacity,pattern);
- if (!siz) {
- cimg_snprintf(txt,txt._width,"%g",round_x?cimg::round((double)*values_x,round_x):(double)*values_x);
- a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
- const int
- _xt = (width() - a_label.width())/2,
- xt = _xt<3?3:_xt + a_label.width()>=width() - 2?width() - 3 - a_label.width():_xt;
- draw_point(width()/2,y - 1,color,opacity).draw_point(width()/2,y + 1,color,opacity);
- if (allow_zero || *txt!='0' || txt[1]!=0)
- draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
- }
- } else { // Regular case
- if (values_x[0]<values_x[siz]) draw_arrow(0,y,_width - 1,y,color,opacity,30,5,pattern);
- else draw_arrow(_width - 1,y,0,y,color,opacity,30,5,pattern);
- cimg_foroff(values_x,x) {
- cimg_snprintf(txt,txt._width,"%g",round_x?cimg::round((double)values_x(x),round_x):(double)values_x(x));
- a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
- const int
- xi = (int)(x*(_width - 1)/siz),
- _xt = xi - a_label.width()/2,
- xt = _xt<3?3:_xt + a_label.width()>=width() - 2?width() - 3 - a_label.width():_xt;
- draw_point(xi,y - 1,color,opacity).draw_point(xi,y + 1,color,opacity);
- if (allow_zero || *txt!='0' || txt[1]!=0)
- draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
- }
- }
- return *this;
- }
- //! Draw a labeled vertical axis.
- /**
- \param x X-coordinate of the vertical axis in the image instance.
- \param values_y Values along the Y-axis.
- \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
- \param opacity Drawing opacity.
- \param pattern Drawing pattern.
- \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise).
- \param allow_zero Enable/disable the drawing of label '0' if found.
- **/
- template<typename t, typename tc>
- CImg<T>& draw_axis(const int x, const CImg<t>& values_y,
- const tc *const color, const float opacity=1,
- const unsigned int pattern=~0U, const unsigned int font_height=13,
- const bool allow_zero=true, const float round_y=0) {
- if (is_empty()) return *this;
- int siz = (int)values_y.size() - 1;
- CImg<charT> txt(32);
- CImg<T> a_label;
- if (siz<=0) { // Degenerated case
- draw_line(x,0,x,_height - 1,color,opacity,pattern);
- if (!siz) {
- cimg_snprintf(txt,txt._width,"%g",round_y?cimg::round((double)*values_y,round_y):(double)*values_y);
- a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
- const int
- _yt = (height() - a_label.height())/2,
- yt = _yt<0?0:_yt + a_label.height()>=height()?height() - 1 - a_label.height():_yt,
- _xt = x - 2 - a_label.width(),
- xt = _xt>=0?_xt:x + 3;
- draw_point(x - 1,height()/2,color,opacity).draw_point(x + 1,height()/2,color,opacity);
- if (allow_zero || *txt!='0' || txt[1]!=0)
- draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
- }
- } else { // Regular case
- if (values_y[0]<values_y[siz]) draw_arrow(x,0,x,_height - 1,color,opacity,30,5,pattern);
- else draw_arrow(x,_height - 1,x,0,color,opacity,30,5,pattern);
- cimg_foroff(values_y,y) {
- cimg_snprintf(txt,txt._width,"%g",round_y?cimg::round((double)values_y(y),round_y):(double)values_y(y));
- a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
- const int
- yi = (int)(y*(_height - 1)/siz),
- _yt = yi - a_label.height()/2,
- yt = _yt<0?0:_yt + a_label.height()>=height()?height() - 1 - a_label.height():_yt,
- _xt = x - 2 - a_label.width(),
- xt = _xt>=0?_xt:x + 3;
- draw_point(x - 1,yi,color,opacity).draw_point(x + 1,yi,color,opacity);
- if (allow_zero || *txt!='0' || txt[1]!=0)
- draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
- }
- }
- return *this;
- }
- //! Draw labeled horizontal and vertical axes.
- /**
- \param values_x Values along the X-axis.
- \param values_y Values along the Y-axis.
- \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
- \param opacity Drawing opacity.
- \param pattern_x Drawing pattern for the X-axis.
- \param pattern_y Drawing pattern for the Y-axis.
- \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise).
- \param allow_zero Enable/disable the drawing of label '0' if found.
- **/
- template<typename tx, typename ty, typename tc>
- CImg<T>& draw_axes(const CImg<tx>& values_x, const CImg<ty>& values_y,
- const tc *const color, const float opacity=1,
- const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U,
- const unsigned int font_height=13, const bool allow_zero=true,
- const float round_x=0, const float round_y=0) {
- if (is_empty()) return *this;
- const CImg<tx> nvalues_x(values_x._data,values_x.size(),1,1,1,true);
- const int sizx = (int)values_x.size() - 1, wm1 = width() - 1;
- if (sizx>=0) {
- float ox = (float)*nvalues_x;
- for (unsigned int x = sizx?1U:0U; x<_width; ++x) {
- const float nx = (float)nvalues_x._linear_atX((float)x*sizx/wm1);
- if (nx*ox<=0) {
- draw_axis(nx==0?x:x - 1,values_y,color,opacity,pattern_y,font_height,allow_zero,round_y);
- break;
- }
- ox = nx;
- }
- }
- const CImg<ty> nvalues_y(values_y._data,values_y.size(),1,1,1,true);
- const int sizy = (int)values_y.size() - 1, hm1 = height() - 1;
- if (sizy>0) {
- float oy = (float)nvalues_y[0];
- for (unsigned int y = sizy?1U:0U; y<_height; ++y) {
- const float ny = (float)nvalues_y._linear_atX((float)y*sizy/hm1);
- if (ny*oy<=0) {
- draw_axis(values_x,ny==0?y:y - 1,color,opacity,pattern_x,font_height,allow_zero,round_x);
- break;
- }
- oy = ny;
- }
- }
- return *this;
- }
- //! Draw labeled horizontal and vertical axes \overloading.
- template<typename tc>
- CImg<T>& draw_axes(const float x0, const float x1, const float y0, const float y1,
- const tc *const color, const float opacity=1,
- const int subdivisionx=-60, const int subdivisiony=-60,
- const float precisionx=0, const float precisiony=0,
- const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U,
- const unsigned int font_height=13) {
- if (is_empty()) return *this;
- const bool allow_zero = (x0*x1>0) || (y0*y1>0);
- const float
- dx = cimg::abs(x1 - x0), dy = cimg::abs(y1 - y0),
- px = dx<=0?1:precisionx==0?(float)std::pow(10.,(int)std::log10(dx) - 2.):precisionx,
- py = dy<=0?1:precisiony==0?(float)std::pow(10.,(int)std::log10(dy) - 2.):precisiony;
- if (x0!=x1 && y0!=y1)
- draw_axes(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1),
- CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1),
- color,opacity,pattern_x,pattern_y,font_height,allow_zero,px,py);
- else if (x0==x1 && y0!=y1)
- draw_axis((int)x0,CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1),
- color,opacity,pattern_y,font_height,py);
- else if (x0!=x1 && y0==y1)
- draw_axis(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1),(int)y0,
- color,opacity,pattern_x,font_height,px);
- return *this;
- }
- //! Draw 2D grid.
- /**
- \param values_x X-coordinates of the vertical lines.
- \param values_y Y-coordinates of the horizontal lines.
- \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
- \param opacity Drawing opacity.
- \param pattern_x Drawing pattern for vertical lines.
- \param pattern_y Drawing pattern for horizontal lines.
- **/
- template<typename tx, typename ty, typename tc>
- CImg<T>& draw_grid(const CImg<tx>& values_x, const CImg<ty>& values_y,
- const tc *const color, const float opacity=1,
- const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) {
- if (is_empty()) return *this;
- if (values_x) cimg_foroff(values_x,x) {
- const int xi = (int)values_x[x];
- if (xi>=0 && xi<width()) draw_line(xi,0,xi,_height - 1,color,opacity,pattern_x);
- }
- if (values_y) cimg_foroff(values_y,y) {
- const int yi = (int)values_y[y];
- if (yi>=0 && yi<height()) draw_line(0,yi,_width - 1,yi,color,opacity,pattern_y);
- }
- return *this;
- }
- //! Draw 2D grid \simplification.
- template<typename tc>
- CImg<T>& draw_grid(const float delta_x, const float delta_y,
- const float offsetx, const float offsety,
- const bool invertx, const bool inverty,
- const tc *const color, const float opacity=1,
- const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) {
- if (is_empty()) return *this;
- CImg<uintT> seqx, seqy;
- if (delta_x!=0) {
- const float dx = delta_x>0?delta_x:_width*-delta_x/100;
- const unsigned int nx = (unsigned int)(_width/dx);
- seqx = CImg<uintT>::sequence(1 + nx,0,(unsigned int)(dx*nx));
- if (offsetx) cimg_foroff(seqx,x) seqx(x) = (unsigned int)cimg::mod(seqx(x) + offsetx,(float)_width);
- if (invertx) cimg_foroff(seqx,x) seqx(x) = _width - 1 - seqx(x);
- }
- if (delta_y!=0) {
- const float dy = delta_y>0?delta_y:_height*-delta_y/100;
- const unsigned int ny = (unsigned int)(_height/dy);
- seqy = CImg<uintT>::sequence(1 + ny,0,(unsigned int)(dy*ny));
- if (offsety) cimg_foroff(seqy,y) seqy(y) = (unsigned int)cimg::mod(seqy(y) + offsety,(float)_height);
- if (inverty) cimg_foroff(seqy,y) seqy(y) = _height - 1 - seqy(y);
- }
- return draw_grid(seqx,seqy,color,opacity,pattern_x,pattern_y);
- }
- //! Draw 1D graph.
- /**
- \param data Image containing the graph values I = f(x).
- \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
- \param opacity Drawing opacity.
- \param plot_type Define the type of the plot:
- - 0 = No plot.
- - 1 = Plot using segments.
- - 2 = Plot using cubic splines.
- - 3 = Plot with bars.
- \param vertex_type Define the type of points:
- - 0 = No points.
- - 1 = Point.
- - 2 = Straight cross.
- - 3 = Diagonal cross.
- - 4 = Filled circle.
- - 5 = Outlined circle.
- - 6 = Square.
- - 7 = Diamond.
- \param ymin Lower bound of the y-range.
- \param ymax Upper bound of the y-range.
- \param pattern Drawing pattern.
- \note
- - if \c ymin==ymax==0, the y-range is computed automatically from the input samples.
- **/
- template<typename t, typename tc>
- CImg<T>& draw_graph(const CImg<t>& data,
- const tc *const color, const float opacity=1,
- const unsigned int plot_type=1, const int vertex_type=1,
- const double ymin=0, const double ymax=0, const unsigned int pattern=~0U) {
- if (is_empty() || _height<=1) return *this;
- if (!color)
- throw CImgArgumentException(_cimg_instance
- "draw_graph(): Specified color is (null).",
- cimg_instance);
- // Create shaded colors for displaying bar plots.
- CImg<tc> color1, color2;
- if (plot_type==3) {
- color1.assign(_spectrum); color2.assign(_spectrum);
- cimg_forC(*this,c) {
- color1[c] = (tc)std::min((float)cimg::type<tc>::max(),(float)color[c]*1.2f);
- color2[c] = (tc)(color[c]*0.4f);
- }
- }
- // Compute min/max and normalization factors.
- const ulongT
- siz = data.size(),
- _siz1 = siz - (plot_type!=3),
- siz1 = _siz1?_siz1:1;
- const unsigned int
- _width1 = _width - (plot_type!=3),
- width1 = _width1?_width1:1;
- double m = ymin, M = ymax;
- if (ymin==ymax) m = (double)data.max_min(M);
- if (m==M) { --m; ++M; }
- const float ca = (float)(M-m)/(_height - 1);
- bool init_hatch = true;
- // Draw graph edges
- switch (plot_type%4) {
- case 1 : { // Segments
- int oX = 0, oY = (int)cimg::round((data[0] - m)/ca);
- if (siz==1) {
- const int Y = (int)cimg::round((*data - m)/ca);
- draw_line(0,Y,width() - 1,Y,color,opacity,pattern);
- } else {
- const float fx = (float)_width/siz1;
- for (ulongT off = 1; off<siz; ++off) {
- const int
- X = (int)cimg::round(off*fx) - 1,
- Y = (int)cimg::round((data[off]-m)/ca);
- draw_line(oX,oY,X,Y,color,opacity,pattern,init_hatch);
- oX = X; oY = Y;
- init_hatch = false;
- }
- }
- } break;
- case 2 : { // Spline
- const CImg<t> ndata(data._data,siz,1,1,1,true);
- int oY = (int)cimg::round((data[0] - m)/ca);
- cimg_forX(*this,x) {
- const int Y = (int)cimg::round((ndata._cubic_atX((float)x*siz1/width1)-m)/ca);
- if (x>0) draw_line(x,oY,x + 1,Y,color,opacity,pattern,init_hatch);
- init_hatch = false;
- oY = Y;
- }
- } break;
- case 3 : { // Bars
- const int Y0 = (int)cimg::round(-m/ca);
- const float fx = (float)_width/siz1;
- int oX = 0;
- cimg_foroff(data,off) {
- const int
- X = (int)cimg::round((off + 1)*fx) - 1,
- Y = (int)cimg::round((data[off] - m)/ca);
- draw_rectangle(oX,Y0,X,Y,color,opacity).
- draw_line(oX,Y,oX,Y0,color2.data(),opacity).
- draw_line(oX,Y0,X,Y0,Y<=Y0?color2.data():color1.data(),opacity).
- draw_line(X,Y,X,Y0,color1.data(),opacity).
- draw_line(oX,Y,X,Y,Y<=Y0?color1.data():color2.data(),opacity);
- oX = X + 1;
- }
- } break;
- default : break; // No edges
- }
- // Draw graph points
- const unsigned int wb2 = plot_type==3?_width1/(2*siz):0;
- const float fx = (float)_width1/siz1;
- switch (vertex_type%8) {
- case 1 : { // Point
- cimg_foroff(data,off) {
- const int
- X = (int)cimg::round(off*fx + wb2),
- Y = (int)cimg::round((data[off]-m)/ca);
- draw_point(X,Y,color,opacity);
- }
- } break;
- case 2 : { // Straight Cross
- cimg_foroff(data,off) {
- const int
- X = (int)cimg::round(off*fx + wb2),
- Y = (int)cimg::round((data[off]-m)/ca);
- draw_line(X - 3,Y,X + 3,Y,color,opacity).draw_line(X,Y - 3,X,Y + 3,color,opacity);
- }
- } break;
- case 3 : { // Diagonal Cross
- cimg_foroff(data,off) {
- const int
- X = (int)cimg::round(off*fx + wb2),
- Y = (int)cimg::round((data[off]-m)/ca);
- draw_line(X - 3,Y - 3,X + 3,Y + 3,color,opacity).draw_line(X - 3,Y + 3,X + 3,Y - 3,color,opacity);
- }
- } break;
- case 4 : { // Filled Circle
- cimg_foroff(data,off) {
- const int
- X = (int)cimg::round(off*fx + wb2),
- Y = (int)cimg::round((data[off]-m)/ca);
- draw_circle(X,Y,3,color,opacity);
- }
- } break;
- case 5 : { // Outlined circle
- cimg_foroff(data,off) {
- const int
- X = (int)cimg::round(off*fx + wb2),
- Y = (int)cimg::round((data[off]-m)/ca);
- draw_circle(X,Y,3,color,opacity,~0U);
- }
- } break;
- case 6 : { // Square
- cimg_foroff(data,off) {
- const int
- X = (int)cimg::round(off*fx + wb2),
- Y = (int)cimg::round((data[off]-m)/ca);
- draw_rectangle(X - 3,Y - 3,X + 3,Y + 3,color,opacity,~0U);
- }
- } break;
- case 7 : { // Diamond
- cimg_foroff(data,off) {
- const int
- X = (int)cimg::round(off*fx + wb2),
- Y = (int)cimg::round((data[off]-m)/ca);
- draw_line(X,Y - 4,X + 4,Y,color,opacity).
- draw_line(X + 4,Y,X,Y + 4,color,opacity).
- draw_line(X,Y + 4,X - 4,Y,color,opacity).
- draw_line(X - 4,Y,X,Y - 4,color,opacity);
- }
- } break;
- default : break; // No points
- }
- return *this;
- }
- bool _draw_fill(const int x, const int y, const int z,
- const CImg<T>& ref, const float tolerance2) const {
- const T *ptr1 = data(x,y,z), *ptr2 = ref._data;
- const ulongT off = _width*_height*_depth;
- float diff = 0;
- cimg_forC(*this,c) { diff += cimg::sqr(*ptr1 - *(ptr2++)); ptr1+=off; }
- return diff<=tolerance2;
- }
- //! Draw filled 3D region with the flood fill algorithm.
- /**
- \param x0 X-coordinate of the starting point of the region to fill.
- \param y0 Y-coordinate of the starting point of the region to fill.
- \param z0 Z-coordinate of the starting point of the region to fill.
- \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
- \param[out] region Image that will contain the mask of the filled region mask, as an output.
- \param tolerance Tolerance concerning neighborhood values.
- \param opacity Opacity of the drawing.
- \param is_high_connectivity Tells if 8-connexity must be used.
- \return \c region is initialized with the binary mask of the filled region.
- **/
- template<typename tc, typename t>
- CImg<T>& draw_fill(const int x0, const int y0, const int z0,
- const tc *const color, const float opacity,
- CImg<t> ®ion,
- const float tolerance = 0,
- const bool is_high_connectivity = false) {
- #define _draw_fill_push(x,y,z) if (N>=stack._width) stack.resize(2*N + 1,1,1,3,0); \
- stack[N] = x; stack(N,1) = y; stack(N++,2) = z
- #define _draw_fill_pop(x,y,z) x = stack[--N]; y = stack(N,1); z = stack(N,2)
- #define _draw_fill_is_inside(x,y,z) !_region(x,y,z) && _draw_fill(x,y,z,ref,tolerance2)
- if (!containsXYZC(x0,y0,z0,0)) return *this;
- const float nopacity = cimg::abs((float)opacity), copacity = 1 - std::max((float)opacity,0.f);
- const float tolerance2 = cimg::sqr(tolerance);
- const CImg<T> ref = get_vector_at(x0,y0,z0);
- CImg<uintT> stack(256,1,1,3);
- CImg<ucharT> _region(_width,_height,_depth,1,0);
- unsigned int N = 0;
- int x, y, z;
- _draw_fill_push(x0,y0,z0);
- while (N>0) {
- _draw_fill_pop(x,y,z);
- if (!_region(x,y,z)) {
- const int yp = y - 1, yn = y + 1, zp = z - 1, zn = z + 1;
- int xl = x, xr = x;
- // Using these booleans reduces the number of pushes drastically.
- bool is_yp = false, is_yn = false, is_zp = false, is_zn = false;
- for (int step = -1; step<2; step+=2) {
- while (x>=0 && x<width() && _draw_fill_is_inside(x,y,z)) {
- if (yp>=0 && _draw_fill_is_inside(x,yp,z)) {
- if (!is_yp) { _draw_fill_push(x,yp,z); is_yp = true; }
- } else is_yp = false;
- if (yn<height() && _draw_fill_is_inside(x,yn,z)) {
- if (!is_yn) { _draw_fill_push(x,yn,z); is_yn = true; }
- } else is_yn = false;
- if (depth()>1) {
- if (zp>=0 && _draw_fill_is_inside(x,y,zp)) {
- if (!is_zp) { _draw_fill_push(x,y,zp); is_zp = true; }
- } else is_zp = false;
- if (zn<depth() && _draw_fill_is_inside(x,y,zn)) {
- if (!is_zn) { _draw_fill_push(x,y,zn); is_zn = true; }
- } else is_zn = false;
- }
- if (is_high_connectivity) {
- const int xp = x - 1, xn = x + 1;
- if (yp>=0 && !is_yp) {
- if (xp>=0 && _draw_fill_is_inside(xp,yp,z)) {
- _draw_fill_push(xp,yp,z); if (step<0) is_yp = true;
- }
- if (xn<width() && _draw_fill_is_inside(xn,yp,z)) {
- _draw_fill_push(xn,yp,z); if (step>0) is_yp = true;
- }
- }
- if (yn<height() && !is_yn) {
- if (xp>=0 && _draw_fill_is_inside(xp,yn,z)) {
- _draw_fill_push(xp,yn,z); if (step<0) is_yn = true;
- }
- if (xn<width() && _draw_fill_is_inside(xn,yn,z)) {
- _draw_fill_push(xn,yn,z); if (step>0) is_yn = true;
- }
- }
- if (depth()>1) {
- if (zp>=0 && !is_zp) {
- if (xp>=0 && _draw_fill_is_inside(xp,y,zp)) {
- _draw_fill_push(xp,y,zp); if (step<0) is_zp = true;
- }
- if (xn<width() && _draw_fill_is_inside(xn,y,zp)) {
- _draw_fill_push(xn,y,zp); if (step>0) is_zp = true;
- }
- if (yp>=0 && !is_yp) {
- if (_draw_fill_is_inside(x,yp,zp)) { _draw_fill_push(x,yp,zp); }
- if (xp>=0 && _draw_fill_is_inside(xp,yp,zp)) { _draw_fill_push(xp,yp,zp); }
- if (xn<width() && _draw_fill_is_inside(xn,yp,zp)) { _draw_fill_push(xn,yp,zp); }
- }
- if (yn<height() && !is_yn) {
- if (_draw_fill_is_inside(x,yn,zp)) { _draw_fill_push(x,yn,zp); }
- if (xp>=0 && _draw_fill_is_inside(xp,yn,zp)) { _draw_fill_push(xp,yn,zp); }
- if (xn<width() && _draw_fill_is_inside(xn,yn,zp)) { _draw_fill_push(xn,yn,zp); }
- }
- }
- if (zn<depth() && !is_zn) {
- if (xp>=0 && _draw_fill_is_inside(xp,y,zn)) {
- _draw_fill_push(xp,y,zn); if (step<0) is_zn = true;
- }
- if (xn<width() && _draw_fill_is_inside(xn,y,zn)) {
- _draw_fill_push(xn,y,zn); if (step>0) is_zn = true;
- }
- if (yp>=0 && !is_yp) {
- if (_draw_fill_is_inside(x,yp,zn)) { _draw_fill_push(x,yp,zn); }
- if (xp>=0 && _draw_fill_is_inside(xp,yp,zn)) { _draw_fill_push(xp,yp,zn); }
- if (xn<width() && _draw_fill_is_inside(xn,yp,zn)) { _draw_fill_push(xn,yp,zn); }
- }
- if (yn<height() && !is_yn) {
- if (_draw_fill_is_inside(x,yn,zn)) { _draw_fill_push(x,yn,zn); }
- if (xp>=0 && _draw_fill_is_inside(xp,yn,zn)) { _draw_fill_push(xp,yn,zn); }
- if (xn<width() && _draw_fill_is_inside(xn,yn,zn)) { _draw_fill_push(xn,yn,zn); }
- }
- }
- }
- }
- x+=step;
- }
- if (step<0) { xl = ++x; x = xr + 1; is_yp = is_yn = is_zp = is_zn = false; }
- else xr = --x;
- }
- std::memset(_region.data(xl,y,z),1,xr - xl + 1);
- if (opacity==1) {
- if (sizeof(T)==1) {
- const int dx = xr - xl + 1;
- cimg_forC(*this,c) std::memset(data(xl,y,z,c),(int)color[c],dx);
- } else cimg_forC(*this,c) {
- const T val = (T)color[c];
- T *ptri = data(xl,y,z,c); for (int k = xl; k<=xr; ++k) *(ptri++) = val;
- }
- } else cimg_forC(*this,c) {
- const T val = (T)(color[c]*nopacity);
- T *ptri = data(xl,y,z,c); for (int k = xl; k<=xr; ++k) { *ptri = (T)(val + *ptri*copacity); ++ptri; }
- }
- }
- }
- _region.move_to(region);
- return *this;
- }
- //! Draw filled 3D region with the flood fill algorithm \simplification.
- template<typename tc>
- CImg<T>& draw_fill(const int x0, const int y0, const int z0,
- const tc *const color, const float opacity=1,
- const float tolerance=0, const bool is_high_connexity=false) {
- CImg<ucharT> tmp;
- return draw_fill(x0,y0,z0,color,opacity,tmp,tolerance,is_high_connexity);
- }
- //! Draw filled 2D region with the flood fill algorithm \simplification.
- template<typename tc>
- CImg<T>& draw_fill(const int x0, const int y0,
- const tc *const color, const float opacity=1,
- const float tolerance=0, const bool is_high_connexity=false) {
- CImg<ucharT> tmp;
- return draw_fill(x0,y0,0,color,opacity,tmp,tolerance,is_high_connexity);
- }
- //! Draw a random plasma texture.
- /**
- \param alpha Alpha-parameter.
- \param beta Beta-parameter.
- \param scale Scale-parameter.
- \note Use the mid-point algorithm to render.
- **/
- CImg<T>& draw_plasma(const float alpha=1, const float beta=0, const unsigned int scale=8) {
- if (is_empty()) return *this;
- const int w = width(), h = height();
- const Tfloat m = (Tfloat)cimg::type<T>::min(), M = (Tfloat)cimg::type<T>::max();
- cimg_uint64 rng = (cimg::_rand(),cimg::rng());
- cimg_forZC(*this,z,c) {
- CImg<T> ref = get_shared_slice(z,c);
- for (int delta = 1<<std::min(scale,31U); delta>1; delta>>=1) {
- const int delta2 = delta>>1;
- const float r = alpha*delta + beta;
- // Square step.
- for (int y0 = 0; y0<h; y0+=delta)
- for (int x0 = 0; x0<w; x0+=delta) {
- const int x1 = (x0 + delta)%w, y1 = (y0 + delta)%h, xc = (x0 + delta2)%w, yc = (y0 + delta2)%h;
- const Tfloat val = (Tfloat)(0.25f*(ref(x0,y0) + ref(x0,y1) + ref(x0,y1) + ref(x1,y1)) +
- r*cimg::rand(-1,1,&rng));
- ref(xc,yc) = (T)(val<m?m:val>M?M:val);
- }
- // Diamond steps.
- for (int y = -delta2; y<h; y+=delta)
- for (int x0=0; x0<w; x0+=delta) {
- const int y0 = cimg::mod(y,h), x1 = (x0 + delta)%w, y1 = (y + delta)%h,
- xc = (x0 + delta2)%w, yc = (y + delta2)%h;
- const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) +
- r*cimg::rand(-1,1,&rng));
- ref(xc,yc) = (T)(val<m?m:val>M?M:val);
- }
- for (int y0 = 0; y0<h; y0+=delta)
- for (int x = -delta2; x<w; x+=delta) {
- const int x0 = cimg::mod(x,w), x1 = (x + delta)%w, y1 = (y0 + delta)%h,
- xc = (x + delta2)%w, yc = (y0 + delta2)%h;
- const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) +
- r*cimg::rand(-1,1,&rng));
- ref(xc,yc) = (T)(val<m?m:val>M?M:val);
- }
- for (int y = -delta2; y<h; y+=delta)
- for (int x = -delta2; x<w; x+=delta) {
- const int x0 = cimg::mod(x,w), y0 = cimg::mod(y,h), x1 = (x + delta)%w, y1 = (y + delta)%h,
- xc = (x + delta2)%w, yc = (y + delta2)%h;
- const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) +
- r*cimg::rand(-1,1,&rng));
- ref(xc,yc) = (T)(val<m?m:val>M?M:val);
- }
- }
- }
- cimg::srand(rng);
- return *this;
- }
- //! Draw a quadratic Mandelbrot or Julia 2D fractal.
- /**
- \param x0 X-coordinate of the upper-left pixel.
- \param y0 Y-coordinate of the upper-left pixel.
- \param x1 X-coordinate of the lower-right pixel.
- \param y1 Y-coordinate of the lower-right pixel.
- \param palette Colormap.
- \param opacity Drawing opacity.
- \param z0r Real part of the upper-left fractal vertex.
- \param z0i Imaginary part of the upper-left fractal vertex.
- \param z1r Real part of the lower-right fractal vertex.
- \param z1i Imaginary part of the lower-right fractal vertex.
- \param iteration_max Maximum number of iterations for each estimated point.
- \param is_normalized_iteration Tells if iterations are normalized.
- \param is_julia_set Tells if the Mandelbrot or Julia set is rendered.
- \param param_r Real part of the Julia set parameter.
- \param param_i Imaginary part of the Julia set parameter.
- \note Fractal rendering is done by the Escape Time Algorithm.
- **/
- template<typename tc>
- CImg<T>& draw_mandelbrot(const int x0, const int y0, const int x1, const int y1,
- const CImg<tc>& colormap, const float opacity=1,
- const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2,
- const unsigned int iteration_max=255,
- const bool is_normalized_iteration=false,
- const bool is_julia_set=false,
- const double param_r=0, const double param_i=0) {
- if (is_empty()) return *this;
- CImg<tc> palette;
- if (colormap) palette.assign(colormap._data,colormap.size()/colormap._spectrum,1,1,colormap._spectrum,true);
- if (palette && palette._spectrum!=_spectrum)
- throw CImgArgumentException(_cimg_instance
- "draw_mandelbrot(): Instance and specified colormap (%u,%u,%u,%u,%p) have "
- "incompatible dimensions.",
- cimg_instance,
- colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
- const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f), ln2 = (float)std::log(2.);
- const int
- _x0 = cimg::cut(x0,0,width() - 1),
- _y0 = cimg::cut(y0,0,height() - 1),
- _x1 = cimg::cut(x1,0,width() - 1),
- _y1 = cimg::cut(y1,0,height() - 1);
- cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
- cimg_openmp_if((1 + _x1 - _x0)*(1 + _y1 - _y0)>=(cimg_openmp_sizefactor)*2048))
- for (int q = _y0; q<=_y1; ++q)
- for (int p = _x0; p<=_x1; ++p) {
- unsigned int iteration = 0;
- const double x = z0r + p*(z1r-z0r)/_width, y = z0i + q*(z1i-z0i)/_height;
- double zr, zi, cr, ci;
- if (is_julia_set) { zr = x; zi = y; cr = param_r; ci = param_i; }
- else { zr = param_r; zi = param_i; cr = x; ci = y; }
- for (iteration=1; zr*zr + zi*zi<=4 && iteration<=iteration_max; ++iteration) {
- const double temp = zr*zr - zi*zi + cr;
- zi = 2*zr*zi + ci;
- zr = temp;
- }
- if (iteration>iteration_max) {
- if (palette) {
- if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette(0,c);
- else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(0,c)*nopacity + (*this)(p,q,0,c)*copacity);
- } else {
- if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)0;
- else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)((*this)(p,q,0,c)*copacity);
- }
- } else if (is_normalized_iteration) {
- const float
- normz = (float)cimg::abs(zr*zr + zi*zi),
- niteration = (float)(iteration + 1 - std::log(std::log(normz))/ln2);
- if (palette) {
- if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._linear_atX(niteration,c);
- else cimg_forC(*this,c)
- (*this)(p,q,0,c) = (T)(palette._linear_atX(niteration,c)*nopacity + (*this)(p,q,0,c)*copacity);
- } else {
- if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)niteration;
- else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(niteration*nopacity + (*this)(p,q,0,c)*copacity);
- }
- } else {
- if (palette) {
- if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._atX(iteration,c);
- else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(iteration,c)*nopacity + (*this)(p,q,0,c)*copacity);
- } else {
- if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)iteration;
- else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(iteration*nopacity + (*this)(p,q,0,c)*copacity);
- }
- }
- }
- return *this;
- }
- //! Draw a quadratic Mandelbrot or Julia 2D fractal \overloading.
- template<typename tc>
- CImg<T>& draw_mandelbrot(const CImg<tc>& colormap, const float opacity=1,
- const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2,
- const unsigned int iteration_max=255,
- const bool is_normalized_iteration=false,
- const bool is_julia_set=false,
- const double param_r=0, const double param_i=0) {
- return draw_mandelbrot(0,0,_width - 1,_height - 1,colormap,opacity,
- z0r,z0i,z1r,z1i,iteration_max,is_normalized_iteration,is_julia_set,param_r,param_i);
- }
- //! Draw a 1D gaussian function.
- /**
- \param xc X-coordinate of the gaussian center.
- \param sigma Standard variation of the gaussian distribution.
- \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
- \param opacity Drawing opacity.
- **/
- template<typename tc>
- CImg<T>& draw_gaussian(const float xc, const float sigma,
- const tc *const color, const float opacity=1) {
- if (is_empty()) return *this;
- if (!color)
- throw CImgArgumentException(_cimg_instance
- "draw_gaussian(): Specified color is (null).",
- cimg_instance);
- const float sigma2 = 2*sigma*sigma, nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
- const ulongT whd = (ulongT)_width*_height*_depth;
- const tc *col = color;
- cimg_forX(*this,x) {
- const float dx = (x - xc), val = (float)std::exp(-dx*dx/sigma2);
- T *ptrd = data(x,0,0,0);
- if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
- else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
- col-=_spectrum;
- }
- return *this;
- }
- //! Draw a 2D gaussian function.
- /**
- \param xc X-coordinate of the gaussian center.
- \param yc Y-coordinate of the gaussian center.
- \param tensor Covariance matrix (must be 2x2).
- \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
- \param opacity Drawing opacity.
- **/
- template<typename t, typename tc>
- CImg<T>& draw_gaussian(const float xc, const float yc, const CImg<t>& tensor,
- const tc *const color, const float opacity=1) {
- if (is_empty()) return *this;
- if (tensor._width!=2 || tensor._height!=2 || tensor._depth!=1 || tensor._spectrum!=1)
- throw CImgArgumentException(_cimg_instance
- "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 2x2 matrix.",
- cimg_instance,
- tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data);
- if (!color)
- throw CImgArgumentException(_cimg_instance
- "draw_gaussian(): Specified color is (null).",
- cimg_instance);
- typedef typename CImg<t>::Tfloat tfloat;
- const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/=-2.;
- const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = invT2(1,1);
- const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
- const ulongT whd = (ulongT)_width*_height*_depth;
- const tc *col = color;
- float dy = -yc;
- cimg_forY(*this,y) {
- float dx = -xc;
- cimg_forX(*this,x) {
- const float val = (float)std::exp(a*dx*dx + b*dx*dy + c*dy*dy);
- T *ptrd = data(x,y,0,0);
- if (opacity>=1) cimg_forC(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
- else cimg_forC(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
- col-=_spectrum;
- ++dx;
- }
- ++dy;
- }
- return *this;
- }
- //! Draw a 2D gaussian function \overloading.
- template<typename tc>
- CImg<T>& draw_gaussian(const int xc, const int yc, const float r1, const float r2, const float ru, const float rv,
- const tc *const color, const float opacity=1) {
- const double
- a = r1*ru*ru + r2*rv*rv,
- b = (r1-r2)*ru*rv,
- c = r1*rv*rv + r2*ru*ru;
- const CImg<Tfloat> tensor(2,2,1,1, a,b,b,c);
- return draw_gaussian(xc,yc,tensor,color,opacity);
- }
- //! Draw a 2D gaussian function \overloading.
- template<typename tc>
- CImg<T>& draw_gaussian(const float xc, const float yc, const float sigma,
- const tc *const color, const float opacity=1) {
- return draw_gaussian(xc,yc,CImg<floatT>::diagonal(sigma,sigma),color,opacity);
- }
- //! Draw a 3D gaussian function \overloading.
- template<typename t, typename tc>
- CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const CImg<t>& tensor,
- const tc *const color, const float opacity=1) {
- if (is_empty()) return *this;
- typedef typename CImg<t>::Tfloat tfloat;
- if (tensor._width!=3 || tensor._height!=3 || tensor._depth!=1 || tensor._spectrum!=1)
- throw CImgArgumentException(_cimg_instance
- "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 3x3 matrix.",
- cimg_instance,
- tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data);
- const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/=-2.;
- const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = 2*invT2(2,0), d = invT2(1,1), e = 2*invT2(2,1), f = invT2(2,2);
- const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
- const ulongT whd = (ulongT)_width*_height*_depth;
- const tc *col = color;
- cimg_forXYZ(*this,x,y,z) {
- const float
- dx = (x - xc), dy = (y - yc), dz = (z - zc),
- val = (float)std::exp(a*dx*dx + b*dx*dy + c*dx*dz + d*dy*dy + e*dy*dz + f*dz*dz);
- T *ptrd = data(x,y,z,0);
- if (opacity>=1) cimg_forC(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
- else cimg_forC(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
- col-=_spectrum;
- }
- return *this;
- }
- //! Draw a 3D gaussian function \overloading.
- template<typename tc>
- CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const float sigma,
- const tc *const color, const float opacity=1) {
- return draw_gaussian(xc,yc,zc,CImg<floatT>::diagonal(sigma,sigma,sigma),color,opacity);
- }
- //! Draw a 3D object.
- /**
- \param x0 X-coordinate of the 3D object position
- \param y0 Y-coordinate of the 3D object position
- \param z0 Z-coordinate of the 3D object position
- \param vertices Image Nx3 describing 3D point coordinates
- \param primitives List of P primitives
- \param colors List of P color (or textures)
- \param opacities Image or list of P opacities
- \param render_type d Render type (0=Points, 1=Lines, 2=Faces (no light), 3=Faces (flat), 4=Faces(Gouraud)
- \param is_double_sided Tells if object faces have two sides or are oriented.
- \param focale length of the focale (0 for parallel projection)
- \param lightx X-coordinate of the light
- \param lighty Y-coordinate of the light
- \param lightz Z-coordinate of the light
- \param specular_lightness Amount of specular light.
- \param specular_shininess Shininess of the object
- \param g_opacity Global opacity of the object.
- **/
- template<typename tp, typename tf, typename tc, typename to>
- CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
- const CImg<tp>& vertices, const CImgList<tf>& primitives,
- const CImgList<tc>& colors, const CImg<to>& opacities,
- const unsigned int render_type=4,
- const bool is_double_sided=false, const float focale=700,
- const float lightx=0, const float lighty=0, const float lightz=-5e8,
- const float specular_lightness=0.2f, const float specular_shininess=0.1f,
- const float g_opacity=1) {
- return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,
- is_double_sided,focale,lightx,lighty,lightz,
- specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
- }
- //! Draw a 3D object \simplification.
- template<typename tp, typename tf, typename tc, typename to, typename tz>
- CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
- const CImg<tp>& vertices, const CImgList<tf>& primitives,
- const CImgList<tc>& colors, const CImg<to>& opacities,
- const unsigned int render_type,
- const bool is_double_sided, const float focale,
- const float lightx, const float lighty, const float lightz,
- const float specular_lightness, const float specular_shininess,
- const float g_opacity, CImg<tz>& zbuffer) {
- return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
- render_type,is_double_sided,focale,lightx,lighty,lightz,
- specular_lightness,specular_shininess,g_opacity,1);
- }
- #ifdef cimg_use_board
- template<typename tp, typename tf, typename tc, typename to>
- CImg<T>& draw_object3d(LibBoard::Board& board,
- const float x0, const float y0, const float z0,
- const CImg<tp>& vertices, const CImgList<tf>& primitives,
- const CImgList<tc>& colors, const CImg<to>& opacities,
- const unsigned int render_type=4,
- const bool is_double_sided=false, const float focale=700,
- const float lightx=0, const float lighty=0, const float lightz=-5e8,
- const float specular_lightness=0.2f, const float specular_shininess=0.1f,
- const float g_opacity=1) {
- return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,
- is_double_sided,focale,lightx,lighty,lightz,
- specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
- }
- template<typename tp, typename tf, typename tc, typename to, typename tz>
- CImg<T>& draw_object3d(LibBoard::Board& board,
- const float x0, const float y0, const float z0,
- const CImg<tp>& vertices, const CImgList<tf>& primitives,
- const CImgList<tc>& colors, const CImg<to>& opacities,
- const unsigned int render_type,
- const bool is_double_sided, const float focale,
- const float lightx, const float lighty, const float lightz,
- const float specular_lightness, const float specular_shininess,
- const float g_opacity, CImg<tz>& zbuffer) {
- return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
- render_type,is_double_sided,focale,lightx,lighty,lightz,
- specular_lightness,specular_shininess,g_opacity,1);
- }
- #endif
- //! Draw a 3D object \simplification.
- template<typename tp, typename tf, typename tc, typename to>
- CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
- const CImg<tp>& vertices, const CImgList<tf>& primitives,
- const CImgList<tc>& colors, const CImgList<to>& opacities,
- const unsigned int render_type=4,
- const bool is_double_sided=false, const float focale=700,
- const float lightx=0, const float lighty=0, const float lightz=-5e8,
- const float specular_lightness=0.2f, const float specular_shininess=0.1f,
- const float g_opacity=1) {
- return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,
- is_double_sided,focale,lightx,lighty,lightz,
- specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
- }
- //! Draw a 3D object \simplification.
- template<typename tp, typename tf, typename tc, typename to, typename tz>
- CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
- const CImg<tp>& vertices, const CImgList<tf>& primitives,
- const CImgList<tc>& colors, const CImgList<to>& opacities,
- const unsigned int render_type,
- const bool is_double_sided, const float focale,
- const float lightx, const float lighty, const float lightz,
- const float specular_lightness, const float specular_shininess,
- const float g_opacity, CImg<tz>& zbuffer) {
- return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
- render_type,is_double_sided,focale,lightx,lighty,lightz,
- specular_lightness,specular_shininess,g_opacity,1);
- }
- #ifdef cimg_use_board
- template<typename tp, typename tf, typename tc, typename to>
- CImg<T>& draw_object3d(LibBoard::Board& board,
- const float x0, const float y0, const float z0,
- const CImg<tp>& vertices, const CImgList<tf>& primitives,
- const CImgList<tc>& colors, const CImgList<to>& opacities,
- const unsigned int render_type=4,
- const bool is_double_sided=false, const float focale=700,
- const float lightx=0, const float lighty=0, const float lightz=-5e8,
- const float specular_lightness=0.2f, const float specular_shininess=0.1f,
- const float g_opacity=1) {
- return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,
- is_double_sided,focale,lightx,lighty,lightz,
- specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
- }
- template<typename tp, typename tf, typename tc, typename to, typename tz>
- CImg<T>& draw_object3d(LibBoard::Board& board,
- const float x0, const float y0, const float z0,
- const CImg<tp>& vertices, const CImgList<tf>& primitives,
- const CImgList<tc>& colors, const CImgList<to>& opacities,
- const unsigned int render_type,
- const bool is_double_sided, const float focale,
- const float lightx, const float lighty, const float lightz,
- const float specular_lightness, const float specular_shininess,
- const float g_opacity, CImg<tz>& zbuffer) {
- return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
- render_type,is_double_sided,focale,lightx,lighty,lightz,
- specular_lightness,specular_shininess,g_opacity,1);
- }
- #endif
- //! Draw a 3D object \simplification.
- template<typename tp, typename tf, typename tc>
- CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
- const CImg<tp>& vertices, const CImgList<tf>& primitives,
- const CImgList<tc>& colors,
- const unsigned int render_type=4,
- const bool is_double_sided=false, const float focale=700,
- const float lightx=0, const float lighty=0, const float lightz=-5e8,
- const float specular_lightness=0.2f, const float specular_shininess=0.1f,
- const float g_opacity=1) {
- return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
- render_type,is_double_sided,focale,lightx,lighty,lightz,
- specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
- }
- //! Draw a 3D object \simplification.
- template<typename tp, typename tf, typename tc, typename tz>
- CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
- const CImg<tp>& vertices, const CImgList<tf>& primitives,
- const CImgList<tc>& colors,
- const unsigned int render_type,
- const bool is_double_sided, const float focale,
- const float lightx, const float lighty, const float lightz,
- const float specular_lightness, const float specular_shininess,
- const float g_opacity, CImg<tz>& zbuffer) {
- return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
- render_type,is_double_sided,focale,lightx,lighty,lightz,
- specular_lightness,specular_shininess,g_opacity,zbuffer);
- }
- #ifdef cimg_use_board
- template<typename tp, typename tf, typename tc, typename to>
- CImg<T>& draw_object3d(LibBoard::Board& board,
- const float x0, const float y0, const float z0,
- const CImg<tp>& vertices, const CImgList<tf>& primitives,
- const CImgList<tc>& colors,
- const unsigned int render_type=4,
- const bool is_double_sided=false, const float focale=700,
- const float lightx=0, const float lighty=0, const float lightz=-5e8,
- const float specular_lightness=0.2f, const float specular_shininess=0.1f,
- const float g_opacity=1) {
- return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
- render_type,is_double_sided,focale,lightx,lighty,lightz,
- specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
- }
- template<typename tp, typename tf, typename tc, typename to, typename tz>
- CImg<T>& draw_object3d(LibBoard::Board& board,
- const float x0, const float y0, const float z0,
- const CImg<tp>& vertices, const CImgList<tf>& primitives,
- const CImgList<tc>& colors,
- const unsigned int render_type,
- const bool is_double_sided, const float focale,
- const float lightx, const float lighty, const float lightz,
- const float specular_lightness, const float specular_shininess,
- const float g_opacity, CImg<tz>& zbuffer) {
- return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
- render_type,is_double_sided,focale,lightx,lighty,lightz,
- specular_lightness,specular_shininess,g_opacity,zbuffer);
- }
- #endif
- template<typename t, typename to>
- static float __draw_object3d(const CImgList<t>& opacities, const unsigned int n_primitive, CImg<to>& opacity) {
- if (n_primitive>=opacities._width || opacities[n_primitive].is_empty()) { opacity.assign(); return 1; }
- if (opacities[n_primitive].size()==1) { opacity.assign(); return opacities(n_primitive,0); }
- opacity.assign(opacities[n_primitive],true);
- return 1.f;
- }
- template<typename t, typename to>
- static float __draw_object3d(const CImg<t>& opacities, const unsigned int n_primitive, CImg<to>& opacity) {
- opacity.assign();
- return n_primitive>=opacities._width?1.f:(float)opacities[n_primitive];
- }
- template<typename t>
- static float ___draw_object3d(const CImgList<t>& opacities, const unsigned int n_primitive) {
- return n_primitive<opacities._width && opacities[n_primitive].size()==1?(float)opacities(n_primitive,0):1.f;
- }
- template<typename t>
- static float ___draw_object3d(const CImg<t>& opacities, const unsigned int n_primitive) {
- return n_primitive<opacities._width?(float)opacities[n_primitive]:1.f;
- }
- template<typename tz, typename tp, typename tf, typename tc, typename to>
- CImg<T>& _draw_object3d(void *const pboard, CImg<tz>& zbuffer,
- const float X, const float Y, const float Z,
- const CImg<tp>& vertices,
- const CImgList<tf>& primitives,
- const CImgList<tc>& colors,
- const to& opacities,
- const unsigned int render_type,
- const bool is_double_sided, const float focale,
- const float lightx, const float lighty, const float lightz,
- const float specular_lightness, const float specular_shininess,
- const float g_opacity, const float sprite_scale) {
- typedef typename cimg::superset2<tp,tz,float>::type tpfloat;
- typedef typename to::value_type _to;
- if (is_empty() || !vertices || !primitives) return *this;
- CImg<char> error_message(1024);
- if (!vertices.is_object3d(primitives,colors,opacities,false,error_message))
- throw CImgArgumentException(_cimg_instance
- "draw_object3d(): Invalid specified 3D object (%u,%u) (%s).",
- cimg_instance,vertices._width,primitives._width,error_message.data());
- #ifndef cimg_use_board
- if (pboard) return *this;
- #endif
- if (render_type==5) cimg::mutex(10); // Static variable used in this case, breaks thread-safety
- const float
- nspec = 1 - (specular_lightness<0.f?0.f:(specular_lightness>1.f?1.f:specular_lightness)),
- nspec2 = 1 + (specular_shininess<0.f?0.f:specular_shininess),
- nsl1 = (nspec2 - 1)/cimg::sqr(nspec - 1),
- nsl2 = 1 - 2*nsl1*nspec,
- nsl3 = nspec2 - nsl1 - nsl2;
- // Create light texture for phong-like rendering.
- CImg<floatT> light_texture;
- if (render_type==5) {
- if (colors._width>primitives._width) {
- static CImg<floatT> default_light_texture;
- static const tc *lptr = 0;
- static tc ref_values[64] = {};
- const CImg<tc>& img = colors.back();
- bool is_same_texture = (lptr==img._data);
- if (is_same_texture)
- for (unsigned int r = 0, j = 0; j<8; ++j)
- for (unsigned int i = 0; i<8; ++i)
- if (ref_values[r++]!=img(i*img._width/9,j*img._height/9,0,(i + j)%img._spectrum)) {
- is_same_texture = false; break;
- }
- if (!is_same_texture || default_light_texture._spectrum<_spectrum) {
- (default_light_texture.assign(img,false)/=255).resize(-100,-100,1,_spectrum);
- lptr = colors.back().data();
- for (unsigned int r = 0, j = 0; j<8; ++j)
- for (unsigned int i = 0; i<8; ++i)
- ref_values[r++] = img(i*img._width/9,j*img._height/9,0,(i + j)%img._spectrum);
- }
- light_texture.assign(default_light_texture,true);
- } else {
- static CImg<floatT> default_light_texture;
- static float olightx = 0, olighty = 0, olightz = 0, ospecular_shininess = 0;
- if (!default_light_texture ||
- lightx!=olightx || lighty!=olighty || lightz!=olightz ||
- specular_shininess!=ospecular_shininess || default_light_texture._spectrum<_spectrum) {
- default_light_texture.assign(512,512);
- const float
- dlx = lightx - X,
- dly = lighty - Y,
- dlz = lightz - Z,
- nl = cimg::hypot(dlx,dly,dlz),
- nlx = (default_light_texture._width - 1)/2*(1 + dlx/nl),
- nly = (default_light_texture._height - 1)/2*(1 + dly/nl),
- white[] = { 1 };
- default_light_texture.draw_gaussian(nlx,nly,default_light_texture._width/3.f,white);
- cimg_forXY(default_light_texture,x,y) {
- const float factor = default_light_texture(x,y);
- if (factor>nspec) default_light_texture(x,y) = std::min(2.f,nsl1*factor*factor + nsl2*factor + nsl3);
- }
- default_light_texture.resize(-100,-100,1,_spectrum);
- olightx = lightx; olighty = lighty; olightz = lightz; ospecular_shininess = specular_shininess;
- }
- light_texture.assign(default_light_texture,true);
- }
- }
- // Compute 3D to 2D projection.
- CImg<tpfloat> projections(vertices._width,2);
- tpfloat parallzmin = cimg::type<tpfloat>::max();
- const float absfocale = focale?cimg::abs(focale):0;
- if (absfocale) {
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(projections.size(),4096))
- cimg_forX(projections,l) { // Perspective projection
- const tpfloat
- x = (tpfloat)vertices(l,0),
- y = (tpfloat)vertices(l,1),
- z = (tpfloat)vertices(l,2);
- const tpfloat projectedz = z + Z + absfocale;
- projections(l,1) = Y + absfocale*y/projectedz;
- projections(l,0) = X + absfocale*x/projectedz;
- }
- } else {
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(projections.size(),4096))
- cimg_forX(projections,l) { // Parallel projection
- const tpfloat
- x = (tpfloat)vertices(l,0),
- y = (tpfloat)vertices(l,1),
- z = (tpfloat)vertices(l,2);
- if (z<parallzmin) parallzmin = z;
- projections(l,1) = Y + y;
- projections(l,0) = X + x;
- }
- }
- const float _focale = absfocale?absfocale:(1e5f-parallzmin);
- float zmax = 0;
- if (zbuffer) zmax = vertices.get_shared_row(2).max();
- // Compute visible primitives.
- CImg<uintT> visibles(primitives._width,1,1,1,~0U);
- CImg<tpfloat> zrange(primitives._width);
- const tpfloat zmin = absfocale?(tpfloat)(1.5f - absfocale):cimg::type<tpfloat>::min();
- bool is_forward = zbuffer?true:false;
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(primitives.size(),4096))
- cimglist_for(primitives,l) {
- const CImg<tf>& primitive = primitives[l];
- switch (primitive.size()) {
- case 1 : { // Point
- CImg<_to> _opacity;
- __draw_object3d(opacities,l,_opacity);
- if (l<=colors.width() && (colors[l].size()!=_spectrum || _opacity)) is_forward = false;
- const unsigned int i0 = (unsigned int)primitive(0);
- const tpfloat z0 = Z + vertices(i0,2);
- if (z0>zmin) {
- visibles(l) = (unsigned int)l;
- zrange(l) = z0;
- }
- } break;
- case 5 : { // Sphere
- const unsigned int
- i0 = (unsigned int)primitive(0),
- i1 = (unsigned int)primitive(1);
- const tpfloat
- Xc = 0.5f*((float)vertices(i0,0) + (float)vertices(i1,0)),
- Yc = 0.5f*((float)vertices(i0,1) + (float)vertices(i1,1)),
- Zc = 0.5f*((float)vertices(i0,2) + (float)vertices(i1,2)),
- _zc = Z + Zc,
- zc = _zc + _focale,
- xc = X + Xc*(absfocale?absfocale/zc:1),
- yc = Y + Yc*(absfocale?absfocale/zc:1),
- radius = 0.5f*cimg::hypot(vertices(i1,0) - vertices(i0,0),
- vertices(i1,1) - vertices(i0,1),
- vertices(i1,2) - vertices(i0,2))*(absfocale?absfocale/zc:1),
- xm = xc - radius,
- ym = yc - radius,
- xM = xc + radius,
- yM = yc + radius;
- if (xM>=0 && xm<_width && yM>=0 && ym<_height && _zc>zmin) {
- visibles(l) = (unsigned int)l;
- zrange(l) = _zc;
- }
- is_forward = false;
- } break;
- case 2 : case 6 : { // Segment
- const unsigned int
- i0 = (unsigned int)primitive(0),
- i1 = (unsigned int)primitive(1);
- const tpfloat
- x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
- x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2);
- tpfloat xm, xM, ym, yM;
- if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
- if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
- if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin) {
- visibles(l) = (unsigned int)l;
- zrange(l) = (z0 + z1)/2;
- }
- } break;
- case 3 : case 9 : { // Triangle
- const unsigned int
- i0 = (unsigned int)primitive(0),
- i1 = (unsigned int)primitive(1),
- i2 = (unsigned int)primitive(2);
- const tpfloat
- x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
- x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2),
- x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2);
- tpfloat xm, xM, ym, yM;
- if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
- if (x2<xm) xm = x2;
- if (x2>xM) xM = x2;
- if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
- if (y2<ym) ym = y2;
- if (y2>yM) yM = y2;
- if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin) {
- const tpfloat d = (x1-x0)*(y2-y0) - (x2-x0)*(y1-y0);
- if (is_double_sided || d<0) {
- visibles(l) = (unsigned int)l;
- zrange(l) = (z0 + z1 + z2)/3;
- }
- }
- } break;
- case 4 : case 12 : { // Quadrangle
- const unsigned int
- i0 = (unsigned int)primitive(0),
- i1 = (unsigned int)primitive(1),
- i2 = (unsigned int)primitive(2),
- i3 = (unsigned int)primitive(3);
- const tpfloat
- x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
- x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2),
- x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2),
- x3 = projections(i3,0), y3 = projections(i3,1), z3 = Z + vertices(i3,2);
- tpfloat xm, xM, ym, yM;
- if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
- if (x2<xm) xm = x2;
- if (x2>xM) xM = x2;
- if (x3<xm) xm = x3;
- if (x3>xM) xM = x3;
- if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
- if (y2<ym) ym = y2;
- if (y2>yM) yM = y2;
- if (y3<ym) ym = y3;
- if (y3>yM) yM = y3;
- if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin && z3>zmin) {
- const float d = (x1 - x0)*(y2 - y0) - (x2 - x0)*(y1 - y0);
- if (is_double_sided || d<0) {
- visibles(l) = (unsigned int)l;
- zrange(l) = (z0 + z1 + z2 + z3)/4;
- }
- }
- } break;
- default :
- if (render_type==5) cimg::mutex(10,0);
- throw CImgArgumentException(_cimg_instance
- "draw_object3d(): Invalid primitive[%u] with size %u "
- "(should have size 1,2,3,4,5,6,9 or 12).",
- cimg_instance,
- l,primitive.size());
- }
- }
- // Force transparent primitives to be drawn last when zbuffer is activated
- // (and if object contains no spheres or sprites).
- if (is_forward)
- cimglist_for(primitives,l)
- if (___draw_object3d(opacities,l)!=1) zrange(l) = 2*zmax - zrange(l);
- // Sort only visibles primitives.
- unsigned int *p_visibles = visibles._data;
- tpfloat *p_zrange = zrange._data;
- const tpfloat *ptrz = p_zrange;
- cimg_for(visibles,ptr,unsigned int) {
- if (*ptr!=~0U) { *(p_visibles++) = *ptr; *(p_zrange++) = *ptrz; }
- ++ptrz;
- }
- const unsigned int nb_visibles = (unsigned int)(p_zrange - zrange._data);
- if (!nb_visibles) {
- if (render_type==5) cimg::mutex(10,0);
- return *this;
- }
- CImg<uintT> permutations;
- CImg<tpfloat>(zrange._data,nb_visibles,1,1,1,true).sort(permutations,is_forward);
- // Compute light properties
- CImg<floatT> lightprops;
- switch (render_type) {
- case 3 : { // Flat Shading
- lightprops.assign(nb_visibles);
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096))
- cimg_forX(lightprops,l) {
- const CImg<tf>& primitive = primitives(visibles(permutations(l)));
- const unsigned int psize = (unsigned int)primitive.size();
- if (psize==3 || psize==4 || psize==9 || psize==12) {
- const unsigned int
- i0 = (unsigned int)primitive(0),
- i1 = (unsigned int)primitive(1),
- i2 = (unsigned int)primitive(2);
- const tpfloat
- x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2),
- x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2),
- x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2),
- dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
- dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
- nx = dy1*dz2 - dz1*dy2,
- ny = dz1*dx2 - dx1*dz2,
- nz = dx1*dy2 - dy1*dx2,
- norm = 1e-5f + cimg::hypot(nx,ny,nz),
- lx = X + (x0 + x1 + x2)/3 - lightx,
- ly = Y + (y0 + y1 + y2)/3 - lighty,
- lz = Z + (z0 + z1 + z2)/3 - lightz,
- nl = 1e-5f + cimg::hypot(lx,ly,lz),
- factor = std::max(cimg::abs(-lx*nx - ly*ny - lz*nz)/(norm*nl),(tpfloat)0);
- lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
- } else lightprops[l] = 1;
- }
- } break;
- case 4 : // Gouraud Shading
- case 5 : { // Phong-Shading
- CImg<tpfloat> vertices_normals(vertices._width,6,1,1,0);
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096))
- for (int l = 0; l<(int)nb_visibles; ++l) {
- const CImg<tf>& primitive = primitives[visibles(l)];
- const unsigned int psize = (unsigned int)primitive.size();
- const bool
- triangle_flag = (psize==3) || (psize==9),
- quadrangle_flag = (psize==4) || (psize==12);
- if (triangle_flag || quadrangle_flag) {
- const unsigned int
- i0 = (unsigned int)primitive(0),
- i1 = (unsigned int)primitive(1),
- i2 = (unsigned int)primitive(2),
- i3 = quadrangle_flag?(unsigned int)primitive(3):0;
- const tpfloat
- x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2),
- x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2),
- x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2),
- dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
- dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
- nnx = dy1*dz2 - dz1*dy2,
- nny = dz1*dx2 - dx1*dz2,
- nnz = dx1*dy2 - dy1*dx2,
- norm = 1e-5f + cimg::hypot(nnx,nny,nnz),
- nx = nnx/norm,
- ny = nny/norm,
- nz = nnz/norm;
- unsigned int ix = 0, iy = 1, iz = 2;
- if (is_double_sided && nz>0) { ix = 3; iy = 4; iz = 5; }
- vertices_normals(i0,ix)+=nx; vertices_normals(i0,iy)+=ny; vertices_normals(i0,iz)+=nz;
- vertices_normals(i1,ix)+=nx; vertices_normals(i1,iy)+=ny; vertices_normals(i1,iz)+=nz;
- vertices_normals(i2,ix)+=nx; vertices_normals(i2,iy)+=ny; vertices_normals(i2,iz)+=nz;
- if (quadrangle_flag) {
- vertices_normals(i3,ix)+=nx; vertices_normals(i3,iy)+=ny; vertices_normals(i3,iz)+=nz;
- }
- }
- }
- if (is_double_sided) cimg_forX(vertices_normals,p) {
- const float
- nx0 = vertices_normals(p,0), ny0 = vertices_normals(p,1), nz0 = vertices_normals(p,2),
- nx1 = vertices_normals(p,3), ny1 = vertices_normals(p,4), nz1 = vertices_normals(p,5),
- n0 = nx0*nx0 + ny0*ny0 + nz0*nz0, n1 = nx1*nx1 + ny1*ny1 + nz1*nz1;
- if (n1>n0) {
- vertices_normals(p,0) = -nx1;
- vertices_normals(p,1) = -ny1;
- vertices_normals(p,2) = -nz1;
- }
- }
- if (render_type==4) {
- lightprops.assign(vertices._width);
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096))
- cimg_forX(lightprops,l) {
- const tpfloat
- nx = vertices_normals(l,0),
- ny = vertices_normals(l,1),
- nz = vertices_normals(l,2),
- norm = 1e-5f + cimg::hypot(nx,ny,nz),
- lx = X + vertices(l,0) - lightx,
- ly = Y + vertices(l,1) - lighty,
- lz = Z + vertices(l,2) - lightz,
- nl = 1e-5f + cimg::hypot(lx,ly,lz),
- factor = std::max((-lx*nx - ly*ny - lz*nz)/(norm*nl),(tpfloat)0);
- lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
- }
- } else {
- const unsigned int
- lw2 = light_texture._width/2 - 1,
- lh2 = light_texture._height/2 - 1;
- lightprops.assign(vertices._width,2);
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096))
- cimg_forX(lightprops,l) {
- const tpfloat
- nx = vertices_normals(l,0),
- ny = vertices_normals(l,1),
- nz = vertices_normals(l,2),
- norm = 1e-5f + cimg::hypot(nx,ny,nz),
- nnx = nx/norm,
- nny = ny/norm;
- lightprops(l,0) = lw2*(1 + nnx);
- lightprops(l,1) = lh2*(1 + nny);
- }
- }
- } break;
- }
- // Draw visible primitives
- const CImg<tc> default_color(1,_spectrum,1,1,(tc)200);
- CImg<_to> _opacity;
- for (unsigned int l = 0; l<nb_visibles; ++l) {
- const unsigned int n_primitive = visibles(permutations(l));
- const CImg<tf>& primitive = primitives[n_primitive];
- const CImg<tc>
- &__color = n_primitive<colors._width?colors[n_primitive]:CImg<tc>(),
- _color = (__color && __color.size()!=_spectrum && __color._spectrum<_spectrum)?
- __color.get_resize(-100,-100,-100,_spectrum,0):CImg<tc>(),
- &color = _color?_color:(__color?__color:default_color);
- const tc *const pcolor = color._data;
- float opacity = __draw_object3d(opacities,n_primitive,_opacity);
- if (_opacity.is_empty()) opacity*=g_opacity;
- #ifdef cimg_use_board
- LibBoard::Board &board = *(LibBoard::Board*)pboard;
- #endif
- switch (primitive.size()) {
- case 1 : { // Colored point or sprite
- const unsigned int n0 = (unsigned int)primitive[0];
- const int x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1));
- if (_opacity.is_empty()) { // Scalar opacity
- if (color.size()==_spectrum) { // Colored point
- draw_point(x0,y0,pcolor,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
- board.drawDot((float)x0,height()-(float)y0);
- }
- #endif
- } else { // Sprite
- const tpfloat z = Z + vertices(n0,2);
- const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1);
- const unsigned int
- _sw = (unsigned int)(color._width*factor),
- _sh = (unsigned int)(color._height*factor),
- sw = _sw?_sw:1, sh = _sh?_sh:1;
- const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2;
- if (sw<=3*_width/2 && sh<=3*_height/2 &&
- (nx0 + (int)sw/2>=0 || nx0 - (int)sw/2<width() || ny0 + (int)sh/2>=0 || ny0 - (int)sh/2<height())) {
- const CImg<tc>
- _sprite = (sw!=color._width || sh!=color._height)?
- color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<tc>(),
- &sprite = _sprite?_sprite:color;
- draw_image(nx0,ny0,sprite,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi(128,128,128);
- board.setFillColor(LibBoard::Color::Null);
- board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh);
- }
- #endif
- }
- }
- } else { // Opacity mask
- const tpfloat z = Z + vertices(n0,2);
- const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1);
- const unsigned int
- _sw = (unsigned int)(std::max(color._width,_opacity._width)*factor),
- _sh = (unsigned int)(std::max(color._height,_opacity._height)*factor),
- sw = _sw?_sw:1, sh = _sh?_sh:1;
- const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2;
- if (sw<=3*_width/2 && sh<=3*_height/2 &&
- (nx0 + (int)sw/2>=0 || nx0 - (int)sw/2<width() || ny0 + (int)sh/2>=0 || ny0 - (int)sh/2<height())) {
- const CImg<tc>
- _sprite = (sw!=color._width || sh!=color._height)?
- color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<tc>(),
- &sprite = _sprite?_sprite:color;
- const CImg<_to>
- _nopacity = (sw!=_opacity._width || sh!=_opacity._height)?
- _opacity.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<_to>(),
- &nopacity = _nopacity?_nopacity:_opacity;
- draw_image(nx0,ny0,sprite,nopacity,g_opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi(128,128,128);
- board.setFillColor(LibBoard::Color::Null);
- board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh);
- }
- #endif
- }
- }
- } break;
- case 2 : { // Colored line
- const unsigned int
- n0 = (unsigned int)primitive[0],
- n1 = (unsigned int)primitive[1];
- const int
- x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
- x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1));
- const float
- z0 = vertices(n0,2) + Z + _focale,
- z1 = vertices(n1,2) + Z + _focale;
- if (render_type) {
- if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity);
- else draw_line(x0,y0,x1,y1,pcolor,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
- board.drawLine((float)x0,height() - (float)y0,x1,height() - (float)y1);
- }
- #endif
- } else {
- draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
- board.drawDot((float)x0,height() - (float)y0);
- board.drawDot((float)x1,height() - (float)y1);
- }
- #endif
- }
- } break;
- case 5 : { // Colored sphere
- const unsigned int
- n0 = (unsigned int)primitive[0],
- n1 = (unsigned int)primitive[1],
- is_wireframe = (unsigned int)primitive[2],
- is_radius = (unsigned int)primitive[3];
- float Xc,Yc,Zc,radius;
- if (is_radius) {
- Xc = (float)vertices(n0,0);
- Yc = (float)vertices(n0,1);
- Zc = (float)vertices(n0,2);
- radius = cimg::hypot(vertices(n1,0) - vertices(n0,0),
- vertices(n1,1) - vertices(n0,1),
- vertices(n1,2) - vertices(n0,2));
- } else {
- Xc = 0.5f*((float)vertices(n0,0) + (float)vertices(n1,0));
- Yc = 0.5f*((float)vertices(n0,1) + (float)vertices(n1,1));
- Zc = 0.5f*((float)vertices(n0,2) + (float)vertices(n1,2));
- radius = 0.5f*cimg::hypot(vertices(n1,0) - vertices(n0,0),
- vertices(n1,1) - vertices(n0,1),
- vertices(n1,2) - vertices(n0,2));
- }
- const float
- zc = Z + Zc + _focale,
- af = absfocale?absfocale/zc:1,
- xc = X + Xc*af,
- yc = Y + Yc*af;
- radius*=af;
- switch (render_type) {
- case 0 :
- draw_point((int)xc,(int)yc,pcolor,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
- board.drawDot(xc,height() - yc);
- }
- #endif
- break;
- case 1 :
- draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
- board.setFillColor(LibBoard::Color::Null);
- board.drawCircle(xc,height() - yc,radius);
- }
- #endif
- break;
- default :
- if (is_wireframe) draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U);
- else draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
- if (!is_wireframe) board.fillCircle(xc,height() - yc,radius);
- else {
- board.setFillColor(LibBoard::Color::Null);
- board.drawCircle(xc,height() - yc,radius);
- }
- }
- #endif
- break;
- }
- } break;
- case 6 : { // Textured line
- if (!__color) {
- if (render_type==5) cimg::mutex(10,0);
- throw CImgArgumentException(_cimg_instance
- "draw_object3d(): Undefined texture for line primitive [%u].",
- cimg_instance,n_primitive);
- }
- const unsigned int
- n0 = (unsigned int)primitive[0],
- n1 = (unsigned int)primitive[1];
- const int
- tx0 = (int)primitive[2], ty0 = (int)primitive[3],
- tx1 = (int)primitive[4], ty1 = (int)primitive[5],
- x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
- x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1));
- const float
- z0 = vertices(n0,2) + Z + _focale,
- z1 = vertices(n1,2) + Z + _focale;
- if (render_type) {
- if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity);
- else draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
- board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
- }
- #endif
- } else {
- draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0,
- ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity).
- draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1,
- ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
- board.drawDot((float)x0,height() - (float)y0);
- board.drawDot((float)x1,height() - (float)y1);
- }
- #endif
- }
- } break;
- case 3 : { // Colored triangle
- const unsigned int
- n0 = (unsigned int)primitive[0],
- n1 = (unsigned int)primitive[1],
- n2 = (unsigned int)primitive[2];
- const int
- x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
- x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)),
- x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1));
- const float
- z0 = vertices(n0,2) + Z + _focale,
- z1 = vertices(n1,2) + Z + _focale,
- z2 = vertices(n2,2) + Z + _focale;
- switch (render_type) {
- case 0 :
- draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity).draw_point(x2,y2,pcolor,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
- board.drawDot((float)x0,height() - (float)y0);
- board.drawDot((float)x1,height() - (float)y1);
- board.drawDot((float)x2,height() - (float)y2);
- }
- #endif
- break;
- case 1 :
- if (zbuffer)
- draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x0,y0,z0,x2,y2,z2,pcolor,opacity).
- draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity);
- else
- draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x0,y0,x2,y2,pcolor,opacity).
- draw_line(x1,y1,x2,y2,pcolor,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
- board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
- board.drawLine((float)x0,height() - (float)y0,(float)x2,height() - (float)y2);
- board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
- }
- #endif
- break;
- case 2 :
- if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity);
- else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
- board.fillTriangle((float)x0,height() - (float)y0,
- (float)x1,height() - (float)y1,
- (float)x2,height() - (float)y2);
- }
- #endif
- break;
- case 3 :
- if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l));
- else _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l));
- #ifdef cimg_use_board
- if (pboard) {
- const float lp = std::min(lightprops(l),1.f);
- board.setPenColorRGBi((unsigned char)(color[0]*lp),
- (unsigned char)(color[1]*lp),
- (unsigned char)(color[2]*lp),
- (unsigned char)(opacity*255));
- board.fillTriangle((float)x0,height() - (float)y0,
- (float)x1,height() - (float)y1,
- (float)x2,height() - (float)y2);
- }
- #endif
- break;
- case 4 :
- if (zbuffer)
- draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,
- lightprops(n0),lightprops(n1),lightprops(n2),opacity);
- else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprops(n0),lightprops(n1),lightprops(n2),opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi((unsigned char)(color[0]),
- (unsigned char)(color[1]),
- (unsigned char)(color[2]),
- (unsigned char)(opacity*255));
- board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprops(n0),
- (float)x1,height() - (float)y1,lightprops(n1),
- (float)x2,height() - (float)y2,lightprops(n2));
- }
- #endif
- break;
- case 5 : {
- const unsigned int
- lx0 = (unsigned int)cimg::uiround(lightprops(n0,0)), ly0 = (unsigned int)cimg::uiround(lightprops(n0,1)),
- lx1 = (unsigned int)cimg::uiround(lightprops(n1,0)), ly1 = (unsigned int)cimg::uiround(lightprops(n1,1)),
- lx2 = (unsigned int)cimg::uiround(lightprops(n2,0)), ly2 = (unsigned int)cimg::uiround(lightprops(n2,1));
- if (zbuffer)
- draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
- else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- const float
- l0 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n0,0))),
- (int)(light_texture.height()/2*(1 + lightprops(n0,1)))),
- l1 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n1,0))),
- (int)(light_texture.height()/2*(1 + lightprops(n1,1)))),
- l2 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n2,0))),
- (int)(light_texture.height()/2*(1 + lightprops(n2,1))));
- board.setPenColorRGBi((unsigned char)(color[0]),
- (unsigned char)(color[1]),
- (unsigned char)(color[2]),
- (unsigned char)(opacity*255));
- board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
- (float)x1,height() - (float)y1,l1,
- (float)x2,height() - (float)y2,l2);
- }
- #endif
- } break;
- }
- } break;
- case 4 : { // Colored quadrangle
- const unsigned int
- n0 = (unsigned int)primitive[0],
- n1 = (unsigned int)primitive[1],
- n2 = (unsigned int)primitive[2],
- n3 = (unsigned int)primitive[3];
- const int
- x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
- x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)),
- x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1)),
- x3 = cimg::uiround(projections(n3,0)), y3 = cimg::uiround(projections(n3,1)),
- xc = (x0 + x1 + x2 + x3)/4, yc = (y0 + y1 + y2 + y3)/4;
- const float
- z0 = vertices(n0,2) + Z + _focale,
- z1 = vertices(n1,2) + Z + _focale,
- z2 = vertices(n2,2) + Z + _focale,
- z3 = vertices(n3,2) + Z + _focale,
- zc = (z0 + z1 + z2 + z3)/4;
- switch (render_type) {
- case 0 :
- draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity).
- draw_point(x2,y2,pcolor,opacity).draw_point(x3,y3,pcolor,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
- board.drawDot((float)x0,height() - (float)y0);
- board.drawDot((float)x1,height() - (float)y1);
- board.drawDot((float)x2,height() - (float)y2);
- board.drawDot((float)x3,height() - (float)y3);
- }
- #endif
- break;
- case 1 :
- if (zbuffer)
- draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity).
- draw_line(zbuffer,x2,y2,z2,x3,y3,z3,pcolor,opacity).draw_line(zbuffer,x3,y3,z3,x0,y0,z0,pcolor,opacity);
- else
- draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x1,y1,x2,y2,pcolor,opacity).
- draw_line(x2,y2,x3,y3,pcolor,opacity).draw_line(x3,y3,x0,y0,pcolor,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
- board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
- board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
- board.drawLine((float)x2,height() - (float)y2,(float)x3,height() - (float)y3);
- board.drawLine((float)x3,height() - (float)y3,(float)x0,height() - (float)y0);
- }
- #endif
- break;
- case 2 :
- if (zbuffer)
- draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity).
- draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity);
- else
- draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity).draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
- board.fillTriangle((float)x0,height() - (float)y0,
- (float)x1,height() - (float)y1,
- (float)x2,height() - (float)y2);
- board.fillTriangle((float)x0,height() - (float)y0,
- (float)x2,height() - (float)y2,
- (float)x3,height() - (float)y3);
- }
- #endif
- break;
- case 3 :
- if (zbuffer)
- draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l)).
- draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity,lightprops(l));
- else
- _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l)).
- _draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity,lightprops(l));
- #ifdef cimg_use_board
- if (pboard) {
- const float lp = std::min(lightprops(l),1.f);
- board.setPenColorRGBi((unsigned char)(color[0]*lp),
- (unsigned char)(color[1]*lp),
- (unsigned char)(color[2]*lp),(unsigned char)(opacity*255));
- board.fillTriangle((float)x0,height() - (float)y0,
- (float)x1,height() - (float)y1,
- (float)x2,height() - (float)y2);
- board.fillTriangle((float)x0,height() - (float)y0,
- (float)x2,height() - (float)y2,
- (float)x3,height() - (float)y3);
- }
- #endif
- break;
- case 4 : {
- const float
- lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
- lightprop2 = lightprops(n2), lightprop3 = lightprops(n3),
- lightpropc = (lightprop0 + lightprop1 + lightprop2 + lightprop2)/4;
- if (zbuffer)
- draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,xc,yc,zc,pcolor,lightprop0,lightprop1,lightpropc,opacity).
- draw_triangle(zbuffer,x1,y1,z1,x2,y2,z2,xc,yc,zc,pcolor,lightprop1,lightprop2,lightpropc,opacity).
- draw_triangle(zbuffer,x2,y2,z2,x3,y3,z3,xc,yc,zc,pcolor,lightprop2,lightprop3,lightpropc,opacity).
- draw_triangle(zbuffer,x3,y3,z3,x0,y0,z0,xc,yc,zc,pcolor,lightprop3,lightprop0,lightpropc,opacity);
- else
- draw_triangle(x0,y0,x1,y1,xc,yc,pcolor,lightprop0,lightprop1,lightpropc,opacity).
- draw_triangle(x1,y1,x2,y2,xc,yc,pcolor,lightprop1,lightprop2,lightpropc,opacity).
- draw_triangle(x2,y2,x3,y3,xc,yc,pcolor,lightprop2,lightprop3,lightpropc,opacity).
- draw_triangle(x3,y3,x0,y0,xc,yc,pcolor,lightprop3,lightprop0,lightpropc,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi((unsigned char)(color[0]),
- (unsigned char)(color[1]),
- (unsigned char)(color[2]),
- (unsigned char)(opacity*255));
- board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0,
- (float)x1,height() - (float)y1,lightprop1,
- (float)x2,height() - (float)y2,lightprop2);
- board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0,
- (float)x2,height() - (float)y2,lightprop2,
- (float)x3,height() - (float)y3,lightprop3);
- }
- #endif
- } break;
- case 5 : {
- const unsigned int
- lx0 = (unsigned int)cimg::uiround(lightprops(n0,0)), ly0 = (unsigned int)cimg::uiround(lightprops(n0,1)),
- lx1 = (unsigned int)cimg::uiround(lightprops(n1,0)), ly1 = (unsigned int)cimg::uiround(lightprops(n1,1)),
- lx2 = (unsigned int)cimg::uiround(lightprops(n2,0)), ly2 = (unsigned int)cimg::uiround(lightprops(n2,1)),
- lx3 = (unsigned int)cimg::uiround(lightprops(n3,0)), ly3 = (unsigned int)cimg::uiround(lightprops(n3,1)),
- lxc = (lx0 + lx1 + lx2 + lx3)/4, lyc = (ly0 + ly1 + ly2 + ly3)/4;
- if (zbuffer)
- draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,xc,yc,zc,pcolor,light_texture,lx0,ly0,lx1,ly1,lxc,lyc,opacity).
- draw_triangle(zbuffer,x1,y1,z1,x2,y2,z2,xc,yc,zc,pcolor,light_texture,lx1,ly1,lx2,ly2,lxc,lyc,opacity).
- draw_triangle(zbuffer,x2,y2,z2,x3,y3,z3,xc,yc,zc,pcolor,light_texture,lx2,ly2,lx3,ly3,lxc,lyc,opacity).
- draw_triangle(zbuffer,x3,y3,z3,x0,y0,z0,xc,yc,zc,pcolor,light_texture,lx3,ly3,lx0,ly0,lxc,lyc,opacity);
- else
- draw_triangle(x0,y0,x1,y1,xc,yc,pcolor,light_texture,lx0,ly0,lx1,ly1,lxc,lyc,opacity).
- draw_triangle(x1,y1,x2,y2,xc,yc,pcolor,light_texture,lx1,ly1,lx2,ly2,lxc,lyc,opacity).
- draw_triangle(x2,y2,x3,y3,xc,yc,pcolor,light_texture,lx2,ly2,lx3,ly3,lxc,lyc,opacity).
- draw_triangle(x3,y3,x0,y0,xc,yc,pcolor,light_texture,lx3,ly3,lx0,ly0,lxc,lyc,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- const float
- l0 = light_texture((int)(light_texture.width()/2*(1 + lx0)), (int)(light_texture.height()/2*(1 + ly0))),
- l1 = light_texture((int)(light_texture.width()/2*(1 + lx1)), (int)(light_texture.height()/2*(1 + ly1))),
- l2 = light_texture((int)(light_texture.width()/2*(1 + lx2)), (int)(light_texture.height()/2*(1 + ly2))),
- l3 = light_texture((int)(light_texture.width()/2*(1 + lx3)), (int)(light_texture.height()/2*(1 + ly3)));
- board.setPenColorRGBi((unsigned char)(color[0]),
- (unsigned char)(color[1]),
- (unsigned char)(color[2]),
- (unsigned char)(opacity*255));
- board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
- (float)x1,height() - (float)y1,l1,
- (float)x2,height() - (float)y2,l2);
- board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
- (float)x2,height() - (float)y2,l2,
- (float)x3,height() - (float)y3,l3);
- }
- #endif
- } break;
- }
- } break;
- case 9 : { // Textured triangle
- if (!__color) {
- if (render_type==5) cimg::mutex(10,0);
- throw CImgArgumentException(_cimg_instance
- "draw_object3d(): Undefined texture for triangle primitive [%u].",
- cimg_instance,n_primitive);
- }
- const unsigned int
- n0 = (unsigned int)primitive[0],
- n1 = (unsigned int)primitive[1],
- n2 = (unsigned int)primitive[2];
- const int
- tx0 = (int)primitive[3], ty0 = (int)primitive[4],
- tx1 = (int)primitive[5], ty1 = (int)primitive[6],
- tx2 = (int)primitive[7], ty2 = (int)primitive[8],
- x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
- x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)),
- x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1));
- const float
- z0 = vertices(n0,2) + Z + _focale,
- z1 = vertices(n1,2) + Z + _focale,
- z2 = vertices(n2,2) + Z + _focale;
- switch (render_type) {
- case 0 :
- draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0,
- ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity).
- draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1,
- ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity).
- draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2,
- ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
- board.drawDot((float)x0,height() - (float)y0);
- board.drawDot((float)x1,height() - (float)y1);
- board.drawDot((float)x2,height() - (float)y2);
- }
- #endif
- break;
- case 1 :
- if (zbuffer)
- draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
- draw_line(zbuffer,x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity).
- draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity);
- else
- draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
- draw_line(x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity).
- draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
- board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
- board.drawLine((float)x0,height() - (float)y0,(float)x2,height() - (float)y2);
- board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
- }
- #endif
- break;
- case 2 :
- if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity);
- else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
- board.fillTriangle((float)x0,height() - (float)y0,
- (float)x1,height() - (float)y1,
- (float)x2,height() - (float)y2);
- }
- #endif
- break;
- case 3 :
- if (zbuffer)
- draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l));
- else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l));
- #ifdef cimg_use_board
- if (pboard) {
- const float lp = std::min(lightprops(l),1.f);
- board.setPenColorRGBi((unsigned char)(128*lp),
- (unsigned char)(128*lp),
- (unsigned char)(128*lp),
- (unsigned char)(opacity*255));
- board.fillTriangle((float)x0,height() - (float)y0,
- (float)x1,height() - (float)y1,
- (float)x2,height() - (float)y2);
- }
- #endif
- break;
- case 4 :
- if (zbuffer)
- draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
- lightprops(n0),lightprops(n1),lightprops(n2),opacity);
- else
- draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
- lightprops(n0),lightprops(n1),lightprops(n2),opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
- board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprops(n0),
- (float)x1,height() - (float)y1,lightprops(n1),
- (float)x2,height() - (float)y2,lightprops(n2));
- }
- #endif
- break;
- case 5 :
- if (zbuffer)
- draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
- (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
- (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
- (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
- opacity);
- else
- draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
- (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
- (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
- (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
- opacity);
- #ifdef cimg_use_board
- if (pboard) {
- const float
- l0 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n0,0))),
- (int)(light_texture.height()/2*(1 + lightprops(n0,1)))),
- l1 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n1,0))),
- (int)(light_texture.height()/2*(1 + lightprops(n1,1)))),
- l2 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n2,0))),
- (int)(light_texture.height()/2*(1 + lightprops(n2,1))));
- board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
- board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
- (float)x1,height() - (float)y1,l1,
- (float)x2,height() - (float)y2,l2);
- }
- #endif
- break;
- }
- } break;
- case 12 : { // Textured quadrangle
- if (!__color) {
- if (render_type==5) cimg::mutex(10,0);
- throw CImgArgumentException(_cimg_instance
- "draw_object3d(): Undefined texture for quadrangle primitive [%u].",
- cimg_instance,n_primitive);
- }
- const unsigned int
- n0 = (unsigned int)primitive[0],
- n1 = (unsigned int)primitive[1],
- n2 = (unsigned int)primitive[2],
- n3 = (unsigned int)primitive[3];
- const int
- tx0 = (int)primitive[4], ty0 = (int)primitive[5],
- tx1 = (int)primitive[6], ty1 = (int)primitive[7],
- tx2 = (int)primitive[8], ty2 = (int)primitive[9],
- tx3 = (int)primitive[10], ty3 = (int)primitive[11],
- x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
- x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)),
- x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1)),
- x3 = cimg::uiround(projections(n3,0)), y3 = cimg::uiround(projections(n3,1));
- const float
- z0 = vertices(n0,2) + Z + _focale,
- z1 = vertices(n1,2) + Z + _focale,
- z2 = vertices(n2,2) + Z + _focale,
- z3 = vertices(n3,2) + Z + _focale;
- switch (render_type) {
- case 0 :
- draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0,
- ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity).
- draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1,
- ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity).
- draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2,
- ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity).
- draw_point(x3,y3,color.get_vector_at(tx3<=0?0:tx3>=color.width()?color.width() - 1:tx3,
- ty3<=0?0:ty3>=color.height()?color.height() - 1:ty3)._data,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
- board.drawDot((float)x0,height() - (float)y0);
- board.drawDot((float)x1,height() - (float)y1);
- board.drawDot((float)x2,height() - (float)y2);
- board.drawDot((float)x3,height() - (float)y3);
- }
- #endif
- break;
- case 1 :
- if (zbuffer)
- draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
- draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity).
- draw_line(zbuffer,x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity).
- draw_line(zbuffer,x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity);
- else
- draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
- draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity).
- draw_line(x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity).
- draw_line(x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
- board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
- board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
- board.drawLine((float)x2,height() - (float)y2,(float)x3,height() - (float)y3);
- board.drawLine((float)x3,height() - (float)y3,(float)x0,height() - (float)y0);
- }
- #endif
- break;
- case 2 :
- if (zbuffer)
- draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity).
- draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity);
- else
- draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity).
- draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
- board.fillTriangle((float)x0,height() - (float)y0,
- (float)x1,height() - (float)y1,
- (float)x2,height() - (float)y2);
- board.fillTriangle((float)x0,height() - (float)y0,
- (float)x2,height() - (float)y2,
- (float)x3,height() - (float)y3);
- }
- #endif
- break;
- case 3 :
- if (zbuffer)
- draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)).
- draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l));
- else
- draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)).
- draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l));
- #ifdef cimg_use_board
- if (pboard) {
- const float lp = std::min(lightprops(l),1.f);
- board.setPenColorRGBi((unsigned char)(128*lp),
- (unsigned char)(128*lp),
- (unsigned char)(128*lp),
- (unsigned char)(opacity*255));
- board.fillTriangle((float)x0,height() - (float)y0,
- (float)x1,height() - (float)y1,
- (float)x2,height() - (float)y2);
- board.fillTriangle((float)x0,height() - (float)y0,
- (float)x2,height() - (float)y2,
- (float)x3,height() - (float)y3);
- }
- #endif
- break;
- case 4 : {
- const float
- lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
- lightprop2 = lightprops(n2), lightprop3 = lightprops(n3);
- if (zbuffer)
- draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
- lightprop0,lightprop1,lightprop2,opacity).
- draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
- lightprop0,lightprop2,lightprop3,opacity);
- else
- draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
- lightprop0,lightprop1,lightprop2,opacity).
- draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
- lightprop0,lightprop2,lightprop3,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
- board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0,
- (float)x1,height() - (float)y1,lightprop1,
- (float)x2,height() - (float)y2,lightprop2);
- board.fillGouraudTriangle((float)x0,height() -(float)y0,lightprop0,
- (float)x2,height() - (float)y2,lightprop2,
- (float)x3,height() - (float)y3,lightprop3);
- }
- #endif
- } break;
- case 5 : {
- const unsigned int
- lx0 = (unsigned int)cimg::uiround(lightprops(n0,0)), ly0 = (unsigned int)cimg::uiround(lightprops(n0,1)),
- lx1 = (unsigned int)cimg::uiround(lightprops(n1,0)), ly1 = (unsigned int)cimg::uiround(lightprops(n1,1)),
- lx2 = (unsigned int)cimg::uiround(lightprops(n2,0)), ly2 = (unsigned int)cimg::uiround(lightprops(n2,1)),
- lx3 = (unsigned int)cimg::uiround(lightprops(n3,0)), ly3 = (unsigned int)cimg::uiround(lightprops(n3,1));
- if (zbuffer)
- draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
- light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity).
- draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
- light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity);
- else
- draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
- light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity).
- draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
- light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity);
- #ifdef cimg_use_board
- if (pboard) {
- const float
- l0 = light_texture((int)(light_texture.width()/2*(1 + lx0)), (int)(light_texture.height()/2*(1 + ly0))),
- l1 = light_texture((int)(light_texture.width()/2*(1 + lx1)), (int)(light_texture.height()/2*(1 + ly1))),
- l2 = light_texture((int)(light_texture.width()/2*(1 + lx2)), (int)(light_texture.height()/2*(1 + ly2))),
- l3 = light_texture((int)(light_texture.width()/2*(1 + lx3)), (int)(light_texture.height()/2*(1 + ly3)));
- board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
- board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
- (float)x1,height() - (float)y1,l1,
- (float)x2,height() - (float)y2,l2);
- board.fillGouraudTriangle((float)x0,height() -(float)y0,l0,
- (float)x2,height() - (float)y2,l2,
- (float)x3,height() - (float)y3,l3);
- }
- #endif
- } break;
- }
- } break;
- }
- }
- if (render_type==5) cimg::mutex(10,0);
- return *this;
- }
- //@}
- //---------------------------
- //
- //! \name Data Input
- //@{
- //---------------------------
- //! Launch simple interface to select a shape from an image.
- /**
- \param disp Display window to use.
- \param feature_type Type of feature to select. Can be <tt>{ 0=point | 1=line | 2=rectangle | 3=ellipse }</tt>.
- \param XYZ Pointer to 3 values X,Y,Z which tells about the projection point coordinates, for volumetric images.
- \param exit_on_anykey Exit function when any key is pressed.
- **/
- CImg<T>& select(CImgDisplay &disp,
- const unsigned int feature_type=2, unsigned int *const XYZ=0,
- const bool exit_on_anykey=false,
- const bool is_deep_selection_default=false) {
- return get_select(disp,feature_type,XYZ,exit_on_anykey,is_deep_selection_default).move_to(*this);
- }
- //! Simple interface to select a shape from an image \overloading.
- CImg<T>& select(const char *const title,
- const unsigned int feature_type=2, unsigned int *const XYZ=0,
- const bool exit_on_anykey=false,
- const bool is_deep_selection_default=false) {
- return get_select(title,feature_type,XYZ,exit_on_anykey,is_deep_selection_default).move_to(*this);
- }
- //! Simple interface to select a shape from an image \newinstance.
- CImg<intT> get_select(CImgDisplay &disp,
- const unsigned int feature_type=2, unsigned int *const XYZ=0,
- const bool exit_on_anykey=false,
- const bool is_deep_selection_default=false) const {
- return _select(disp,0,feature_type,XYZ,0,0,0,exit_on_anykey,true,false,is_deep_selection_default);
- }
- //! Simple interface to select a shape from an image \newinstance.
- CImg<intT> get_select(const char *const title,
- const unsigned int feature_type=2, unsigned int *const XYZ=0,
- const bool exit_on_anykey=false,
- const bool is_deep_selection_default=false) const {
- CImgDisplay disp;
- return _select(disp,title,feature_type,XYZ,0,0,0,exit_on_anykey,true,false,is_deep_selection_default);
- }
- CImg<intT> _select(CImgDisplay &disp, const char *const title,
- const unsigned int feature_type, unsigned int *const XYZ,
- const int origX, const int origY, const int origZ,
- const bool exit_on_anykey,
- const bool reset_view3d,
- const bool force_display_z_coord,
- const bool is_deep_selection_default) const {
- if (is_empty()) return CImg<intT>(1,feature_type==0?3:6,1,1,-1);
- if (!disp) {
- disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1);
- if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
- } else {
- if (title) disp.set_title("%s",title);
- disp.move_inside_screen();
- }
- CImg<T> thumb;
- if (width()>disp.screen_width() || height()>disp.screen_height())
- get_resize(cimg_fitscreen(width(),height(),depth()),depth(),-100).move_to(thumb);
- const unsigned int old_normalization = disp.normalization();
- bool old_is_resized = disp.is_resized();
- disp._normalization = 0;
- disp.show().set_key(0).set_wheel().show_mouse();
- static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
- int area = 0, area_started = 0, area_clicked = 0, phase = 0,
- X0 = (int)((XYZ?XYZ[0]:_width/2)%_width),
- Y0 = (int)((XYZ?XYZ[1]:_height/2)%_height),
- Z0 = (int)((XYZ?XYZ[2]:_depth/2)%_depth),
- X1 =-1, Y1 = -1, Z1 = -1,
- X3d = -1, Y3d = -1,
- oX3d = X3d, oY3d = -1,
- omx = -1, omy = -1;
- float X = -1, Y = -1, Z = -1;
- unsigned int key = 0, font_size = 32;
- bool is_deep_selection = is_deep_selection_default,
- shape_selected = false, text_down = false, visible_cursor = true;
- static CImg<floatT> pose3d;
- static bool is_view3d = false, is_axes = true;
- if (reset_view3d) { pose3d.assign(); is_view3d = false; }
- CImg<floatT> points3d, opacities3d, sel_opacities3d;
- CImgList<uintT> primitives3d, sel_primitives3d;
- CImgList<ucharT> colors3d, sel_colors3d;
- CImg<ucharT> visu, visu0, view3d;
- CImg<charT> text(1024); *text = 0;
- while (!key && !disp.is_closed() && !shape_selected) {
- // Handle mouse motion and selection
- int
- mx = disp.mouse_x(),
- my = disp.mouse_y();
- const float
- mX = mx<0?-1.f:(float)mx*(width() + (depth()>1?depth():0))/disp.width(),
- mY = my<0?-1.f:(float)my*(height() + (depth()>1?depth():0))/disp.height();
- area = 0;
- if (mX>=0 && mY>=0 && mX<width() && mY<height()) { area = 1; X = mX; Y = mY; Z = (float)(phase?Z1:Z0); }
- if (mX>=0 && mX<width() && mY>=height()) { area = 2; X = mX; Z = mY - _height; Y = (float)(phase?Y1:Y0); }
- if (mY>=0 && mX>=width() && mY<height()) { area = 3; Y = mY; Z = mX - _width; X = (float)(phase?X1:X0); }
- if (mX>=width() && mY>=height()) area = 4;
- if (disp.button()) { if (!area_clicked) area_clicked = area; } else area_clicked = 0;
- CImg<charT> filename(32);
- switch (key = disp.key()) {
- #if cimg_OS!=2
- case cimg::keyCTRLRIGHT :
- #endif
- case 0 : case cimg::keyCTRLLEFT : key = 0; break;
- case cimg::keyPAGEUP :
- if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(1); key = 0; } break;
- case cimg::keyPAGEDOWN :
- if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(-1); key = 0; } break;
- case cimg::keyX : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- is_axes = !is_axes; disp.set_key(key,false); key = 0; visu0.assign();
- } break;
- case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- disp.set_fullscreen(false).
- resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
- CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
- _is_resized = true;
- disp.set_key(key,false); key = 0; visu0.assign();
- } break;
- case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- disp.set_fullscreen(false).
- resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
- disp.set_key(key,false); key = 0; visu0.assign();
- } break;
- case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
- disp.set_key(key,false); key = 0; visu0.assign();
- } break;
- case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
- disp.set_key(key,false); key = 0; visu0.assign();
- } break;
- case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- is_view3d = !is_view3d; disp.set_key(key,false); key = 0; visu0.assign();
- } break;
- case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- static unsigned int snap_number = 0;
- std::FILE *file;
- do {
- cimg_snprintf(filename,filename._width,cimg_appname "_%.6u."
- #ifdef cimg_use_png
- "png",
- #else
- "bmp",
- #endif
- snap_number++);
- if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
- } while (file);
- if (visu0) {
- (+visu0).__draw_text(" Saving snapshot...",font_size,(int)text_down).display(disp);
- visu0.save(filename);
- (+visu0).__draw_text(" Snapshot '%s' saved. ",font_size,(int)text_down,filename._data).display(disp);
- }
- disp.set_key(key,false); key = 0;
- } break;
- case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- static unsigned int snap_number = 0;
- std::FILE *file;
- do {
- #ifdef cimg_use_zlib
- cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimgz",snap_number++);
- #else
- cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimg",snap_number++);
- #endif
- if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
- } while (file);
- (+visu0).__draw_text(" Saving instance... ",font_size,(int)text_down).display(disp);
- save(filename);
- (+visu0).__draw_text(" Instance '%s' saved. ",font_size,(int)text_down,filename._data).display(disp);
- disp.set_key(key,false); key = 0;
- } break;
- }
- switch (area) {
- case 0 : // When mouse is out of image range
- mx = my = -1; X = Y = Z = -1;
- break;
- case 1 : case 2 : case 3 : { // When mouse is over the XY,XZ or YZ projections
- const unsigned int but = disp.button();
- const bool b1 = (bool)(but&1), b2 = (bool)(but&2), b3 = (bool)(but&4);
- if (b1 && phase==1 && area_clicked==area) { // When selection has been started (1st step)
- if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign();
- X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z;
- }
- if (!b1 && phase==2 && area_clicked!=area) { // When selection is at 2nd step (for volumes)
- switch (area_started) {
- case 1 : if (Z1!=(int)Z) visu0.assign(); Z1 = (int)Z; break;
- case 2 : if (Y1!=(int)Y) visu0.assign(); Y1 = (int)Y; break;
- case 3 : if (X1!=(int)X) visu0.assign(); X1 = (int)X; break;
- }
- }
- if (b2 && area_clicked==area) { // When moving through the image/volume
- if (phase) {
- if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign();
- X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z;
- } else {
- if (_depth>1 && (X0!=(int)X || Y0!=(int)Y || Z0!=(int)Z)) visu0.assign();
- X0 = (int)X; Y0 = (int)Y; Z0 = (int)Z;
- }
- }
- if (b3) { // Reset selection
- X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = area = area_clicked = area_started = 0;
- visu0.assign();
- }
- if (disp.wheel()) { // When moving through the slices of the volume (with mouse wheel)
- if (_depth>1 && !disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT() &&
- !disp.is_keySHIFTLEFT() && !disp.is_keySHIFTRIGHT()) {
- switch (area) {
- case 1 :
- if (phase) Z = (float)(Z1+=disp.wheel()); else Z = (float)(Z0+=disp.wheel());
- visu0.assign(); break;
- case 2 :
- if (phase) Y = (float)(Y1+=disp.wheel()); else Y = (float)(Y0+=disp.wheel());
- visu0.assign(); break;
- case 3 :
- if (phase) X = (float)(X1+=disp.wheel()); else X = (float)(X0+=disp.wheel());
- visu0.assign(); break;
- }
- disp.set_wheel();
- } else key = ~0U;
- }
- if ((phase==0 && b1) ||
- (phase==1 && !b1) ||
- (phase==2 && b1)) switch (phase) { // Detect change of phase
- case 0 :
- if (area==area_clicked) {
- X0 = X1 = (int)X; Y0 = Y1 = (int)Y; Z0 = Z1 = (int)Z; area_started = area; ++phase;
- } break;
- case 1 :
- if (area==area_started) {
- X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; ++phase;
- if (_depth>1) {
- if (disp.is_keyCTRLLEFT()) is_deep_selection = !is_deep_selection_default;
- if (is_deep_selection) ++phase;
- }
- } else if (!b1) { X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = 0; visu0.assign(); }
- break;
- case 2 : ++phase; break;
- }
- } break;
- case 4 : // When mouse is over the 3D view
- if (is_view3d && points3d) {
- X3d = mx - width()*disp.width()/(width() + (depth()>1?depth():0));
- Y3d = my - height()*disp.height()/(height() + (depth()>1?depth():0));
- if (oX3d<0) { oX3d = X3d; oY3d = Y3d; }
- // Left + right buttons: reset.
- if ((disp.button()&3)==3) { pose3d.assign(); view3d.assign(); oX3d = oY3d = X3d = Y3d = -1; }
- else if (disp.button()&1 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Left button: rotate
- const float
- R = 0.45f*std::min(view3d._width,view3d._height),
- R2 = R*R,
- u0 = (float)(oX3d - view3d.width()/2),
- v0 = (float)(oY3d - view3d.height()/2),
- u1 = (float)(X3d - view3d.width()/2),
- v1 = (float)(Y3d - view3d.height()/2),
- n0 = cimg::hypot(u0,v0),
- n1 = cimg::hypot(u1,v1),
- nu0 = n0>R?(u0*R/n0):u0,
- nv0 = n0>R?(v0*R/n0):v0,
- nw0 = (float)std::sqrt(std::max(0.f,R2 - nu0*nu0 - nv0*nv0)),
- nu1 = n1>R?(u1*R/n1):u1,
- nv1 = n1>R?(v1*R/n1):v1,
- nw1 = (float)std::sqrt(std::max(0.f,R2 - nu1*nu1 - nv1*nv1)),
- u = nv0*nw1 - nw0*nv1,
- v = nw0*nu1 - nu0*nw1,
- w = nv0*nu1 - nu0*nv1,
- n = cimg::hypot(u,v,w),
- alpha = (float)std::asin(n/R2)*180/cimg::PI;
- pose3d.draw_image(CImg<floatT>::rotation_matrix(u,v,w,-alpha)*pose3d.get_crop(0,0,2,2));
- view3d.assign();
- } else if (disp.button()&2 && pose3d && oY3d!=Y3d) { // Right button: zoom
- pose3d(3,2)+=(Y3d - oY3d)*1.5f; view3d.assign();
- }
- if (disp.wheel()) { // Wheel: zoom
- pose3d(3,2)-=disp.wheel()*15; view3d.assign(); disp.set_wheel();
- }
- if (disp.button()&4 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Middle button: shift
- pose3d(3,0)-=oX3d - X3d; pose3d(3,1)-=oY3d - Y3d; view3d.assign();
- }
- oX3d = X3d; oY3d = Y3d;
- }
- mx = my = -1; X = Y = Z = -1;
- break;
- }
- if (phase) {
- if (!feature_type) shape_selected = phase?true:false;
- else {
- if (_depth>1) shape_selected = (phase==3)?true:false;
- else shape_selected = (phase==2)?true:false;
- }
- }
- if (X0<0) X0 = 0;
- if (X0>=width()) X0 = width() - 1;
- if (Y0<0) Y0 = 0;
- if (Y0>=height()) Y0 = height() - 1;
- if (Z0<0) Z0 = 0;
- if (Z0>=depth()) Z0 = depth() - 1;
- if (X1<1) X1 = 0;
- if (X1>=width()) X1 = width() - 1;
- if (Y1<0) Y1 = 0;
- if (Y1>=height()) Y1 = height() - 1;
- if (Z1<0) Z1 = 0;
- if (Z1>=depth()) Z1 = depth() - 1;
- // Draw visualization image on the display
- if (mx!=omx || my!=omy || !visu0 || (_depth>1 && !view3d)) {
- if (!visu0) { // Create image of projected planes
- if (thumb) thumb._get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0);
- else _get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0);
- visu0.resize(disp);
- view3d.assign();
- points3d.assign();
- }
- if (is_view3d && _depth>1 && !view3d) { // Create 3D view for volumetric images
- const unsigned int
- _x3d = (unsigned int)cimg::round((float)_width*visu0._width/(_width + _depth),1,1),
- _y3d = (unsigned int)cimg::round((float)_height*visu0._height/(_height + _depth),1,1),
- x3d = _x3d>=visu0._width?visu0._width - 1:_x3d,
- y3d = _y3d>=visu0._height?visu0._height - 1:_y3d;
- CImg<ucharT>(1,2,1,1,64,128).resize(visu0._width - x3d,visu0._height - y3d,1,visu0._spectrum,3).
- move_to(view3d);
- if (!points3d) {
- get_projections3d(primitives3d,colors3d,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0,true).move_to(points3d);
- points3d.append(CImg<floatT>(8,3,1,1,
- 0,_width - 1,_width - 1,0,0,_width - 1,_width - 1,0,
- 0,0,_height - 1,_height - 1,0,0,_height - 1,_height - 1,
- 0,0,0,0,_depth - 1,_depth - 1,_depth - 1,_depth - 1),'x');
- CImg<uintT>::vector(12,13).move_to(primitives3d); CImg<uintT>::vector(13,14).move_to(primitives3d);
- CImg<uintT>::vector(14,15).move_to(primitives3d); CImg<uintT>::vector(15,12).move_to(primitives3d);
- CImg<uintT>::vector(16,17).move_to(primitives3d); CImg<uintT>::vector(17,18).move_to(primitives3d);
- CImg<uintT>::vector(18,19).move_to(primitives3d); CImg<uintT>::vector(19,16).move_to(primitives3d);
- CImg<uintT>::vector(12,16).move_to(primitives3d); CImg<uintT>::vector(13,17).move_to(primitives3d);
- CImg<uintT>::vector(14,18).move_to(primitives3d); CImg<uintT>::vector(15,19).move_to(primitives3d);
- colors3d.insert(12,CImg<ucharT>::vector(255,255,255));
- opacities3d.assign(primitives3d.width(),1,1,1,0.5f);
- if (!phase) {
- opacities3d[0] = opacities3d[1] = opacities3d[2] = 0.8f;
- sel_primitives3d.assign();
- sel_colors3d.assign();
- sel_opacities3d.assign();
- } else {
- if (feature_type==2) {
- points3d.append(CImg<floatT>(8,3,1,1,
- X0,X1,X1,X0,X0,X1,X1,X0,
- Y0,Y0,Y1,Y1,Y0,Y0,Y1,Y1,
- Z0,Z0,Z0,Z0,Z1,Z1,Z1,Z1),'x');
- sel_primitives3d.assign();
- CImg<uintT>::vector(20,21).move_to(sel_primitives3d);
- CImg<uintT>::vector(21,22).move_to(sel_primitives3d);
- CImg<uintT>::vector(22,23).move_to(sel_primitives3d);
- CImg<uintT>::vector(23,20).move_to(sel_primitives3d);
- CImg<uintT>::vector(24,25).move_to(sel_primitives3d);
- CImg<uintT>::vector(25,26).move_to(sel_primitives3d);
- CImg<uintT>::vector(26,27).move_to(sel_primitives3d);
- CImg<uintT>::vector(27,24).move_to(sel_primitives3d);
- CImg<uintT>::vector(20,24).move_to(sel_primitives3d);
- CImg<uintT>::vector(21,25).move_to(sel_primitives3d);
- CImg<uintT>::vector(22,26).move_to(sel_primitives3d);
- CImg<uintT>::vector(23,27).move_to(sel_primitives3d);
- } else {
- points3d.append(CImg<floatT>(2,3,1,1,
- X0,X1,
- Y0,Y1,
- Z0,Z1),'x');
- sel_primitives3d.assign(CImg<uintT>::vector(20,21));
- }
- sel_colors3d.assign(sel_primitives3d._width,CImg<ucharT>::vector(255,255,255));
- sel_opacities3d.assign(sel_primitives3d._width,1,1,1,0.8f);
- }
- points3d.shift_object3d(-0.5f*(_width - 1),-0.5f*(_height - 1),-0.5f*(_depth - 1)).resize_object3d();
- points3d*=0.75f*std::min(view3d._width,view3d._height);
- }
- if (!pose3d) CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose3d);
- CImg<floatT> zbuffer3d(view3d._width,view3d._height,1,1,0);
- const CImg<floatT> rotated_points3d = pose3d.get_crop(0,0,2,2)*points3d;
- if (sel_primitives3d)
- view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width,
- pose3d(3,1) + 0.5f*view3d._height,
- pose3d(3,2),
- rotated_points3d,sel_primitives3d,sel_colors3d,sel_opacities3d,
- 2,true,500,0,0,0,0,0,1,zbuffer3d);
- view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width,
- pose3d(3,1) + 0.5f*view3d._height,
- pose3d(3,2),
- rotated_points3d,primitives3d,colors3d,opacities3d,
- 2,true,500,0,0,0,0,0,1,zbuffer3d);
- visu0.draw_image(x3d,y3d,view3d);
- }
- visu = visu0;
- if (X<0 || Y<0 || Z<0) { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }}
- else {
- if (is_axes) { if (visible_cursor) { disp.hide_mouse(); visible_cursor = false; }}
- else { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }}
- const int d = (depth()>1)?depth():0;
- int _vX = (int)X, _vY = (int)Y, _vZ = (int)Z;
- if (phase>=2) { _vX = X1; _vY = Y1; _vZ = Z1; }
- int
- w = disp.width(), W = width() + d,
- h = disp.height(), H = height() + d,
- _xp = (int)(_vX*(float)w/W), xp = _xp + ((int)(_xp*(float)W/w)!=_vX),
- _yp = (int)(_vY*(float)h/H), yp = _yp + ((int)(_yp*(float)H/h)!=_vY),
- _xn = (int)((_vX + 1.f)*w/W - 1), xn = _xn + ((int)((_xn + 1.f)*W/w)!=_vX + 1),
- _yn = (int)((_vY + 1.f)*h/H - 1), yn = _yn + ((int)((_yn + 1.f)*H/h)!=_vY + 1),
- _zxp = (int)((_vZ + width())*(float)w/W), zxp = _zxp + ((int)(_zxp*(float)W/w)!=_vZ + width()),
- _zyp = (int)((_vZ + height())*(float)h/H), zyp = _zyp + ((int)(_zyp*(float)H/h)!=_vZ + height()),
- _zxn = (int)((_vZ + width() + 1.f)*w/W - 1),
- zxn = _zxn + ((int)((_zxn + 1.f)*W/w)!=_vZ + width() + 1),
- _zyn = (int)((_vZ + height() + 1.f)*h/H - 1),
- zyn = _zyn + ((int)((_zyn + 1.f)*H/h)!=_vZ + height() + 1),
- _xM = (int)(width()*(float)w/W - 1), xM = _xM + ((int)((_xM + 1.f)*W/w)!=width()),
- _yM = (int)(height()*(float)h/H - 1), yM = _yM + ((int)((_yM + 1.f)*H/h)!=height()),
- xc = (xp + xn)/2,
- yc = (yp + yn)/2,
- zxc = (zxp + zxn)/2,
- zyc = (zyp + zyn)/2,
- xf = (int)(X*w/W),
- yf = (int)(Y*h/H),
- zxf = (int)((Z + width())*w/W),
- zyf = (int)((Z + height())*h/H);
- if (is_axes) { // Draw axes
- visu.draw_line(0,yf,visu.width() - 1,yf,foreground_color,0.7f,0xFF00FF00).
- draw_line(0,yf,visu.width() - 1,yf,background_color,0.7f,0x00FF00FF).
- draw_line(xf,0,xf,visu.height() - 1,foreground_color,0.7f,0xFF00FF00).
- draw_line(xf,0,xf,visu.height() - 1,background_color,0.7f,0x00FF00FF);
- if (_depth>1)
- visu.draw_line(zxf,0,zxf,yM,foreground_color,0.7f,0xFF00FF00).
- draw_line(zxf,0,zxf,yM,background_color,0.7f,0x00FF00FF).
- draw_line(0,zyf,xM,zyf,foreground_color,0.7f,0xFF00FF00).
- draw_line(0,zyf,xM,zyf,background_color,0.7f,0x00FF00FF);
- }
- // Draw box cursor.
- if (xn - xp>=4 && yn - yp>=4)
- visu.draw_rectangle(xp,yp,xn,yn,foreground_color,0.2f).
- draw_rectangle(xp,yp,xn,yn,foreground_color,1,0xAAAAAAAA).
- draw_rectangle(xp,yp,xn,yn,background_color,1,0x55555555);
- if (_depth>1) {
- if (yn - yp>=4 && zxn - zxp>=4)
- visu.draw_rectangle(zxp,yp,zxn,yn,background_color,0.2f).
- draw_rectangle(zxp,yp,zxn,yn,foreground_color,1,0xAAAAAAAA).
- draw_rectangle(zxp,yp,zxn,yn,background_color,1,0x55555555);
- if (xn - xp>=4 && zyn - zyp>=4)
- visu.draw_rectangle(xp,zyp,xn,zyn,background_color,0.2f).
- draw_rectangle(xp,zyp,xn,zyn,foreground_color,1,0xAAAAAAAA).
- draw_rectangle(xp,zyp,xn,zyn,background_color,1,0x55555555);
- }
- // Draw selection.
- if (phase && (phase!=1 || area_started==area)) {
- const int
- _xp0 = (int)(X0*(float)w/W), xp0 = _xp0 + ((int)(_xp0*(float)W/w)!=X0),
- _yp0 = (int)(Y0*(float)h/H), yp0 = _yp0 + ((int)(_yp0*(float)H/h)!=Y0),
- _xn0 = (int)((X0 + 1.f)*w/W - 1), xn0 = _xn0 + ((int)((_xn0 + 1.f)*W/w)!=X0 + 1),
- _yn0 = (int)((Y0 + 1.f)*h/H - 1), yn0 = _yn0 + ((int)((_yn0 + 1.f)*H/h)!=Y0 + 1),
- _zxp0 = (int)((Z0 + width())*(float)w/W), zxp0 = _zxp0 + ((int)(_zxp0*(float)W/w)!=Z0 + width()),
- _zyp0 = (int)((Z0 + height())*(float)h/H), zyp0 = _zyp0 + ((int)(_zyp0*(float)H/h)!=Z0 + height()),
- _zxn0 = (int)((Z0 + width() + 1.f)*w/W - 1),
- zxn0 = _zxn0 + ((int)((_zxn0 + 1.f)*W/w)!=Z0 + width() + 1),
- _zyn0 = (int)((Z0 + height() + 1.f)*h/H - 1),
- zyn0 = _zyn0 + ((int)((_zyn0 + 1.f)*H/h)!=Z0 + height() + 1),
- xc0 = (xp0 + xn0)/2,
- yc0 = (yp0 + yn0)/2,
- zxc0 = (zxp0 + zxn0)/2,
- zyc0 = (zyp0 + zyn0)/2;
- switch (feature_type) {
- case 1 : { // Vector
- visu.draw_arrow(xc0,yc0,xc,yc,background_color,0.9f,30,5,0x33333333).
- draw_arrow(xc0,yc0,xc,yc,foreground_color,0.9f,30,5,0xCCCCCCCC);
- if (d) {
- visu.draw_arrow(zxc0,yc0,zxc,yc,background_color,0.9f,30,5,0x33333333).
- draw_arrow(zxc0,yc0,zxc,yc,foreground_color,0.9f,30,5,0xCCCCCCCC).
- draw_arrow(xc0,zyc0,xc,zyc,background_color,0.9f,30,5,0x33333333).
- draw_arrow(xc0,zyc0,xc,zyc,foreground_color,0.9f,30,5,0xCCCCCCCC);
- }
- } break;
- case 2 : { // Box
- visu.draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,background_color,0.2f).
- draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,background_color,0.9f,0x55555555).
- draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,foreground_color,0.9f,0xAAAAAAAA);
- if (xc0!=xc && yc0!=yc)
- visu.draw_line(xc0,yc0,xc,yc,background_color,0.9f,0x33333333).
- draw_line(xc0,yc0,xc,yc,foreground_color,0.9f,0xCCCCCCCC);
- if (d) {
- visu.draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,background_color,0.2f).
- draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,
- background_color,0.9f,0x55555555).
- draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,
- foreground_color,0.9f,0xAAAAAAAA);
- if (zxc0!=zxc && yc0!=yc)
- visu.draw_line(zxc0,yc0,zxc,yc,background_color,0.9f,0x33333333).
- draw_line(zxc0,yc0,zxc,yc,foreground_color,0.9f,0xCCCCCCCC);
- visu.draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
- background_color,0.2f).
- draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
- background_color,0.9f,0x55555555).
- draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
- foreground_color,0.9f,0xAAAAAAAA);
- if (xp0!=xn && zyp0!=zyn)
- visu.draw_line(xp0,zyp0,xn,zyn,background_color,0.9f,0x33333333).
- draw_line(xp0,zyp0,xn,zyn,foreground_color,0.9f,0xCCCCCCCC);
- }
- } break;
- case 3 : { // Ellipse
- visu.draw_ellipse(xc0,yc0,
- (float)cimg::abs(xc - xc0),
- (float)cimg::abs(yc - yc0),0,background_color,0.2f).
- draw_ellipse(xc0,yc0,
- (float)cimg::abs(xc - xc0),
- (float)cimg::abs(yc - yc0),0,foreground_color,0.9f,~0U).
- draw_point(xc0,yc0,foreground_color,0.9f);
- if (d) {
- visu.draw_ellipse(zxc0,yc0,(float)cimg::abs(zxc - zxc0),(float)cimg::abs(yc - yc0),0,
- background_color,0.2f).
- draw_ellipse(zxc0,yc0,(float)cimg::abs(zxc - zxc0),(float)cimg::abs(yc - yc0),0,
- foreground_color,0.9f,~0U).
- draw_point(zxc0,yc0,foreground_color,0.9f).
- draw_ellipse(xc0,zyc0,(float)cimg::abs(xc - xc0),(float)cimg::abs(zyc - zyc0),0,
- background_color,0.2f).
- draw_ellipse(xc0,zyc0,(float)cimg::abs(xc - xc0),(float)cimg::abs(zyc - zyc0),0,
- foreground_color,0.9f,~0U).
- draw_point(xc0,zyc0,foreground_color,0.9f);
- }
- } break;
- }
- }
- // Draw text info.
- if (my>=0 && my<13) text_down = true; else if (my>=visu.height() - 13) text_down = false;
- if (!feature_type || !phase) {
- if (X>=0 && Y>=0 && Z>=0 && X<width() && Y<height() && Z<depth()) {
- if (_depth>1 || force_display_z_coord)
- cimg_snprintf(text,text._width," Point (%d,%d,%d) = [ ",origX + (int)X,origY + (int)Y,origZ + (int)Z);
- else cimg_snprintf(text,text._width," Point (%d,%d) = [ ",origX + (int)X,origY + (int)Y);
- CImg<T> values = get_vector_at((int)X,(int)Y,(int)Z);
- const bool is_large_spectrum = values._height>8;
- if (is_large_spectrum)
- values.draw_image(0,4,values.get_rows(values._height - 4,values._height - 1)).resize(1,8,1,1,0);
- char *ctext = text._data + std::strlen(text), *const ltext = text._data + 512;
- for (unsigned int c = 0; c<values._height && ctext<ltext; ++c) {
- cimg_snprintf(ctext,24,cimg::type<T>::format_s(),
- cimg::type<T>::format(values[c]));
- ctext += std::strlen(ctext);
- if (c==3 && is_large_spectrum) {
- cimg_snprintf(ctext,24," ...");
- ctext += std::strlen(ctext);
- }
- *(ctext++) = ' '; *ctext = 0;
- }
- std::strcpy(text._data + std::strlen(text),"] ");
- }
- } else switch (feature_type) {
- case 1 : {
- const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1),
- length = cimg::round(cimg::hypot(dX,dY,dZ),0.1);
- if (_depth>1 || force_display_z_coord)
- cimg_snprintf(text,text._width," Vect (%d,%d,%d)-(%d,%d,%d), Length = %g ",
- origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1,length);
- else if (_width!=1 && _height!=1)
- cimg_snprintf(text,text._width," Vect (%d,%d)-(%d,%d), Length = %g, Angle = %g\260 ",
- origX + X0,origY + Y0,origX + X1,origY + Y1,length,
- cimg::round(cimg::mod(180*std::atan2(-dY,-dX)/cimg::PI,360.),0.1));
- else
- cimg_snprintf(text,text._width," Vect (%d,%d)-(%d,%d), Length = %g ",
- origX + X0,origY + Y0,origX + X1,origY + Y1,length);
- } break;
- case 2 : {
- const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1),
- length = cimg::round(cimg::hypot(dX,dY,dZ),0.1);
- if (_depth>1 || force_display_z_coord)
- cimg_snprintf(text,text._width,
- " Box ( %d,%d,%d ) - ( %d,%d,%d )\n Size = ( %d,%d,%d ), Length = %g ",
- origX + (X0<X1?X0:X1),origY + (Y0<Y1?Y0:Y1),origZ + (Z0<Z1?Z0:Z1),
- origX + (X0<X1?X1:X0),origY + (Y0<Y1?Y1:Y0),origZ + (Z0<Z1?Z1:Z0),
- 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),1 + cimg::abs(Z0 - Z1),length);
- else if (_width!=1 && _height!=1)
- cimg_snprintf(text,text._width,
- " Box ( %d,%d ) - ( %d,%d )\n Size = ( %d,%d ), Length = %g \n Angle = %g\260 ",
- origX + (X0<X1?X0:X1),origY + (Y0<Y1?Y0:Y1),
- origX + (X0<X1?X1:X0),origY + (Y0<Y1?Y1:Y0),
- 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),length,
- cimg::round(cimg::mod(180*std::atan2(-dY,-dX)/cimg::PI,360.),0.1));
- else
- cimg_snprintf(text,text._width,
- " Box ( %d,%d ) - ( %d,%d )\n Size = (%d,%d), Length = %g ",
- origX + (X0<X1?X0:X1),origY + (Y0<Y1?Y0:Y1),
- origX + (X0<X1?X1:X0),origY + (Y0<Y1?Y1:Y0),
- 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),length);
- } break;
- default :
- if (_depth>1 || force_display_z_coord)
- cimg_snprintf(text,text._width," Ellipse ( %d,%d,%d ) - ( %d,%d,%d ), Radii = ( %d,%d,%d ) ",
- origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1,
- 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),1 + cimg::abs(Z0 - Z1));
- else cimg_snprintf(text,text._width," Ellipse ( %d,%d ) - ( %d,%d ), Radii = ( %d,%d ) ",
- origX + X0,origY + Y0,origX + X1,origY + Y1,
- 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1));
- }
- if (phase || (mx>=0 && my>=0)) visu.__draw_text("%s",font_size,(int)text_down,text._data);
- }
- disp.display(visu);
- }
- if (!shape_selected) disp.wait();
- if (disp.is_resized()) { disp.resize(false)._is_resized = false; old_is_resized = true; visu0.assign(); }
- omx = mx; omy = my;
- if (!exit_on_anykey && key && key!=cimg::keyESC &&
- (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
- key = 0;
- }
- }
- // Return result.
- CImg<intT> res(1,feature_type==0?3:6,1,1,-1);
- if (XYZ) { XYZ[0] = (unsigned int)X0; XYZ[1] = (unsigned int)Y0; XYZ[2] = (unsigned int)Z0; }
- if (shape_selected) {
- if (feature_type==2) {
- if (is_deep_selection) switch (area_started) {
- case 1 : Z0 = 0; Z1 = _depth - 1; break;
- case 2 : Y0 = 0; Y1 = _height - 1; break;
- case 3 : X0 = 0; X1 = _width - 1; break;
- }
- if (X0>X1) cimg::swap(X0,X1);
- if (Y0>Y1) cimg::swap(Y0,Y1);
- if (Z0>Z1) cimg::swap(Z0,Z1);
- }
- if (X1<0 || Y1<0 || Z1<0) X0 = Y0 = Z0 = X1 = Y1 = Z1 = -1;
- switch (feature_type) {
- case 1 : case 2 : res[0] = X0; res[1] = Y0; res[2] = Z0; res[3] = X1; res[4] = Y1; res[5] = Z1; break;
- case 3 :
- res[3] = cimg::abs(X1 - X0); res[4] = cimg::abs(Y1 - Y0); res[5] = cimg::abs(Z1 - Z0);
- res[0] = X0; res[1] = Y0; res[2] = Z0;
- break;
- default : res[0] = X0; res[1] = Y0; res[2] = Z0;
- }
- }
- if (!exit_on_anykey || !(disp.button()&4)) disp.set_button();
- if (!visible_cursor) disp.show_mouse();
- disp._normalization = old_normalization;
- disp._is_resized = old_is_resized;
- if (key!=~0U) disp.set_key(key);
- return res;
- }
- // Return a visualizable 'uchar8' image for display routines.
- CImg<ucharT> _get_select(const CImgDisplay& disp, const int normalization,
- const int x, const int y, const int z) const {
- if (is_empty()) return CImg<ucharT>(1,1,1,1,0);
- const CImg<T> crop = get_shared_channels(0,std::min(2,spectrum() - 1));
- CImg<Tuchar> img2d;
- if (_depth>1) {
- const int mdisp = std::min(disp.screen_width(),disp.screen_height());
- if (depth()>mdisp) {
- crop.get_resize(-100,-100,mdisp,-100,0).move_to(img2d);
- img2d.projections2d(x,y,z*img2d._depth/_depth);
- } else crop.get_projections2d(x,y,z).move_to(img2d);
- } else CImg<Tuchar>(crop,false).move_to(img2d);
- // Check for inf and NaN values.
- if (cimg::type<T>::is_float() && normalization) {
- bool is_inf = false, is_nan = false;
- cimg_for(img2d,ptr,Tuchar)
- if (cimg::type<T>::is_inf(*ptr)) { is_inf = true; break; }
- else if (cimg::type<T>::is_nan(*ptr)) { is_nan = true; break; }
- if (is_inf || is_nan) {
- Tint m0 = (Tint)cimg::type<T>::max(), M0 = (Tint)cimg::type<T>::min();
- if (!normalization) { m0 = 0; M0 = 255; }
- else if (normalization==2) { m0 = (Tint)disp._min; M0 = (Tint)disp._max; }
- else {
- cimg_for(img2d,ptr,Tuchar)
- if (!cimg::type<T>::is_inf(*ptr) && !cimg::type<T>::is_nan(*ptr)) {
- if (*ptr<(Tuchar)m0) m0 = *ptr;
- if (*ptr>(Tuchar)M0) M0 = *ptr;
- }
- }
- const T
- val_minf = (T)(normalization==1 || normalization==3?m0 - cimg::abs(m0):m0),
- val_pinf = (T)(normalization==1 || normalization==3?M0 + cimg::abs(M0):M0);
- if (is_nan)
- cimg_for(img2d,ptr,Tuchar)
- if (cimg::type<T>::is_nan(*ptr)) *ptr = val_minf; // Replace NaN values
- if (is_inf)
- cimg_for(img2d,ptr,Tuchar)
- if (cimg::type<T>::is_inf(*ptr)) *ptr = (float)*ptr<0?val_minf:val_pinf; // Replace +-inf values
- }
- }
- switch (normalization) {
- case 1 : img2d.normalize((ucharT)0,(ucharT)255); break;
- case 2 : {
- const float m = disp._min, M = disp._max;
- (img2d-=m)*=255.f/(M - m>0?M - m:1);
- } break;
- case 3 :
- if (cimg::type<T>::is_float()) img2d.normalize((ucharT)0,(ucharT)255);
- else {
- const float
- m = (float)cimg::type<T>::min(),
- M = (float)cimg::type<T>::max();
- (img2d-=m)*=255.f/(M - m>0?M - m:1);
- } break;
- }
- if (img2d.spectrum()==2) img2d.channels(0,2);
- return img2d;
- }
- //! Select sub-graph in a graph.
- CImg<intT> get_select_graph(CImgDisplay &disp,
- const unsigned int plot_type=1, const unsigned int vertex_type=1,
- const char *const labelx=0, const double xmin=0, const double xmax=0,
- const char *const labely=0, const double ymin=0, const double ymax=0,
- const bool exit_on_anykey=false) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "select_graph(): Empty instance.",
- cimg_instance);
- if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0).
- set_title("CImg<%s>",pixel_type());
- const ulongT siz = (ulongT)_width*_height*_depth;
- const unsigned int old_normalization = disp.normalization();
- disp.show().set_button().set_wheel()._normalization = 0;
- double nymin = ymin, nymax = ymax, nxmin = xmin, nxmax = xmax;
- if (nymin==nymax) { nymin = (Tfloat)min_max(nymax); const double dy = nymax - nymin; nymin-=dy/20; nymax+=dy/20; }
- if (nymin==nymax) { --nymin; ++nymax; }
- if (nxmin==nxmax && nxmin==0) { nxmin = 0; nxmax = siz - 1.; }
- static const unsigned char black[] = { 0, 0, 0 }, white[] = { 255, 255, 255 }, gray[] = { 220, 220, 220 };
- static const unsigned char gray2[] = { 110, 110, 110 }, ngray[] = { 35, 35, 35 };
- CImg<ucharT> colormap(3,_spectrum);
- if (_spectrum==1) { colormap[0] = colormap[1] = 120; colormap[2] = 200; }
- else {
- colormap(0,0) = 220; colormap(1,0) = 10; colormap(2,0) = 10;
- if (_spectrum>1) { colormap(0,1) = 10; colormap(1,1) = 220; colormap(2,1) = 10; }
- if (_spectrum>2) { colormap(0,2) = 10; colormap(1,2) = 10; colormap(2,2) = 220; }
- if (_spectrum>3) { colormap(0,3) = 220; colormap(1,3) = 220; colormap(2,3) = 10; }
- if (_spectrum>4) { colormap(0,4) = 220; colormap(1,4) = 10; colormap(2,4) = 220; }
- if (_spectrum>5) { colormap(0,5) = 10; colormap(1,5) = 220; colormap(2,5) = 220; }
- if (_spectrum>6) {
- cimg_uint64 rng = 10;
- cimg_for_inY(colormap,6,colormap.height()-1,k) {
- colormap(0,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng));
- colormap(1,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng));
- colormap(2,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng));
- }
- }
- }
- CImg<ucharT> visu0, visu, graph, text, axes;
- int x0 = -1, x1 = -1, y0 = -1, y1 = -1, omouse_x = -2, omouse_y = -2;
- const unsigned int one = plot_type==3?0U:1U;
- unsigned int okey = 0, obutton = 0, font_size = 32;
- CImg<charT> message(1024);
- CImg_3x3(I,unsigned char);
- for (bool selected = false; !selected && !disp.is_closed() && !okey && !disp.wheel(); ) {
- const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
- const unsigned int key = disp.key(), button = disp.button();
- // Generate graph representation.
- if (!visu0) {
- visu0.assign(disp.width(),disp.height(),1,3,220);
- const int gdimx = disp.width() - 32, gdimy = disp.height() - 32;
- if (gdimx>0 && gdimy>0) {
- graph.assign(gdimx,gdimy,1,3,255);
- if (siz<32) {
- if (siz>1) graph.draw_grid(gdimx/(float)(siz - one),gdimy/(float)(siz - one),0,0,
- false,true,black,0.2f,0x33333333,0x33333333);
- } else graph.draw_grid(-10,-10,0,0,false,true,black,0.2f,0x33333333,0x33333333);
- cimg_forC(*this,c)
- graph.draw_graph(get_shared_channel(c),&colormap(0,c),(plot_type!=3 || _spectrum==1)?1:0.6f,
- plot_type,vertex_type,nymax,nymin);
- axes.assign(gdimx,gdimy,1,1,0);
- const float
- dx = (float)cimg::abs(nxmax - nxmin), dy = (float)cimg::abs(nymax - nymin),
- px = (float)std::pow(10.,(int)std::log10(dx?dx:1) - 2.),
- py = (float)std::pow(10.,(int)std::log10(dy?dy:1) - 2.);
- const CImg<Tdouble>
- seqx = dx<=0?CImg<Tdouble>::vector(nxmin):
- CImg<Tdouble>::sequence(1 + gdimx/60,nxmin,one?nxmax:nxmin + (nxmax - nxmin)*(siz + 1)/siz),
- seqy = CImg<Tdouble>::sequence(1 + gdimy/60,nymax,nymin);
- const bool allow_zero = (nxmin*nxmax>0) || (nymin*nymax>0);
- axes.draw_axes(seqx,seqy,white,1,~0U,~0U,13,allow_zero,px,py);
- if (nymin>0) axes.draw_axis(seqx,gdimy - 1,gray,1,~0U,13,allow_zero,px);
- if (nymax<0) axes.draw_axis(seqx,0,gray,1,~0U,13,allow_zero,px);
- if (nxmin>0) axes.draw_axis(0,seqy,gray,1,~0U,13,allow_zero,py);
- if (nxmax<0) axes.draw_axis(gdimx - 1,seqy,gray,1,~0U,13,allow_zero,py);
- cimg_for3x3(axes,x,y,0,0,I,unsigned char)
- if (Icc) {
- if (Icc==255) cimg_forC(graph,c) graph(x,y,c) = 0;
- else cimg_forC(graph,c) graph(x,y,c) = (unsigned char)(2*graph(x,y,c)/3);
- }
- else if (Ipc || Inc || Icp || Icn || Ipp || Inn || Ipn || Inp)
- cimg_forC(graph,c) graph(x,y,c) = (unsigned char)((graph(x,y,c) + 511)/3);
- visu0.draw_image(16,16,graph);
- visu0.draw_line(15,15,16 + gdimx,15,gray2).draw_line(16 + gdimx,15,16 + gdimx,16 + gdimy,gray2).
- draw_line(16 + gdimx,16 + gdimy,15,16 + gdimy,white).draw_line(15,16 + gdimy,15,15,white);
- } else graph.assign();
- text.assign().draw_text(0,0,labelx?labelx:"X-axis",white,ngray,1,13).resize(-100,-100,1,3);
- visu0.draw_image((visu0.width() - text.width())/2,visu0.height() - 14,~text);
- text.assign().draw_text(0,0,labely?labely:"Y-axis",white,ngray,1,13).rotate(-90).resize(-100,-100,1,3);
- visu0.draw_image(1,(visu0.height() - text.height())/2,~text);
- visu.assign();
- }
- // Generate and display current view.
- if (!visu) {
- visu.assign(visu0);
- if (graph && x0>=0 && x1>=0) {
- const int
- nx0 = x0<=x1?x0:x1,
- nx1 = x0<=x1?x1:x0,
- ny0 = y0<=y1?y0:y1,
- ny1 = y0<=y1?y1:y0,
- sx0 = (int)(16 + nx0*(visu.width() - 32)/std::max((ulongT)1,siz - one)),
- sx1 = (int)(15 + (nx1 + 1)*(visu.width() - 32)/std::max((ulongT)1,siz - one)),
- sy0 = 16 + ny0,
- sy1 = 16 + ny1;
- if (y0>=0 && y1>=0)
- visu.draw_rectangle(sx0,sy0,sx1,sy1,gray,0.5f).draw_rectangle(sx0,sy0,sx1,sy1,black,0.5f,0xCCCCCCCCU);
- else visu.draw_rectangle(sx0,0,sx1,visu.height() - 17,gray,0.5f).
- draw_line(sx0,16,sx0,visu.height() - 17,black,0.5f,0xCCCCCCCCU).
- draw_line(sx1,16,sx1,visu.height() - 17,black,0.5f,0xCCCCCCCCU);
- }
- if (mouse_x>=16 && mouse_y>=16 && mouse_x<visu.width() - 16 && mouse_y<visu.height() - 16) {
- if (graph) visu.draw_line(mouse_x,16,mouse_x,visu.height() - 17,black,0.5f,0x55555555U);
- const unsigned int
- x = (unsigned int)cimg::round((mouse_x - 16.f)*(siz - one)/(disp.width() - 32),1,one?0:-1);
- const double cx = nxmin + x*(nxmax - nxmin)/std::max((ulongT)1,siz - 1);
- if (_spectrum>=7)
- cimg_snprintf(message,message._width,"Value[%u:%g] = ( %g %g %g ... %g %g %g )",x,cx,
- (double)(*this)(x,0,0,0),(double)(*this)(x,0,0,1),(double)(*this)(x,0,0,2),
- (double)(*this)(x,0,0,_spectrum - 4),(double)(*this)(x,0,0,_spectrum - 3),
- (double)(*this)(x,0,0,_spectrum - 1));
- else {
- cimg_snprintf(message,message._width,"Value[%u:%g] = ( ",x,cx);
- unsigned int len = (unsigned int)std::strlen(message);
- cimg_forC(*this,c)
- cimg_snprintf(message._data + len,message._width - len,"%g ",(double)(*this)(x,0,0,c));
- len = (unsigned int)std::strlen(message);
- cimg_snprintf(message._data + len,message._width - len,")");
- }
- if (x0>=0 && x1>=0) {
- const unsigned int
- nx0 = (unsigned int)(x0<=x1?x0:x1),
- nx1 = (unsigned int)(x0<=x1?x1:x0),
- ny0 = (unsigned int)(y0<=y1?y0:y1),
- ny1 = (unsigned int)(y0<=y1?y1:y0),
- len = (unsigned int)std::strlen(message);
- const double
- cx0 = nxmin + nx0*(nxmax - nxmin)/std::max((ulongT)1,siz - 1),
- cx1 = nxmin + (nx1 + one)*(nxmax - nxmin)/std::max((ulongT)1,siz - 1),
- cy0 = nymax - ny0*(nymax - nymin)/(visu._height - 32),
- cy1 = nymax - ny1*(nymax - nymin)/(visu._height - 32);
- if (y0>=0 && y1>=0)
- cimg_snprintf(message._data + len,message._width - len," - Range ( %u:%g, %g ) - ( %u:%g, %g )",
- x0,cx0,cy0,x1 + one,cx1,cy1);
- else
- cimg_snprintf(message._data + len,message._width - len," - Range [ %u:%g - %u:%g ]",
- x0,cx0,x1 + one,cx1);
- }
- text.assign().draw_text(0,0,message,white,ngray,1,13).resize(-100,-100,1,3);
- visu.draw_image((visu.width() - text.width())/2,1,~text);
- }
- visu.display(disp);
- }
- // Test keys.
- CImg<charT> filename(32);
- switch (okey = key) {
- #if cimg_OS!=2
- case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT :
- #endif
- case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : okey = 0; break;
- case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- disp.set_fullscreen(false).
- resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
- CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
- _is_resized = true;
- disp.set_key(key,false); okey = 0;
- } break;
- case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- disp.set_fullscreen(false).
- resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
- disp.set_key(key,false); okey = 0;
- } break;
- case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- disp.set_fullscreen(false).
- resize(cimg_fitscreen(CImgDisplay::screen_width()/2,
- CImgDisplay::screen_height()/2,1),false)._is_resized = true;
- disp.set_key(key,false); okey = 0;
- } break;
- case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
- disp.set_key(key,false); okey = 0;
- } break;
- case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- static unsigned int snap_number = 0;
- if (visu || visu0) {
- CImg<ucharT> &screen = visu?visu:visu0;
- std::FILE *file;
- do {
- cimg_snprintf(filename,filename._width,cimg_appname "_%.6u."
- #ifdef cimg_use_png
- "png",
- #else
- "bmp",
- #endif
- snap_number++);
- if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
- } while (file);
- (+screen).__draw_text(" Saving snapshot... ",font_size,0).display(disp);
- screen.save(filename);
- (+screen).__draw_text(" Snapshot '%s' saved. ",font_size,0,filename._data).display(disp);
- }
- disp.set_key(key,false); okey = 0;
- } break;
- case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- static unsigned int snap_number = 0;
- if (visu || visu0) {
- CImg<ucharT> &screen = visu?visu:visu0;
- std::FILE *file;
- do {
- #ifdef cimg_use_zlib
- cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimgz",snap_number++);
- #else
- cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimg",snap_number++);
- #endif
- if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
- } while (file);
- (+screen).__draw_text(" Saving instance... ",font_size,0).display(disp);
- save(filename);
- (+screen).__draw_text(" Instance '%s' saved. ",font_size,0,filename._data).display(disp);
- }
- disp.set_key(key,false); okey = 0;
- } break;
- }
- // Handle mouse motion and mouse buttons.
- if (obutton!=button || omouse_x!=mouse_x || omouse_y!=mouse_y) {
- visu.assign();
- if (disp.mouse_x()>=0 && disp.mouse_y()>=0) {
- const int
- mx = (mouse_x - 16)*(int)(siz - one)/(disp.width() - 32),
- cx = cimg::cut(mx,0,(int)(siz - 1 - one)),
- my = mouse_y - 16,
- cy = cimg::cut(my,0,disp.height() - 32);
- if (button&1) {
- if (!obutton) { x0 = cx; y0 = -1; } else { x1 = cx; y1 = -1; }
- }
- else if (button&2) {
- if (!obutton) { x0 = cx; y0 = cy; } else { x1 = cx; y1 = cy; }
- }
- else if (obutton) { x1 = x1>=0?cx:-1; y1 = y1>=0?cy:-1; selected = true; }
- } else if (!button && obutton) selected = true;
- obutton = button; omouse_x = mouse_x; omouse_y = mouse_y;
- }
- if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
- if (visu && visu0) disp.wait();
- if (!exit_on_anykey && okey && okey!=cimg::keyESC &&
- (okey!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
- disp.set_key(key,false);
- okey = 0;
- }
- }
- disp._normalization = old_normalization;
- if (x1>=0 && x1<x0) cimg::swap(x0,x1);
- if (y1<y0) cimg::swap(y0,y1);
- disp.set_key(okey);
- return CImg<intT>(4,1,1,1,x0,y0,x1>=0?x1 + (int)one:-1,y1);
- }
- //! Load image from a file.
- /**
- \param filename Filename, as a C-string.
- \note The extension of \c filename defines the file format. If no filename
- extension is provided, CImg<T>::get_load() will try to load the file as a .cimg or .cimgz file.
- **/
- CImg<T>& load(const char *const filename) {
- if (!filename)
- throw CImgArgumentException(_cimg_instance
- "load(): Specified filename is (null).",
- cimg_instance);
- if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) {
- CImg<charT> filename_local(256);
- load(cimg::load_network(filename,filename_local));
- std::remove(filename_local);
- return *this;
- }
- const char *const ext = cimg::split_filename(filename);
- const unsigned int omode = cimg::exception_mode();
- cimg::exception_mode(0);
- bool is_loaded = true;
- try {
- #ifdef cimg_load_plugin
- cimg_load_plugin(filename);
- #endif
- #ifdef cimg_load_plugin1
- cimg_load_plugin1(filename);
- #endif
- #ifdef cimg_load_plugin2
- cimg_load_plugin2(filename);
- #endif
- #ifdef cimg_load_plugin3
- cimg_load_plugin3(filename);
- #endif
- #ifdef cimg_load_plugin4
- cimg_load_plugin4(filename);
- #endif
- #ifdef cimg_load_plugin5
- cimg_load_plugin5(filename);
- #endif
- #ifdef cimg_load_plugin6
- cimg_load_plugin6(filename);
- #endif
- #ifdef cimg_load_plugin7
- cimg_load_plugin7(filename);
- #endif
- #ifdef cimg_load_plugin8
- cimg_load_plugin8(filename);
- #endif
- // Text formats
- if (!cimg::strcasecmp(ext,"asc")) load_ascii(filename);
- else if (!cimg::strcasecmp(ext,"csv") ||
- !cimg::strcasecmp(ext,"dlm") ||
- !cimg::strcasecmp(ext,"txt")) load_dlm(filename);
- else if (!cimg::strcasecmp(ext,"pdf")) load_pdf_external(filename);
- // 2D binary formats
- else if (!cimg::strcasecmp(ext,"bmp")) load_bmp(filename);
- else if (!cimg::strcasecmp(ext,"jpg") ||
- !cimg::strcasecmp(ext,"jpeg") ||
- !cimg::strcasecmp(ext,"jpe") ||
- !cimg::strcasecmp(ext,"jfif") ||
- !cimg::strcasecmp(ext,"jif")) load_jpeg(filename);
- else if (!cimg::strcasecmp(ext,"png")) load_png(filename);
- else if (!cimg::strcasecmp(ext,"ppm") ||
- !cimg::strcasecmp(ext,"pgm") ||
- !cimg::strcasecmp(ext,"pnm") ||
- !cimg::strcasecmp(ext,"pbm") ||
- !cimg::strcasecmp(ext,"pnk")) load_pnm(filename);
- else if (!cimg::strcasecmp(ext,"pfm")) load_pfm(filename);
- else if (!cimg::strcasecmp(ext,"tif") ||
- !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
- else if (!cimg::strcasecmp(ext,"exr")) load_exr(filename);
- else if (!cimg::strcasecmp(ext,"arw") ||
- !cimg::strcasecmp(ext,"cr2") ||
- !cimg::strcasecmp(ext,"crw") ||
- !cimg::strcasecmp(ext,"dcr") ||
- !cimg::strcasecmp(ext,"dng") ||
- !cimg::strcasecmp(ext,"mrw") ||
- !cimg::strcasecmp(ext,"nef") ||
- !cimg::strcasecmp(ext,"orf") ||
- !cimg::strcasecmp(ext,"pix") ||
- !cimg::strcasecmp(ext,"ptx") ||
- !cimg::strcasecmp(ext,"raf") ||
- !cimg::strcasecmp(ext,"srf")) load_dcraw_external(filename);
- else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename);
- else if (!cimg::strcasecmp(ext,"heic") ||
- !cimg::strcasecmp(ext,"avif")) load_heif(filename);
- // 3D binary formats
- else if (!cimg::strcasecmp(ext,"dcm") ||
- !cimg::strcasecmp(ext,"dicom")) load_medcon_external(filename);
- else if (!cimg::strcasecmp(ext,"hdr") ||
- !cimg::strcasecmp(ext,"nii")) load_analyze(filename);
- else if (!cimg::strcasecmp(ext,"par") ||
- !cimg::strcasecmp(ext,"rec")) load_parrec(filename);
- else if (!cimg::strcasecmp(ext,"mnc")) load_minc2(filename);
- else if (!cimg::strcasecmp(ext,"inr")) load_inr(filename);
- else if (!cimg::strcasecmp(ext,"pan")) load_pandore(filename);
- else if (!cimg::strcasecmp(ext,"cimg") ||
- !cimg::strcasecmp(ext,"cimgz") ||
- !*ext) return load_cimg(filename);
- // Archive files
- else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
- // Image sequences
- else if (!cimg::strcasecmp(ext,"avi") ||
- !cimg::strcasecmp(ext,"mov") ||
- !cimg::strcasecmp(ext,"asf") ||
- !cimg::strcasecmp(ext,"divx") ||
- !cimg::strcasecmp(ext,"flv") ||
- !cimg::strcasecmp(ext,"mpg") ||
- !cimg::strcasecmp(ext,"m1v") ||
- !cimg::strcasecmp(ext,"m2v") ||
- !cimg::strcasecmp(ext,"m4v") ||
- !cimg::strcasecmp(ext,"mjp") ||
- !cimg::strcasecmp(ext,"mp4") ||
- !cimg::strcasecmp(ext,"mkv") ||
- !cimg::strcasecmp(ext,"mpe") ||
- !cimg::strcasecmp(ext,"movie") ||
- !cimg::strcasecmp(ext,"ogm") ||
- !cimg::strcasecmp(ext,"ogg") ||
- !cimg::strcasecmp(ext,"ogv") ||
- !cimg::strcasecmp(ext,"qt") ||
- !cimg::strcasecmp(ext,"rm") ||
- !cimg::strcasecmp(ext,"vob") ||
- !cimg::strcasecmp(ext,"webm") ||
- !cimg::strcasecmp(ext,"wmv") ||
- !cimg::strcasecmp(ext,"xvid") ||
- !cimg::strcasecmp(ext,"mpeg")) load_video(filename);
- else is_loaded = false;
- } catch (CImgIOException&) { is_loaded = false; }
- // If nothing loaded, try to guess file format from magic number in file.
- if (!is_loaded) {
- std::FILE *file = cimg::std_fopen(filename,"rb");
- if (!file) {
- cimg::exception_mode(omode);
- throw CImgIOException(_cimg_instance
- "load(): Failed to open file '%s'.",
- cimg_instance,
- filename);
- }
- const char *const f_type = cimg::ftype(file,filename);
- cimg::fclose(file);
- is_loaded = true;
- try {
- if (!cimg::strcasecmp(f_type,"pnm")) load_pnm(filename);
- else if (!cimg::strcasecmp(f_type,"pfm")) load_pfm(filename);
- else if (!cimg::strcasecmp(f_type,"bmp")) load_bmp(filename);
- else if (!cimg::strcasecmp(f_type,"inr")) load_inr(filename);
- else if (!cimg::strcasecmp(f_type,"jpg")) load_jpeg(filename);
- else if (!cimg::strcasecmp(f_type,"pan")) load_pandore(filename);
- else if (!cimg::strcasecmp(f_type,"png")) load_png(filename);
- else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename);
- else if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename);
- else if (!cimg::strcasecmp(f_type,"dcm")) load_medcon_external(filename);
- else is_loaded = false;
- } catch (CImgIOException&) { is_loaded = false; }
- }
- // If nothing loaded, try to load file with other means.
- if (!is_loaded) {
- try {
- load_other(filename);
- } catch (CImgIOException&) {
- cimg::exception_mode(omode);
- throw CImgIOException(_cimg_instance
- "load(): Failed to recognize format of file '%s'.",
- cimg_instance,
- filename);
- }
- }
- cimg::exception_mode(omode);
- return *this;
- }
- //! Load image from a file \newinstance.
- static CImg<T> get_load(const char *const filename) {
- return CImg<T>().load(filename);
- }
- //! Load image from an ascii file.
- /**
- \param filename Filename, as a C -string.
- **/
- CImg<T>& load_ascii(const char *const filename) {
- return _load_ascii(0,filename);
- }
- //! Load image from an ascii file \inplace.
- static CImg<T> get_load_ascii(const char *const filename) {
- return CImg<T>().load_ascii(filename);
- }
- //! Load image from an ascii file \overloading.
- CImg<T>& load_ascii(std::FILE *const file) {
- return _load_ascii(file,0);
- }
- //! Loadimage from an ascii file \newinstance.
- static CImg<T> get_load_ascii(std::FILE *const file) {
- return CImg<T>().load_ascii(file);
- }
- CImg<T>& _load_ascii(std::FILE *const file, const char *const filename) {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "load_ascii(): Specified filename is (null).",
- cimg_instance);
- std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
- CImg<charT> line(256); *line = 0;
- int err = std::fscanf(nfile,"%255[^\n]",line._data);
- unsigned int dx = 0, dy = 1, dz = 1, dc = 1;
- cimg_sscanf(line,"%u%*c%u%*c%u%*c%u",&dx,&dy,&dz,&dc);
- err = std::fscanf(nfile,"%*[^0-9.eEinfa+-]");
- if (!dx || !dy || !dz || !dc) {
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimg_instance
- "load_ascii(): Invalid ascii header in file '%s', image dimensions are set "
- "to (%u,%u,%u,%u).",
- cimg_instance,
- filename?filename:"(FILE*)",dx,dy,dz,dc);
- }
- assign(dx,dy,dz,dc);
- const ulongT siz = size();
- ulongT off = 0;
- double val;
- T *ptr = _data;
- for (err = 1, off = 0; off<siz && err==1; ++off) {
- err = std::fscanf(nfile,"%lf%*[^0-9.eEinfa+-]",&val);
- *(ptr++) = (T)val;
- }
- if (err!=1)
- cimg::warn(_cimg_instance
- "load_ascii(): Only %lu/%lu values read from file '%s'.",
- cimg_instance,
- off - 1,siz,filename?filename:"(FILE*)");
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Load image from a DLM file.
- /**
- \param filename Filename, as a C-string.
- **/
- CImg<T>& load_dlm(const char *const filename) {
- return _load_dlm(0,filename);
- }
- //! Load image from a DLM file \newinstance.
- static CImg<T> get_load_dlm(const char *const filename) {
- return CImg<T>().load_dlm(filename);
- }
- //! Load image from a DLM file \overloading.
- CImg<T>& load_dlm(std::FILE *const file) {
- return _load_dlm(file,0);
- }
- //! Load image from a DLM file \newinstance.
- static CImg<T> get_load_dlm(std::FILE *const file) {
- return CImg<T>().load_dlm(file);
- }
- CImg<T>& _load_dlm(std::FILE *const file, const char *const filename) {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "load_dlm(): Specified filename is (null).",
- cimg_instance);
- std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
- CImg<charT> delimiter(256), tmp(256); *delimiter = *tmp = 0;
- unsigned int cdx = 0, dx = 0, dy = 0;
- int err = 0;
- double val;
- assign(256,256,1,1,(T)0);
- while ((err = std::fscanf(nfile,"%lf%255[^0-9eEinfa.+-]",&val,delimiter._data))>0) {
- if (err>0) (*this)(cdx++,dy) = (T)val;
- if (cdx>=_width) resize(3*_width/2,_height,1,1,0);
- char c = 0;
- if (!cimg_sscanf(delimiter,"%255[^\n]%c",tmp._data,&c) || c=='\n') {
- dx = std::max(cdx,dx);
- if (++dy>=_height) resize(_width,3*_height/2,1,1,0);
- cdx = 0;
- }
- }
- if (cdx && err==1) { dx = cdx; ++dy; }
- if (!dx || !dy) {
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimg_instance
- "load_dlm(): Invalid DLM file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- }
- resize(dx,dy,1,1,0);
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Load image from a BMP file.
- /**
- \param filename Filename, as a C-string.
- **/
- CImg<T>& load_bmp(const char *const filename) {
- return _load_bmp(0,filename);
- }
- //! Load image from a BMP file \newinstance.
- static CImg<T> get_load_bmp(const char *const filename) {
- return CImg<T>().load_bmp(filename);
- }
- //! Load image from a BMP file \overloading.
- CImg<T>& load_bmp(std::FILE *const file) {
- return _load_bmp(file,0);
- }
- //! Load image from a BMP file \newinstance.
- static CImg<T> get_load_bmp(std::FILE *const file) {
- return CImg<T>().load_bmp(file);
- }
- CImg<T>& _load_bmp(std::FILE *const file, const char *const filename) {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "load_bmp(): Specified filename is (null).",
- cimg_instance);
- const ulongT fsiz = (ulongT)(file?cimg::fsize(file):cimg::fsize(filename));
- std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
- CImg<ucharT> header(54);
- cimg::fread(header._data,54,nfile);
- if (*header!='B' || header[1]!='M') {
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimg_instance
- "load_bmp(): Invalid BMP file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- }
- // Read header and pixel buffer
- int
- file_size = header[0x02] + (header[0x03]<<8) + (header[0x04]<<16) + (header[0x05]<<24),
- offset = header[0x0A] + (header[0x0B]<<8) + (header[0x0C]<<16) + (header[0x0D]<<24),
- header_size = header[0x0E] + (header[0x0F]<<8) + (header[0x10]<<16) + (header[0x11]<<24),
- dx = header[0x12] + (header[0x13]<<8) + (header[0x14]<<16) + (header[0x15]<<24),
- dy = header[0x16] + (header[0x17]<<8) + (header[0x18]<<16) + (header[0x19]<<24),
- compression = header[0x1E] + (header[0x1F]<<8) + (header[0x20]<<16) + (header[0x21]<<24),
- nb_colors = header[0x2E] + (header[0x2F]<<8) + (header[0x30]<<16) + (header[0x31]<<24),
- bpp = header[0x1C] + (header[0x1D]<<8);
- if ((ulongT)file_size!=fsiz)
- throw CImgIOException(_cimg_instance
- "load_bmp(): Invalid file_size %d specified in filename '%s' (expected %lu).",
- cimg_instance,
- file_size,filename?filename:"(FILE*)",fsiz);
- if (header_size<0 || header_size>=file_size)
- throw CImgIOException(_cimg_instance
- "load_bmp(): Invalid header size %d specified in filename '%s'.",
- cimg_instance,
- header_size,filename?filename:"(FILE*)");
- if (offset<0 || offset>=file_size)
- throw CImgIOException(_cimg_instance
- "load_bmp(): Invalid offset %d specified in filename '%s'.",
- cimg_instance,
- offset,filename?filename:"(FILE*)");
- if (header_size>40) cimg::fseek(nfile,header_size - 40,SEEK_CUR);
- const int
- dx_bytes = (bpp==1)?(dx/8 + (dx%8?1:0)):((bpp==4)?(dx/2 + (dx%2)):(int)((longT)dx*bpp/8)),
- align_bytes = (4 - dx_bytes%4)%4;
- const ulongT
- cimg_iobuffer = (ulongT)24*1024*1024,
- buf_size = (ulongT)cimg::abs(dy)*(dx_bytes + align_bytes);
- if (buf_size>=fsiz)
- throw CImgIOException(_cimg_instance
- "load_bmp(): File size %lu for filename '%s' does not match "
- "encoded image dimensions (%d,%d).",
- cimg_instance,
- (long)fsiz,filename?filename:"(FILE*)",dx,dy);
- CImg<intT> colormap;
- if (bpp<16) { if (!nb_colors) nb_colors = 1<<bpp; } else nb_colors = 0;
- if (nb_colors) { colormap.assign(nb_colors); cimg::fread(colormap._data,nb_colors,nfile); }
- const int xoffset = offset - 14 - header_size - 4*nb_colors;
- if (xoffset<0 || xoffset>=file_size)
- throw CImgIOException(_cimg_instance
- "load_bmp(): Malformed header in filename '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- cimg::fseek(nfile,xoffset,SEEK_CUR);
- CImg<ucharT> buffer;
- if (buf_size<cimg_iobuffer) {
- buffer.assign(buf_size,1,1,1,0);
- cimg::fread(buffer._data,buf_size,nfile);
- } else buffer.assign(dx_bytes + align_bytes);
- unsigned char *ptrs = buffer;
- // Decompress buffer (if necessary)
- if (compression==1 || compression==2) {
- if (file)
- throw CImgIOException(_cimg_instance
- "load_bmp(): Unable to load compressed data from '(*FILE)' inputs.",
- cimg_instance);
- else {
- if (!file) cimg::fclose(nfile);
- return load_other(filename);
- }
- }
- // Read pixel data
- assign(dx,cimg::abs(dy),1,3,0);
- switch (bpp) {
- case 1 : { // Monochrome
- if (colormap._width>=2) for (int y = height() - 1; y>=0; --y) {
- if (buf_size>=cimg_iobuffer) {
- if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
- cimg::fseek(nfile,align_bytes,SEEK_CUR);
- }
- unsigned char mask = 0x80, val = 0;
- cimg_forX(*this,x) {
- if (mask==0x80) val = *(ptrs++);
- const unsigned char *col = (unsigned char*)(colormap._data + (val&mask?1:0));
- (*this)(x,y,2) = (T)*(col++);
- (*this)(x,y,1) = (T)*(col++);
- (*this)(x,y,0) = (T)*(col++);
- mask = cimg::ror(mask);
- }
- ptrs+=align_bytes;
- }
- } break;
- case 4 : { // 16 colors
- if (colormap._width>=16) for (int y = height() - 1; y>=0; --y) {
- if (buf_size>=cimg_iobuffer) {
- if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
- cimg::fseek(nfile,align_bytes,SEEK_CUR);
- }
- unsigned char mask = 0xF0, val = 0;
- cimg_forX(*this,x) {
- if (mask==0xF0) val = *(ptrs++);
- const unsigned char color = (unsigned char)((mask<16)?(val&mask):((val&mask)>>4));
- const unsigned char *col = (unsigned char*)(colormap._data + color);
- (*this)(x,y,2) = (T)*(col++);
- (*this)(x,y,1) = (T)*(col++);
- (*this)(x,y,0) = (T)*(col++);
- mask = cimg::ror(mask,4);
- }
- ptrs+=align_bytes;
- }
- } break;
- case 8 : { // 256 colors
- if (colormap._width>=256) for (int y = height() - 1; y>=0; --y) {
- if (buf_size>=cimg_iobuffer) {
- if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
- cimg::fseek(nfile,align_bytes,SEEK_CUR);
- }
- cimg_forX(*this,x) {
- const unsigned char *col = (unsigned char*)(colormap._data + *(ptrs++));
- (*this)(x,y,2) = (T)*(col++);
- (*this)(x,y,1) = (T)*(col++);
- (*this)(x,y,0) = (T)*(col++);
- }
- ptrs+=align_bytes;
- }
- } break;
- case 16 : { // 16 bits colors (RGB565)
- for (int y = height() - 1; y>=0; --y) {
- if (buf_size>=cimg_iobuffer) {
- if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
- cimg::fseek(nfile,align_bytes,SEEK_CUR);
- }
- cimg_forX(*this,x) {
- const unsigned char c1 = *(ptrs++), c2 = *(ptrs++);
- const unsigned short col = (unsigned short)c2<<8 | c1;
- (*this)(x,y,2) = (T)((col&0x1F)<<3);
- (*this)(x,y,1) = (T)(((col>>5)&0x3F)<<3);
- (*this)(x,y,0) = (T)(((col>>11)&0x1F)<<3);
- }
- ptrs+=align_bytes;
- }
- } break;
- case 24 : { // 24 bits colors
- for (int y = height() - 1; y>=0; --y) {
- if (buf_size>=cimg_iobuffer) {
- if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
- cimg::fseek(nfile,align_bytes,SEEK_CUR);
- }
- cimg_forX(*this,x) {
- (*this)(x,y,2) = (T)*(ptrs++);
- (*this)(x,y,1) = (T)*(ptrs++);
- (*this)(x,y,0) = (T)*(ptrs++);
- }
- ptrs+=align_bytes;
- }
- } break;
- case 32 : { // 32 bits colors
- for (int y = height() - 1; y>=0; --y) {
- if (buf_size>=cimg_iobuffer) {
- if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
- cimg::fseek(nfile,align_bytes,SEEK_CUR);
- }
- cimg_forX(*this,x) {
- (*this)(x,y,2) = (T)*(ptrs++);
- (*this)(x,y,1) = (T)*(ptrs++);
- (*this)(x,y,0) = (T)*(ptrs++);
- ++ptrs;
- }
- ptrs+=align_bytes;
- }
- } break;
- }
- if (dy<0) mirror('y');
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Load image from a JPEG file.
- /**
- \param filename Filename, as a C-string.
- **/
- CImg<T>& load_jpeg(const char *const filename) {
- return _load_jpeg(0,filename);
- }
- //! Load image from a JPEG file \newinstance.
- static CImg<T> get_load_jpeg(const char *const filename) {
- return CImg<T>().load_jpeg(filename);
- }
- //! Load image from a JPEG file \overloading.
- CImg<T>& load_jpeg(std::FILE *const file) {
- return _load_jpeg(file,0);
- }
- //! Load image from a JPEG file \newinstance.
- static CImg<T> get_load_jpeg(std::FILE *const file) {
- return CImg<T>().load_jpeg(file);
- }
- // Custom error handler for libjpeg.
- #ifdef cimg_use_jpeg
- struct _cimg_error_mgr {
- struct jpeg_error_mgr original;
- jmp_buf setjmp_buffer;
- char message[JMSG_LENGTH_MAX];
- };
- typedef struct _cimg_error_mgr *_cimg_error_ptr;
- METHODDEF(void) _cimg_jpeg_error_exit(j_common_ptr cinfo) {
- _cimg_error_ptr c_err = (_cimg_error_ptr) cinfo->err; // Return control to the setjmp point
- (*cinfo->err->format_message)(cinfo,c_err->message);
- jpeg_destroy(cinfo); // Clean memory and temp files
- longjmp(c_err->setjmp_buffer,1);
- }
- #endif
- CImg<T>& _load_jpeg(std::FILE *const file, const char *const filename) {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "load_jpeg(): Specified filename is (null).",
- cimg_instance);
- #ifndef cimg_use_jpeg
- if (file)
- throw CImgIOException(_cimg_instance
- "load_jpeg(): Unable to load data from '(FILE*)' unless libjpeg is enabled.",
- cimg_instance);
- else return load_other(filename);
- #else
- std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
- struct jpeg_decompress_struct cinfo;
- struct _cimg_error_mgr jerr;
- cinfo.err = jpeg_std_error(&jerr.original);
- jerr.original.error_exit = _cimg_jpeg_error_exit;
- if (setjmp(jerr.setjmp_buffer)) { // JPEG error
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimg_instance
- "load_jpeg(): Error message returned by libjpeg: %s.",
- cimg_instance,jerr.message);
- }
- jpeg_create_decompress(&cinfo);
- jpeg_stdio_src(&cinfo,nfile);
- jpeg_read_header(&cinfo,TRUE);
- jpeg_start_decompress(&cinfo);
- if (cinfo.output_components!=1 && cinfo.output_components!=3 && cinfo.output_components!=4) {
- if (!file) {
- cimg::fclose(nfile);
- return load_other(filename);
- } else
- throw CImgIOException(_cimg_instance
- "load_jpeg(): Failed to load JPEG data from file '%s'.",
- cimg_instance,filename?filename:"(FILE*)");
- }
- CImg<ucharT> buffer(cinfo.output_width*cinfo.output_components);
- JSAMPROW row_pointer[1];
- try { assign(cinfo.output_width,cinfo.output_height,1,cinfo.output_components); }
- catch (...) { if (!file) cimg::fclose(nfile); throw; }
- T *ptr_r = _data, *ptr_g = _data + 1UL*_width*_height, *ptr_b = _data + 2UL*_width*_height,
- *ptr_a = _data + 3UL*_width*_height;
- while (cinfo.output_scanline<cinfo.output_height) {
- *row_pointer = buffer._data;
- if (jpeg_read_scanlines(&cinfo,row_pointer,1)!=1) {
- cimg::warn(_cimg_instance
- "load_jpeg(): Incomplete data in file '%s'.",
- cimg_instance,filename?filename:"(FILE*)");
- break;
- }
- const unsigned char *ptrs = buffer._data;
- switch (_spectrum) {
- case 1 : {
- cimg_forX(*this,x) *(ptr_r++) = (T)*(ptrs++);
- } break;
- case 3 : {
- cimg_forX(*this,x) {
- *(ptr_r++) = (T)*(ptrs++);
- *(ptr_g++) = (T)*(ptrs++);
- *(ptr_b++) = (T)*(ptrs++);
- }
- } break;
- case 4 : {
- cimg_forX(*this,x) {
- *(ptr_r++) = (T)*(ptrs++);
- *(ptr_g++) = (T)*(ptrs++);
- *(ptr_b++) = (T)*(ptrs++);
- *(ptr_a++) = (T)*(ptrs++);
- }
- } break;
- }
- }
- jpeg_finish_decompress(&cinfo);
- jpeg_destroy_decompress(&cinfo);
- if (!file) cimg::fclose(nfile);
- return *this;
- #endif
- }
- //! Load image from a file, using Magick++ library.
- /**
- \param filename Filename, as a C-string.
- **/
- // Added April/may 2006 by Christoph Hormann <chris_hormann@gmx.de>
- // This is experimental code, not much tested, use with care.
- CImg<T>& load_magick(const char *const filename) {
- if (!filename)
- throw CImgArgumentException(_cimg_instance
- "load_magick(): Specified filename is (null).",
- cimg_instance);
- #ifdef cimg_use_magick
- Magick::Image image(filename);
- const unsigned int W = image.size().width(), H = image.size().height();
- switch (image.type()) {
- case Magick::PaletteMatteType :
- case Magick::TrueColorMatteType :
- case Magick::ColorSeparationType : {
- assign(W,H,1,4);
- T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3);
- Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
- for (ulongT off = (ulongT)W*H; off; --off) {
- *(ptr_r++) = (T)(pixels->red);
- *(ptr_g++) = (T)(pixels->green);
- *(ptr_b++) = (T)(pixels->blue);
- *(ptr_a++) = (T)(pixels->opacity);
- ++pixels;
- }
- } break;
- case Magick::PaletteType :
- case Magick::TrueColorType : {
- assign(W,H,1,3);
- T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
- Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
- for (ulongT off = (ulongT)W*H; off; --off) {
- *(ptr_r++) = (T)(pixels->red);
- *(ptr_g++) = (T)(pixels->green);
- *(ptr_b++) = (T)(pixels->blue);
- ++pixels;
- }
- } break;
- case Magick::GrayscaleMatteType : {
- assign(W,H,1,2);
- T *ptr_r = data(0,0,0,0), *ptr_a = data(0,0,0,1);
- Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
- for (ulongT off = (ulongT)W*H; off; --off) {
- *(ptr_r++) = (T)(pixels->red);
- *(ptr_a++) = (T)(pixels->opacity);
- ++pixels;
- }
- } break;
- default : {
- assign(W,H,1,1);
- T *ptr_r = data(0,0,0,0);
- Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
- for (ulongT off = (ulongT)W*H; off; --off) {
- *(ptr_r++) = (T)(pixels->red);
- ++pixels;
- }
- }
- }
- return *this;
- #else
- throw CImgIOException(_cimg_instance
- "load_magick(): Unable to load file '%s' unless libMagick++ is enabled.",
- cimg_instance,
- filename);
- #endif
- }
- //! Load image from a file, using Magick++ library \newinstance.
- static CImg<T> get_load_magick(const char *const filename) {
- return CImg<T>().load_magick(filename);
- }
- //! Load image from a PNG file.
- /**
- \param filename Filename, as a C-string.
- \param[out] bits_per_value Number of bits used to store a scalar value in the image file.
- **/
- CImg<T>& load_png(const char *const filename, unsigned int *const bits_per_value=0) {
- return _load_png(0,filename,bits_per_value);
- }
- //! Load image from a PNG file \newinstance.
- static CImg<T> get_load_png(const char *const filename, unsigned int *const bits_per_value=0) {
- return CImg<T>().load_png(filename,bits_per_value);
- }
- //! Load image from a PNG file \overloading.
- CImg<T>& load_png(std::FILE *const file, unsigned int *const bits_per_value=0) {
- return _load_png(file,0,bits_per_value);
- }
- //! Load image from a PNG file \newinstance.
- static CImg<T> get_load_png(std::FILE *const file, unsigned int *const bits_per_value=0) {
- return CImg<T>().load_png(file,bits_per_value);
- }
- // (Note: Most of this function has been written by Eric Fausett)
- CImg<T>& _load_png(std::FILE *const file, const char *const filename, unsigned int *const bits_per_value) {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "load_png(): Specified filename is (null).",
- cimg_instance);
- #ifndef cimg_use_png
- cimg::unused(bits_per_value);
- if (file)
- throw CImgIOException(_cimg_instance
- "load_png(): Unable to load data from '(FILE*)' unless libpng is enabled.",
- cimg_instance);
- else return load_other(filename);
- #else
- // Open file and check for PNG validity
- #if defined __GNUC__
- const char *volatile nfilename = filename; // Use 'volatile' to avoid (wrong) g++ warning
- std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"rb");
- #else
- const char *nfilename = filename;
- std::FILE *nfile = file?file:cimg::fopen(nfilename,"rb");
- #endif
- unsigned char pngCheck[8] = {};
- cimg::fread(pngCheck,8,(std::FILE*)nfile);
- if (png_sig_cmp(pngCheck,0,8)) {
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimg_instance
- "load_png(): Invalid PNG file '%s'.",
- cimg_instance,
- nfilename?nfilename:"(FILE*)");
- }
- // Setup PNG structures for read
- png_voidp user_error_ptr = 0;
- png_error_ptr user_error_fn = 0, user_warning_fn = 0;
- png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,user_error_ptr,user_error_fn,user_warning_fn);
- if (!png_ptr) {
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimg_instance
- "load_png(): Failed to initialize 'png_ptr' structure for file '%s'.",
- cimg_instance,
- nfilename?nfilename:"(FILE*)");
- }
- png_infop info_ptr = png_create_info_struct(png_ptr);
- if (!info_ptr) {
- if (!file) cimg::fclose(nfile);
- png_destroy_read_struct(&png_ptr,(png_infopp)0,(png_infopp)0);
- throw CImgIOException(_cimg_instance
- "load_png(): Failed to initialize 'info_ptr' structure for file '%s'.",
- cimg_instance,
- nfilename?nfilename:"(FILE*)");
- }
- png_infop end_info = png_create_info_struct(png_ptr);
- if (!end_info) {
- if (!file) cimg::fclose(nfile);
- png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)0);
- throw CImgIOException(_cimg_instance
- "load_png(): Failed to initialize 'end_info' structure for file '%s'.",
- cimg_instance,
- nfilename?nfilename:"(FILE*)");
- }
- // Error handling callback for png file reading
- if (setjmp(png_jmpbuf(png_ptr))) {
- if (!file) cimg::fclose((std::FILE*)nfile);
- png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0);
- throw CImgIOException(_cimg_instance
- "load_png(): Encountered unknown fatal error in libpng for file '%s'.",
- cimg_instance,
- nfilename?nfilename:"(FILE*)");
- }
- png_init_io(png_ptr, nfile);
- png_set_sig_bytes(png_ptr, 8);
- // Get PNG Header Info up to data block
- png_read_info(png_ptr,info_ptr);
- png_uint_32 W, H;
- int bit_depth, color_type, interlace_type;
- bool is_gray = false;
- png_get_IHDR(png_ptr,info_ptr,&W,&H,&bit_depth,&color_type,&interlace_type,(int*)0,(int*)0);
- png_set_interlace_handling(png_ptr);
- if (bits_per_value) *bits_per_value = (unsigned int)bit_depth;
- // Transforms to unify image data
- if (color_type==PNG_COLOR_TYPE_PALETTE) {
- png_set_palette_to_rgb(png_ptr);
- color_type = PNG_COLOR_TYPE_RGB;
- bit_depth = 8;
- }
- if (color_type==PNG_COLOR_TYPE_GRAY && bit_depth<8) {
- png_set_expand_gray_1_2_4_to_8(png_ptr);
- is_gray = true;
- bit_depth = 8;
- }
- if (png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS)) {
- png_set_tRNS_to_alpha(png_ptr);
- color_type |= PNG_COLOR_MASK_ALPHA;
- }
- if (color_type==PNG_COLOR_TYPE_GRAY || color_type==PNG_COLOR_TYPE_GRAY_ALPHA) {
- png_set_gray_to_rgb(png_ptr);
- color_type |= PNG_COLOR_MASK_COLOR;
- is_gray = true;
- }
- if (color_type==PNG_COLOR_TYPE_RGB)
- png_set_filler(png_ptr,0xffffU,PNG_FILLER_AFTER);
- png_read_update_info(png_ptr,info_ptr);
- if (bit_depth!=8 && bit_depth!=16) {
- if (!file) cimg::fclose(nfile);
- png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
- throw CImgIOException(_cimg_instance
- "load_png(): Invalid bit depth %u in file '%s'.",
- cimg_instance,
- bit_depth,nfilename?nfilename:"(FILE*)");
- }
- const int byte_depth = bit_depth>>3;
- // Allocate memory for image reading
- png_bytep *const imgData = new png_bytep[H];
- for (unsigned int row = 0; row<H; ++row) imgData[row] = new png_byte[(size_t)byte_depth*4*W];
- png_read_image(png_ptr,imgData);
- png_read_end(png_ptr,end_info);
- // Read pixel data
- if (color_type!=PNG_COLOR_TYPE_RGB && color_type!=PNG_COLOR_TYPE_RGB_ALPHA) {
- if (!file) cimg::fclose(nfile);
- png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
- throw CImgIOException(_cimg_instance
- "load_png(): Invalid color coding type %u in file '%s'.",
- cimg_instance,
- color_type,nfilename?nfilename:"(FILE*)");
- }
- const bool is_alpha = (color_type==PNG_COLOR_TYPE_RGBA);
- try { assign(W,H,1,(is_gray?1:3) + (is_alpha?1:0)); }
- catch (...) { if (!file) cimg::fclose(nfile); throw; }
- T
- *ptr_r = data(0,0,0,0),
- *ptr_g = is_gray?0:data(0,0,0,1),
- *ptr_b = is_gray?0:data(0,0,0,2),
- *ptr_a = !is_alpha?0:data(0,0,0,is_gray?1:3);
- switch (bit_depth) {
- case 8 : {
- cimg_forY(*this,y) {
- const unsigned char *ptrs = (unsigned char*)imgData[y];
- cimg_forX(*this,x) {
- *(ptr_r++) = (T)*(ptrs++);
- if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs;
- if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs;
- if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs;
- }
- }
- } break;
- case 16 : {
- cimg_forY(*this,y) {
- const unsigned short *ptrs = (unsigned short*)(imgData[y]);
- if (!cimg::endianness()) cimg::invert_endianness(ptrs,4*_width);
- cimg_forX(*this,x) {
- *(ptr_r++) = (T)*(ptrs++);
- if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs;
- if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs;
- if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs;
- }
- }
- } break;
- }
- png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
- // Deallocate image read memory
- cimg_forY(*this,n) delete[] imgData[n];
- delete[] imgData;
- if (!file) cimg::fclose(nfile);
- return *this;
- #endif
- }
- //! Load image from a PNM file.
- /**
- \param filename Filename, as a C-string.
- **/
- CImg<T>& load_pnm(const char *const filename) {
- return _load_pnm(0,filename);
- }
- //! Load image from a PNM file \newinstance.
- static CImg<T> get_load_pnm(const char *const filename) {
- return CImg<T>().load_pnm(filename);
- }
- //! Load image from a PNM file \overloading.
- CImg<T>& load_pnm(std::FILE *const file) {
- return _load_pnm(file,0);
- }
- //! Load image from a PNM file \newinstance.
- static CImg<T> get_load_pnm(std::FILE *const file) {
- return CImg<T>().load_pnm(file);
- }
- CImg<T>& _load_pnm(std::FILE *const file, const char *const filename) {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "load_pnm(): Specified filename is (null).",
- cimg_instance);
- std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
- unsigned int ppm_type, W, H, D = 1, colormax = 255;
- CImg<charT> item(16384,1,1,1,0);
- int err, rval, gval, bval;
- const longT cimg_iobuffer = (longT)24*1024*1024;
- while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
- if (cimg_sscanf(item," P%u",&ppm_type)!=1) {
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimg_instance
- "load_pnm(): PNM header not found in file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- }
- while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
- if ((err=cimg_sscanf(item," %u %u %u %u",&W,&H,&D,&colormax))<2) {
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimg_instance
- "load_pnm(): WIDTH and HEIGHT fields undefined in file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- }
- if (ppm_type!=1 && ppm_type!=4) {
- if (err==2 || (err==3 && (ppm_type==5 || ppm_type==7 || ppm_type==8 || ppm_type==9))) {
- while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
- if (cimg_sscanf(item,"%u",&colormax)!=1)
- cimg::warn(_cimg_instance
- "load_pnm(): COLORMAX field is undefined in file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- } else { colormax = D; D = 1; }
- }
- std::fgetc(nfile);
- if (filename) { // Check that dimensions specified in file does not exceed the buffer dimension
- const cimg_int64 siz = cimg::fsize(filename);
- if (W*H*D>siz)
- throw CImgIOException(_cimg_instance
- "load_pnm(): Specified image dimensions in file '%s' exceed file size.",
- cimg_instance,
- filename);
- }
- switch (ppm_type) {
- case 1 : { // 2D B&W ascii
- assign(W,H,1,1);
- T* ptrd = _data;
- cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)(rval?0:255); else break; }
- } break;
- case 2 : { // 2D grey ascii
- assign(W,H,1,1);
- T* ptrd = _data;
- cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)rval; else break; }
- } break;
- case 3 : { // 2D color ascii
- assign(W,H,1,3);
- T *ptrd = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
- cimg_forXY(*this,x,y) {
- if (std::fscanf(nfile,"%d %d %d",&rval,&gval,&bval)==3) {
- *(ptrd++) = (T)rval; *(ptr_g++) = (T)gval; *(ptr_b++) = (T)bval;
- } else break;
- }
- } break;
- case 4 : { // 2D b&w binary (support 3D PINK extension)
- CImg<ucharT> raw;
- assign(W,H,D,1);
- T *ptrd = data(0,0,0,0);
- unsigned int w = 0, h = 0, d = 0;
- for (longT to_read = (longT)((W/8 + (W%8?1:0))*H*D); to_read>0; ) {
- raw.assign(std::min(to_read,cimg_iobuffer));
- cimg::fread(raw._data,raw._width,nfile);
- to_read-=raw._width;
- const unsigned char *ptrs = raw._data;
- unsigned char mask = 0, val = 0;
- for (ulongT off = (ulongT)raw._width; off || mask; mask>>=1) {
- if (!mask) { if (off--) val = *(ptrs++); mask = 128; }
- *(ptrd++) = (T)((val&mask)?0:255);
- if (++w==W) { w = 0; mask = 0; if (++h==H) { h = 0; if (++d==D) break; }}
- }
- }
- } break;
- case 5 : case 7 : { // 2D/3D grey binary (support 3D PINK extension)
- if (colormax<256) { // 8 bits
- CImg<ucharT> raw;
- assign(W,H,D,1);
- T *ptrd = data(0,0,0,0);
- for (longT to_read = (longT)size(); to_read>0; ) {
- raw.assign(std::min(to_read,cimg_iobuffer));
- cimg::fread(raw._data,raw._width,nfile);
- to_read-=raw._width;
- const unsigned char *ptrs = raw._data;
- for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
- }
- } else { // 16 bits
- CImg<ushortT> raw;
- assign(W,H,D,1);
- T *ptrd = data(0,0,0,0);
- for (longT to_read = (longT)size(); to_read>0; ) {
- raw.assign(std::min(to_read,cimg_iobuffer/2));
- cimg::fread(raw._data,raw._width,nfile);
- if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
- to_read-=raw._width;
- const unsigned short *ptrs = raw._data;
- for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
- }
- }
- } break;
- case 6 : { // 2D color binary
- if (colormax<256) { // 8 bits
- CImg<ucharT> raw;
- assign(W,H,1,3);
- T
- *ptr_r = data(0,0,0,0),
- *ptr_g = data(0,0,0,1),
- *ptr_b = data(0,0,0,2);
- for (longT to_read = (longT)size(); to_read>0; ) {
- raw.assign(std::min(to_read,cimg_iobuffer));
- cimg::fread(raw._data,raw._width,nfile);
- to_read-=raw._width;
- const unsigned char *ptrs = raw._data;
- for (ulongT off = (ulongT)raw._width/3; off; --off) {
- *(ptr_r++) = (T)*(ptrs++);
- *(ptr_g++) = (T)*(ptrs++);
- *(ptr_b++) = (T)*(ptrs++);
- }
- }
- } else { // 16 bits
- CImg<ushortT> raw;
- assign(W,H,1,3);
- T
- *ptr_r = data(0,0,0,0),
- *ptr_g = data(0,0,0,1),
- *ptr_b = data(0,0,0,2);
- for (longT to_read = (longT)size(); to_read>0; ) {
- raw.assign(std::min(to_read,cimg_iobuffer/2));
- cimg::fread(raw._data,raw._width,nfile);
- if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
- to_read-=raw._width;
- const unsigned short *ptrs = raw._data;
- for (ulongT off = (ulongT)raw._width/3; off; --off) {
- *(ptr_r++) = (T)*(ptrs++);
- *(ptr_g++) = (T)*(ptrs++);
- *(ptr_b++) = (T)*(ptrs++);
- }
- }
- }
- } break;
- case 8 : { // 2D/3D grey binary with int32 integers (PINK extension)
- CImg<intT> raw;
- assign(W,H,D,1);
- T *ptrd = data(0,0,0,0);
- for (longT to_read = (longT)size(); to_read>0; ) {
- raw.assign(std::min(to_read,cimg_iobuffer));
- cimg::fread(raw._data,raw._width,nfile);
- to_read-=raw._width;
- const int *ptrs = raw._data;
- for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
- }
- } break;
- case 9 : { // 2D/3D grey binary with float values (PINK extension)
- CImg<floatT> raw;
- assign(W,H,D,1);
- T *ptrd = data(0,0,0,0);
- for (longT to_read = (longT)size(); to_read>0; ) {
- raw.assign(std::min(to_read,cimg_iobuffer));
- cimg::fread(raw._data,raw._width,nfile);
- to_read-=raw._width;
- const float *ptrs = raw._data;
- for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
- }
- } break;
- default :
- assign();
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimg_instance
- "load_pnm(): PNM type 'P%d' found, but type is not supported.",
- cimg_instance,
- filename?filename:"(FILE*)",ppm_type);
- }
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Load image from a PFM file.
- /**
- \param filename Filename, as a C-string.
- **/
- CImg<T>& load_pfm(const char *const filename) {
- return _load_pfm(0,filename);
- }
- //! Load image from a PFM file \newinstance.
- static CImg<T> get_load_pfm(const char *const filename) {
- return CImg<T>().load_pfm(filename);
- }
- //! Load image from a PFM file \overloading.
- CImg<T>& load_pfm(std::FILE *const file) {
- return _load_pfm(file,0);
- }
- //! Load image from a PFM file \newinstance.
- static CImg<T> get_load_pfm(std::FILE *const file) {
- return CImg<T>().load_pfm(file);
- }
- CImg<T>& _load_pfm(std::FILE *const file, const char *const filename) {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "load_pfm(): Specified filename is (null).",
- cimg_instance);
- std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
- char pfm_type;
- CImg<charT> item(16384,1,1,1,0);
- int W = 0, H = 0, err = 0;
- double scale = 0;
- while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
- if (cimg_sscanf(item," P%c",&pfm_type)!=1) {
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimg_instance
- "load_pfm(): PFM header not found in file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- }
- while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
- if ((err=cimg_sscanf(item," %d %d",&W,&H))<2) {
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimg_instance
- "load_pfm(): WIDTH and HEIGHT fields are undefined in file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- } else if (W<=0 || H<=0) {
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimg_instance
- "load_pfm(): WIDTH (%d) and HEIGHT (%d) fields are invalid in file '%s'.",
- cimg_instance,W,H,
- filename?filename:"(FILE*)");
- }
- if (err==2) {
- while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
- if (cimg_sscanf(item,"%lf",&scale)!=1)
- cimg::warn(_cimg_instance
- "load_pfm(): SCALE field is undefined in file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- }
- std::fgetc(nfile);
- const bool is_color = (pfm_type=='F'), is_inverted = (scale>0)!=cimg::endianness();
- if (is_color) {
- assign(W,H,1,3,(T)0);
- CImg<floatT> buf(3*W);
- T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
- cimg_forY(*this,y) {
- cimg::fread(buf._data,3*W,nfile);
- if (is_inverted) cimg::invert_endianness(buf._data,3*W);
- const float *ptrs = buf._data;
- cimg_forX(*this,x) {
- *(ptr_r++) = (T)*(ptrs++);
- *(ptr_g++) = (T)*(ptrs++);
- *(ptr_b++) = (T)*(ptrs++);
- }
- }
- } else {
- assign(W,H,1,1,(T)0);
- CImg<floatT> buf(W);
- T *ptrd = data(0,0,0,0);
- cimg_forY(*this,y) {
- cimg::fread(buf._data,W,nfile);
- if (is_inverted) cimg::invert_endianness(buf._data,W);
- const float *ptrs = buf._data;
- cimg_forX(*this,x) *(ptrd++) = (T)*(ptrs++);
- }
- }
- if (!file) cimg::fclose(nfile);
- return mirror('y'); // Most of the .pfm files are flipped along the y-axis
- }
- //! Load image from a RGB file.
- /**
- \param filename Filename, as a C-string.
- \param dimw Width of the image buffer.
- \param dimh Height of the image buffer.
- **/
- CImg<T>& load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
- return _load_rgb(0,filename,dimw,dimh);
- }
- //! Load image from a RGB file \newinstance.
- static CImg<T> get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
- return CImg<T>().load_rgb(filename,dimw,dimh);
- }
- //! Load image from a RGB file \overloading.
- CImg<T>& load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
- return _load_rgb(file,0,dimw,dimh);
- }
- //! Load image from a RGB file \newinstance.
- static CImg<T> get_load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
- return CImg<T>().load_rgb(file,dimw,dimh);
- }
- CImg<T>& _load_rgb(std::FILE *const file, const char *const filename,
- const unsigned int dimw, const unsigned int dimh) {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "load_rgb(): Specified filename is (null).",
- cimg_instance);
- if (!dimw || !dimh) return assign();
- const longT cimg_iobuffer = (longT)24*1024*1024;
- std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
- CImg<ucharT> raw;
- assign(dimw,dimh,1,3);
- T
- *ptr_r = data(0,0,0,0),
- *ptr_g = data(0,0,0,1),
- *ptr_b = data(0,0,0,2);
- for (longT to_read = (longT)size(); to_read>0; ) {
- raw.assign(std::min(to_read,cimg_iobuffer));
- cimg::fread(raw._data,raw._width,nfile);
- to_read-=raw._width;
- const unsigned char *ptrs = raw._data;
- for (ulongT off = raw._width/3UL; off; --off) {
- *(ptr_r++) = (T)*(ptrs++);
- *(ptr_g++) = (T)*(ptrs++);
- *(ptr_b++) = (T)*(ptrs++);
- }
- }
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Load image from a RGBA file.
- /**
- \param filename Filename, as a C-string.
- \param dimw Width of the image buffer.
- \param dimh Height of the image buffer.
- **/
- CImg<T>& load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
- return _load_rgba(0,filename,dimw,dimh);
- }
- //! Load image from a RGBA file \newinstance.
- static CImg<T> get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
- return CImg<T>().load_rgba(filename,dimw,dimh);
- }
- //! Load image from a RGBA file \overloading.
- CImg<T>& load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
- return _load_rgba(file,0,dimw,dimh);
- }
- //! Load image from a RGBA file \newinstance.
- static CImg<T> get_load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
- return CImg<T>().load_rgba(file,dimw,dimh);
- }
- CImg<T>& _load_rgba(std::FILE *const file, const char *const filename,
- const unsigned int dimw, const unsigned int dimh) {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "load_rgba(): Specified filename is (null).",
- cimg_instance);
- if (!dimw || !dimh) return assign();
- const longT cimg_iobuffer = (longT)24*1024*1024;
- std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
- CImg<ucharT> raw;
- assign(dimw,dimh,1,4);
- T
- *ptr_r = data(0,0,0,0),
- *ptr_g = data(0,0,0,1),
- *ptr_b = data(0,0,0,2),
- *ptr_a = data(0,0,0,3);
- for (longT to_read = (longT)size(); to_read>0; ) {
- raw.assign(std::min(to_read,cimg_iobuffer));
- cimg::fread(raw._data,raw._width,nfile);
- to_read-=raw._width;
- const unsigned char *ptrs = raw._data;
- for (ulongT off = raw._width/4UL; off; --off) {
- *(ptr_r++) = (T)*(ptrs++);
- *(ptr_g++) = (T)*(ptrs++);
- *(ptr_b++) = (T)*(ptrs++);
- *(ptr_a++) = (T)*(ptrs++);
- }
- }
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Load image from a TIFF file.
- /**
- \param filename Filename, as a C-string.
- \param first_frame First frame to read (for multi-pages tiff).
- \param last_frame Last frame to read (for multi-pages tiff).
- \param step_frame Step value of frame reading.
- \param[out] bits_per_value Number of bits used to store a scalar value in the image file.
- \param[out] voxel_size Voxel size, as stored in the filename.
- \param[out] description Description, as stored in the filename.
- \note
- - libtiff support is enabled by defining the precompilation
- directive \c cimg_use_tiff.
- - When libtiff is enabled, 2D and 3D (multipage) several
- channel per pixel are supported for
- <tt>char,uchar,short,ushort,float</tt> and \c double pixel types.
- - If \c cimg_use_tiff is not defined at compile time the
- function uses CImg<T>& load_other(const char*).
- **/
- CImg<T>& load_tiff(const char *const filename,
- const unsigned int first_frame=0, const unsigned int last_frame=~0U,
- const unsigned int step_frame=1, unsigned int *const bits_per_value=0,
- float *const voxel_size=0, CImg<charT> *const description=0) {
- if (!filename)
- throw CImgArgumentException(_cimg_instance
- "load_tiff(): Specified filename is (null).",
- cimg_instance);
- const unsigned int
- nfirst_frame = first_frame<last_frame?first_frame:last_frame,
- nstep_frame = step_frame?step_frame:1;
- unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
- #ifndef cimg_use_tiff
- cimg::unused(bits_per_value,voxel_size,description);
- if (nfirst_frame || nlast_frame!=~0U || nstep_frame>1)
- throw CImgArgumentException(_cimg_instance
- "load_tiff(): Unable to read sub-images from file '%s' unless libtiff is enabled.",
- cimg_instance,
- filename);
- return load_other(filename);
- #else
- #if cimg_verbosity<3
- TIFFSetWarningHandler(0);
- TIFFSetErrorHandler(0);
- #endif
- TIFF *tif = TIFFOpen(filename,"r");
- if (tif) {
- unsigned int nb_images = 0;
- do ++nb_images; while (TIFFReadDirectory(tif));
- if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
- cimg::warn(_cimg_instance
- "load_tiff(): File '%s' contains %u image(s) while specified frame range is [%u,%u] (step %u).",
- cimg_instance,
- filename,nb_images,nfirst_frame,nlast_frame,nstep_frame);
- if (nfirst_frame>=nb_images) return assign();
- if (nlast_frame>=nb_images) nlast_frame = nb_images - 1;
- TIFFSetDirectory(tif,0);
- CImg<T> frame;
- for (unsigned int l = nfirst_frame; l<=nlast_frame; l+=nstep_frame) {
- frame._load_tiff(tif,l,bits_per_value,voxel_size,description);
- if (l==nfirst_frame)
- assign(frame._width,frame._height,1 + (nlast_frame - nfirst_frame)/nstep_frame,frame._spectrum);
- if (frame._width>_width || frame._height>_height || frame._spectrum>_spectrum)
- resize(std::max(frame._width,_width),
- std::max(frame._height,_height),-100,
- std::max(frame._spectrum,_spectrum),0);
- draw_image(0,0,(l - nfirst_frame)/nstep_frame,frame);
- }
- TIFFClose(tif);
- } else throw CImgIOException(_cimg_instance
- "load_tiff(): Failed to open file '%s'.",
- cimg_instance,
- filename);
- return *this;
- #endif
- }
- //! Load image from a TIFF file \newinstance.
- static CImg<T> get_load_tiff(const char *const filename,
- const unsigned int first_frame=0, const unsigned int last_frame=~0U,
- const unsigned int step_frame=1, unsigned int *const bits_per_value=0,
- float *const voxel_size=0, CImg<charT> *const description=0) {
- return CImg<T>().load_tiff(filename,first_frame,last_frame,step_frame,bits_per_value,voxel_size,description);
- }
- // (Original contribution by Jerome Boulanger).
- #ifdef cimg_use_tiff
- template<typename t>
- void _load_tiff_tiled_contig(TIFF *const tif, const cimg_uint16 samplesperpixel,
- const cimg_uint32 nx, const cimg_uint32 ny,
- const cimg_uint32 tw, const cimg_uint32 th) {
- t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
- if (buf) {
- for (unsigned int row = 0; row<ny; row+=th)
- for (unsigned int col = 0; col<nx; col+=tw) {
- if (TIFFReadTile(tif,buf,col,row,0,0)<0) {
- _TIFFfree(buf); TIFFClose(tif);
- throw CImgIOException(_cimg_instance
- "load_tiff(): Invalid tile in file '%s'.",
- cimg_instance,
- TIFFFileName(tif));
- }
- const t *ptr = buf;
- for (unsigned int rr = row; rr<std::min((unsigned int)(row + th),(unsigned int)ny); ++rr)
- for (unsigned int cc = col; cc<std::min((unsigned int)(col + tw),(unsigned int)nx); ++cc)
- for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
- (*this)(cc,rr,vv) = (T)(ptr[(rr - row)*th*samplesperpixel + (cc - col)*samplesperpixel + vv]);
- }
- _TIFFfree(buf);
- }
- }
- template<typename t>
- void _load_tiff_tiled_separate(TIFF *const tif, const cimg_uint16 samplesperpixel,
- const cimg_uint32 nx, const cimg_uint32 ny,
- const cimg_uint32 tw, const cimg_uint32 th) {
- t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
- if (buf) {
- for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
- for (unsigned int row = 0; row<ny; row+=th)
- for (unsigned int col = 0; col<nx; col+=tw) {
- if (TIFFReadTile(tif,buf,col,row,0,vv)<0) {
- _TIFFfree(buf); TIFFClose(tif);
- throw CImgIOException(_cimg_instance
- "load_tiff(): Invalid tile in file '%s'.",
- cimg_instance,
- TIFFFileName(tif));
- }
- const t *ptr = buf;
- for (unsigned int rr = row; rr<std::min((unsigned int)(row + th),(unsigned int)ny); ++rr)
- for (unsigned int cc = col; cc<std::min((unsigned int)(col + tw),(unsigned int)nx); ++cc)
- (*this)(cc,rr,vv) = (T)*(ptr++);
- }
- _TIFFfree(buf);
- }
- }
- template<typename t>
- void _load_tiff_contig(TIFF *const tif, const cimg_uint16 samplesperpixel,
- const cimg_uint32 nx, const cimg_uint32 ny) {
- t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
- if (buf) {
- cimg_uint32 row, rowsperstrip = (cimg_uint32)-1;
- TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
- for (row = 0; row<ny; row+= rowsperstrip) {
- cimg_uint32 nrow = (row + rowsperstrip>ny?ny - row:rowsperstrip);
- tstrip_t strip = TIFFComputeStrip(tif, row, 0);
- if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
- _TIFFfree(buf); TIFFClose(tif);
- throw CImgIOException(_cimg_instance
- "load_tiff(): Invalid strip in file '%s'.",
- cimg_instance,
- TIFFFileName(tif));
- }
- const t *ptr = buf;
- for (unsigned int rr = 0; rr<nrow; ++rr)
- for (unsigned int cc = 0; cc<nx; ++cc)
- for (unsigned int vv = 0; vv<samplesperpixel; ++vv) (*this)(cc,row + rr,vv) = (T)*(ptr++);
- }
- _TIFFfree(buf);
- }
- }
- template<typename t>
- void _load_tiff_separate(TIFF *const tif, const cimg_uint16 samplesperpixel,
- const cimg_uint32 nx, const cimg_uint32 ny) {
- t *buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
- if (buf) {
- cimg_uint32 row, rowsperstrip = (cimg_uint32)-1;
- TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
- for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
- for (row = 0; row<ny; row+= rowsperstrip) {
- cimg_uint32 nrow = (row + rowsperstrip>ny?ny - row:rowsperstrip);
- tstrip_t strip = TIFFComputeStrip(tif, row, vv);
- if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
- _TIFFfree(buf); TIFFClose(tif);
- throw CImgIOException(_cimg_instance
- "load_tiff(): Invalid strip in file '%s'.",
- cimg_instance,
- TIFFFileName(tif));
- }
- const t *ptr = buf;
- for (unsigned int rr = 0;rr<nrow; ++rr)
- for (unsigned int cc = 0; cc<nx; ++cc)
- (*this)(cc,row + rr,vv) = (T)*(ptr++);
- }
- _TIFFfree(buf);
- }
- }
- CImg<T>& _load_tiff(TIFF *const tif, const unsigned int directory, unsigned int *const bits_per_value,
- float *const voxel_size, CImg<charT> *const description) {
- if (!TIFFSetDirectory(tif,directory)) return assign();
- cimg_uint16 samplesperpixel = 1, bitspersample = 8, photo = 0;
- cimg_uint16 sampleformat = 1;
- cimg_uint32 nx = 1, ny = 1;
- const char *const filename = TIFFFileName(tif);
- const bool is_spp = (bool)TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL,&samplesperpixel);
- TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&nx);
- TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&ny);
- TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat);
- TIFFGetFieldDefaulted(tif,TIFFTAG_BITSPERSAMPLE,&bitspersample);
- TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo);
- if (bits_per_value) *bits_per_value = (unsigned int)bitspersample;
- if (voxel_size) {
- const char *s_description = 0;
- float vx = 0, vy = 0, vz = 0;
- if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description) {
- const char *s_desc = std::strstr(s_description,"VX=");
- if (s_desc && cimg_sscanf(s_desc,"VX=%f VY=%f VZ=%f",&vx,&vy,&vz)==3) { // CImg format
- voxel_size[0] = vx; voxel_size[1] = vy; voxel_size[2] = vz;
- }
- s_desc = std::strstr(s_description,"spacing=");
- if (s_desc && cimg_sscanf(s_desc,"spacing=%f",&vz)==1) { // Fiji format
- voxel_size[2] = vz;
- }
- }
- TIFFGetField(tif,TIFFTAG_XRESOLUTION,voxel_size);
- TIFFGetField(tif,TIFFTAG_YRESOLUTION,voxel_size + 1);
- voxel_size[0] = 1.f/voxel_size[0];
- voxel_size[1] = 1.f/voxel_size[1];
- }
- if (description) {
- const char *s_description = 0;
- if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description)
- CImg<charT>::string(s_description).move_to(*description);
- }
- const unsigned int spectrum = !is_spp || photo>=3?(photo>1?3:1):samplesperpixel;
- assign(nx,ny,1,spectrum);
- if ((photo>=3 && sampleformat==1 &&
- (bitspersample==4 || bitspersample==8) &&
- (samplesperpixel==1 || samplesperpixel==3 || samplesperpixel==4)) ||
- (bitspersample==1 && samplesperpixel==1)) {
- // Special case for unsigned color images.
- cimg_uint32 *const raster = (cimg_uint32*)_TIFFmalloc(nx*ny*sizeof(cimg_uint32));
- if (!raster) {
- _TIFFfree(raster); TIFFClose(tif);
- throw CImgException(_cimg_instance
- "load_tiff(): Failed to allocate memory (%s) for file '%s'.",
- cimg_instance,
- cimg::strbuffersize(nx*ny*sizeof(cimg_uint32)),filename);
- }
- TIFFReadRGBAImage(tif,nx,ny,raster,0);
- switch (spectrum) {
- case 1 :
- cimg_forXY(*this,x,y)
- (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 -y) + x]);
- break;
- case 3 :
- cimg_forXY(*this,x,y) {
- (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 - y) + x]);
- (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 - y) + x]);
- (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 - y) + x]);
- }
- break;
- case 4 :
- cimg_forXY(*this,x,y) {
- (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 - y) + x]);
- (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 - y) + x]);
- (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 - y) + x]);
- (*this)(x,y,3) = (T)(float)TIFFGetA(raster[nx*(ny - 1 - y) + x]);
- }
- break;
- }
- _TIFFfree(raster);
- } else { // Other cases
- cimg_uint16 config;
- TIFFGetField(tif,TIFFTAG_PLANARCONFIG,&config);
- if (TIFFIsTiled(tif)) {
- cimg_uint32 tw = 1, th = 1;
- TIFFGetField(tif,TIFFTAG_TILEWIDTH,&tw);
- TIFFGetField(tif,TIFFTAG_TILELENGTH,&th);
- if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
- case 8 :
- if (sampleformat==SAMPLEFORMAT_UINT)
- _load_tiff_tiled_contig<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
- else _load_tiff_tiled_contig<signed char>(tif,samplesperpixel,nx,ny,tw,th);
- break;
- case 16 :
- if (sampleformat==SAMPLEFORMAT_UINT)
- _load_tiff_tiled_contig<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
- else _load_tiff_tiled_contig<short>(tif,samplesperpixel,nx,ny,tw,th);
- break;
- case 32 :
- if (sampleformat==SAMPLEFORMAT_UINT)
- _load_tiff_tiled_contig<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
- else if (sampleformat==SAMPLEFORMAT_INT)
- _load_tiff_tiled_contig<int>(tif,samplesperpixel,nx,ny,tw,th);
- else _load_tiff_tiled_contig<float>(tif,samplesperpixel,nx,ny,tw,th);
- break;
- case 64 :
- if (sampleformat==SAMPLEFORMAT_UINT)
- _load_tiff_tiled_contig<uint64T>(tif,samplesperpixel,nx,ny,tw,th);
- else if (sampleformat==SAMPLEFORMAT_INT)
- _load_tiff_tiled_contig<int64T>(tif,samplesperpixel,nx,ny,tw,th);
- else _load_tiff_tiled_contig<double>(tif,samplesperpixel,nx,ny,tw,th);
- break;
- } else switch (bitspersample) {
- case 8 :
- if (sampleformat==SAMPLEFORMAT_UINT)
- _load_tiff_tiled_separate<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
- else _load_tiff_tiled_separate<signed char>(tif,samplesperpixel,nx,ny,tw,th);
- break;
- case 16 :
- if (sampleformat==SAMPLEFORMAT_UINT)
- _load_tiff_tiled_separate<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
- else _load_tiff_tiled_separate<short>(tif,samplesperpixel,nx,ny,tw,th);
- break;
- case 32 :
- if (sampleformat==SAMPLEFORMAT_UINT)
- _load_tiff_tiled_separate<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
- else if (sampleformat==SAMPLEFORMAT_INT)
- _load_tiff_tiled_separate<int>(tif,samplesperpixel,nx,ny,tw,th);
- else _load_tiff_tiled_separate<float>(tif,samplesperpixel,nx,ny,tw,th);
- break;
- case 64 :
- if (sampleformat==SAMPLEFORMAT_UINT)
- _load_tiff_tiled_separate<uint64T>(tif,samplesperpixel,nx,ny,tw,th);
- else if (sampleformat==SAMPLEFORMAT_INT)
- _load_tiff_tiled_separate<int64T>(tif,samplesperpixel,nx,ny,tw,th);
- else _load_tiff_tiled_separate<double>(tif,samplesperpixel,nx,ny,tw,th);
- break;
- }
- } else {
- if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
- case 8 :
- if (sampleformat==SAMPLEFORMAT_UINT)
- _load_tiff_contig<unsigned char>(tif,samplesperpixel,nx,ny);
- else _load_tiff_contig<signed char>(tif,samplesperpixel,nx,ny);
- break;
- case 16 :
- if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned short>(tif,samplesperpixel,nx,ny);
- else _load_tiff_contig<short>(tif,samplesperpixel,nx,ny);
- break;
- case 32 :
- if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned int>(tif,samplesperpixel,nx,ny);
- else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig<int>(tif,samplesperpixel,nx,ny);
- else _load_tiff_contig<float>(tif,samplesperpixel,nx,ny);
- break;
- case 64 :
- if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<uint64T>(tif,samplesperpixel,nx,ny);
- else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig<int64T>(tif,samplesperpixel,nx,ny);
- else _load_tiff_contig<double>(tif,samplesperpixel,nx,ny);
- break;
- } else switch (bitspersample) {
- case 8 :
- if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned char>(tif,samplesperpixel,nx,ny);
- else _load_tiff_separate<signed char>(tif,samplesperpixel,nx,ny);
- break;
- case 16 :
- if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned short>(tif,samplesperpixel,nx,ny);
- else _load_tiff_separate<short>(tif,samplesperpixel,nx,ny);
- break;
- case 32 :
- if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned int>(tif,samplesperpixel,nx,ny);
- else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate<int>(tif,samplesperpixel,nx,ny);
- else _load_tiff_separate<float>(tif,samplesperpixel,nx,ny);
- break;
- case 64 :
- if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<uint64T>(tif,samplesperpixel,nx,ny);
- else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate<int64T>(tif,samplesperpixel,nx,ny);
- else _load_tiff_separate<double>(tif,samplesperpixel,nx,ny);
- break;
- }
- }
- }
- return *this;
- }
- #endif
- //! Load image from a MINC2 file.
- /**
- \param filename Filename, as a C-string.
- **/
- // (Original code by Haz-Edine Assemlal).
- CImg<T>& load_minc2(const char *const filename) {
- if (!filename)
- throw CImgArgumentException(_cimg_instance
- "load_minc2(): Specified filename is (null).",
- cimg_instance);
- #ifndef cimg_use_minc2
- return load_other(filename);
- #else
- minc::minc_1_reader rdr;
- rdr.open(filename);
- assign(rdr.ndim(1)?rdr.ndim(1):1,
- rdr.ndim(2)?rdr.ndim(2):1,
- rdr.ndim(3)?rdr.ndim(3):1,
- rdr.ndim(4)?rdr.ndim(4):1);
- if (pixel_type()==cimg::type<unsigned char>::string())
- rdr.setup_read_byte();
- else if (pixel_type()==cimg::type<int>::string())
- rdr.setup_read_int();
- else if (pixel_type()==cimg::type<double>::string())
- rdr.setup_read_double();
- else
- rdr.setup_read_float();
- minc::load_standard_volume(rdr,this->_data);
- return *this;
- #endif
- }
- //! Load image from a MINC2 file \newinstance.
- static CImg<T> get_load_minc2(const char *const filename) {
- return CImg<T>().load_analyze(filename);
- }
- //! Load image from an ANALYZE7.5/NIFTI file.
- /**
- \param filename Filename, as a C-string.
- \param[out] voxel_size Pointer to the three voxel sizes read from the file.
- **/
- CImg<T>& load_analyze(const char *const filename, float *const voxel_size=0) {
- return _load_analyze(0,filename,voxel_size);
- }
- //! Load image from an ANALYZE7.5/NIFTI file \newinstance.
- static CImg<T> get_load_analyze(const char *const filename, float *const voxel_size=0) {
- return CImg<T>().load_analyze(filename,voxel_size);
- }
- //! Load image from an ANALYZE7.5/NIFTI file \overloading.
- CImg<T>& load_analyze(std::FILE *const file, float *const voxel_size=0) {
- return _load_analyze(file,0,voxel_size);
- }
- //! Load image from an ANALYZE7.5/NIFTI file \newinstance.
- static CImg<T> get_load_analyze(std::FILE *const file, float *const voxel_size=0) {
- return CImg<T>().load_analyze(file,voxel_size);
- }
- CImg<T>& _load_analyze(std::FILE *const file, const char *const filename, float *const voxel_size=0) {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "load_analyze(): Specified filename is (null).",
- cimg_instance);
- std::FILE *nfile_header = 0, *nfile = 0;
- if (!file) {
- CImg<charT> body(1024);
- const char *const ext = cimg::split_filename(filename,body);
- const unsigned int len = (unsigned int)std::strlen(body);
- if (!cimg::strcasecmp(ext,"hdr")) { // File is an Analyze header file
- nfile_header = cimg::fopen(filename,"rb");
- cimg_snprintf(body._data + len,body._width - len,".img");
- nfile = cimg::fopen(body,"rb");
- } else if (!cimg::strcasecmp(ext,"img")) { // File is an Analyze data file
- nfile = cimg::fopen(filename,"rb");
- cimg_snprintf(body._data + len,body._width - len,".hdr");
- nfile_header = cimg::fopen(body,"rb");
- } else nfile_header = nfile = cimg::fopen(filename,"rb"); // File is a Niftii file
- } else nfile_header = nfile = file; // File is a Niftii file
- if (!nfile || !nfile_header)
- throw CImgIOException(_cimg_instance
- "load_analyze(): Invalid Analyze7.5 or NIFTI header in file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- // Read header.
- bool endian = false;
- unsigned int header_size;
- cimg::fread(&header_size,1,nfile_header);
- if (!header_size)
- throw CImgIOException(_cimg_instance
- "load_analyze(): Invalid zero-size header in file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- if (header_size>=4096) { endian = true; cimg::invert_endianness(header_size); }
- unsigned char *const header = new unsigned char[header_size];
- cimg::fread(header + 4,header_size - 4,nfile_header);
- if (!file && nfile_header!=nfile) cimg::fclose(nfile_header);
- if (endian) {
- cimg::invert_endianness((short*)(header + 40),5);
- cimg::invert_endianness((short*)(header + 70),1);
- cimg::invert_endianness((short*)(header + 72),1);
- cimg::invert_endianness((float*)(header + 76),4);
- cimg::invert_endianness((float*)(header + 108),1);
- cimg::invert_endianness((float*)(header + 112),1);
- }
- if (nfile_header==nfile) {
- const unsigned int vox_offset = (unsigned int)*(float*)(header + 108);
- std::fseek(nfile,vox_offset,SEEK_SET);
- }
- unsigned short *dim = (unsigned short*)(header + 40), dimx = 1, dimy = 1, dimz = 1, dimv = 1;
- if (!dim[0])
- cimg::warn(_cimg_instance
- "load_analyze(): File '%s' defines an image with zero dimensions.",
- cimg_instance,
- filename?filename:"(FILE*)");
- if (dim[0]>4)
- cimg::warn(_cimg_instance
- "load_analyze(): File '%s' defines an image with %u dimensions, reading only the 4 first.",
- cimg_instance,
- filename?filename:"(FILE*)",dim[0]);
- if (dim[0]>=1) dimx = dim[1];
- if (dim[0]>=2) dimy = dim[2];
- if (dim[0]>=3) dimz = dim[3];
- if (dim[0]>=4) dimv = dim[4];
- float scalefactor = *(float*)(header + 112); if (scalefactor==0) scalefactor = 1;
- const unsigned short datatype = *(unsigned short*)(header + 70);
- if (voxel_size) {
- const float *vsize = (float*)(header + 76);
- voxel_size[0] = vsize[1]; voxel_size[1] = vsize[2]; voxel_size[2] = vsize[3];
- }
- delete[] header;
- // Read pixel data.
- assign(dimx,dimy,dimz,dimv);
- const size_t pdim = (size_t)dimx*dimy*dimz*dimv;
- switch (datatype) {
- case 2 : {
- unsigned char *const buffer = new unsigned char[pdim];
- cimg::fread(buffer,pdim,nfile);
- cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
- delete[] buffer;
- } break;
- case 4 : {
- short *const buffer = new short[pdim];
- cimg::fread(buffer,pdim,nfile);
- if (endian) cimg::invert_endianness(buffer,pdim);
- cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
- delete[] buffer;
- } break;
- case 8 : {
- int *const buffer = new int[pdim];
- cimg::fread(buffer,pdim,nfile);
- if (endian) cimg::invert_endianness(buffer,pdim);
- cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
- delete[] buffer;
- } break;
- case 16 : {
- float *const buffer = new float[pdim];
- cimg::fread(buffer,pdim,nfile);
- if (endian) cimg::invert_endianness(buffer,pdim);
- cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
- delete[] buffer;
- } break;
- case 64 : {
- double *const buffer = new double[pdim];
- cimg::fread(buffer,pdim,nfile);
- if (endian) cimg::invert_endianness(buffer,pdim);
- cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
- delete[] buffer;
- } break;
- default :
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimg_instance
- "load_analyze(): Unable to load datatype %d in file '%s'",
- cimg_instance,
- datatype,filename?filename:"(FILE*)");
- }
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Load image from a .cimg[z] file.
- /**
- \param filename Filename, as a C-string.
- \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
- \param align Appending alignment.
- **/
- CImg<T>& load_cimg(const char *const filename, const char axis='z', const float align=0) {
- CImgList<T> list;
- list.load_cimg(filename);
- if (list._width==1) return list[0].move_to(*this);
- return assign(list.get_append(axis,align));
- }
- //! Load image from a .cimg[z] file \newinstance
- static CImg<T> get_load_cimg(const char *const filename, const char axis='z', const float align=0) {
- return CImg<T>().load_cimg(filename,axis,align);
- }
- //! Load image from a .cimg[z] file \overloading.
- CImg<T>& load_cimg(std::FILE *const file, const char axis='z', const float align=0) {
- CImgList<T> list;
- list.load_cimg(file);
- if (list._width==1) return list[0].move_to(*this);
- return assign(list.get_append(axis,align));
- }
- //! Load image from a .cimg[z] file \newinstance
- static CImg<T> get_load_cimg(std::FILE *const file, const char axis='z', const float align=0) {
- return CImg<T>().load_cimg(file,axis,align);
- }
- //! Load sub-images of a .cimg file.
- /**
- \param filename Filename, as a C-string.
- \param n0 Starting frame.
- \param n1 Ending frame (~0U for max).
- \param x0 X-coordinate of the starting sub-image vertex.
- \param y0 Y-coordinate of the starting sub-image vertex.
- \param z0 Z-coordinate of the starting sub-image vertex.
- \param c0 C-coordinate of the starting sub-image vertex.
- \param x1 X-coordinate of the ending sub-image vertex (~0U for max).
- \param y1 Y-coordinate of the ending sub-image vertex (~0U for max).
- \param z1 Z-coordinate of the ending sub-image vertex (~0U for max).
- \param c1 C-coordinate of the ending sub-image vertex (~0U for max).
- \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
- \param align Appending alignment.
- **/
- CImg<T>& load_cimg(const char *const filename,
- const unsigned int n0, const unsigned int n1,
- const unsigned int x0, const unsigned int y0,
- const unsigned int z0, const unsigned int c0,
- const unsigned int x1, const unsigned int y1,
- const unsigned int z1, const unsigned int c1,
- const char axis='z', const float align=0) {
- CImgList<T> list;
- list.load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
- if (list._width==1) return list[0].move_to(*this);
- return assign(list.get_append(axis,align));
- }
- //! Load sub-images of a .cimg file \newinstance.
- static CImg<T> get_load_cimg(const char *const filename,
- const unsigned int n0, const unsigned int n1,
- const unsigned int x0, const unsigned int y0,
- const unsigned int z0, const unsigned int c0,
- const unsigned int x1, const unsigned int y1,
- const unsigned int z1, const unsigned int c1,
- const char axis='z', const float align=0) {
- return CImg<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
- }
- //! Load sub-images of a .cimg file \overloading.
- CImg<T>& load_cimg(std::FILE *const file,
- const unsigned int n0, const unsigned int n1,
- const unsigned int x0, const unsigned int y0,
- const unsigned int z0, const unsigned int c0,
- const unsigned int x1, const unsigned int y1,
- const unsigned int z1, const unsigned int c1,
- const char axis='z', const float align=0) {
- CImgList<T> list;
- list.load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
- if (list._width==1) return list[0].move_to(*this);
- return assign(list.get_append(axis,align));
- }
- //! Load sub-images of a .cimg file \newinstance.
- static CImg<T> get_load_cimg(std::FILE *const file,
- const unsigned int n0, const unsigned int n1,
- const unsigned int x0, const unsigned int y0,
- const unsigned int z0, const unsigned int c0,
- const unsigned int x1, const unsigned int y1,
- const unsigned int z1, const unsigned int c1,
- const char axis='z', const float align=0) {
- return CImg<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
- }
- //! Load image from an INRIMAGE-4 file.
- /**
- \param filename Filename, as a C-string.
- \param[out] voxel_size Pointer to the three voxel sizes read from the file.
- **/
- CImg<T>& load_inr(const char *const filename, float *const voxel_size=0) {
- return _load_inr(0,filename,voxel_size);
- }
- //! Load image from an INRIMAGE-4 file \newinstance.
- static CImg<T> get_load_inr(const char *const filename, float *const voxel_size=0) {
- return CImg<T>().load_inr(filename,voxel_size);
- }
- //! Load image from an INRIMAGE-4 file \overloading.
- CImg<T>& load_inr(std::FILE *const file, float *const voxel_size=0) {
- return _load_inr(file,0,voxel_size);
- }
- //! Load image from an INRIMAGE-4 file \newinstance.
- static CImg<T> get_load_inr(std::FILE *const file, float *voxel_size=0) {
- return CImg<T>().load_inr(file,voxel_size);
- }
- static void _load_inr_header(std::FILE *file, int out[8], float *const voxel_size) {
- CImg<charT> item(1024), tmp1(64), tmp2(64);
- *item = *tmp1 = *tmp2 = 0;
- out[0] = std::fscanf(file,"%63s",item._data);
- out[0] = out[1] = out[2] = out[3] = out[5] = 1; out[4] = out[6] = out[7] = -1;
- if (cimg::strncasecmp(item,"#INRIMAGE-4#{",13)!=0)
- throw CImgIOException("CImg<%s>::load_inr(): INRIMAGE-4 header not found.",
- pixel_type());
- while (std::fscanf(file," %63[^\n]%*c",item._data)!=EOF && std::strncmp(item,"##}",3)) {
- cimg_sscanf(item," XDIM%*[^0-9]%d",out);
- cimg_sscanf(item," YDIM%*[^0-9]%d",out + 1);
- cimg_sscanf(item," ZDIM%*[^0-9]%d",out + 2);
- cimg_sscanf(item," VDIM%*[^0-9]%d",out + 3);
- cimg_sscanf(item," PIXSIZE%*[^0-9]%d",out + 6);
- if (voxel_size) {
- cimg_sscanf(item," VX%*[^0-9.+-]%f",voxel_size);
- cimg_sscanf(item," VY%*[^0-9.+-]%f",voxel_size + 1);
- cimg_sscanf(item," VZ%*[^0-9.+-]%f",voxel_size + 2);
- }
- if (cimg_sscanf(item," CPU%*[ =]%s",tmp1._data)) out[7] = cimg::strncasecmp(tmp1,"sun",3)?0:1;
- switch (cimg_sscanf(item," TYPE%*[ =]%s %s",tmp1._data,tmp2._data)) {
- case 0 : break;
- case 2 :
- out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0;
- std::strncpy(tmp1,tmp2,tmp1._width - 1); // Fallthrough
- case 1 :
- if (!cimg::strncasecmp(tmp1,"int",3) || !cimg::strncasecmp(tmp1,"fixed",5)) out[4] = 0;
- if (!cimg::strncasecmp(tmp1,"float",5) || !cimg::strncasecmp(tmp1,"double",6)) out[4] = 1;
- if (!cimg::strncasecmp(tmp1,"packed",6)) out[4] = 2;
- if (out[4]>=0) break; // Fallthrough
- default :
- throw CImgIOException("CImg<%s>::load_inr(): Invalid pixel type '%s' defined in header.",
- pixel_type(),
- tmp2._data);
- }
- }
- if (out[0]<0 || out[1]<0 || out[2]<0 || out[3]<0)
- throw CImgIOException("CImg<%s>::load_inr(): Invalid dimensions (%d,%d,%d,%d) defined in header.",
- pixel_type(),
- out[0],out[1],out[2],out[3]);
- if (out[4]<0 || out[5]<0)
- throw CImgIOException("CImg<%s>::load_inr(): Incomplete pixel type defined in header.",
- pixel_type());
- if (out[6]<0)
- throw CImgIOException("CImg<%s>::load_inr(): Incomplete PIXSIZE field defined in header.",
- pixel_type());
- if (out[7]<0)
- throw CImgIOException("CImg<%s>::load_inr(): Big/Little Endian coding type undefined in header.",
- pixel_type());
- }
- CImg<T>& _load_inr(std::FILE *const file, const char *const filename, float *const voxel_size) {
- #define _cimg_load_inr_case(Tf,sign,pixsize,Ts) \
- if (!loaded && fopt[6]==pixsize && fopt[4]==Tf && fopt[5]==sign) { \
- Ts *xval, *const val = new Ts[(size_t)fopt[0]*fopt[3]]; \
- cimg_forYZ(*this,y,z) { \
- cimg::fread(val,fopt[0]*fopt[3],nfile); \
- if (fopt[7]!=endian) cimg::invert_endianness(val,fopt[0]*fopt[3]); \
- xval = val; cimg_forX(*this,x) cimg_forC(*this,c) (*this)(x,y,z,c) = (T)*(xval++); \
- } \
- delete[] val; \
- loaded = true; \
- }
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "load_inr(): Specified filename is (null).",
- cimg_instance);
- std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
- int fopt[8], endian = cimg::endianness()?1:0;
- bool loaded = false;
- if (voxel_size) voxel_size[0] = voxel_size[1] = voxel_size[2] = 1;
- _load_inr_header(nfile,fopt,voxel_size);
- assign(fopt[0],fopt[1],fopt[2],fopt[3]);
- _cimg_load_inr_case(0,0,8,unsigned char);
- _cimg_load_inr_case(0,1,8,char);
- _cimg_load_inr_case(0,0,16,unsigned short);
- _cimg_load_inr_case(0,1,16,short);
- _cimg_load_inr_case(0,0,32,unsigned int);
- _cimg_load_inr_case(0,1,32,int);
- _cimg_load_inr_case(1,0,32,float);
- _cimg_load_inr_case(1,1,32,float);
- _cimg_load_inr_case(1,0,64,double);
- _cimg_load_inr_case(1,1,64,double);
- if (!loaded) {
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimg_instance
- "load_inr(): Unknown pixel type defined in file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- }
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Load image from a EXR file.
- /**
- \param filename Filename, as a C-string.
- **/
- CImg<T>& load_exr(const char *const filename) {
- if (!filename)
- throw CImgArgumentException(_cimg_instance
- "load_exr(): Specified filename is (null).",
- cimg_instance);
- #if defined(cimg_use_openexr)
- Imf::RgbaInputFile file(filename);
- Imath::Box2i dw = file.dataWindow();
- const int
- inwidth = dw.max.x - dw.min.x + 1,
- inheight = dw.max.y - dw.min.y + 1;
- Imf::Array2D<Imf::Rgba> pixels;
- pixels.resizeErase(inheight,inwidth);
- file.setFrameBuffer(&pixels[0][0] - dw.min.x - dw.min.y*inwidth, 1, inwidth);
- file.readPixels(dw.min.y, dw.max.y);
- assign(inwidth,inheight,1,4);
- T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3);
- cimg_forXY(*this,x,y) {
- *(ptr_r++) = (T)pixels[y][x].r;
- *(ptr_g++) = (T)pixels[y][x].g;
- *(ptr_b++) = (T)pixels[y][x].b;
- *(ptr_a++) = (T)pixels[y][x].a;
- }
- return *this;
- #elif defined(cimg_use_tinyexr)
- float *res;
- const char *err = 0;
- int width = 0, height = 0;
- const int ret = LoadEXR(&res,&width,&height,filename,&err);
- if (ret) throw CImgIOException(_cimg_instance
- "load_exr(): Unable to load EXR file '%s'.",
- cimg_instance,filename);
- CImg<floatT>(res,4,width,height,1,true).get_permute_axes("yzcx").move_to(*this);
- std::free(res);
- return *this;
- #else
- return load_other(filename);
- #endif
- }
- //! Load image from a EXR file \newinstance.
- static CImg<T> get_load_exr(const char *const filename) {
- return CImg<T>().load_exr(filename);
- }
- //! Load image from a PANDORE-5 file.
- /**
- \param filename Filename, as a C-string.
- **/
- CImg<T>& load_pandore(const char *const filename) {
- return _load_pandore(0,filename);
- }
- //! Load image from a PANDORE-5 file \newinstance.
- static CImg<T> get_load_pandore(const char *const filename) {
- return CImg<T>().load_pandore(filename);
- }
- //! Load image from a PANDORE-5 file \overloading.
- CImg<T>& load_pandore(std::FILE *const file) {
- return _load_pandore(file,0);
- }
- //! Load image from a PANDORE-5 file \newinstance.
- static CImg<T> get_load_pandore(std::FILE *const file) {
- return CImg<T>().load_pandore(file);
- }
- CImg<T>& _load_pandore(std::FILE *const file, const char *const filename) {
- #define __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,ndim,stype) \
- cimg::fread(dims,nbdim,nfile); \
- if (endian) cimg::invert_endianness(dims,nbdim); \
- if ((ulongT)nwidth*nheight*ndepth*ndim>fsiz) \
- throw CImgIOException(_cimg_instance \
- "load_pandore(): File size %lu for filename '%s' does not match "\
- "encoded image dimensions (%d,%d,%d,%d).",\
- cimg_instance,\
- (long)fsiz,filename?filename:"(FILE*)",\
- (int)nwidth,(int)nheight,(int)ndepth,(int)ndim); \
- assign(nwidth,nheight,ndepth,ndim); \
- const size_t siz = size(); \
- stype *buffer = new stype[siz]; \
- cimg::fread(buffer,siz,nfile); \
- if (endian) cimg::invert_endianness(buffer,siz); \
- T *ptrd = _data; \
- cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); \
- buffer-=siz; \
- delete[] buffer
- #define _cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1,stype2,stype3,ltype) { \
- if (sizeof(stype1)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1); } \
- else if (sizeof(stype2)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype2); } \
- else if (sizeof(stype3)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype3); } \
- else throw CImgIOException(_cimg_instance \
- "load_pandore(): Unknown pixel datatype in file '%s'.", \
- cimg_instance, \
- filename?filename:"(FILE*)"); }
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "load_pandore(): Specified filename is (null).",
- cimg_instance);
- const ulongT fsiz = file?(ulongT)cimg_max_buf_size:(ulongT)cimg::fsize(filename);
- std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
- CImg<charT> header(32);
- cimg::fread(header._data,12,nfile);
- if (cimg::strncasecmp("PANDORE",header,7)) {
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimg_instance
- "load_pandore(): PANDORE header not found in file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- }
- unsigned int imageid, dims[8] = {};
- int ptbuf[4] = {};
- cimg::fread(&imageid,1,nfile);
- const bool endian = imageid>255;
- if (endian) cimg::invert_endianness(imageid);
- cimg::fread(header._data,20,nfile);
- switch (imageid) {
- case 2 : _cimg_load_pandore_case(2,dims[1],1,1,1,unsigned char,unsigned char,unsigned char,1); break;
- case 3 : _cimg_load_pandore_case(2,dims[1],1,1,1,long,int,short,4); break;
- case 4 : _cimg_load_pandore_case(2,dims[1],1,1,1,double,float,float,4); break;
- case 5 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,unsigned char,unsigned char,unsigned char,1); break;
- case 6 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,long,int,short,4); break;
- case 7 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,double,float,float,4); break;
- case 8 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,unsigned char,unsigned char,unsigned char,1); break;
- case 9 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,long,int,short,4); break;
- case 10 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,double,float,float,4); break;
- case 11 : { // Region 1D
- cimg::fread(dims,3,nfile);
- if (endian) cimg::invert_endianness(dims,3);
- assign(dims[1],1,1,1);
- const unsigned siz = size();
- if (dims[2]<256) {
- unsigned char *buffer = new unsigned char[siz];
- cimg::fread(buffer,siz,nfile);
- T *ptrd = _data;
- cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
- buffer-=siz;
- delete[] buffer;
- } else {
- if (dims[2]<65536) {
- unsigned short *buffer = new unsigned short[siz];
- cimg::fread(buffer,siz,nfile);
- if (endian) cimg::invert_endianness(buffer,siz);
- T *ptrd = _data;
- cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
- buffer-=siz;
- delete[] buffer;
- } else {
- unsigned int *buffer = new unsigned int[siz];
- cimg::fread(buffer,siz,nfile);
- if (endian) cimg::invert_endianness(buffer,siz);
- T *ptrd = _data;
- cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
- buffer-=siz;
- delete[] buffer;
- }
- }
- }
- break;
- case 12 : { // Region 2D
- cimg::fread(dims,4,nfile);
- if (endian) cimg::invert_endianness(dims,4);
- assign(dims[2],dims[1],1,1);
- const size_t siz = size();
- if (dims[3]<256) {
- unsigned char *buffer = new unsigned char[siz];
- cimg::fread(buffer,siz,nfile);
- T *ptrd = _data;
- cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
- buffer-=siz;
- delete[] buffer;
- } else {
- if (dims[3]<65536) {
- unsigned short *buffer = new unsigned short[siz];
- cimg::fread(buffer,siz,nfile);
- if (endian) cimg::invert_endianness(buffer,siz);
- T *ptrd = _data;
- cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
- buffer-=siz;
- delete[] buffer;
- } else {
- unsigned int *buffer = new unsigned int[siz];
- cimg::fread(buffer,siz,nfile);
- if (endian) cimg::invert_endianness(buffer,siz);
- T *ptrd = _data;
- cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
- buffer-=siz;
- delete[] buffer;
- }
- }
- }
- break;
- case 13 : { // Region 3D
- cimg::fread(dims,5,nfile);
- if (endian) cimg::invert_endianness(dims,5);
- assign(dims[3],dims[2],dims[1],1);
- const size_t siz = size();
- if (dims[4]<256) {
- unsigned char *buffer = new unsigned char[siz];
- cimg::fread(buffer,siz,nfile);
- T *ptrd = _data;
- cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
- buffer-=siz;
- delete[] buffer;
- } else {
- if (dims[4]<65536) {
- unsigned short *buffer = new unsigned short[siz];
- cimg::fread(buffer,siz,nfile);
- if (endian) cimg::invert_endianness(buffer,siz);
- T *ptrd = _data;
- cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
- buffer-=siz;
- delete[] buffer;
- } else {
- unsigned int *buffer = new unsigned int[siz];
- cimg::fread(buffer,siz,nfile);
- if (endian) cimg::invert_endianness(buffer,siz);
- T *ptrd = _data;
- cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
- buffer-=siz;
- delete[] buffer;
- }
- }
- }
- break;
- case 16 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,unsigned char,unsigned char,unsigned char,1); break;
- case 17 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,long,int,short,4); break;
- case 18 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,double,float,float,4); break;
- case 19 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,unsigned char,unsigned char,unsigned char,1); break;
- case 20 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,long,int,short,4); break;
- case 21 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,double,float,float,4); break;
- case 22 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
- case 23 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],long,int,short,4); break;
- case 24 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
- case 25 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],double,float,float,4); break;
- case 26 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
- case 27 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],long,int,short,4); break;
- case 28 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
- case 29 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],double,float,float,4); break;
- case 30 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned char,unsigned char,unsigned char,1);
- break;
- case 31 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],long,int,short,4); break;
- case 32 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned long,unsigned int,unsigned short,4);
- break;
- case 33 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],double,float,float,4); break;
- case 34 : { // Points 1D
- cimg::fread(ptbuf,1,nfile);
- if (endian) cimg::invert_endianness(ptbuf,1);
- assign(1); (*this)(0) = (T)ptbuf[0];
- } break;
- case 35 : { // Points 2D
- cimg::fread(ptbuf,2,nfile);
- if (endian) cimg::invert_endianness(ptbuf,2);
- assign(2); (*this)(0) = (T)ptbuf[1]; (*this)(1) = (T)ptbuf[0];
- } break;
- case 36 : { // Points 3D
- cimg::fread(ptbuf,3,nfile);
- if (endian) cimg::invert_endianness(ptbuf,3);
- assign(3); (*this)(0) = (T)ptbuf[2]; (*this)(1) = (T)ptbuf[1]; (*this)(2) = (T)ptbuf[0];
- } break;
- default :
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimg_instance
- "load_pandore(): Unable to load data with ID_type %u in file '%s'.",
- cimg_instance,
- imageid,filename?filename:"(FILE*)");
- }
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Load image from a PAR-REC (Philips) file.
- /**
- \param filename Filename, as a C-string.
- \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
- \param align Appending alignment.
- **/
- CImg<T>& load_parrec(const char *const filename, const char axis='c', const float align=0) {
- CImgList<T> list;
- list.load_parrec(filename);
- if (list._width==1) return list[0].move_to(*this);
- return assign(list.get_append(axis,align));
- }
- //! Load image from a PAR-REC (Philips) file \newinstance.
- static CImg<T> get_load_parrec(const char *const filename, const char axis='c', const float align=0) {
- return CImg<T>().load_parrec(filename,axis,align);
- }
- //! Load image from a raw binary file.
- /**
- \param filename Filename, as a C-string.
- \param size_x Width of the image buffer.
- \param size_y Height of the image buffer.
- \param size_z Depth of the image buffer.
- \param size_c Spectrum of the image buffer.
- \param is_multiplexed Tells if the image values are multiplexed along the C-axis.
- \param invert_endianness Tells if the endianness of the image buffer must be inverted.
- \param offset Starting offset of the read in the specified file.
- **/
- CImg<T>& load_raw(const char *const filename,
- const unsigned int size_x=0, const unsigned int size_y=1,
- const unsigned int size_z=1, const unsigned int size_c=1,
- const bool is_multiplexed=false, const bool invert_endianness=false,
- const ulongT offset=0) {
- return _load_raw(0,filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
- }
- //! Load image from a raw binary file \newinstance.
- static CImg<T> get_load_raw(const char *const filename,
- const unsigned int size_x=0, const unsigned int size_y=1,
- const unsigned int size_z=1, const unsigned int size_c=1,
- const bool is_multiplexed=false, const bool invert_endianness=false,
- const ulongT offset=0) {
- return CImg<T>().load_raw(filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
- }
- //! Load image from a raw binary file \overloading.
- CImg<T>& load_raw(std::FILE *const file,
- const unsigned int size_x=0, const unsigned int size_y=1,
- const unsigned int size_z=1, const unsigned int size_c=1,
- const bool is_multiplexed=false, const bool invert_endianness=false,
- const ulongT offset=0) {
- return _load_raw(file,0,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
- }
- //! Load image from a raw binary file \newinstance.
- static CImg<T> get_load_raw(std::FILE *const file,
- const unsigned int size_x=0, const unsigned int size_y=1,
- const unsigned int size_z=1, const unsigned int size_c=1,
- const bool is_multiplexed=false, const bool invert_endianness=false,
- const ulongT offset=0) {
- return CImg<T>().load_raw(file,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
- }
- CImg<T>& _load_raw(std::FILE *const file, const char *const filename,
- const unsigned int size_x, const unsigned int size_y,
- const unsigned int size_z, const unsigned int size_c,
- const bool is_multiplexed, const bool invert_endianness,
- const ulongT offset) {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "load_raw(): Specified filename is (null).",
- cimg_instance);
- if (cimg::is_directory(filename))
- throw CImgArgumentException(_cimg_instance
- "load_raw(): Specified filename '%s' is a directory.",
- cimg_instance,filename);
- const bool is_bool = pixel_type()==cimg::type<bool>::string();
- ulongT siz = (ulongT)size_x*size_y*size_z*size_c;
- unsigned int
- _size_x = size_x,
- _size_y = size_y,
- _size_z = size_z,
- _size_c = size_c;
- std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
- if (!siz) { // Retrieve file size
- const longT fpos = cimg::ftell(nfile);
- if (fpos<0) throw CImgArgumentException(_cimg_instance
- "load_raw(): Cannot determine size of input file '%s'.",
- cimg_instance,filename?filename:"(FILE*)");
- cimg::fseek(nfile,0,SEEK_END);
- siz = (ulongT)cimg::ftell(nfile);
- if (!is_bool) { siz/=sizeof(T); _size_y = (unsigned int)siz; }
- else _size_y = (unsigned int)(siz*8);
- _size_x = _size_z = _size_c = 1;
- cimg::fseek(nfile,fpos,SEEK_SET);
- }
- cimg::fseek(nfile,(longT)offset,SEEK_SET);
- assign(_size_x,_size_y,_size_z,_size_c,0);
- if (is_bool) { // Boolean data (bitwise)
- unsigned char *const buf = new unsigned char[siz];
- cimg::fread(buf,siz,nfile);
- _uchar2bool(buf,siz,is_multiplexed);
- delete[] buf;
- } else { // Non-boolean data
- if (siz && (!is_multiplexed || size_c==1)) { // Non-multiplexed
- cimg::fread(_data,siz,nfile);
- if (invert_endianness) cimg::invert_endianness(_data,siz);
- } else if (siz) { // Multiplexed
- CImg<T> buf(1,1,1,_size_c);
- cimg_forXYZ(*this,x,y,z) {
- cimg::fread(buf._data,_size_c,nfile);
- if (invert_endianness) cimg::invert_endianness(buf._data,_size_c);
- set_vector_at(buf,x,y,z);
- }
- }
- }
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Load image sequence from a YUV file.
- /**
- \param filename Filename, as a C-string.
- \param size_x Width of the frames.
- \param size_y Height of the frames.
- \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
- \param first_frame Index of the first frame to read.
- \param last_frame Index of the last frame to read.
- \param step_frame Step value for frame reading.
- \param yuv2rgb Tells if the YUV to RGB transform must be applied.
- \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
- **/
- CImg<T>& load_yuv(const char *const filename,
- const unsigned int size_x, const unsigned int size_y=1,
- const unsigned int chroma_subsampling=444,
- const unsigned int first_frame=0, const unsigned int last_frame=~0U,
- const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
- return get_load_yuv(filename,size_x,size_y,chroma_subsampling,
- first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this);
- }
- //! Load image sequence from a YUV file \newinstance.
- static CImg<T> get_load_yuv(const char *const filename,
- const unsigned int size_x, const unsigned int size_y=1,
- const unsigned int chroma_subsampling=444,
- const unsigned int first_frame=0, const unsigned int last_frame=~0U,
- const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
- return CImgList<T>().load_yuv(filename,size_x,size_y,chroma_subsampling,
- first_frame,last_frame,step_frame,yuv2rgb).get_append(axis);
- }
- //! Load image sequence from a YUV file \overloading.
- CImg<T>& load_yuv(std::FILE *const file,
- const unsigned int size_x, const unsigned int size_y=1,
- const unsigned int chroma_subsampling=444,
- const unsigned int first_frame=0, const unsigned int last_frame=~0U,
- const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
- return get_load_yuv(file,size_x,size_y,chroma_subsampling,
- first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this);
- }
- //! Load image sequence from a YUV file \newinstance.
- static CImg<T> get_load_yuv(std::FILE *const file,
- const unsigned int size_x, const unsigned int size_y=1,
- const unsigned int chroma_subsampling=444,
- const unsigned int first_frame=0, const unsigned int last_frame=~0U,
- const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
- return CImgList<T>().load_yuv(file,size_x,size_y,chroma_subsampling,
- first_frame,last_frame,step_frame,yuv2rgb).get_append(axis);
- }
- //! Load 3D object from a .OFF file.
- /**
- \param[out] primitives Primitives data of the 3D object.
- \param[out] colors Colors data of the 3D object.
- \param filename Filename, as a C-string.
- **/
- template<typename tf, typename tc>
- CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) {
- return _load_off(primitives,colors,0,filename);
- }
- //! Load 3D object from a .OFF file \newinstance.
- template<typename tf, typename tc>
- static CImg<T> get_load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) {
- return CImg<T>().load_off(primitives,colors,filename);
- }
- //! Load 3D object from a .OFF file \overloading.
- template<typename tf, typename tc>
- CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) {
- return _load_off(primitives,colors,file,0);
- }
- //! Load 3D object from a .OFF file \newinstance.
- template<typename tf, typename tc>
- static CImg<T> get_load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) {
- return CImg<T>().load_off(primitives,colors,file);
- }
- template<typename tf, typename tc>
- CImg<T>& _load_off(CImgList<tf>& primitives, CImgList<tc>& colors,
- std::FILE *const file, const char *const filename) {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "load_off(): Specified filename is (null).",
- cimg_instance);
- std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
- unsigned int nb_points = 0, nb_primitives = 0, nb_read = 0;
- CImg<charT> line(256); *line = 0;
- int err;
- // Skip comments, and read magic string OFF
- do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#'));
- if (cimg::strncasecmp(line,"OFF",3) && cimg::strncasecmp(line,"COFF",4)) {
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimg_instance
- "load_off(): OFF header not found in file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- }
- do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#'));
- if ((err = cimg_sscanf(line,"%u%u%*[^\n] ",&nb_points,&nb_primitives))!=2) {
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimg_instance
- "load_off(): Invalid number of vertices or primitives specified in file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- }
- // Read points data
- assign(nb_points,3);
- float X = 0, Y = 0, Z = 0;
- cimg_forX(*this,l) {
- do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#'));
- if ((err = cimg_sscanf(line,"%f%f%f%*[^\n] ",&X,&Y,&Z))!=3) {
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimg_instance
- "load_off(): Failed to read vertex %u/%u in file '%s'.",
- cimg_instance,
- l + 1,nb_points,filename?filename:"(FILE*)");
- }
- (*this)(l,0) = (T)X; (*this)(l,1) = (T)Y; (*this)(l,2) = (T)Z;
- }
- // Read primitive data
- primitives.assign();
- colors.assign();
- bool stop_flag = false;
- while (!stop_flag) {
- float c0 = 0.7f, c1 = 0.7f, c2 = 0.7f;
- unsigned int prim = 0, i0 = 0, i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0, i7 = 0;
- *line = 0;
- if ((err = std::fscanf(nfile,"%u",&prim))!=1) stop_flag = true;
- else {
- ++nb_read;
- switch (prim) {
- case 1 : {
- if ((err = std::fscanf(nfile,"%u%255[^\n] ",&i0,line._data))<2) {
- cimg::warn(_cimg_instance
- "load_off(): Failed to read primitive %u/%u from file '%s'.",
- cimg_instance,
- nb_read,nb_primitives,filename?filename:"(FILE*)");
- err = std::fscanf(nfile,"%*[^\n] ");
- } else {
- err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
- CImg<tf>::vector(i0).move_to(primitives);
- CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
- }
- } break;
- case 2 : {
- if ((err = std::fscanf(nfile,"%u%u%255[^\n] ",&i0,&i1,line._data))<2) {
- cimg::warn(_cimg_instance
- "load_off(): Failed to read primitive %u/%u from file '%s'.",
- cimg_instance,
- nb_read,nb_primitives,filename?filename:"(FILE*)");
- err = std::fscanf(nfile,"%*[^\n] ");
- } else {
- err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
- CImg<tf>::vector(i0,i1).move_to(primitives);
- CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
- }
- } break;
- case 3 : {
- if ((err = std::fscanf(nfile,"%u%u%u%255[^\n] ",&i0,&i1,&i2,line._data))<3) {
- cimg::warn(_cimg_instance
- "load_off(): Failed to read primitive %u/%u from file '%s'.",
- cimg_instance,
- nb_read,nb_primitives,filename?filename:"(FILE*)");
- err = std::fscanf(nfile,"%*[^\n] ");
- } else {
- err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
- CImg<tf>::vector(i0,i2,i1).move_to(primitives);
- CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
- }
- } break;
- case 4 : {
- if ((err = std::fscanf(nfile,"%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,line._data))<4) {
- cimg::warn(_cimg_instance
- "load_off(): Failed to read primitive %u/%u from file '%s'.",
- cimg_instance,
- nb_read,nb_primitives,filename?filename:"(FILE*)");
- err = std::fscanf(nfile,"%*[^\n] ");
- } else {
- err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
- CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
- CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
- }
- } break;
- case 5 : {
- if ((err = std::fscanf(nfile,"%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,line._data))<5) {
- cimg::warn(_cimg_instance
- "load_off(): Failed to read primitive %u/%u from file '%s'.",
- cimg_instance,
- nb_read,nb_primitives,filename?filename:"(FILE*)");
- err = std::fscanf(nfile,"%*[^\n] ");
- } else {
- err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
- CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
- CImg<tf>::vector(i0,i4,i3).move_to(primitives);
- colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
- ++nb_primitives;
- }
- } break;
- case 6 : {
- if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,line._data))<6) {
- cimg::warn(_cimg_instance
- "load_off(): Failed to read primitive %u/%u from file '%s'.",
- cimg_instance,
- nb_read,nb_primitives,filename?filename:"(FILE*)");
- err = std::fscanf(nfile,"%*[^\n] ");
- } else {
- err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
- CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
- CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
- colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
- ++nb_primitives;
- }
- } break;
- case 7 : {
- if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,line._data))<7) {
- cimg::warn(_cimg_instance
- "load_off(): Failed to read primitive %u/%u from file '%s'.",
- cimg_instance,
- nb_read,nb_primitives,filename?filename:"(FILE*)");
- err = std::fscanf(nfile,"%*[^\n] ");
- } else {
- err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
- CImg<tf>::vector(i0,i4,i3,i1).move_to(primitives);
- CImg<tf>::vector(i0,i6,i5,i4).move_to(primitives);
- CImg<tf>::vector(i3,i2,i1).move_to(primitives);
- colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
- ++(++nb_primitives);
- }
- } break;
- case 8 : {
- if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,&i7,line._data))<7) {
- cimg::warn(_cimg_instance
- "load_off(): Failed to read primitive %u/%u from file '%s'.",
- cimg_instance,
- nb_read,nb_primitives,filename?filename:"(FILE*)");
- err = std::fscanf(nfile,"%*[^\n] ");
- } else {
- err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
- CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
- CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
- CImg<tf>::vector(i0,i7,i6,i5).move_to(primitives);
- colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
- ++(++nb_primitives);
- }
- } break;
- default :
- cimg::warn(_cimg_instance
- "load_off(): Failed to read primitive %u/%u (%u vertices) from file '%s'.",
- cimg_instance,
- nb_read,nb_primitives,prim,filename?filename:"(FILE*)");
- err = std::fscanf(nfile,"%*[^\n] ");
- }
- }
- }
- if (!file) cimg::fclose(nfile);
- if (primitives._width!=nb_primitives)
- cimg::warn(_cimg_instance
- "load_off(): Only %u/%u primitives read from file '%s'.",
- cimg_instance,
- primitives._width,nb_primitives,filename?filename:"(FILE*)");
- return *this;
- }
- //! Load image sequence from a video file, using OpenCV library.
- /**
- \param filename Filename, as a C-string.
- \param first_frame Index of the first frame to read.
- \param last_frame Index of the last frame to read.
- \param step_frame Step value for frame reading.
- \param axis Alignment axis.
- \param align Appending alignment.
- **/
- CImg<T>& load_video(const char *const filename,
- const unsigned int first_frame=0, const unsigned int last_frame=~0U,
- const unsigned int step_frame=1,
- const char axis='z', const float align=0) {
- return get_load_video(filename,first_frame,last_frame,step_frame,axis,align).move_to(*this);
- }
- //! Load image sequence from a video file, using OpenCV library \newinstance.
- static CImg<T> get_load_video(const char *const filename,
- const unsigned int first_frame=0, const unsigned int last_frame=~0U,
- const unsigned int step_frame=1,
- const char axis='z', const float align=0) {
- return CImgList<T>().load_video(filename,first_frame,last_frame,step_frame).get_append(axis,align);
- }
- //! Load image sequence using FFMPEG's external tool 'ffmpeg'.
- /**
- \param filename Filename, as a C-string.
- \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
- \param align Appending alignment.
- **/
- CImg<T>& load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) {
- return get_load_ffmpeg_external(filename,axis,align).move_to(*this);
- }
- //! Load image sequence using FFMPEG's external tool 'ffmpeg' \newinstance.
- static CImg<T> get_load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) {
- return CImgList<T>().load_ffmpeg_external(filename).get_append(axis,align);
- }
- //! Load gif file, using Imagemagick or GraphicsMagicks's external tools.
- /**
- \param filename Filename, as a C-string.
- \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
- \param align Appending alignment.
- **/
- CImg<T>& load_gif_external(const char *const filename,
- const char axis='z', const float align=0) {
- return get_load_gif_external(filename,axis,align).move_to(*this);
- }
- //! Load gif file, using ImageMagick or GraphicsMagick's external tool 'convert' \newinstance.
- static CImg<T> get_load_gif_external(const char *const filename,
- const char axis='z', const float align=0) {
- return CImgList<T>().load_gif_external(filename).get_append(axis,align);
- }
- //! Load image from a HEIC file.
- /**
- \param filename Filename, as a C-string.
- **/
- CImg<T>& load_heif(const char *const filename) {
- return _load_heif(filename);
- }
- //! Load image from a HEIC file \newinstance.
- static CImg<T> get_load_heif(const char *const filename) {
- return CImg<T>().load_heif(filename);
- }
- CImg<T>& _load_heif(const char *const filename) {
- #ifndef cimg_use_heif
- return load_other(filename);
- #else
- try {
- heif::Context ctx;
- ctx.read_from_file(filename);
- heif::ImageHandle handle = ctx.get_primary_image_handle();
- const heif::Image image =
- handle.decode_image(heif_colorspace_RGB,handle.has_alpha_channel()?heif_chroma_interleaved_RGBA:
- heif_chroma_interleaved_RGB);
- const int
- W = image.get_width(heif_channel_interleaved),
- H = image.get_height(heif_channel_interleaved),
- S = handle.has_alpha_channel()?4:3;
- assign(W,H,1,S);
- int stride;
- const unsigned char *const buffer = image.get_plane(heif_channel_interleaved,&stride);
- T *ptr_r = _data, *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = S>3?data(0,0,0,3):0;
- cimg_forY(*this,y) {
- const unsigned char *ptrs = buffer + y*stride;
- if (ptr_a) cimg_forX(*this,x) { // RGBA
- *(ptr_r++) = (T)*(ptrs++);
- *(ptr_g++) = (T)*(ptrs++);
- *(ptr_b++) = (T)*(ptrs++);
- *(ptr_a++) = (T)*(ptrs++);
- }
- else cimg_forX(*this,x) { // RGB
- *(ptr_r++) = (T)*(ptrs++);
- *(ptr_g++) = (T)*(ptrs++);
- *(ptr_b++) = (T)*(ptrs++);
- }
- }
- } catch (const heif::Error& e) {
- throw CImgInstanceException(_cimg_instance
- "load_heif(): Unable to decode image: %s",
- cimg_instance,
- e.get_message().c_str());
- } catch (...) {
- throw;
- }
- return *this;
- #endif
- }
- //! Load image using GraphicsMagick's external tool 'gm'.
- /**
- \param filename Filename, as a C-string.
- **/
- CImg<T>& load_graphicsmagick_external(const char *const filename) {
- if (!filename)
- throw CImgArgumentException(_cimg_instance
- "load_graphicsmagick_external(): Specified filename is (null).",
- cimg_instance);
- cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
- CImg<charT> command(1024), filename_tmp(256);
- std::FILE *file = 0;
- const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
- #if cimg_OS==1
- if (!cimg::system("which gm")) {
- cimg_snprintf(command,command._width,"%s convert \"%s\" %s:-",
- cimg::graphicsmagick_path(),
- s_filename.data(),
- #ifdef cimg_use_png
- "png"
- #else
- "pnm"
- #endif
- );
- file = popen(command,"r");
- if (file) {
- const unsigned int omode = cimg::exception_mode();
- cimg::exception_mode(0);
- try {
- #ifdef cimg_use_png
- load_png(file);
- #else
- load_pnm(file);
- #endif
- } catch (...) {
- pclose(file);
- cimg::exception_mode(omode);
- throw CImgIOException(_cimg_instance
- "load_graphicsmagick_external(): Failed to load file '%s' "
- "with external command 'gm'.",
- cimg_instance,
- filename);
- }
- pclose(file);
- return *this;
- }
- }
- #endif
- do {
- cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
- cimg::temporary_path(),
- cimg_file_separator,
- cimg::filenamerand(),
- #ifdef cimg_use_png
- "png"
- #else
- "pnm"
- #endif
- );
- if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
- } while (file);
- cimg_snprintf(command,command._width,"\"%s\" convert \"%s\" \"%s\"",
- cimg::graphicsmagick_path(),
- s_filename.data(),
- CImg<charT>::string(filename_tmp)._system_strescape().data());
- cimg::system(command,cimg::graphicsmagick_path());
- if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
- cimg::fclose(cimg::fopen(filename,"r"));
- throw CImgIOException(_cimg_instance
- "load_graphicsmagick_external(): Failed to load file '%s' with external command 'gm'.",
- cimg_instance,
- filename);
- } else cimg::fclose(file);
- #ifdef cimg_use_png
- load_png(filename_tmp);
- #else
- load_pnm(filename_tmp);
- #endif
- std::remove(filename_tmp);
- return *this;
- }
- //! Load image using GraphicsMagick's external tool 'gm' \newinstance.
- static CImg<T> get_load_graphicsmagick_external(const char *const filename) {
- return CImg<T>().load_graphicsmagick_external(filename);
- }
- //! Load gzipped image file, using external tool 'gunzip'.
- /**
- \param filename Filename, as a C-string.
- **/
- CImg<T>& load_gzip_external(const char *const filename) {
- if (!filename)
- throw CImgIOException(_cimg_instance
- "load_gzip_external(): Specified filename is (null).",
- cimg_instance);
- cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
- CImg<charT> command(1024), filename_tmp(256), body(256);
- const char
- *const ext = cimg::split_filename(filename,body),
- *const ext2 = cimg::split_filename(body,0);
- std::FILE *file = 0;
- do {
- if (!cimg::strcasecmp(ext,"gz")) {
- if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
- cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
- else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
- cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
- } else {
- if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
- cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
- else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
- cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
- }
- if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
- } while (file);
- cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"",
- cimg::gunzip_path(),
- CImg<charT>::string(filename)._system_strescape().data(),
- CImg<charT>::string(filename_tmp)._system_strescape().data());
- cimg::system(command);
- if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
- cimg::fclose(cimg::fopen(filename,"r"));
- throw CImgIOException(_cimg_instance
- "load_gzip_external(): Failed to load file '%s' with external command 'gunzip'.",
- cimg_instance,
- filename);
- } else cimg::fclose(file);
- load(filename_tmp);
- std::remove(filename_tmp);
- return *this;
- }
- //! Load gzipped image file, using external tool 'gunzip' \newinstance.
- static CImg<T> get_load_gzip_external(const char *const filename) {
- return CImg<T>().load_gzip_external(filename);
- }
- //! Load image using ImageMagick's external tool 'convert'.
- /**
- \param filename Filename, as a C-string.
- **/
- CImg<T>& load_imagemagick_external(const char *const filename) {
- if (!filename)
- throw CImgArgumentException(_cimg_instance
- "load_imagemagick_external(): Specified filename is (null).",
- cimg_instance);
- cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
- CImg<charT> command(1024), filename_tmp(256);
- std::FILE *file = 0;
- const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
- #if cimg_OS==1
- if (!cimg::system("which convert")) {
- cimg_snprintf(command,command._width,"%s%s \"%s\" %s:-",
- cimg::imagemagick_path(),
- !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"",
- s_filename.data(),
- #ifdef cimg_use_png
- "png"
- #else
- "pnm"
- #endif
- );
- file = popen(command,"r");
- if (file) {
- const unsigned int omode = cimg::exception_mode();
- cimg::exception_mode(0);
- try {
- #ifdef cimg_use_png
- load_png(file);
- #else
- load_pnm(file);
- #endif
- } catch (...) {
- pclose(file);
- cimg::exception_mode(omode);
- throw CImgIOException(_cimg_instance
- "load_imagemagick_external(): Failed to load file '%s' with "
- "external command 'magick/convert'.",
- cimg_instance,
- filename);
- }
- pclose(file);
- return *this;
- }
- }
- #endif
- do {
- cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
- cimg::temporary_path(),
- cimg_file_separator,
- cimg::filenamerand(),
- #ifdef cimg_use_png
- "png"
- #else
- "pnm"
- #endif
- );
- if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
- } while (file);
- cimg_snprintf(command,command._width,"\"%s\"%s \"%s\" \"%s\"",
- cimg::imagemagick_path(),
- !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"",
- s_filename.data(),
- CImg<charT>::string(filename_tmp)._system_strescape().data());
- cimg::system(command,cimg::imagemagick_path());
- if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
- cimg::fclose(cimg::fopen(filename,"r"));
- throw CImgIOException(_cimg_instance
- "load_imagemagick_external(): Failed to load file '%s' with "
- "external command 'magick/convert'.",
- cimg_instance,
- filename);
- } else cimg::fclose(file);
- #ifdef cimg_use_png
- load_png(filename_tmp);
- #else
- load_pnm(filename_tmp);
- #endif
- std::remove(filename_tmp);
- return *this;
- }
- //! Load image using ImageMagick's external tool 'convert' \newinstance.
- static CImg<T> get_load_imagemagick_external(const char *const filename) {
- return CImg<T>().load_imagemagick_external(filename);
- }
- //! Load image from a DICOM file, using Medcon's external tool 'medcon'.
- /**
- \param filename Filename, as a C-string.
- **/
- CImg<T>& load_medcon_external(const char *const filename) {
- if (!filename)
- throw CImgArgumentException(_cimg_instance
- "load_medcon_external(): Specified filename is (null).",
- cimg_instance);
- cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
- CImg<charT> command(1024), filename_tmp(256), body(256);
- cimg::fclose(cimg::fopen(filename,"r"));
- std::FILE *file = 0;
- do {
- cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand());
- if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
- } while (file);
- cimg_snprintf(command,command._width,"\"%s\" -w -c anlz -o \"%s\" -f \"%s\"",
- cimg::medcon_path(),
- CImg<charT>::string(filename_tmp)._system_strescape().data(),
- CImg<charT>::string(filename)._system_strescape().data());
- cimg::system(command,cimg::medcon_path());
- cimg::split_filename(filename_tmp,body);
- cimg_snprintf(command,command._width,"%s.hdr",body._data);
- file = cimg::std_fopen(command,"rb");
- if (!file) {
- cimg_snprintf(command,command._width,"m000-%s.hdr",body._data);
- file = cimg::std_fopen(command,"rb");
- if (!file) {
- throw CImgIOException(_cimg_instance
- "load_medcon_external(): Failed to load file '%s' with external command 'medcon'.",
- cimg_instance,
- filename);
- }
- }
- cimg::fclose(file);
- load_analyze(command);
- std::remove(command);
- cimg::split_filename(command,body);
- cimg_snprintf(command,command._width,"%s.img",body._data);
- std::remove(command);
- return *this;
- }
- //! Load image from a DICOM file, using Medcon's external tool 'medcon' \newinstance.
- static CImg<T> get_load_medcon_external(const char *const filename) {
- return CImg<T>().load_medcon_external(filename);
- }
- //! Load image from a .pdf file.
- /**
- \param filename Filename, as a C-string.
- \param resolution Image resolution.
- **/
- CImg<T>& load_pdf_external(const char *const filename, const unsigned int resolution=400) {
- if (!filename)
- throw CImgArgumentException(_cimg_instance
- "load_pdf_external(): Specified filename is (null).",
- cimg_instance);
- CImg<charT> command(1024), filename_tmp(256);
- std::FILE *file = 0;
- const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
- #if cimg_OS==1
- cimg_snprintf(command,command._width,"gs -q -dNOPAUSE -sDEVICE=ppmraw -o - -r%u \"%s\"",
- resolution,s_filename.data());
- file = popen(command,"r");
- if (file) {
- const unsigned int omode = cimg::exception_mode();
- cimg::exception_mode(0);
- try { load_pnm(file); } catch (...) {
- pclose(file);
- cimg::exception_mode(omode);
- throw CImgIOException(_cimg_instance
- "load_pdf_external(): Failed to load file '%s' with external command 'gs'.",
- cimg_instance,
- filename);
- }
- pclose(file);
- return *this;
- }
- #endif
- do {
- cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.ppm",
- cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
- if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
- } while (file);
- cimg_snprintf(command,command._width,"gs -q -dNOPAUSE -sDEVICE=ppmraw -o \"%s\" -r%u \"%s\"",
- CImg<charT>::string(filename_tmp)._system_strescape().data(),resolution,s_filename.data());
- cimg::system(command,"gs");
- if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
- cimg::fclose(cimg::fopen(filename,"r"));
- throw CImgIOException(_cimg_instance
- "load_pdf_external(): Failed to load file '%s' with external command 'gs'.",
- cimg_instance,
- filename);
- } else cimg::fclose(file);
- load_pnm(filename_tmp);
- std::remove(filename_tmp);
- return *this;
- }
- //! Load image from a .pdf file \newinstance.
- static CImg<T> get_load_pdf_external(const char *const filename, const unsigned int resolution=400) {
- return CImg<T>().load_pdf_external(filename,resolution);
- }
- //! Load image from a RAW Color Camera file, using external tool 'dcraw'.
- /**
- \param filename Filename, as a C-string.
- **/
- CImg<T>& load_dcraw_external(const char *const filename) {
- if (!filename)
- throw CImgArgumentException(_cimg_instance
- "load_dcraw_external(): Specified filename is (null).",
- cimg_instance);
- cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
- CImg<charT> command(1024), filename_tmp(256);
- std::FILE *file = 0;
- const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
- #if cimg_OS==1
- cimg_snprintf(command,command._width,"%s -w -4 -c \"%s\"",
- cimg::dcraw_path(),s_filename.data());
- file = popen(command,"r");
- if (file) {
- const unsigned int omode = cimg::exception_mode();
- cimg::exception_mode(0);
- try { load_pnm(file); } catch (...) {
- pclose(file);
- cimg::exception_mode(omode);
- throw CImgIOException(_cimg_instance
- "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.",
- cimg_instance,
- filename);
- }
- pclose(file);
- return *this;
- }
- #endif
- do {
- cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.ppm",
- cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
- if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
- } while (file);
- cimg_snprintf(command,command._width,"\"%s\" -w -4 -c \"%s\" > \"%s\"",
- cimg::dcraw_path(),s_filename.data(),CImg<charT>::string(filename_tmp)._system_strescape().data());
- cimg::system(command,cimg::dcraw_path());
- if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
- cimg::fclose(cimg::fopen(filename,"r"));
- throw CImgIOException(_cimg_instance
- "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.",
- cimg_instance,
- filename);
- } else cimg::fclose(file);
- load_pnm(filename_tmp);
- std::remove(filename_tmp);
- return *this;
- }
- //! Load image from a RAW Color Camera file, using external tool 'dcraw' \newinstance.
- static CImg<T> get_load_dcraw_external(const char *const filename) {
- return CImg<T>().load_dcraw_external(filename);
- }
- #ifdef cimg_use_opencv
- // Convert a continuous cv::Mat<uchar> to a CImg<uchar>.
- static CImg<ucharT> _cvmat2cimg(const cv::Mat &src) {
- if (src.channels()==1) return CImg<ucharT>(src.ptr(),src.cols,src.rows,1,1);
- else if (src.channels()==3) { // BGR
- CImg<ucharT> res(src.cols,src.rows,1,src.channels());
- const unsigned char *ptrs = src.ptr();
- unsigned char *pR = res.data(), *pG = res.data(0,0,0,1), *pB = res.data(0,0,0,2);
- cimg_forXY(res,x,y) { *(pB++) = *(ptrs++); *(pG++) = *(ptrs++); *(pR++) = *(ptrs++); }
- return res;
- }
- return CImg<ucharT>(src.ptr(),src.channels(),src.cols,src.rows,1,true).get_permute_axes("yzcx");
- }
- // Convert a CImg<T> to a cv::Mat.
- cv::Mat _cimg2cvmat() const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "_cimg2cvmat() : Instance image is empty.",
- cimg_instance);
- if (_spectrum==2)
- throw CImgInstanceException(_cimg_instance
- "_cimg2cvmat() : Invalid number of channels (should be '1' or '3+').",
- cimg_instance);
- if (_depth!=1)
- throw CImgInstanceException(_cimg_instance
- "_cimg2cvmat() : Invalid number of slices (should be '1').",
- cimg_instance);
- int mat_type = -1;
- if (pixel_type()==cimg::type<unsigned char>::string()) mat_type = CV_8UC1;
- if (pixel_type()==cimg::type<char>::string()) mat_type = CV_8SC1;
- if (pixel_type()==cimg::type<unsigned short>::string()) mat_type = CV_16UC1;
- if (pixel_type()==cimg::type<short>::string()) mat_type = CV_16SC1;
- if (pixel_type()==cimg::type<int>::string()) mat_type = CV_32SC1;
- if (pixel_type()==cimg::type<float>::string()) mat_type = CV_32FC1;
- if (pixel_type()==cimg::type<double>::string()) mat_type = CV_64FC1;
- if (mat_type<0)
- throw CImgInstanceException(_cimg_instance
- "_cvmat2cimg() : pixel type '%s' is not supported.",
- cimg_instance,pixel_type());
- cv::Mat res;
- std::vector<cv::Mat> channels(_spectrum);
- if (_spectrum>1) {
- cimg_forC(*this,c)
- channels[c] = cv::Mat(_height,_width,mat_type,_data + _width*_height*(_spectrum - 1 - c));
- cv::merge(channels,res);
- } else res = cv::Mat(_height,_width,mat_type,_data).clone();
- return res;
- }
- #endif
- //! Load image from a camera stream, using OpenCV.
- /**
- \param index Index of the camera to capture images from (from 0 to 63).
- \param capture_width Width of the desired image ('0' stands for default value).
- \param capture_height Height of the desired image ('0' stands for default value).
- \param skip_frames Number of frames to skip before the capture.
- \param release_camera Tells if the camera resource must be released at the end of the method.
- **/
- CImg<T>& load_camera(const unsigned int camera_index=0,
- const unsigned int capture_width=0, const unsigned int capture_height=0,
- const unsigned int skip_frames=0, const bool release_camera=true) {
- #ifdef cimg_use_opencv
- if (camera_index>=64)
- throw CImgArgumentException(_cimg_instance
- "load_camera(): Invalid request for camera #%u "
- "(no more than 100 cameras can be managed simultaneously).",
- cimg_instance,
- camera_index);
- static cv::VideoCapture *captures[64] = {};
- static unsigned int captures_w[64], captures_h[64];
- if (release_camera) {
- cimg::mutex(9);
- if (captures[camera_index]) captures[camera_index]->release();
- delete captures[camera_index];
- captures[camera_index] = 0;
- captures_w[camera_index] = captures_h[camera_index] = 0;
- cimg::mutex(9,0);
- return *this;
- }
- if (!captures[camera_index]) {
- cimg::mutex(9);
- captures[camera_index] = new cv::VideoCapture(camera_index);
- captures_w[camera_index] = captures_h[camera_index] = 0;
- if (!captures[camera_index]->isOpened()) {
- delete captures[camera_index];
- captures[camera_index] = 0;
- cimg::mutex(9,0);
- throw CImgIOException(_cimg_instance
- "load_camera(): Failed to initialize camera #%u.",
- cimg_instance,
- camera_index);
- }
- cimg::mutex(9,0);
- }
- cimg::mutex(9);
- if (capture_width!=captures_w[camera_index]) {
- captures[camera_index]->set(_cimg_cap_prop_frame_width,capture_width);
- captures_w[camera_index] = capture_width;
- }
- if (capture_height!=captures_h[camera_index]) {
- captures[camera_index]->set(_cimg_cap_prop_frame_height,capture_height);
- captures_h[camera_index] = capture_height;
- }
- for (unsigned int i = 0; i<skip_frames; ++i) captures[camera_index]->grab();
- cv::Mat cvimg;
- captures[camera_index]->read(cvimg);
- if (cvimg.empty()) {
- cimg::mutex(9,0);
- load_camera(camera_index,0,0,0,true); // Release camera
- throw CImgIOException(_cimg_instance
- "load_camera(): Failed to retrieve a %ux%u frame from camera #%u.",
- cimg_instance,
- capture_width,capture_height,camera_index);
- } else _cvmat2cimg(cvimg).move_to(*this);
- cimg::mutex(9,0);
- return *this;
- #else
- cimg::unused(camera_index,skip_frames,release_camera,capture_width,capture_height);
- throw CImgIOException(_cimg_instance
- "load_camera(): This function requires features from the OpenCV library "
- "('-Dcimg_use_opencv' must be defined).",
- cimg_instance);
- #endif
- }
- //! Load image from a camera stream, using OpenCV \newinstance.
- static CImg<T> get_load_camera(const unsigned int camera_index=0,
- const unsigned int capture_width=0, const unsigned int capture_height=0,
- const unsigned int skip_frames=0, const bool release_camera=true) {
- return CImg<T>().load_camera(camera_index,capture_width,capture_height,skip_frames,release_camera);
- }
- //! Load image using various non-native ways.
- /**
- \param filename Filename, as a C-string.
- **/
- CImg<T>& load_other(const char *const filename) {
- if (!filename)
- throw CImgArgumentException(_cimg_instance
- "load_other(): Specified filename is (null).",
- cimg_instance);
- const unsigned int omode = cimg::exception_mode();
- cimg::exception_mode(0);
- try { load_magick(filename); }
- catch (CImgException&) {
- try { load_imagemagick_external(filename); }
- catch (CImgException&) {
- try { load_graphicsmagick_external(filename); }
- catch (CImgException&) {
- try { load_cimg(filename); }
- catch (CImgException&) {
- try {
- cimg::fclose(cimg::fopen(filename,"rb"));
- } catch (CImgException&) {
- cimg::exception_mode(omode);
- throw CImgIOException(_cimg_instance
- "load_other(): Failed to open file '%s'.",
- cimg_instance,
- filename);
- }
- cimg::exception_mode(omode);
- throw CImgIOException(_cimg_instance
- "load_other(): Failed to recognize format of file '%s'.",
- cimg_instance,
- filename);
- }
- }
- }
- }
- cimg::exception_mode(omode);
- return *this;
- }
- //! Load image using various non-native ways \newinstance.
- static CImg<T> get_load_other(const char *const filename) {
- return CImg<T>().load_other(filename);
- }
- //@}
- //---------------------------
- //
- //! \name Data Output
- //@{
- //---------------------------
- //! Display information about the image data.
- /**
- \param title Name for the considered image.
- \param display_stats Tells to compute and display image statistics.
- **/
- const CImg<T>& print(const char *const title=0, const bool display_stats=true) const {
- int xm = 0, ym = 0, zm = 0, vm = 0, xM = 0, yM = 0, zM = 0, vM = 0;
- CImg<doubleT> st;
- if (!is_empty() && display_stats) {
- st = get_stats();
- xm = (int)st[4]; ym = (int)st[5], zm = (int)st[6], vm = (int)st[7];
- xM = (int)st[8]; yM = (int)st[9], zM = (int)st[10], vM = (int)st[11];
- }
- const ulongT siz = size(), msiz = siz*sizeof(T), siz1 = siz - 1,
- mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U, width1 = _width - 1;
- CImg<charT> _title(64);
- if (!title) cimg_snprintf(_title,_title._width,"CImg<%s>",pixel_type());
- std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = (%u,%u,%u,%u) [%lu %s], %sdata%s = (%s*)%p",
- cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal,
- cimg::t_bold,cimg::t_normal,(void*)this,
- cimg::t_bold,cimg::t_normal,_width,_height,_depth,_spectrum,
- (unsigned long)(mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20))),
- mdisp==0?"b":(mdisp==1?"Kio":"Mio"),
- cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin());
- if (_data)
- std::fprintf(cimg::output(),"..%p (%s) = [ ",(void*)((char*)end() - 1),_is_shared?"shared":"non-shared");
- else std::fprintf(cimg::output()," (%s) = [ ",_is_shared?"shared":"non-shared");
- if (!is_empty()) cimg_foroff(*this,off) {
- std::fprintf(cimg::output(),cimg::type<T>::format_s(),cimg::type<T>::format(_data[off]));
- if (off!=siz1) std::fprintf(cimg::output(),"%s",off%_width==width1?" ; ":" ");
- if (off==7 && siz>16) { off = siz1 - 8; std::fprintf(cimg::output(),"... "); }
- }
- if (!is_empty() && display_stats)
- std::fprintf(cimg::output(),
- " ], %smin%s = %g, %smax%s = %g, %smean%s = %g, %sstd%s = %g, %scoords_min%s = (%u,%u,%u,%u), "
- "%scoords_max%s = (%u,%u,%u,%u).\n",
- cimg::t_bold,cimg::t_normal,st[0],
- cimg::t_bold,cimg::t_normal,st[1],
- cimg::t_bold,cimg::t_normal,st[2],
- cimg::t_bold,cimg::t_normal,std::sqrt(st[3]),
- cimg::t_bold,cimg::t_normal,xm,ym,zm,vm,
- cimg::t_bold,cimg::t_normal,xM,yM,zM,vM);
- else std::fprintf(cimg::output(),"%s].\n",is_empty()?"":" ");
- std::fflush(cimg::output());
- return *this;
- }
- //! Display image into a CImgDisplay window.
- /**
- \param disp Display window.
- **/
- const CImg<T>& display(CImgDisplay& disp) const {
- disp.display(*this);
- return *this;
- }
- //! Display image into a CImgDisplay window, in an interactive way.
- /**
- \param disp Display window.
- \param display_info Tells if image information are displayed on the standard output.
- \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
- \param exit_on_anykey Exit function when any key is pressed.
- **/
- const CImg<T>& display(CImgDisplay &disp, const bool display_info, unsigned int *const XYZ=0,
- const bool exit_on_anykey=false) const {
- return _display(disp,0,display_info,XYZ,exit_on_anykey,false);
- }
- //! Display image into an interactive window.
- /**
- \param title Window title
- \param display_info Tells if image information are displayed on the standard output.
- \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
- \param exit_on_anykey Exit function when any key is pressed.
- **/
- const CImg<T>& display(const char *const title=0, const bool display_info=true, unsigned int *const XYZ=0,
- const bool exit_on_anykey=false) const {
- CImgDisplay disp;
- return _display(disp,title,display_info,XYZ,exit_on_anykey,false);
- }
- const CImg<T>& _display(CImgDisplay &disp, const char *const title, const bool display_info,
- unsigned int *const XYZ, const bool exit_on_anykey,
- const bool exit_on_singleclick) const {
- unsigned int oldw = 0, oldh = 0, _XYZ[3] = {}, key = 0;
- int x0 = 0, y0 = 0, z0 = 0, x1 = width() - 1, y1 = height() - 1, z1 = depth() - 1,
- old_mouse_x = -1, old_mouse_y = -1;
- if (!disp) {
- disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1);
- if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
- else disp.set_title("%s",title);
- } else if (title) disp.set_title("%s",title);
- disp.show().flush();
- const CImg<char> dtitle = CImg<char>::string(disp.title());
- if (display_info) print(dtitle);
- CImg<T> zoom;
- for (bool reset_view = true, resize_disp = false, is_first_select = true; !key && !disp.is_closed(); ) {
- if (reset_view) {
- if (XYZ) { _XYZ[0] = XYZ[0]; _XYZ[1] = XYZ[1]; _XYZ[2] = XYZ[2]; }
- else {
- _XYZ[0] = (unsigned int)(x0 + x1 + 1)/2;
- _XYZ[1] = (unsigned int)(y0 + y1 + 1)/2;
- _XYZ[2] = (unsigned int)(z0 + z1 + 1)/2;
- }
- x0 = 0; y0 = 0; z0 = 0; x1 = width() - 1; y1 = height() - 1; z1 = depth() - 1;
- disp.resize(cimg_fitscreen(_width,_height,_depth),false);
- oldw = disp._width; oldh = disp._height;
- resize_disp = true;
- reset_view = false;
- }
- if (!x0 && !y0 && !z0 && x1==width() - 1 && y1==height() - 1 && z1==depth() - 1) {
- if (is_empty()) zoom.assign(1,1,1,1,(T)0); else zoom.assign();
- } else zoom = get_crop(x0,y0,z0,x1,y1,z1);
- const CImg<T>& visu = zoom?zoom:*this;
- const unsigned int
- dx = 1U + x1 - x0, dy = 1U + y1 - y0, dz = 1U + z1 - z0,
- tw = dx + (dz>1?dz:0U), th = dy + (dz>1?dz:0U);
- if (!is_empty() && !disp.is_fullscreen() && resize_disp) {
- const float
- ttw = (float)tw*disp.width()/oldw, tth = (float)th*disp.height()/oldh,
- dM = std::max(ttw,tth), diM = (float)std::max(disp.width(),disp.height());
- const unsigned int
- imgw = (unsigned int)(ttw*diM/dM), imgh = (unsigned int)(tth*diM/dM);
- disp.set_fullscreen(false).resize(cimg_fitscreen(imgw,imgh,1),false);
- resize_disp = false;
- }
- oldw = tw; oldh = th;
- bool
- go_up = false, go_down = false, go_left = false, go_right = false,
- go_inc = false, go_dec = false, go_in = false, go_out = false,
- go_in_center = false;
- disp.set_title("%s",dtitle._data);
- if (_width>1 && visu._width==1) disp.set_title("%s | x=%u",disp._title,x0);
- if (_height>1 && visu._height==1) disp.set_title("%s | y=%u",disp._title,y0);
- if (_depth>1 && visu._depth==1) disp.set_title("%s | z=%u",disp._title,z0);
- disp._mouse_x = old_mouse_x; disp._mouse_y = old_mouse_y;
- CImg<intT> selection = visu._select(disp,0,2,_XYZ,x0,y0,z0,true,is_first_select,_depth>1,true);
- old_mouse_x = disp._mouse_x; old_mouse_y = disp._mouse_y;
- is_first_select = false;
- if (disp.wheel()) {
- if ((disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) &&
- (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT())) {
- go_left = !(go_right = disp.wheel()>0);
- } else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) {
- go_down = !(go_up = disp.wheel()>0);
- } else if (depth()==1 || disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- go_out = !(go_in = disp.wheel()>0); go_in_center = false;
- }
- disp.set_wheel();
- }
- const int
- sx0 = selection(0), sy0 = selection(1), sz0 = selection(2),
- sx1 = selection(3), sy1 = selection(4), sz1 = selection(5);
- if (sx0>=0 && sy0>=0 && sz0>=0 && sx1>=0 && sy1>=0 && sz1>=0) {
- x1 = x0 + sx1; y1 = y0 + sy1; z1 = z0 + sz1;
- x0+=sx0; y0+=sy0; z0+=sz0;
- if ((sx0==sx1 && sy0==sy1) || (_depth>1 && sx0==sx1 && sz0==sz1) || (_depth>1 && sy0==sy1 && sz0==sz1)) {
- if (exit_on_singleclick && (!zoom || is_empty())) break; else reset_view = true;
- }
- resize_disp = true;
- } else switch (key = disp.key()) {
- #if cimg_OS!=2
- case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT :
- #endif
- case 0 : case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : key = 0; break;
- case cimg::keyP : if (visu._depth>1 && (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT())) {
- // Special mode: play stack of frames
- const unsigned int
- w1 = visu._width*disp.width()/(visu._width + (visu._depth>1?visu._depth:0)),
- h1 = visu._height*disp.height()/(visu._height + (visu._depth>1?visu._depth:0));
- float frame_timing = 5;
- bool is_stopped = false;
- disp.set_key(key,false).set_wheel().resize(cimg_fitscreen(w1,h1,1),false); key = 0;
- for (unsigned int timer = 0; !key && !disp.is_closed() && !disp.button(); ) {
- if (disp.is_resized()) disp.resize(false);
- if (!timer) {
- visu.get_slice((int)_XYZ[2]).display(disp.set_title("%s | z=%d",dtitle.data(),_XYZ[2]));
- (++_XYZ[2])%=visu._depth;
- }
- if (!is_stopped) { if (++timer>(unsigned int)frame_timing) timer = 0; } else timer = ~0U;
- if (disp.wheel()) { frame_timing-=disp.wheel()/3.f; disp.set_wheel(); }
- switch (key = disp.key()) {
- #if cimg_OS!=2
- case cimg::keyCTRLRIGHT :
- #endif
- case cimg::keyCTRLLEFT : key = 0; break;
- case cimg::keyPAGEUP : frame_timing-=0.3f; key = 0; break;
- case cimg::keyPAGEDOWN : frame_timing+=0.3f; key = 0; break;
- case cimg::keySPACE : is_stopped = !is_stopped; disp.set_key(key,false); key = 0; break;
- case cimg::keyARROWLEFT : case cimg::keyARROWUP : is_stopped = true; timer = 0; key = 0; break;
- case cimg::keyARROWRIGHT : case cimg::keyARROWDOWN : is_stopped = true;
- (_XYZ[2]+=visu._depth - 2)%=visu._depth; timer = 0; key = 0; break;
- case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- disp.set_fullscreen(false).
- resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
- CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false);
- disp.set_key(key,false); key = 0;
- } break;
- case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- disp.set_fullscreen(false).
- resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false).set_key(key,false); key = 0;
- } break;
- case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- disp.set_fullscreen(false).
- resize(cimg_fitscreen(_width,_height,_depth),false).set_key(key,false); key = 0;
- } break;
- case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- disp.resize(disp.screen_width(),disp.screen_height(),false).
- toggle_fullscreen().set_key(key,false); key = 0;
- } break;
- }
- frame_timing = frame_timing<1?1:(frame_timing>39?39:frame_timing);
- disp.wait(20);
- }
- const unsigned int
- w2 = (visu._width + (visu._depth>1?visu._depth:0))*disp.width()/visu._width,
- h2 = (visu._height + (visu._depth>1?visu._depth:0))*disp.height()/visu._height;
- disp.resize(cimg_fitscreen(w2,h2,1),false).set_title(dtitle.data()).set_key().set_button().set_wheel();
- key = 0;
- } break;
- case cimg::keyHOME : reset_view = resize_disp = true; key = 0; break;
- case cimg::keyPADADD : go_in = true; go_in_center = true; key = 0; break;
- case cimg::keyPADSUB : go_out = true; key = 0; break;
- case cimg::keyARROWLEFT : case cimg::keyPAD4: go_left = true; key = 0; break;
- case cimg::keyARROWRIGHT : case cimg::keyPAD6: go_right = true; key = 0; break;
- case cimg::keyARROWUP : case cimg::keyPAD8: go_up = true; key = 0; break;
- case cimg::keyARROWDOWN : case cimg::keyPAD2: go_down = true; key = 0; break;
- case cimg::keyPAD7 : go_up = go_left = true; key = 0; break;
- case cimg::keyPAD9 : go_up = go_right = true; key = 0; break;
- case cimg::keyPAD1 : go_down = go_left = true; key = 0; break;
- case cimg::keyPAD3 : go_down = go_right = true; key = 0; break;
- case cimg::keyPAGEUP : go_inc = true; key = 0; break;
- case cimg::keyPAGEDOWN : go_dec = true; key = 0; break;
- }
- if (go_in) {
- const int
- mx = go_in_center?disp.width()/2:disp.mouse_x(),
- my = go_in_center?disp.height()/2:disp.mouse_y(),
- mX = mx*(width() + (depth()>1?depth():0))/disp.width(),
- mY = my*(height() + (depth()>1?depth():0))/disp.height();
- int X = (int)_XYZ[0], Y = (int)_XYZ[1], Z = (int)_XYZ[2];
- if (mX<width() && mY<height()) {
- X = x0 + mX*(1 + x1 - x0)/width(); Y = y0 + mY*(1 + y1 - y0)/height();
- }
- if (mX<width() && mY>=height()) {
- X = x0 + mX*(1 + x1 - x0)/width(); Z = z0 + (mY - height())*(1 + z1 - z0)/depth();
- }
- if (mX>=width() && mY<height()) {
- Y = y0 + mY*(1 + y1 - y0)/height(); Z = z0 + (mX - width())*(1 + z1 - z0)/depth();
- }
- if (x1 - x0>4) { x0 = X - 3*(X - x0)/4; x1 = X + 3*(x1 - X)/4; }
- if (y1 - y0>4) { y0 = Y - 3*(Y - y0)/4; y1 = Y + 3*(y1 - Y)/4; }
- if (z1 - z0>4) { z0 = Z - 3*(Z - z0)/4; z1 = Z + 3*(z1 - Z)/4; }
- }
- if (go_out) {
- const int
- delta_x = (x1 - x0)/8, delta_y = (y1 - y0)/8, delta_z = (z1 - z0)/8,
- ndelta_x = delta_x?delta_x:(_width>1),
- ndelta_y = delta_y?delta_y:(_height>1),
- ndelta_z = delta_z?delta_z:(_depth>1);
- x0-=ndelta_x; y0-=ndelta_y; z0-=ndelta_z;
- x1+=ndelta_x; y1+=ndelta_y; z1+=ndelta_z;
- if (x0<0) { x1-=x0; x0 = 0; if (x1>=width()) x1 = width() - 1; }
- if (y0<0) { y1-=y0; y0 = 0; if (y1>=height()) y1 = height() - 1; }
- if (z0<0) { z1-=z0; z0 = 0; if (z1>=depth()) z1 = depth() - 1; }
- if (x1>=width()) { x0-=(x1 - width() + 1); x1 = width() - 1; if (x0<0) x0 = 0; }
- if (y1>=height()) { y0-=(y1 - height() + 1); y1 = height() - 1; if (y0<0) y0 = 0; }
- if (z1>=depth()) { z0-=(z1 - depth() + 1); z1 = depth() - 1; if (z0<0) z0 = 0; }
- const float
- ratio = (float)(x1-x0)/(y1-y0),
- ratiow = (float)disp._width/disp._height,
- sub = std::min(cimg::abs(ratio - ratiow),cimg::abs(1/ratio-1/ratiow));
- if (sub>0.01) resize_disp = true;
- }
- if (go_left) {
- const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1);
- if (x0 - ndelta>=0) { x0-=ndelta; x1-=ndelta; }
- else { x1-=x0; x0 = 0; }
- }
- if (go_right) {
- const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1);
- if (x1+ndelta<width()) { x0+=ndelta; x1+=ndelta; }
- else { x0+=(width() - 1 - x1); x1 = width() - 1; }
- }
- if (go_up) {
- const int delta = (y1 - y0)/4, ndelta = delta?delta:(_height>1);
- if (y0 - ndelta>=0) { y0-=ndelta; y1-=ndelta; }
- else { y1-=y0; y0 = 0; }
- }
- if (go_down) {
- const int delta = (y1 - y0)/4, ndelta = delta?delta:(_height>1);
- if (y1+ndelta<height()) { y0+=ndelta; y1+=ndelta; }
- else { y0+=(height() - 1 - y1); y1 = height() - 1; }
- }
- if (go_inc) {
- const int delta = (z1 - z0)/4, ndelta = delta?delta:(_depth>1);
- if (z0 - ndelta>=0) { z0-=ndelta; z1-=ndelta; }
- else { z1-=z0; z0 = 0; }
- }
- if (go_dec) {
- const int delta = (z1 - z0)/4, ndelta = delta?delta:(_depth>1);
- if (z1+ndelta<depth()) { z0+=ndelta; z1+=ndelta; }
- else { z0+=(depth() - 1 - z1); z1 = depth() - 1; }
- }
- disp.wait(100);
- if (!exit_on_anykey && key && key!=cimg::keyESC &&
- (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
- key = 0;
- }
- }
- disp.set_key(key);
- if (XYZ) { XYZ[0] = _XYZ[0]; XYZ[1] = _XYZ[1]; XYZ[2] = _XYZ[2]; }
- return *this;
- }
- //! Display object 3D in an interactive window.
- /**
- \param disp Display window.
- \param vertices Vertices data of the 3D object.
- \param primitives Primitives data of the 3D object.
- \param colors Colors data of the 3D object.
- \param opacities Opacities data of the 3D object.
- \param centering Tells if the 3D object must be centered for the display.
- \param render_static Rendering mode.
- \param render_motion Rendering mode, when the 3D object is moved.
- \param is_double_sided Tells if the object primitives are double-sided.
- \param focale Focale
- \param light_x X-coordinate of the light source.
- \param light_y Y-coordinate of the light source.
- \param light_z Z-coordinate of the light source.
- \param specular_lightness Amount of specular light.
- \param specular_shininess Shininess of the object material.
- \param display_axes Tells if the 3D axes are displayed.
- \param pose_matrix Pointer to 12 values, defining a 3D pose (as a 4x3 matrix).
- \param exit_on_anykey Exit function when any key is pressed.
- **/
- template<typename tp, typename tf, typename tc, typename to>
- const CImg<T>& display_object3d(CImgDisplay& disp,
- const CImg<tp>& vertices,
- const CImgList<tf>& primitives,
- const CImgList<tc>& colors,
- const to& opacities,
- const bool centering=true,
- const int render_static=4, const int render_motion=1,
- const bool is_double_sided=true, const float focale=700,
- const float light_x=0, const float light_y=0, const float light_z=-5e8f,
- const float specular_lightness=0.2f, const float specular_shininess=0.1f,
- const bool display_axes=true, float *const pose_matrix=0,
- const bool exit_on_anykey=false) const {
- return _display_object3d(disp,0,vertices,primitives,colors,opacities,centering,render_static,
- render_motion,is_double_sided,focale,
- light_x,light_y,light_z,specular_lightness,specular_shininess,
- display_axes,pose_matrix,exit_on_anykey);
- }
- //! Display object 3D in an interactive window \simplification.
- template<typename tp, typename tf, typename tc, typename to>
- const CImg<T>& display_object3d(const char *const title,
- const CImg<tp>& vertices,
- const CImgList<tf>& primitives,
- const CImgList<tc>& colors,
- const to& opacities,
- const bool centering=true,
- const int render_static=4, const int render_motion=1,
- const bool is_double_sided=true, const float focale=700,
- const float light_x=0, const float light_y=0, const float light_z=-5e8f,
- const float specular_lightness=0.2f, const float specular_shininess=0.1f,
- const bool display_axes=true, float *const pose_matrix=0,
- const bool exit_on_anykey=false) const {
- CImgDisplay disp;
- return _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,render_static,
- render_motion,is_double_sided,focale,
- light_x,light_y,light_z,specular_lightness,specular_shininess,
- display_axes,pose_matrix,exit_on_anykey);
- }
- //! Display object 3D in an interactive window \simplification.
- template<typename tp, typename tf, typename tc>
- const CImg<T>& display_object3d(CImgDisplay &disp,
- const CImg<tp>& vertices,
- const CImgList<tf>& primitives,
- const CImgList<tc>& colors,
- const bool centering=true,
- const int render_static=4, const int render_motion=1,
- const bool is_double_sided=true, const float focale=700,
- const float light_x=0, const float light_y=0, const float light_z=-5e8f,
- const float specular_lightness=0.2f, const float specular_shininess=0.1f,
- const bool display_axes=true, float *const pose_matrix=0,
- const bool exit_on_anykey=false) const {
- return display_object3d(disp,vertices,primitives,colors,CImgList<floatT>(),centering,
- render_static,render_motion,is_double_sided,focale,
- light_x,light_y,light_z,specular_lightness,specular_shininess,
- display_axes,pose_matrix,exit_on_anykey);
- }
- //! Display object 3D in an interactive window \simplification.
- template<typename tp, typename tf, typename tc>
- const CImg<T>& display_object3d(const char *const title,
- const CImg<tp>& vertices,
- const CImgList<tf>& primitives,
- const CImgList<tc>& colors,
- const bool centering=true,
- const int render_static=4, const int render_motion=1,
- const bool is_double_sided=true, const float focale=700,
- const float light_x=0, const float light_y=0, const float light_z=-5e8f,
- const float specular_lightness=0.2f, const float specular_shininess=0.1f,
- const bool display_axes=true, float *const pose_matrix=0,
- const bool exit_on_anykey=false) const {
- return display_object3d(title,vertices,primitives,colors,CImgList<floatT>(),centering,
- render_static,render_motion,is_double_sided,focale,
- light_x,light_y,light_z,specular_lightness,specular_shininess,
- display_axes,pose_matrix,exit_on_anykey);
- }
- //! Display object 3D in an interactive window \simplification.
- template<typename tp, typename tf>
- const CImg<T>& display_object3d(CImgDisplay &disp,
- const CImg<tp>& vertices,
- const CImgList<tf>& primitives,
- const bool centering=true,
- const int render_static=4, const int render_motion=1,
- const bool is_double_sided=true, const float focale=700,
- const float light_x=0, const float light_y=0, const float light_z=-5e8f,
- const float specular_lightness=0.2f, const float specular_shininess=0.1f,
- const bool display_axes=true, float *const pose_matrix=0,
- const bool exit_on_anykey=false) const {
- return display_object3d(disp,vertices,primitives,CImgList<T>(),centering,
- render_static,render_motion,is_double_sided,focale,
- light_x,light_y,light_z,specular_lightness,specular_shininess,
- display_axes,pose_matrix,exit_on_anykey);
- }
- //! Display object 3D in an interactive window \simplification.
- template<typename tp, typename tf>
- const CImg<T>& display_object3d(const char *const title,
- const CImg<tp>& vertices,
- const CImgList<tf>& primitives,
- const bool centering=true,
- const int render_static=4, const int render_motion=1,
- const bool is_double_sided=true, const float focale=700,
- const float light_x=0, const float light_y=0, const float light_z=-5e8f,
- const float specular_lightness=0.2f, const float specular_shininess=0.1f,
- const bool display_axes=true, float *const pose_matrix=0,
- const bool exit_on_anykey=false) const {
- return display_object3d(title,vertices,primitives,CImgList<T>(),centering,
- render_static,render_motion,is_double_sided,focale,
- light_x,light_y,light_z,specular_lightness,specular_shininess,
- display_axes,pose_matrix,exit_on_anykey);
- }
- //! Display object 3D in an interactive window \simplification.
- template<typename tp>
- const CImg<T>& display_object3d(CImgDisplay &disp,
- const CImg<tp>& vertices,
- const bool centering=true,
- const int render_static=4, const int render_motion=1,
- const bool is_double_sided=true, const float focale=700,
- const float light_x=0, const float light_y=0, const float light_z=-5e8f,
- const float specular_lightness=0.2f, const float specular_shininess=0.1f,
- const bool display_axes=true, float *const pose_matrix=0,
- const bool exit_on_anykey=false) const {
- return display_object3d(disp,vertices,CImgList<uintT>(),centering,
- render_static,render_motion,is_double_sided,focale,
- light_x,light_y,light_z,specular_lightness,specular_shininess,
- display_axes,pose_matrix,exit_on_anykey);
- }
- //! Display object 3D in an interactive window \simplification.
- template<typename tp>
- const CImg<T>& display_object3d(const char *const title,
- const CImg<tp>& vertices,
- const bool centering=true,
- const int render_static=4, const int render_motion=1,
- const bool is_double_sided=true, const float focale=700,
- const float light_x=0, const float light_y=0, const float light_z=-5e8f,
- const float specular_lightness=0.2f, const float specular_shininess=0.1f,
- const bool display_axes=true, float *const pose_matrix=0,
- const bool exit_on_anykey=false) const {
- return display_object3d(title,vertices,CImgList<uintT>(),centering,
- render_static,render_motion,is_double_sided,focale,
- light_x,light_y,light_z,specular_lightness,specular_shininess,
- display_axes,pose_matrix,exit_on_anykey);
- }
- template<typename tp, typename tf, typename tc, typename to>
- const CImg<T>& _display_object3d(CImgDisplay& disp, const char *const title,
- const CImg<tp>& vertices,
- const CImgList<tf>& primitives,
- const CImgList<tc>& colors,
- const to& opacities,
- const bool centering,
- const int render_static, const int render_motion,
- const bool is_double_sided, const float focale,
- const float light_x, const float light_y, const float light_z,
- const float specular_lightness, const float specular_shininess,
- const bool display_axes, float *const pose_matrix,
- const bool exit_on_anykey) const {
- typedef typename cimg::superset<tp,float>::type tpfloat;
- // Check input arguments
- if (is_empty()) {
- CImg<T> background;
- if (colors && colors[0].size()==1) background.assign(1,2,1,1,64,128);
- else background.assign(1,2,1,3,32,64,32,116,64,96);
- if (disp) background.resize(disp.width(),disp.height(),1,-100,3);
- else background.resize(cimg_fitscreen(CImgDisplay::screen_width()/2,
- CImgDisplay::screen_height()/2,1),1,-100,3);
- return background._display_object3d(disp,title,vertices,primitives,colors,opacities,centering,
- render_static,render_motion,is_double_sided,focale,
- light_x,light_y,light_z,specular_lightness,specular_shininess,
- display_axes,pose_matrix,exit_on_anykey);
- } else { if (disp) disp.resize(*this,false); }
- CImg<charT> error_message(1024);
- if (!vertices.is_object3d(primitives,colors,opacities,true,error_message))
- throw CImgArgumentException(_cimg_instance
- "display_object3d(): Invalid specified 3D object (%u,%u) (%s).",
- cimg_instance,vertices._width,primitives._width,error_message.data());
- if (vertices._width && !primitives) {
- CImgList<tf> nprimitives(vertices._width,1,1,1,1);
- cimglist_for(nprimitives,l) nprimitives(l,0) = (tf)l;
- return _display_object3d(disp,title,vertices,nprimitives,colors,opacities,centering,
- render_static,render_motion,is_double_sided,focale,
- light_x,light_y,light_z,specular_lightness,specular_shininess,
- display_axes,pose_matrix,exit_on_anykey);
- }
- if (!disp) {
- disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,3);
- if (!title) disp.set_title("CImg<%s> (%u vertices, %u primitives)",
- pixel_type(),vertices._width,primitives._width);
- } else if (title) disp.set_title("%s",title);
- // Init 3D objects and compute object statistics
- CImg<floatT>
- pose,
- rotated_vertices(vertices._width,3),
- bbox_vertices, rotated_bbox_vertices,
- axes_vertices, rotated_axes_vertices,
- bbox_opacities, axes_opacities;
- CImgList<uintT> bbox_primitives, axes_primitives;
- CImgList<tf> reverse_primitives;
- CImgList<T> bbox_colors, bbox_colors2, axes_colors;
- unsigned int ns_width = 0, ns_height = 0;
- int _is_double_sided = (int)is_double_sided;
- bool ndisplay_axes = display_axes;
- const CImg<T>
- background_color(1,1,1,_spectrum,0),
- foreground_color(1,1,1,_spectrum,(T)std::min((int)cimg::type<T>::max(),255));
- float
- Xoff = 0, Yoff = 0, Zoff = 0, sprite_scale = 1,
- xm = 0, xM = vertices?vertices.get_shared_row(0).max_min(xm):0,
- ym = 0, yM = vertices?vertices.get_shared_row(1).max_min(ym):0,
- zm = 0, zM = vertices?vertices.get_shared_row(2).max_min(zm):0;
- const float delta = cimg::max(xM - xm,yM - ym,zM - zm);
- rotated_bbox_vertices = bbox_vertices.assign(8,3,1,1,
- xm,xM,xM,xm,xm,xM,xM,xm,
- ym,ym,yM,yM,ym,ym,yM,yM,
- zm,zm,zm,zm,zM,zM,zM,zM);
- bbox_primitives.assign(6,1,4,1,1, 0,3,2,1, 4,5,6,7, 1,2,6,5, 0,4,7,3, 0,1,5,4, 2,3,7,6);
- bbox_colors.assign(6,_spectrum,1,1,1,background_color[0]);
- bbox_colors2.assign(6,_spectrum,1,1,1,foreground_color[0]);
- bbox_opacities.assign(bbox_colors._width,1,1,1,0.3f);
- rotated_axes_vertices = axes_vertices.assign(7,3,1,1,
- 0,20,0,0,22,-6,-6,
- 0,0,20,0,-6,22,-6,
- 0,0,0,20,0,0,22);
- axes_opacities.assign(3,1,1,1,1);
- axes_colors.assign(3,_spectrum,1,1,1,foreground_color[0]);
- axes_primitives.assign(3,1,2,1,1, 0,1, 0,2, 0,3);
- // Begin user interaction loop
- CImg<T> visu0(*this,false), visu;
- CImg<tpfloat> zbuffer(visu0.width(),visu0.height(),1,1,0);
- bool init_pose = true, clicked = false, redraw = true;
- unsigned int key = 0, font_size = 32;
- int
- x0 = 0, y0 = 0, x1 = 0, y1 = 0,
- nrender_static = render_static,
- nrender_motion = render_motion;
- disp.show().flush();
- while (!disp.is_closed() && !key) {
- // Init object pose
- if (init_pose) {
- const float
- ratio = delta>0?(2.f*std::min(disp.width(),disp.height())/(3.f*delta)):1,
- dx = (xM + xm)/2, dy = (yM + ym)/2, dz = (zM + zm)/2;
- if (centering)
- CImg<floatT>(4,3,1,1, ratio,0.,0.,-ratio*dx, 0.,ratio,0.,-ratio*dy, 0.,0.,ratio,-ratio*dz).move_to(pose);
- else CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose);
- if (pose_matrix) {
- CImg<floatT> pose0(pose_matrix,4,3,1,1,false);
- pose0.resize(4,4,1,1,0); pose.resize(4,4,1,1,0);
- pose0(3,3) = pose(3,3) = 1;
- (pose0*pose).get_crop(0,0,3,2).move_to(pose);
- Xoff = pose_matrix[12]; Yoff = pose_matrix[13]; Zoff = pose_matrix[14]; sprite_scale = pose_matrix[15];
- } else { Xoff = Yoff = Zoff = 0; sprite_scale = 1; }
- init_pose = false;
- redraw = true;
- }
- // Rotate and draw 3D object
- if (redraw) {
- const float
- r00 = pose(0,0), r10 = pose(1,0), r20 = pose(2,0), r30 = pose(3,0),
- r01 = pose(0,1), r11 = pose(1,1), r21 = pose(2,1), r31 = pose(3,1),
- r02 = pose(0,2), r12 = pose(1,2), r22 = pose(2,2), r32 = pose(3,2);
- if ((clicked && nrender_motion>=0) || (!clicked && nrender_static>=0)) {
- const tp *const pv0 = vertices.data(), *const pv1 = vertices.data(0,1), *const pv2 = vertices.data(0,2);
- float
- *const prv0 = rotated_vertices.data(),
- *const prv1 = rotated_vertices.data(0,1),
- *const prv2 = rotated_vertices.data(0,2);
- cimg_pragma_openmp(parallel for cimg_openmp_if(vertices.width()>(cimg_openmp_sizefactor)*1024))
- cimg_forX(vertices,l) {
- const float x = (float)pv0[l], y = (float)pv1[l], z = (float)pv2[l];
- prv0[l] = r00*x + r10*y + r20*z + r30;
- prv1[l] = r01*x + r11*y + r21*z + r31;
- prv2[l] = r02*x + r12*y + r22*z + r32;
- }
- }
- else cimg_forX(bbox_vertices,l) {
- const float x = bbox_vertices(l,0), y = bbox_vertices(l,1), z = bbox_vertices(l,2);
- rotated_bbox_vertices(l,0) = r00*x + r10*y + r20*z + r30;
- rotated_bbox_vertices(l,1) = r01*x + r11*y + r21*z + r31;
- rotated_bbox_vertices(l,2) = r02*x + r12*y + r22*z + r32;
- }
- // Draw objects
- const bool render_with_zbuffer = !clicked && nrender_static>0;
- visu = visu0;
- if ((clicked && nrender_motion<0) || (!clicked && nrender_static<0))
- visu.draw_object3d(Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
- rotated_bbox_vertices,bbox_primitives,bbox_colors,bbox_opacities,2,false,focale).
- draw_object3d(Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
- rotated_bbox_vertices,bbox_primitives,bbox_colors2,1,false,focale);
- else visu._draw_object3d((void*)0,render_with_zbuffer?zbuffer.fill(0):CImg<tpfloat>::empty(),
- Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
- rotated_vertices,reverse_primitives?reverse_primitives:primitives,
- colors,opacities,clicked?nrender_motion:nrender_static,_is_double_sided==1,focale,
- width()/2.f + light_x,height()/2.f + light_y,light_z + Zoff,
- specular_lightness,specular_shininess,1,sprite_scale);
- // Draw axes
- if (ndisplay_axes) {
- const float
- n = 1e-8f + cimg::hypot(r00,r01,r02),
- _r00 = r00/n, _r10 = r10/n, _r20 = r20/n,
- _r01 = r01/n, _r11 = r11/n, _r21 = r21/n,
- _r02 = r01/n, _r12 = r12/n, _r22 = r22/n,
- Xaxes = 25, Yaxes = visu._height - 38.f;
- cimg_forX(axes_vertices,l) {
- const float
- x = axes_vertices(l,0),
- y = axes_vertices(l,1),
- z = axes_vertices(l,2);
- rotated_axes_vertices(l,0) = _r00*x + _r10*y + _r20*z;
- rotated_axes_vertices(l,1) = _r01*x + _r11*y + _r21*z;
- rotated_axes_vertices(l,2) = _r02*x + _r12*y + _r22*z;
- }
- axes_opacities(0,0) = (rotated_axes_vertices(1,2)>0)?0.5f:1.f;
- axes_opacities(1,0) = (rotated_axes_vertices(2,2)>0)?0.5f:1.f;
- axes_opacities(2,0) = (rotated_axes_vertices(3,2)>0)?0.5f:1.f;
- visu.draw_object3d(Xaxes,Yaxes,0,rotated_axes_vertices,axes_primitives,
- axes_colors,axes_opacities,1,false,focale).
- draw_text((int)(Xaxes + rotated_axes_vertices(4,0)),
- (int)(Yaxes + rotated_axes_vertices(4,1)),
- "X",axes_colors[0]._data,0,axes_opacities(0,0),13).
- draw_text((int)(Xaxes + rotated_axes_vertices(5,0)),
- (int)(Yaxes + rotated_axes_vertices(5,1)),
- "Y",axes_colors[1]._data,0,axes_opacities(1,0),13).
- draw_text((int)(Xaxes + rotated_axes_vertices(6,0)),
- (int)(Yaxes + rotated_axes_vertices(6,1)),
- "Z",axes_colors[2]._data,0,axes_opacities(2,0),13);
- }
- visu.display(disp);
- if (!clicked || nrender_motion==nrender_static) redraw = false;
- }
- // Handle user interaction
- if (!redraw) disp.wait();
- if ((disp.button() || disp.wheel()) && disp.mouse_x()>=0 && disp.mouse_y()>=0) {
- redraw = true;
- if (!clicked) { x0 = x1 = disp.mouse_x(); y0 = y1 = disp.mouse_y(); if (!disp.wheel()) clicked = true; }
- else { x1 = disp.mouse_x(); y1 = disp.mouse_y(); }
- const bool is_keyCTRL = disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT();
- if (disp.button()&1 && !is_keyCTRL) {
- const float
- R = 0.45f*std::min(disp.width(),disp.height()),
- R2 = R*R,
- u0 = (float)(x0 - disp.width()/2),
- v0 = (float)(y0 - disp.height()/2),
- u1 = (float)(x1 - disp.width()/2),
- v1 = (float)(y1 - disp.height()/2),
- n0 = cimg::hypot(u0,v0),
- n1 = cimg::hypot(u1,v1),
- nu0 = n0>R?(u0*R/n0):u0,
- nv0 = n0>R?(v0*R/n0):v0,
- nw0 = (float)std::sqrt(std::max(0.f,R2 - nu0*nu0 - nv0*nv0)),
- nu1 = n1>R?(u1*R/n1):u1,
- nv1 = n1>R?(v1*R/n1):v1,
- nw1 = (float)std::sqrt(std::max(0.f,R2 - nu1*nu1 - nv1*nv1)),
- u = nv0*nw1 - nw0*nv1,
- v = nw0*nu1 - nu0*nw1,
- w = nv0*nu1 - nu0*nv1,
- n = cimg::hypot(u,v,w),
- alpha = (float)std::asin(n/R2)*180/cimg::PI;
- (CImg<floatT>::rotation_matrix(u,v,w,-alpha)*pose).move_to(pose);
- x0 = x1; y0 = y1;
- }
- if (disp.button()&2 && !is_keyCTRL) {
- if (focale>0) Zoff-=(y0 - y1)*focale/400;
- else { const float s = std::exp((y0 - y1)/400.f); pose*=s; sprite_scale*=s; }
- x0 = x1; y0 = y1;
- }
- if (disp.wheel()) {
- if (focale>0) Zoff-=disp.wheel()*focale/20;
- else { const float s = std::exp(disp.wheel()/20.f); pose*=s; sprite_scale*=s; }
- disp.set_wheel();
- }
- if (disp.button()&4 || (disp.button()&1 && is_keyCTRL)) {
- Xoff+=(x1 - x0); Yoff+=(y1 - y0); x0 = x1; y0 = y1;
- }
- if ((disp.button()&1) && (disp.button()&2) && !is_keyCTRL) {
- init_pose = true; disp.set_button(); x0 = x1; y0 = y1;
- pose = CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0);
- }
- } else if (clicked) { x0 = x1; y0 = y1; clicked = false; redraw = true; }
- CImg<charT> filename(32);
- switch (key = disp.key()) {
- #if cimg_OS!=2
- case cimg::keyCTRLRIGHT :
- #endif
- case 0 : case cimg::keyCTRLLEFT : key = 0; break;
- case cimg::keyD: if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- disp.set_fullscreen(false).
- resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
- CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
- _is_resized = true;
- disp.set_key(key,false); key = 0;
- } break;
- case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- disp.set_fullscreen(false).
- resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
- disp.set_key(key,false); key = 0;
- } break;
- case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
- disp.set_key(key,false); key = 0;
- } break;
- case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- if (!ns_width || !ns_height ||
- ns_width>(unsigned int)disp.screen_width() || ns_height>(unsigned int)disp.screen_height()) {
- ns_width = disp.screen_width()*3U/4;
- ns_height = disp.screen_height()*3U/4;
- }
- if (disp.is_fullscreen()) disp.resize(ns_width,ns_height,false);
- else {
- ns_width = disp._width; ns_height = disp._height;
- disp.resize(disp.screen_width(),disp.screen_height(),false);
- }
- disp.toggle_fullscreen()._is_resized = true;
- disp.set_key(key,false); key = 0;
- } break;
- case cimg::keyT : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- // Switch single/double-sided primitives.
- if (--_is_double_sided==-2) _is_double_sided = 1;
- if (_is_double_sided>=0) reverse_primitives.assign();
- else primitives.get_reverse_object3d().move_to(reverse_primitives);
- disp.set_key(key,false); key = 0; redraw = true;
- } break;
- case cimg::keyZ : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Enable/disable Z-buffer
- if (zbuffer) zbuffer.assign();
- else zbuffer.assign(visu0.width(),visu0.height(),1,1,0);
- disp.set_key(key,false); key = 0; redraw = true;
- } break;
- case cimg::keyX : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Show/hide 3D axes
- ndisplay_axes = !ndisplay_axes;
- disp.set_key(key,false); key = 0; redraw = true;
- } break;
- case cimg::keyF1 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to points
- nrender_motion = (nrender_static==0 && nrender_motion!=0)?0:-1; nrender_static = 0;
- disp.set_key(key,false); key = 0; redraw = true;
- } break;
- case cimg::keyF2 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to lines
- nrender_motion = (nrender_static==1 && nrender_motion!=1)?1:-1; nrender_static = 1;
- disp.set_key(key,false); key = 0; redraw = true;
- } break;
- case cimg::keyF3 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat
- nrender_motion = (nrender_static==2 && nrender_motion!=2)?2:-1; nrender_static = 2;
- disp.set_key(key,false); key = 0; redraw = true;
- } break;
- case cimg::keyF4 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat-shaded
- nrender_motion = (nrender_static==3 && nrender_motion!=3)?3:-1; nrender_static = 3;
- disp.set_key(key,false); key = 0; redraw = true;
- } break;
- case cimg::keyF5 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- // Set rendering mode to gouraud-shaded.
- nrender_motion = (nrender_static==4 && nrender_motion!=4)?4:-1; nrender_static = 4;
- disp.set_key(key,false); key = 0; redraw = true;
- } break;
- case cimg::keyF6 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to phong-shaded
- nrender_motion = (nrender_static==5 && nrender_motion!=5)?5:-1; nrender_static = 5;
- disp.set_key(key,false); key = 0; redraw = true;
- } break;
- case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save snapshot
- static unsigned int snap_number = 0;
- std::FILE *file;
- do {
- cimg_snprintf(filename,filename._width,cimg_appname "_%.6u."
- #ifdef cimg_use_png
- "png",
- #else
- "bmp",
- #endif
- snap_number++);
- if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
- } while (file);
- (+visu).__draw_text(" Saving snapshot... ",font_size,0).display(disp);
- visu.save(filename);
- (+visu).__draw_text(" Snapshot '%s' saved. ",font_size,0,filename._data).display(disp);
- disp.set_key(key,false); key = 0;
- } break;
- case cimg::keyG : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .off file
- static unsigned int snap_number = 0;
- std::FILE *file;
- do {
- cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.off",snap_number++);
- if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
- } while (file);
- (+visu).__draw_text(" Saving object... ",font_size,0).display(disp);
- vertices.save_off(reverse_primitives?reverse_primitives:primitives,colors,filename);
- (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp);
- disp.set_key(key,false); key = 0;
- } break;
- case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .cimg file
- static unsigned int snap_number = 0;
- std::FILE *file;
- do {
- #ifdef cimg_use_zlib
- cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimgz",snap_number++);
- #else
- cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimg",snap_number++);
- #endif
- if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
- } while (file);
- (+visu).__draw_text(" Saving object... ",font_size,0).display(disp);
- vertices.get_object3dtoCImg3d(reverse_primitives?reverse_primitives:primitives,colors,opacities).
- save(filename);
- (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp);
- disp.set_key(key,false); key = 0;
- } break;
- #ifdef cimg_use_board
- case cimg::keyP : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .EPS file
- static unsigned int snap_number = 0;
- std::FILE *file;
- do {
- cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.eps",snap_number++);
- if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
- } while (file);
- (+visu).__draw_text(" Saving EPS snapshot... ",font_size,0).display(disp);
- LibBoard::Board board;
- (+visu)._draw_object3d(&board,zbuffer.fill(0),
- Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
- rotated_vertices,reverse_primitives?reverse_primitives:primitives,
- colors,opacities,clicked?nrender_motion:nrender_static,
- _is_double_sided==1,focale,
- visu.width()/2.f + light_x,visu.height()/2.f + light_y,light_z + Zoff,
- specular_lightness,specular_shininess,1,
- sprite_scale);
- board.saveEPS(filename);
- (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp);
- disp.set_key(key,false); key = 0;
- } break;
- case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .SVG file
- static unsigned int snap_number = 0;
- std::FILE *file;
- do {
- cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.svg",snap_number++);
- if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
- } while (file);
- (+visu).__draw_text(" Saving SVG snapshot... ",font_size,0).display(disp);
- LibBoard::Board board;
- (+visu)._draw_object3d(&board,zbuffer.fill(0),
- Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
- rotated_vertices,reverse_primitives?reverse_primitives:primitives,
- colors,opacities,clicked?nrender_motion:nrender_static,
- _is_double_sided==1,focale,
- visu.width()/2.f + light_x,visu.height()/2.f + light_y,light_z + Zoff,
- specular_lightness,specular_shininess,1,
- sprite_scale);
- board.saveSVG(filename);
- (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp);
- disp.set_key(key,false); key = 0;
- } break;
- #endif
- }
- if (disp.is_resized()) {
- disp.resize(false); visu0 = get_resize(disp,1);
- if (zbuffer) zbuffer.assign(disp.width(),disp.height());
- redraw = true;
- }
- if (!exit_on_anykey && key && key!=cimg::keyESC &&
- (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
- key = 0;
- }
- }
- if (pose_matrix) {
- std::memcpy(pose_matrix,pose._data,12*sizeof(float));
- pose_matrix[12] = Xoff; pose_matrix[13] = Yoff; pose_matrix[14] = Zoff; pose_matrix[15] = sprite_scale;
- }
- disp.set_button().set_key(key);
- return *this;
- }
- //! Display 1D graph in an interactive window.
- /**
- \param disp Display window.
- \param plot_type Plot type. Can be <tt>{ 0=points | 1=segments | 2=splines | 3=bars }</tt>.
- \param vertex_type Vertex type.
- \param labelx Title for the horizontal axis, as a C-string.
- \param xmin Minimum value along the X-axis.
- \param xmax Maximum value along the X-axis.
- \param labely Title for the vertical axis, as a C-string.
- \param ymin Minimum value along the X-axis.
- \param ymax Maximum value along the X-axis.
- \param exit_on_anykey Exit function when any key is pressed.
- **/
- const CImg<T>& display_graph(CImgDisplay &disp,
- const unsigned int plot_type=1, const unsigned int vertex_type=1,
- const char *const labelx=0, const double xmin=0, const double xmax=0,
- const char *const labely=0, const double ymin=0, const double ymax=0,
- const bool exit_on_anykey=false) const {
- return _display_graph(disp,0,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey);
- }
- //! Display 1D graph in an interactive window \overloading.
- const CImg<T>& display_graph(const char *const title=0,
- const unsigned int plot_type=1, const unsigned int vertex_type=1,
- const char *const labelx=0, const double xmin=0, const double xmax=0,
- const char *const labely=0, const double ymin=0, const double ymax=0,
- const bool exit_on_anykey=false) const {
- CImgDisplay disp;
- return _display_graph(disp,title,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey);
- }
- const CImg<T>& _display_graph(CImgDisplay &disp, const char *const title=0,
- const unsigned int plot_type=1, const unsigned int vertex_type=1,
- const char *const labelx=0, const double xmin=0, const double xmax=0,
- const char *const labely=0, const double ymin=0, const double ymax=0,
- const bool exit_on_anykey=false) const {
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "display_graph(): Empty instance.",
- cimg_instance);
- if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0).
- set_title(title?"%s":"CImg<%s>",title?title:pixel_type());
- const ulongT siz = (ulongT)_width*_height*_depth, siz1 = std::max((ulongT)1,siz - 1);
- const unsigned int old_normalization = disp.normalization();
- disp.show().flush()._normalization = 0;
- double y0 = ymin, y1 = ymax, nxmin = xmin, nxmax = xmax;
- if (nxmin==nxmax) { nxmin = 0; nxmax = siz1; }
- int x0 = 0, x1 = width()*height()*depth() - 1, key = 0;
- for (bool reset_view = true; !key && !disp.is_closed(); ) {
- if (reset_view) { x0 = 0; x1 = width()*height()*depth() - 1; y0 = ymin; y1 = ymax; reset_view = false; }
- CImg<T> zoom(x1 - x0 + 1,1,1,spectrum());
- cimg_forC(*this,c) zoom.get_shared_channel(c) = CImg<T>(data(x0,0,0,c),x1 - x0 + 1,1,1,1,true);
- if (y0==y1) { y0 = zoom.min_max(y1); const double dy = y1 - y0; y0-=dy/20; y1+=dy/20; }
- if (y0==y1) { --y0; ++y1; }
- const CImg<intT> selection = zoom.get_select_graph(disp,plot_type,vertex_type,
- labelx,
- nxmin + x0*(nxmax - nxmin)/siz1,
- nxmin + x1*(nxmax - nxmin)/siz1,
- labely,y0,y1,true);
- const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
- if (selection[0]>=0) {
- if (selection[2]<0) reset_view = true;
- else {
- x1 = x0 + selection[2]; x0+=selection[0];
- if (selection[1]>=0 && selection[3]>=0) {
- y0 = y1 - selection[3]*(y1 - y0)/(disp.height() - 32);
- y1-=selection[1]*(y1 - y0)/(disp.height() - 32);
- }
- }
- } else {
- bool go_in = false, go_out = false, go_left = false, go_right = false, go_up = false, go_down = false;
- switch (key = (int)disp.key()) {
- case cimg::keyHOME : reset_view = true; key = 0; disp.set_key(); break;
- case cimg::keyPADADD : go_in = true; go_out = false; key = 0; disp.set_key(); break;
- case cimg::keyPADSUB : go_out = true; go_in = false; key = 0; disp.set_key(); break;
- case cimg::keyARROWLEFT : case cimg::keyPAD4 : go_left = true; go_right = false; key = 0; disp.set_key();
- break;
- case cimg::keyARROWRIGHT : case cimg::keyPAD6 : go_right = true; go_left = false; key = 0; disp.set_key();
- break;
- case cimg::keyARROWUP : case cimg::keyPAD8 : go_up = true; go_down = false; key = 0; disp.set_key(); break;
- case cimg::keyARROWDOWN : case cimg::keyPAD2 : go_down = true; go_up = false; key = 0; disp.set_key(); break;
- case cimg::keyPAD7 : go_left = true; go_up = true; key = 0; disp.set_key(); break;
- case cimg::keyPAD9 : go_right = true; go_up = true; key = 0; disp.set_key(); break;
- case cimg::keyPAD1 : go_left = true; go_down = true; key = 0; disp.set_key(); break;
- case cimg::keyPAD3 : go_right = true; go_down = true; key = 0; disp.set_key(); break;
- }
- if (disp.wheel()) {
- if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) go_up = !(go_down = disp.wheel()<0);
- else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) go_left = !(go_right = disp.wheel()>0);
- else go_out = !(go_in = disp.wheel()>0);
- key = 0;
- }
- if (go_in) {
- const int
- xsiz = x1 - x0,
- mx = (mouse_x - 16)*xsiz/(disp.width() - 32),
- cx = x0 + cimg::cut(mx,0,xsiz);
- if (x1 - x0>4) {
- x0 = cx - 7*(cx - x0)/8; x1 = cx + 7*(x1 - cx)/8;
- if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- const double
- ysiz = y1 - y0,
- my = (mouse_y - 16)*ysiz/(disp.height() - 32),
- cy = y1 - cimg::cut(my,0.,ysiz);
- y0 = cy - 7*(cy - y0)/8; y1 = cy + 7*(y1 - cy)/8;
- } else y0 = y1 = 0;
- }
- }
- if (go_out) {
- if (x0>0 || x1<(int)siz1) {
- const int delta_x = (x1 - x0)/8, ndelta_x = delta_x?delta_x:(siz>1);
- const double ndelta_y = (y1 - y0)/8;
- x0-=ndelta_x; x1+=ndelta_x;
- y0-=ndelta_y; y1+=ndelta_y;
- if (x0<0) { x1-=x0; x0 = 0; if (x1>=(int)siz) x1 = (int)siz1; }
- if (x1>=(int)siz) { x0-=(x1 - siz1); x1 = (int)siz1; if (x0<0) x0 = 0; }
- }
- }
- if (go_left) {
- const int delta = (x1 - x0)/5, ndelta = delta?delta:1;
- if (x0 - ndelta>=0) { x0-=ndelta; x1-=ndelta; }
- else { x1-=x0; x0 = 0; }
- go_left = false;
- }
- if (go_right) {
- const int delta = (x1 - x0)/5, ndelta = delta?delta:1;
- if (x1 + ndelta<(int)siz) { x0+=ndelta; x1+=ndelta; }
- else { x0+=(siz1 - x1); x1 = (int)siz1; }
- go_right = false;
- }
- if (go_up) {
- const double delta = (y1 - y0)/10, ndelta = delta?delta:1;
- y0+=ndelta; y1+=ndelta;
- go_up = false;
- }
- if (go_down) {
- const double delta = (y1 - y0)/10, ndelta = delta?delta:1;
- y0-=ndelta; y1-=ndelta;
- go_down = false;
- }
- }
- if (!exit_on_anykey && key && key!=(int)cimg::keyESC &&
- (key!=(int)cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
- disp.set_key(key,false);
- key = 0;
- }
- }
- disp._normalization = old_normalization;
- return *this;
- }
- //! Save image as a file.
- /**
- \param filename Filename, as a C-string.
- \param number When positive, represents an index added to the filename. Otherwise, no number is added.
- \param digits Number of digits used for adding the number to the filename.
- \note
- - The used file format is defined by the file extension in the filename \p filename.
- - Parameter \p number can be used to add a 6-digit number to the filename before saving.
- **/
- const CImg<T>& save(const char *const filename, const int number=-1, const unsigned int digits=6) const {
- if (!filename)
- throw CImgArgumentException(_cimg_instance
- "save(): Specified filename is (null).",
- cimg_instance);
- // Do not test for empty instances, since .cimg format is able to manage empty instances.
- const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.');
- const char *const ext = cimg::split_filename(filename);
- CImg<charT> nfilename(1024);
- const char *const fn = is_stdout?filename:(number>=0)?cimg::number_filename(filename,number,digits,nfilename):
- filename;
- #ifdef cimg_save_plugin
- cimg_save_plugin(fn);
- #endif
- #ifdef cimg_save_plugin1
- cimg_save_plugin1(fn);
- #endif
- #ifdef cimg_save_plugin2
- cimg_save_plugin2(fn);
- #endif
- #ifdef cimg_save_plugin3
- cimg_save_plugin3(fn);
- #endif
- #ifdef cimg_save_plugin4
- cimg_save_plugin4(fn);
- #endif
- #ifdef cimg_save_plugin5
- cimg_save_plugin5(fn);
- #endif
- #ifdef cimg_save_plugin6
- cimg_save_plugin6(fn);
- #endif
- #ifdef cimg_save_plugin7
- cimg_save_plugin7(fn);
- #endif
- #ifdef cimg_save_plugin8
- cimg_save_plugin8(fn);
- #endif
- // Text formats
- if (!cimg::strcasecmp(ext,"asc")) return save_ascii(fn);
- else if (!cimg::strcasecmp(ext,"csv") ||
- !cimg::strcasecmp(ext,"dlm") ||
- !cimg::strcasecmp(ext,"txt")) return save_dlm(fn);
- else if (!cimg::strcasecmp(ext,"cpp") ||
- !cimg::strcasecmp(ext,"hpp") ||
- !cimg::strcasecmp(ext,"h") ||
- !cimg::strcasecmp(ext,"c")) return save_cpp(fn);
- // 2D binary formats
- else if (!cimg::strcasecmp(ext,"bmp")) return save_bmp(fn);
- else if (!cimg::strcasecmp(ext,"jpg") ||
- !cimg::strcasecmp(ext,"jpeg") ||
- !cimg::strcasecmp(ext,"jpe") ||
- !cimg::strcasecmp(ext,"jfif") ||
- !cimg::strcasecmp(ext,"jif")) return save_jpeg(fn);
- else if (!cimg::strcasecmp(ext,"rgb")) return save_rgb(fn);
- else if (!cimg::strcasecmp(ext,"rgba")) return save_rgba(fn);
- else if (!cimg::strcasecmp(ext,"png")) return save_png(fn);
- else if (!cimg::strcasecmp(ext,"pgm") ||
- !cimg::strcasecmp(ext,"ppm") ||
- !cimg::strcasecmp(ext,"pnm")) return save_pnm(fn);
- else if (!cimg::strcasecmp(ext,"pnk")) return save_pnk(fn);
- else if (!cimg::strcasecmp(ext,"pfm")) return save_pfm(fn);
- else if (!cimg::strcasecmp(ext,"exr")) return save_exr(fn);
- else if (!cimg::strcasecmp(ext,"tif") ||
- !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
- // 3D binary formats
- else if (!*ext) {
- #ifdef cimg_use_zlib
- return save_cimg(fn,true);
- #else
- return save_cimg(fn,false);
- #endif
- } else if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
- else if (!cimg::strcasecmp(ext,"cimg")) return save_cimg(fn,false);
- else if (!cimg::strcasecmp(ext,"dcm")) return save_medcon_external(fn);
- else if (!cimg::strcasecmp(ext,"hdr") ||
- !cimg::strcasecmp(ext,"nii")) return save_analyze(fn);
- else if (!cimg::strcasecmp(ext,"inr")) return save_inr(fn);
- else if (!cimg::strcasecmp(ext,"mnc")) return save_minc2(fn);
- else if (!cimg::strcasecmp(ext,"pan")) return save_pandore(fn);
- else if (!cimg::strcasecmp(ext,"raw")) return save_raw(fn);
- // Archive files
- else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
- // Image sequences
- else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true);
- else if (!cimg::strcasecmp(ext,"avi") ||
- !cimg::strcasecmp(ext,"mov") ||
- !cimg::strcasecmp(ext,"asf") ||
- !cimg::strcasecmp(ext,"divx") ||
- !cimg::strcasecmp(ext,"flv") ||
- !cimg::strcasecmp(ext,"mpg") ||
- !cimg::strcasecmp(ext,"m1v") ||
- !cimg::strcasecmp(ext,"m2v") ||
- !cimg::strcasecmp(ext,"m4v") ||
- !cimg::strcasecmp(ext,"mjp") ||
- !cimg::strcasecmp(ext,"mp4") ||
- !cimg::strcasecmp(ext,"mkv") ||
- !cimg::strcasecmp(ext,"mpe") ||
- !cimg::strcasecmp(ext,"movie") ||
- !cimg::strcasecmp(ext,"ogm") ||
- !cimg::strcasecmp(ext,"ogg") ||
- !cimg::strcasecmp(ext,"ogv") ||
- !cimg::strcasecmp(ext,"qt") ||
- !cimg::strcasecmp(ext,"rm") ||
- !cimg::strcasecmp(ext,"vob") ||
- !cimg::strcasecmp(ext,"webm") ||
- !cimg::strcasecmp(ext,"wmv") ||
- !cimg::strcasecmp(ext,"xvid") ||
- !cimg::strcasecmp(ext,"mpeg")) return save_video(fn);
- return save_other(fn);
- }
- //! Save image as an ascii file.
- /**
- \param filename Filename, as a C-string.
- **/
- const CImg<T>& save_ascii(const char *const filename) const {
- return _save_ascii(0,filename);
- }
- //! Save image as an Ascii file \overloading.
- const CImg<T>& save_ascii(std::FILE *const file) const {
- return _save_ascii(file,0);
- }
- const CImg<T>& _save_ascii(std::FILE *const file, const char *const filename) const {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "save_ascii(): Specified filename is (null).",
- cimg_instance);
- std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
- std::fprintf(nfile,"%u %u %u %u\n",_width,_height,_depth,_spectrum);
- const T* ptrs = _data;
- cimg_forYZC(*this,y,z,c) {
- cimg_forX(*this,x) std::fprintf(nfile,"%.17g ",(double)*(ptrs++));
- std::fputc('\n',nfile);
- }
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Save image as a .cpp source file.
- /**
- \param filename Filename, as a C-string.
- **/
- const CImg<T>& save_cpp(const char *const filename) const {
- return _save_cpp(0,filename);
- }
- //! Save image as a .cpp source file \overloading.
- const CImg<T>& save_cpp(std::FILE *const file) const {
- return _save_cpp(file,0);
- }
- const CImg<T>& _save_cpp(std::FILE *const file, const char *const filename) const {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "save_cpp(): Specified filename is (null).",
- cimg_instance);
- std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
- CImg<charT> varname(1024); *varname = 0;
- if (filename) cimg_sscanf(cimg::basename(filename),"%1023[a-zA-Z0-9_]",varname._data);
- if (!*varname) cimg_snprintf(varname,varname._width,"unnamed");
- std::fprintf(nfile,
- "/* Define image '%s' of size %ux%ux%ux%u and type '%s' */\n"
- "%s data_%s[] = { %s\n ",
- varname._data,_width,_height,_depth,_spectrum,pixel_type(),pixel_type(),varname._data,
- is_empty()?"};":"");
- if (!is_empty()) for (ulongT off = 0, siz = size() - 1; off<=siz; ++off) {
- std::fprintf(nfile,cimg::type<T>::format(),cimg::type<T>::format((*this)[off]));
- if (off==siz) std::fprintf(nfile," };\n");
- else if (!((off + 1)%16)) std::fprintf(nfile,",\n ");
- else std::fprintf(nfile,", ");
- }
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Save image as a DLM file.
- /**
- \param filename Filename, as a C-string.
- **/
- const CImg<T>& save_dlm(const char *const filename) const {
- return _save_dlm(0,filename);
- }
- //! Save image as a DLM file \overloading.
- const CImg<T>& save_dlm(std::FILE *const file) const {
- return _save_dlm(file,0);
- }
- const CImg<T>& _save_dlm(std::FILE *const file, const char *const filename) const {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "save_dlm(): Specified filename is (null).",
- cimg_instance);
- if (is_empty()) { cimg::fempty(file,filename); return *this; }
- if (_depth>1)
- cimg::warn(_cimg_instance
- "save_dlm(): Instance is volumetric, values along Z will be unrolled in file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- if (_spectrum>1)
- cimg::warn(_cimg_instance
- "save_dlm(): Instance is multispectral, values along C will be unrolled in file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
- const T* ptrs = _data;
- cimg_forYZC(*this,y,z,c) {
- cimg_forX(*this,x) std::fprintf(nfile,"%.17g%s",(double)*(ptrs++),(x==width() - 1)?"":",");
- std::fputc('\n',nfile);
- }
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Save image as a BMP file.
- /**
- \param filename Filename, as a C-string.
- **/
- const CImg<T>& save_bmp(const char *const filename) const {
- return _save_bmp(0,filename);
- }
- //! Save image as a BMP file \overloading.
- const CImg<T>& save_bmp(std::FILE *const file) const {
- return _save_bmp(file,0);
- }
- const CImg<T>& _save_bmp(std::FILE *const file, const char *const filename) const {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "save_bmp(): Specified filename is (null).",
- cimg_instance);
- if (is_empty()) { cimg::fempty(file,filename); return *this; }
- if (_depth>1)
- cimg::warn(_cimg_instance
- "save_bmp(): Instance is volumetric, only the first slice will be saved in file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- if (_spectrum>3)
- cimg::warn(_cimg_instance
- "save_bmp(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
- CImg<ucharT> header(54,1,1,1,0);
- unsigned char align_buf[4] = {};
- const unsigned int
- align = (4 - (3*_width)%4)%4,
- buf_size = (3*_width + align)*height(),
- file_size = 54 + buf_size;
- header[0] = 'B'; header[1] = 'M';
- header[0x02] = file_size&0xFF;
- header[0x03] = (file_size>>8)&0xFF;
- header[0x04] = (file_size>>16)&0xFF;
- header[0x05] = (file_size>>24)&0xFF;
- header[0x0A] = 0x36;
- header[0x0E] = 0x28;
- header[0x12] = _width&0xFF;
- header[0x13] = (_width>>8)&0xFF;
- header[0x14] = (_width>>16)&0xFF;
- header[0x15] = (_width>>24)&0xFF;
- header[0x16] = _height&0xFF;
- header[0x17] = (_height>>8)&0xFF;
- header[0x18] = (_height>>16)&0xFF;
- header[0x19] = (_height>>24)&0xFF;
- header[0x1A] = 1;
- header[0x1B] = 0;
- header[0x1C] = 24;
- header[0x1D] = 0;
- header[0x22] = buf_size&0xFF;
- header[0x23] = (buf_size>>8)&0xFF;
- header[0x24] = (buf_size>>16)&0xFF;
- header[0x25] = (buf_size>>24)&0xFF;
- header[0x27] = 0x1;
- header[0x2B] = 0x1;
- cimg::fwrite(header._data,54,nfile);
- const T
- *ptr_r = data(0,_height - 1,0,0),
- *ptr_g = (_spectrum>=2)?data(0,_height - 1,0,1):0,
- *ptr_b = (_spectrum>=3)?data(0,_height - 1,0,2):0;
- switch (_spectrum) {
- case 1 : {
- cimg_forY(*this,y) {
- cimg_forX(*this,x) {
- const unsigned char val = (unsigned char)*(ptr_r++);
- std::fputc(val,nfile); std::fputc(val,nfile); std::fputc(val,nfile);
- }
- cimg::fwrite(align_buf,align,nfile);
- ptr_r-=2*_width;
- }
- } break;
- case 2 : {
- cimg_forY(*this,y) {
- cimg_forX(*this,x) {
- std::fputc(0,nfile);
- std::fputc((unsigned char)(*(ptr_g++)),nfile);
- std::fputc((unsigned char)(*(ptr_r++)),nfile);
- }
- cimg::fwrite(align_buf,align,nfile);
- ptr_r-=2*_width; ptr_g-=2*_width;
- }
- } break;
- default : {
- cimg_forY(*this,y) {
- cimg_forX(*this,x) {
- std::fputc((unsigned char)(*(ptr_b++)),nfile);
- std::fputc((unsigned char)(*(ptr_g++)),nfile);
- std::fputc((unsigned char)(*(ptr_r++)),nfile);
- }
- cimg::fwrite(align_buf,align,nfile);
- ptr_r-=2*_width; ptr_g-=2*_width; ptr_b-=2*_width;
- }
- }
- }
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Save image as a JPEG file.
- /**
- \param filename Filename, as a C-string.
- \param quality Image quality (in %)
- **/
- const CImg<T>& save_jpeg(const char *const filename, const unsigned int quality=100) const {
- return _save_jpeg(0,filename,quality);
- }
- //! Save image as a JPEG file \overloading.
- const CImg<T>& save_jpeg(std::FILE *const file, const unsigned int quality=100) const {
- return _save_jpeg(file,0,quality);
- }
- const CImg<T>& _save_jpeg(std::FILE *const file, const char *const filename, const unsigned int quality) const {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "save_jpeg(): Specified filename is (null).",
- cimg_instance);
- if (is_empty()) { cimg::fempty(file,filename); return *this; }
- if (_depth>1)
- cimg::warn(_cimg_instance
- "save_jpeg(): Instance is volumetric, only the first slice will be saved in file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- #ifndef cimg_use_jpeg
- if (!file) return save_other(filename,quality);
- else throw CImgIOException(_cimg_instance
- "save_jpeg(): Unable to save data in '(*FILE)' unless libjpeg is enabled.",
- cimg_instance);
- #else
- unsigned int dimbuf = 0;
- J_COLOR_SPACE colortype = JCS_RGB;
- switch (_spectrum) {
- case 1 : dimbuf = 1; colortype = JCS_GRAYSCALE; break;
- case 2 : dimbuf = 3; colortype = JCS_RGB; break;
- case 3 : dimbuf = 3; colortype = JCS_RGB; break;
- default : dimbuf = 4; colortype = JCS_CMYK; break;
- }
- // Call libjpeg functions
- struct jpeg_compress_struct cinfo;
- struct jpeg_error_mgr jerr;
- cinfo.err = jpeg_std_error(&jerr);
- jpeg_create_compress(&cinfo);
- std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
- jpeg_stdio_dest(&cinfo,nfile);
- cinfo.image_width = _width;
- cinfo.image_height = _height;
- cinfo.input_components = dimbuf;
- cinfo.in_color_space = colortype;
- jpeg_set_defaults(&cinfo);
- jpeg_set_quality(&cinfo,quality<100?quality:100,TRUE);
- jpeg_start_compress(&cinfo,TRUE);
- JSAMPROW row_pointer[1];
- CImg<ucharT> buffer(_width*dimbuf);
- while (cinfo.next_scanline<cinfo.image_height) {
- unsigned char *ptrd = buffer._data;
- // Fill pixel buffer
- switch (_spectrum) {
- case 1 : { // Greyscale images
- const T *ptr_g = data(0, cinfo.next_scanline);
- for (unsigned int b = 0; b<cinfo.image_width; b++)
- *(ptrd++) = (unsigned char)*(ptr_g++);
- } break;
- case 2 : { // RG images
- const T *ptr_r = data(0,cinfo.next_scanline,0,0),
- *ptr_g = data(0,cinfo.next_scanline,0,1);
- for (unsigned int b = 0; b<cinfo.image_width; ++b) {
- *(ptrd++) = (unsigned char)*(ptr_r++);
- *(ptrd++) = (unsigned char)*(ptr_g++);
- *(ptrd++) = 0;
- }
- } break;
- case 3 : { // RGB images
- const T *ptr_r = data(0,cinfo.next_scanline,0,0),
- *ptr_g = data(0,cinfo.next_scanline,0,1),
- *ptr_b = data(0,cinfo.next_scanline,0,2);
- for (unsigned int b = 0; b<cinfo.image_width; ++b) {
- *(ptrd++) = (unsigned char)*(ptr_r++);
- *(ptrd++) = (unsigned char)*(ptr_g++);
- *(ptrd++) = (unsigned char)*(ptr_b++);
- }
- } break;
- default : { // CMYK images
- const T *ptr_r = data(0,cinfo.next_scanline,0,0),
- *ptr_g = data(0,cinfo.next_scanline,0,1),
- *ptr_b = data(0,cinfo.next_scanline,0,2),
- *ptr_a = data(0,cinfo.next_scanline,0,3);
- for (unsigned int b = 0; b<cinfo.image_width; ++b) {
- *(ptrd++) = (unsigned char)*(ptr_r++);
- *(ptrd++) = (unsigned char)*(ptr_g++);
- *(ptrd++) = (unsigned char)*(ptr_b++);
- *(ptrd++) = (unsigned char)*(ptr_a++);
- }
- }
- }
- *row_pointer = buffer._data;
- jpeg_write_scanlines(&cinfo,row_pointer,1);
- }
- jpeg_finish_compress(&cinfo);
- if (!file) cimg::fclose(nfile);
- jpeg_destroy_compress(&cinfo);
- return *this;
- #endif
- }
- //! Save image, using built-in ImageMagick++ library.
- /**
- \param filename Filename, as a C-string.
- \param bytes_per_pixel Force the number of bytes per pixel for the saving, when possible.
- **/
- const CImg<T>& save_magick(const char *const filename, const unsigned int bytes_per_pixel=0) const {
- if (!filename)
- throw CImgArgumentException(_cimg_instance
- "save_magick(): Specified filename is (null).",
- cimg_instance);
- if (is_empty()) { cimg::fempty(0,filename); return *this; }
- #ifdef cimg_use_magick
- double stmin, stmax = (double)max_min(stmin);
- if (_depth>1)
- cimg::warn(_cimg_instance
- "save_magick(): Instance is volumetric, only the first slice will be saved in file '%s'.",
- cimg_instance,
- filename);
- if (_spectrum>3)
- cimg::warn(_cimg_instance
- "save_magick(): Instance is multispectral, only the three first channels will be "
- "saved in file '%s'.",
- cimg_instance,
- filename);
- if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
- cimg::warn(_cimg_instance
- "save_magick(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
- cimg_instance,
- stmin,stmax,filename);
- Magick::Image image(Magick::Geometry(_width,_height),"black");
- image.type(Magick::TrueColorType);
- image.depth(bytes_per_pixel?(8*bytes_per_pixel):(stmax>=256?16:8));
- const T
- *ptr_r = data(0,0,0,0),
- *ptr_g = _spectrum>1?data(0,0,0,1):0,
- *ptr_b = _spectrum>2?data(0,0,0,2):0;
- Magick::PixelPacket *pixels = image.getPixels(0,0,_width,_height);
- switch (_spectrum) {
- case 1 : // Scalar images
- for (ulongT off = (ulongT)_width*_height; off; --off) {
- pixels->red = pixels->green = pixels->blue = (Magick::Quantum)*(ptr_r++);
- ++pixels;
- }
- break;
- case 2 : // RG images
- for (ulongT off = (ulongT)_width*_height; off; --off) {
- pixels->red = (Magick::Quantum)*(ptr_r++);
- pixels->green = (Magick::Quantum)*(ptr_g++);
- pixels->blue = 0; ++pixels;
- }
- break;
- default : // RGB images
- for (ulongT off = (ulongT)_width*_height; off; --off) {
- pixels->red = (Magick::Quantum)*(ptr_r++);
- pixels->green = (Magick::Quantum)*(ptr_g++);
- pixels->blue = (Magick::Quantum)*(ptr_b++);
- ++pixels;
- }
- }
- image.syncPixels();
- image.write(filename);
- return *this;
- #else
- cimg::unused(bytes_per_pixel);
- throw CImgIOException(_cimg_instance
- "save_magick(): Unable to save file '%s' unless libMagick++ is enabled.",
- cimg_instance,
- filename);
- #endif
- }
- //! Save image as a PNG file.
- /**
- \param filename Filename, as a C-string.
- \param bytes_per_pixel Force the number of bytes per pixels for the saving, when possible.
- **/
- const CImg<T>& save_png(const char *const filename, const unsigned int bytes_per_pixel=0) const {
- return _save_png(0,filename,bytes_per_pixel);
- }
- //! Save image as a PNG file \overloading.
- const CImg<T>& save_png(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
- return _save_png(file,0,bytes_per_pixel);
- }
- const CImg<T>& _save_png(std::FILE *const file, const char *const filename,
- const unsigned int bytes_per_pixel=0) const {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "save_png(): Specified filename is (null).",
- cimg_instance);
- if (is_empty()) { cimg::fempty(file,filename); return *this; }
- #ifndef cimg_use_png
- cimg::unused(bytes_per_pixel);
- if (!file) return save_other(filename);
- else throw CImgIOException(_cimg_instance
- "save_png(): Unable to save data in '(*FILE)' unless libpng is enabled.",
- cimg_instance);
- #else
- #if defined __GNUC__
- const char *volatile nfilename = filename; // Use 'volatile' to avoid (wrong) g++ warning
- std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"wb");
- volatile double stmin, stmax = (double)max_min(stmin);
- #else
- const char *nfilename = filename;
- std::FILE *nfile = file?file:cimg::fopen(nfilename,"wb");
- double stmin, stmax = (double)max_min(stmin);
- #endif
- if (_depth>1)
- cimg::warn(_cimg_instance
- "save_png(): Instance is volumetric, only the first slice will be saved in file '%s'.",
- cimg_instance,
- filename);
- if (_spectrum>4)
- cimg::warn(_cimg_instance
- "save_png(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
- cimg_instance,
- filename);
- if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
- cimg::warn(_cimg_instance
- "save_png(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
- cimg_instance,
- stmin,stmax,filename);
- // Setup PNG structures for write
- png_voidp user_error_ptr = 0;
- png_error_ptr user_error_fn = 0, user_warning_fn = 0;
- png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,user_error_ptr, user_error_fn,
- user_warning_fn);
- if (!png_ptr){
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimg_instance
- "save_png(): Failed to initialize 'png_ptr' structure when saving file '%s'.",
- cimg_instance,
- nfilename?nfilename:"(FILE*)");
- }
- png_infop info_ptr = png_create_info_struct(png_ptr);
- if (!info_ptr) {
- png_destroy_write_struct(&png_ptr,(png_infopp)0);
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimg_instance
- "save_png(): Failed to initialize 'info_ptr' structure when saving file '%s'.",
- cimg_instance,
- nfilename?nfilename:"(FILE*)");
- }
- if (setjmp(png_jmpbuf(png_ptr))) {
- png_destroy_write_struct(&png_ptr, &info_ptr);
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimg_instance
- "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.",
- cimg_instance,
- nfilename?nfilename:"(FILE*)");
- }
- png_init_io(png_ptr, nfile);
- const int bit_depth = bytes_per_pixel?(bytes_per_pixel*8):(stmax>=256?16:8);
- int color_type;
- switch (spectrum()) {
- case 1 : color_type = PNG_COLOR_TYPE_GRAY; break;
- case 2 : color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break;
- case 3 : color_type = PNG_COLOR_TYPE_RGB; break;
- default : color_type = PNG_COLOR_TYPE_RGB_ALPHA;
- }
- const int interlace_type = PNG_INTERLACE_NONE;
- const int compression_type = PNG_COMPRESSION_TYPE_DEFAULT;
- const int filter_method = PNG_FILTER_TYPE_DEFAULT;
- png_set_IHDR(png_ptr,info_ptr,_width,_height,bit_depth,color_type,interlace_type,compression_type,filter_method);
- png_write_info(png_ptr,info_ptr);
- const int byte_depth = bit_depth>>3;
- const int numChan = spectrum()>4?4:spectrum();
- const int pixel_bit_depth_flag = numChan * (bit_depth - 1);
- // Allocate Memory for Image Save and Fill pixel data
- png_bytep *const imgData = new png_byte*[_height];
- for (unsigned int row = 0; row<_height; ++row) imgData[row] = new png_byte[byte_depth*numChan*_width];
- const T *pC0 = data(0,0,0,0);
- switch (pixel_bit_depth_flag) {
- case 7 : { // Gray 8-bit
- cimg_forY(*this,y) {
- unsigned char *ptrd = imgData[y];
- cimg_forX(*this,x) *(ptrd++) = (unsigned char)*(pC0++);
- }
- } break;
- case 14 : { // Gray w/ Alpha 8-bit
- const T *pC1 = data(0,0,0,1);
- cimg_forY(*this,y) {
- unsigned char *ptrd = imgData[y];
- cimg_forX(*this,x) {
- *(ptrd++) = (unsigned char)*(pC0++);
- *(ptrd++) = (unsigned char)*(pC1++);
- }
- }
- } break;
- case 21 : { // RGB 8-bit
- const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
- cimg_forY(*this,y) {
- unsigned char *ptrd = imgData[y];
- cimg_forX(*this,x) {
- *(ptrd++) = (unsigned char)*(pC0++);
- *(ptrd++) = (unsigned char)*(pC1++);
- *(ptrd++) = (unsigned char)*(pC2++);
- }
- }
- } break;
- case 28 : { // RGB x/ Alpha 8-bit
- const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
- cimg_forY(*this,y){
- unsigned char *ptrd = imgData[y];
- cimg_forX(*this,x){
- *(ptrd++) = (unsigned char)*(pC0++);
- *(ptrd++) = (unsigned char)*(pC1++);
- *(ptrd++) = (unsigned char)*(pC2++);
- *(ptrd++) = (unsigned char)*(pC3++);
- }
- }
- } break;
- case 15 : { // Gray 16-bit
- cimg_forY(*this,y){
- unsigned short *ptrd = (unsigned short*)(imgData[y]);
- cimg_forX(*this,x) *(ptrd++) = (unsigned short)*(pC0++);
- if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],_width);
- }
- } break;
- case 30 : { // Gray w/ Alpha 16-bit
- const T *pC1 = data(0,0,0,1);
- cimg_forY(*this,y){
- unsigned short *ptrd = (unsigned short*)(imgData[y]);
- cimg_forX(*this,x) {
- *(ptrd++) = (unsigned short)*(pC0++);
- *(ptrd++) = (unsigned short)*(pC1++);
- }
- if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],2*_width);
- }
- } break;
- case 45 : { // RGB 16-bit
- const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
- cimg_forY(*this,y) {
- unsigned short *ptrd = (unsigned short*)(imgData[y]);
- cimg_forX(*this,x) {
- *(ptrd++) = (unsigned short)*(pC0++);
- *(ptrd++) = (unsigned short)*(pC1++);
- *(ptrd++) = (unsigned short)*(pC2++);
- }
- if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],3*_width);
- }
- } break;
- case 60 : { // RGB w/ Alpha 16-bit
- const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
- cimg_forY(*this,y) {
- unsigned short *ptrd = (unsigned short*)(imgData[y]);
- cimg_forX(*this,x) {
- *(ptrd++) = (unsigned short)*(pC0++);
- *(ptrd++) = (unsigned short)*(pC1++);
- *(ptrd++) = (unsigned short)*(pC2++);
- *(ptrd++) = (unsigned short)*(pC3++);
- }
- if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],4*_width);
- }
- } break;
- default :
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimg_instance
- "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.",
- cimg_instance,
- nfilename?nfilename:"(FILE*)");
- }
- png_write_image(png_ptr,imgData);
- png_write_end(png_ptr,info_ptr);
- png_destroy_write_struct(&png_ptr, &info_ptr);
- // Deallocate Image Write Memory
- cimg_forY(*this,n) delete[] imgData[n];
- delete[] imgData;
- if (!file) cimg::fclose(nfile);
- return *this;
- #endif
- }
- //! Save image as a PNM file.
- /**
- \param filename Filename, as a C-string.
- \param bytes_per_pixel Force the number of bytes per pixels for the saving.
- **/
- const CImg<T>& save_pnm(const char *const filename, const unsigned int bytes_per_pixel=0) const {
- return _save_pnm(0,filename,bytes_per_pixel);
- }
- //! Save image as a PNM file \overloading.
- const CImg<T>& save_pnm(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
- return _save_pnm(file,0,bytes_per_pixel);
- }
- const CImg<T>& _save_pnm(std::FILE *const file, const char *const filename,
- const unsigned int bytes_per_pixel=0) const {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "save_pnm(): Specified filename is (null).",
- cimg_instance);
- if (is_empty()) { cimg::fempty(file,filename); return *this; }
- double stmin, stmax = (double)max_min(stmin);
- if (_depth>1)
- cimg::warn(_cimg_instance
- "save_pnm(): Instance is volumetric, only the first slice will be saved in file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- if (_spectrum>3)
- cimg::warn(_cimg_instance
- "save_pnm(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
- cimg::warn(_cimg_instance
- "save_pnm(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
- cimg_instance,
- stmin,stmax,filename?filename:"(FILE*)");
- std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
- const T
- *ptr_r = data(0,0,0,0),
- *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
- *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
- const ulongT buf_size = std::min((ulongT)(1024*1024),(ulongT)(_width*_height*(_spectrum==1?1UL:3UL)));
- std::fprintf(nfile,"P%c\n%u %u\n%u\n",
- (_spectrum==1?'5':'6'),_width,_height,stmax<256?255:(stmax<4096?4095:65535));
- switch (_spectrum) {
- case 1 : { // Scalar image
- if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PGM 8 bits
- CImg<ucharT> buf((unsigned int)buf_size);
- for (longT to_write = (longT)width()*height(); to_write>0; ) {
- const ulongT N = std::min((ulongT)to_write,buf_size);
- unsigned char *ptrd = buf._data;
- for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr_r++);
- cimg::fwrite(buf._data,N,nfile);
- to_write-=N;
- }
- } else { // Binary PGM 16 bits
- CImg<ushortT> buf((unsigned int)buf_size);
- for (longT to_write = (longT)width()*height(); to_write>0; ) {
- const ulongT N = std::min((ulongT)to_write,buf_size);
- unsigned short *ptrd = buf._data;
- for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned short)*(ptr_r++);
- if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
- cimg::fwrite(buf._data,N,nfile);
- to_write-=N;
- }
- }
- } break;
- case 2 : { // RG image
- if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
- CImg<ucharT> buf((unsigned int)buf_size);
- for (longT to_write = (longT)width()*height(); to_write>0; ) {
- const ulongT N = std::min((ulongT)to_write,buf_size/3);
- unsigned char *ptrd = buf._data;
- for (ulongT i = N; i>0; --i) {
- *(ptrd++) = (unsigned char)*(ptr_r++);
- *(ptrd++) = (unsigned char)*(ptr_g++);
- *(ptrd++) = 0;
- }
- cimg::fwrite(buf._data,3*N,nfile);
- to_write-=N;
- }
- } else { // Binary PPM 16 bits
- CImg<ushortT> buf((unsigned int)buf_size);
- for (longT to_write = (longT)width()*height(); to_write>0; ) {
- const ulongT N = std::min((ulongT)to_write,buf_size/3);
- unsigned short *ptrd = buf._data;
- for (ulongT i = N; i>0; --i) {
- *(ptrd++) = (unsigned short)*(ptr_r++);
- *(ptrd++) = (unsigned short)*(ptr_g++);
- *(ptrd++) = 0;
- }
- if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
- cimg::fwrite(buf._data,3*N,nfile);
- to_write-=N;
- }
- }
- } break;
- default : { // RGB image
- if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
- CImg<ucharT> buf((unsigned int)buf_size);
- for (longT to_write = (longT)width()*height(); to_write>0; ) {
- const ulongT N = std::min((ulongT)to_write,buf_size/3);
- unsigned char *ptrd = buf._data;
- for (ulongT i = N; i>0; --i) {
- *(ptrd++) = (unsigned char)*(ptr_r++);
- *(ptrd++) = (unsigned char)*(ptr_g++);
- *(ptrd++) = (unsigned char)*(ptr_b++);
- }
- cimg::fwrite(buf._data,3*N,nfile);
- to_write-=N;
- }
- } else { // Binary PPM 16 bits
- CImg<ushortT> buf((unsigned int)buf_size);
- for (longT to_write = (longT)width()*height(); to_write>0; ) {
- const ulongT N = std::min((ulongT)to_write,buf_size/3);
- unsigned short *ptrd = buf._data;
- for (ulongT i = N; i>0; --i) {
- *(ptrd++) = (unsigned short)*(ptr_r++);
- *(ptrd++) = (unsigned short)*(ptr_g++);
- *(ptrd++) = (unsigned short)*(ptr_b++);
- }
- if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
- cimg::fwrite(buf._data,3*N,nfile);
- to_write-=N;
- }
- }
- }
- }
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Save image as a PNK file.
- /**
- \param filename Filename, as a C-string.
- **/
- const CImg<T>& save_pnk(const char *const filename) const {
- return _save_pnk(0,filename);
- }
- //! Save image as a PNK file \overloading.
- const CImg<T>& save_pnk(std::FILE *const file) const {
- return _save_pnk(file,0);
- }
- const CImg<T>& _save_pnk(std::FILE *const file, const char *const filename) const {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "save_pnk(): Specified filename is (null).",
- cimg_instance);
- if (is_empty()) { cimg::fempty(file,filename); return *this; }
- if (_spectrum>1)
- cimg::warn(_cimg_instance
- "save_pnk(): Instance is multispectral, only the first channel will be saved in file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- const ulongT buf_size = std::min((ulongT)1024*1024,(ulongT)_width*_height*_depth);
- std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
- const T *ptr = data(0,0,0,0);
- if (!cimg::type<T>::is_float() && sizeof(T)==1 && _depth<2) // Can be saved as regular PNM file
- _save_pnm(file,filename,0);
- else if (!cimg::type<T>::is_float() && sizeof(T)==1) { // Save as extended P5 file: Binary byte-valued 3D
- std::fprintf(nfile,"P5\n%u %u %u\n255\n",_width,_height,_depth);
- CImg<ucharT> buf((unsigned int)buf_size);
- for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) {
- const ulongT N = std::min((ulongT)to_write,buf_size);
- unsigned char *ptrd = buf._data;
- for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr++);
- cimg::fwrite(buf._data,N,nfile);
- to_write-=N;
- }
- } else if (!cimg::type<T>::is_float()) { // Save as P8: Binary int32-valued 3D
- if (_depth>1) std::fprintf(nfile,"P8\n%u %u %u\n%d\n",_width,_height,_depth,(int)max());
- else std::fprintf(nfile,"P8\n%u %u\n%d\n",_width,_height,(int)max());
- CImg<intT> buf((unsigned int)buf_size);
- for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) {
- const ulongT N = std::min((ulongT)to_write,buf_size);
- int *ptrd = buf._data;
- for (ulongT i = N; i>0; --i) *(ptrd++) = (int)*(ptr++);
- cimg::fwrite(buf._data,N,nfile);
- to_write-=N;
- }
- } else { // Save as P9: Binary float-valued 3D
- if (_depth>1) std::fprintf(nfile,"P9\n%u %u %u\n%g\n",_width,_height,_depth,(double)max());
- else std::fprintf(nfile,"P9\n%u %u\n%g\n",_width,_height,(double)max());
- CImg<floatT> buf((unsigned int)buf_size);
- for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) {
- const ulongT N = std::min((ulongT)to_write,buf_size);
- float *ptrd = buf._data;
- for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr++);
- cimg::fwrite(buf._data,N,nfile);
- to_write-=N;
- }
- }
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Save image as a PFM file.
- /**
- \param filename Filename, as a C-string.
- **/
- const CImg<T>& save_pfm(const char *const filename) const {
- get_mirror('y')._save_pfm(0,filename);
- return *this;
- }
- //! Save image as a PFM file \overloading.
- const CImg<T>& save_pfm(std::FILE *const file) const {
- get_mirror('y')._save_pfm(file,0);
- return *this;
- }
- const CImg<T>& _save_pfm(std::FILE *const file, const char *const filename) const {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "save_pfm(): Specified filename is (null).",
- cimg_instance);
- if (is_empty()) { cimg::fempty(file,filename); return *this; }
- if (_depth>1)
- cimg::warn(_cimg_instance
- "save_pfm(): Instance is volumetric, only the first slice will be saved in file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- if (_spectrum>3)
- cimg::warn(_cimg_instance
- "save_pfm(): image instance is multispectral, only the three first channels will be saved "
- "in file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
- const T
- *ptr_r = data(0,0,0,0),
- *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
- *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
- const unsigned int buf_size = std::min(1024*1024U,_width*_height*(_spectrum==1?1:3));
- std::fprintf(nfile,"P%c\n%u %u\n1.0\n",
- (_spectrum==1?'f':'F'),_width,_height);
- switch (_spectrum) {
- case 1 : { // Scalar image
- CImg<floatT> buf(buf_size);
- for (longT to_write = (longT)width()*height(); to_write>0; ) {
- const ulongT N = std::min((ulongT)to_write,(ulongT)buf_size);
- float *ptrd = buf._data;
- for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr_r++);
- if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
- cimg::fwrite(buf._data,N,nfile);
- to_write-=N;
- }
- } break;
- case 2 : { // RG image
- CImg<floatT> buf(buf_size);
- for (longT to_write = (longT)width()*height(); to_write>0; ) {
- const unsigned int N = std::min((unsigned int)to_write,buf_size/3);
- float *ptrd = buf._data;
- for (ulongT i = N; i>0; --i) {
- *(ptrd++) = (float)*(ptr_r++);
- *(ptrd++) = (float)*(ptr_g++);
- *(ptrd++) = 0;
- }
- if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
- cimg::fwrite(buf._data,3*N,nfile);
- to_write-=N;
- }
- } break;
- default : { // RGB image
- CImg<floatT> buf(buf_size);
- for (longT to_write = (longT)width()*height(); to_write>0; ) {
- const unsigned int N = std::min((unsigned int)to_write,buf_size/3);
- float *ptrd = buf._data;
- for (ulongT i = N; i>0; --i) {
- *(ptrd++) = (float)*(ptr_r++);
- *(ptrd++) = (float)*(ptr_g++);
- *(ptrd++) = (float)*(ptr_b++);
- }
- if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
- cimg::fwrite(buf._data,3*N,nfile);
- to_write-=N;
- }
- }
- }
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Save image as a RGB file.
- /**
- \param filename Filename, as a C-string.
- **/
- const CImg<T>& save_rgb(const char *const filename) const {
- return _save_rgb(0,filename);
- }
- //! Save image as a RGB file \overloading.
- const CImg<T>& save_rgb(std::FILE *const file) const {
- return _save_rgb(file,0);
- }
- const CImg<T>& _save_rgb(std::FILE *const file, const char *const filename) const {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "save_rgb(): Specified filename is (null).",
- cimg_instance);
- if (is_empty()) { cimg::fempty(file,filename); return *this; }
- if (_spectrum!=3)
- cimg::warn(_cimg_instance
- "save_rgb(): image instance has not exactly 3 channels, for file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
- const ulongT wh = (ulongT)_width*_height;
- unsigned char *const buffer = new unsigned char[3*wh], *nbuffer = buffer;
- const T
- *ptr1 = data(0,0,0,0),
- *ptr2 = _spectrum>1?data(0,0,0,1):0,
- *ptr3 = _spectrum>2?data(0,0,0,2):0;
- switch (_spectrum) {
- case 1 : { // Scalar image
- for (ulongT k = 0; k<wh; ++k) {
- const unsigned char val = (unsigned char)*(ptr1++);
- *(nbuffer++) = val;
- *(nbuffer++) = val;
- *(nbuffer++) = val;
- }
- } break;
- case 2 : { // RG image
- for (ulongT k = 0; k<wh; ++k) {
- *(nbuffer++) = (unsigned char)(*(ptr1++));
- *(nbuffer++) = (unsigned char)(*(ptr2++));
- *(nbuffer++) = 0;
- }
- } break;
- default : { // RGB image
- for (ulongT k = 0; k<wh; ++k) {
- *(nbuffer++) = (unsigned char)(*(ptr1++));
- *(nbuffer++) = (unsigned char)(*(ptr2++));
- *(nbuffer++) = (unsigned char)(*(ptr3++));
- }
- }
- }
- cimg::fwrite(buffer,3*wh,nfile);
- if (!file) cimg::fclose(nfile);
- delete[] buffer;
- return *this;
- }
- //! Save image as a RGBA file.
- /**
- \param filename Filename, as a C-string.
- **/
- const CImg<T>& save_rgba(const char *const filename) const {
- return _save_rgba(0,filename);
- }
- //! Save image as a RGBA file \overloading.
- const CImg<T>& save_rgba(std::FILE *const file) const {
- return _save_rgba(file,0);
- }
- const CImg<T>& _save_rgba(std::FILE *const file, const char *const filename) const {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "save_rgba(): Specified filename is (null).",
- cimg_instance);
- if (is_empty()) { cimg::fempty(file,filename); return *this; }
- if (_spectrum!=4)
- cimg::warn(_cimg_instance
- "save_rgba(): image instance has not exactly 4 channels, for file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
- const ulongT wh = (ulongT)_width*_height;
- unsigned char *const buffer = new unsigned char[4*wh], *nbuffer = buffer;
- const T
- *ptr1 = data(0,0,0,0),
- *ptr2 = _spectrum>1?data(0,0,0,1):0,
- *ptr3 = _spectrum>2?data(0,0,0,2):0,
- *ptr4 = _spectrum>3?data(0,0,0,3):0;
- switch (_spectrum) {
- case 1 : { // Scalar images
- for (ulongT k = 0; k<wh; ++k) {
- const unsigned char val = (unsigned char)*(ptr1++);
- *(nbuffer++) = val;
- *(nbuffer++) = val;
- *(nbuffer++) = val;
- *(nbuffer++) = 255;
- }
- } break;
- case 2 : { // RG images
- for (ulongT k = 0; k<wh; ++k) {
- *(nbuffer++) = (unsigned char)(*(ptr1++));
- *(nbuffer++) = (unsigned char)(*(ptr2++));
- *(nbuffer++) = 0;
- *(nbuffer++) = 255;
- }
- } break;
- case 3 : { // RGB images
- for (ulongT k = 0; k<wh; ++k) {
- *(nbuffer++) = (unsigned char)(*(ptr1++));
- *(nbuffer++) = (unsigned char)(*(ptr2++));
- *(nbuffer++) = (unsigned char)(*(ptr3++));
- *(nbuffer++) = 255;
- }
- } break;
- default : { // RGBA images
- for (ulongT k = 0; k<wh; ++k) {
- *(nbuffer++) = (unsigned char)(*(ptr1++));
- *(nbuffer++) = (unsigned char)(*(ptr2++));
- *(nbuffer++) = (unsigned char)(*(ptr3++));
- *(nbuffer++) = (unsigned char)(*(ptr4++));
- }
- }
- }
- cimg::fwrite(buffer,4*wh,nfile);
- if (!file) cimg::fclose(nfile);
- delete[] buffer;
- return *this;
- }
- //! Save image as a TIFF file.
- /**
- \param filename Filename, as a C-string.
- \param compression_type Type of data compression. Can be <tt>{ 0=None | 1=LZW | 2=JPEG }</tt>.
- \param[out] voxel_size Voxel size, to be stored in the filename.
- \param[out] description Description, to be stored in the filename.
- \param use_bigtiff Allow to save big tiff files (>4Gb).
- \note
- - libtiff support is enabled by defining the precompilation
- directive \c cimg_use_tiff.
- - When libtiff is enabled, 2D and 3D (multipage) several
- channel per pixel are supported for
- <tt>char,uchar,short,ushort,float</tt> and \c double pixel types.
- - If \c cimg_use_tiff is not defined at compile time the
- function uses CImg<T>&save_other(const char*).
- **/
- const CImg<T>& save_tiff(const char *const filename, const unsigned int compression_type=0,
- const float *const voxel_size=0, const char *const description=0,
- const bool use_bigtiff=true) const {
- if (!filename)
- throw CImgArgumentException(_cimg_instance
- "save_tiff(): Specified filename is (null).",
- cimg_instance);
- if (is_empty()) { cimg::fempty(0,filename); return *this; }
- #ifdef cimg_use_tiff
- const bool
- _use_bigtiff = use_bigtiff && sizeof(ulongT)>=8 && size()*sizeof(T)>=1UL<<31; // No bigtiff for small images
- TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4");
- if (tif) {
- cimg_forZ(*this,z) _save_tiff(tif,z,z,compression_type,voxel_size,description);
- TIFFClose(tif);
- } else throw CImgIOException(_cimg_instance
- "save_tiff(): Failed to open file '%s' for writing.",
- cimg_instance,
- filename);
- return *this;
- #else
- cimg::unused(compression_type,voxel_size,description,use_bigtiff);
- return save_other(filename);
- #endif
- }
- #ifdef cimg_use_tiff
- #define _cimg_save_tiff(types,typed) if (!std::strcmp(types,pixel_type())) { \
- const typed foo = (typed)0; return _save_tiff(tif,directory,z,foo,compression_type,voxel_size,description); }
- // [internal] Save a plane into a tiff file
- template<typename t>
- const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z, const t& pixel_t,
- const unsigned int compression_type, const float *const voxel_size,
- const char *const description) const {
- if (is_empty() || !tif || pixel_t) return *this;
- const char *const filename = TIFFFileName(tif);
- cimg_uint32 rowsperstrip = (cimg_uint32)-1;
- cimg_uint16 spp = _spectrum, bpp = sizeof(t)*8, photometric;
- if (spp==3 || spp==4) photometric = PHOTOMETRIC_RGB;
- else photometric = PHOTOMETRIC_MINISBLACK;
- TIFFSetDirectory(tif,directory);
- TIFFSetField(tif,TIFFTAG_IMAGEWIDTH,_width);
- TIFFSetField(tif,TIFFTAG_IMAGELENGTH,_height);
- if (voxel_size) {
- const float vx = voxel_size[0], vy = voxel_size[1], vz = voxel_size[2];
- TIFFSetField(tif,TIFFTAG_RESOLUTIONUNIT,RESUNIT_NONE);
- TIFFSetField(tif,TIFFTAG_XRESOLUTION,1.f/vx);
- TIFFSetField(tif,TIFFTAG_YRESOLUTION,1.f/vy);
- CImg<charT> s_description(256);
- cimg_snprintf(s_description,s_description._width,"VX=%g VY=%g VZ=%g spacing=%g",vx,vy,vz,vz);
- TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,s_description.data());
- }
- if (description) TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,description);
- TIFFSetField(tif,TIFFTAG_ORIENTATION,ORIENTATION_TOPLEFT);
- TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,spp);
- if (cimg::type<t>::is_float()) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,3);
- else if (cimg::type<t>::min()==0) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,1);
- else TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,2);
- double valm, valM = max_min(valm);
- TIFFSetField(tif,TIFFTAG_SMINSAMPLEVALUE,valm);
- TIFFSetField(tif,TIFFTAG_SMAXSAMPLEVALUE,valM);
- TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,bpp);
- TIFFSetField(tif,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG);
- TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,photometric);
- TIFFSetField(tif,TIFFTAG_COMPRESSION,compression_type==2?COMPRESSION_JPEG:
- compression_type==1?COMPRESSION_LZW:COMPRESSION_NONE);
- rowsperstrip = TIFFDefaultStripSize(tif,rowsperstrip);
- TIFFSetField(tif,TIFFTAG_ROWSPERSTRIP,rowsperstrip);
- TIFFSetField(tif,TIFFTAG_FILLORDER,FILLORDER_MSB2LSB);
- TIFFSetField(tif,TIFFTAG_SOFTWARE,cimg_appname);
- t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
- if (buf) {
- for (unsigned int row = 0; row<_height; row+=rowsperstrip) {
- cimg_uint32 nrow = (row + rowsperstrip>_height?_height - row:rowsperstrip);
- tstrip_t strip = TIFFComputeStrip(tif,row,0);
- tsize_t i = 0;
- for (unsigned int rr = 0; rr<nrow; ++rr)
- for (unsigned int cc = 0; cc<_width; ++cc)
- for (unsigned int vv = 0; vv<spp; ++vv)
- buf[i++] = (t)(*this)(cc,row + rr,z,vv);
- if (TIFFWriteEncodedStrip(tif,strip,buf,i*sizeof(t))<0)
- throw CImgIOException(_cimg_instance
- "save_tiff(): Invalid strip writing when saving file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- }
- _TIFFfree(buf);
- }
- TIFFWriteDirectory(tif);
- return *this;
- }
- const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z,
- const unsigned int compression_type, const float *const voxel_size,
- const char *const description) const {
- _cimg_save_tiff("uint8",cimg_uint8);
- _cimg_save_tiff("int8",cimg_int8);
- _cimg_save_tiff("uint16",cimg_uint16);
- _cimg_save_tiff("int16",cimg_int16);
- _cimg_save_tiff("uint32",cimg_uint32);
- _cimg_save_tiff("int32",cimg_int32);
- _cimg_save_tiff("uint64",cimg_uint32); // 'int64' as 'int32'
- _cimg_save_tiff("int64",cimg_int32);
- _cimg_save_tiff("float32",cimg_float32);
- _cimg_save_tiff("float64",cimg_float32); // 'float64' as 'float32'
- const char *const filename = TIFFFileName(tif);
- throw CImgInstanceException(_cimg_instance
- "save_tiff(): Unsupported pixel type '%s' for file '%s'.",
- cimg_instance,
- pixel_type(),filename?filename:"(FILE*)");
- return *this;
- }
- #endif
- //! Save image as a MINC2 file.
- /**
- \param filename Filename, as a C-string.
- \param imitate_file If non-zero, reference filename, as a C-string, to borrow header from.
- **/
- const CImg<T>& save_minc2(const char *const filename,
- const char *const imitate_file=0) const {
- if (!filename)
- throw CImgArgumentException(_cimg_instance
- "save_minc2(): Specified filename is (null).",
- cimg_instance);
- if (is_empty()) { cimg::fempty(0,filename); return *this; }
- #ifndef cimg_use_minc2
- cimg::unused(imitate_file);
- return save_other(filename);
- #else
- minc::minc_1_writer wtr;
- if (imitate_file)
- wtr.open(filename, imitate_file);
- else {
- minc::minc_info di;
- if (width()) di.push_back(minc::dim_info(width(),width()*0.5,-1,minc::dim_info::DIM_X));
- if (height()) di.push_back(minc::dim_info(height(),height()*0.5,-1,minc::dim_info::DIM_Y));
- if (depth()) di.push_back(minc::dim_info(depth(),depth()*0.5,-1,minc::dim_info::DIM_Z));
- if (spectrum()) di.push_back(minc::dim_info(spectrum(),spectrum()*0.5,-1,minc::dim_info::DIM_TIME));
- wtr.open(filename,di,1,NC_FLOAT,0);
- }
- if (pixel_type()==cimg::type<unsigned char>::string())
- wtr.setup_write_byte();
- else if (pixel_type()==cimg::type<int>::string())
- wtr.setup_write_int();
- else if (pixel_type()==cimg::type<double>::string())
- wtr.setup_write_double();
- else
- wtr.setup_write_float();
- minc::save_standard_volume(wtr, this->_data);
- return *this;
- #endif
- }
- //! Save image as an ANALYZE7.5 or NIFTI file.
- /**
- \param filename Filename, as a C-string.
- \param voxel_size Pointer to 3 consecutive values that tell about the voxel sizes along the X,Y and Z dimensions.
- **/
- const CImg<T>& save_analyze(const char *const filename, const float *const voxel_size=0) const {
- if (!filename)
- throw CImgArgumentException(_cimg_instance
- "save_analyze(): Specified filename is (null).",
- cimg_instance);
- if (is_empty()) { cimg::fempty(0,filename); return *this; }
- std::FILE *file;
- CImg<charT> hname(1024), iname(1024);
- const char *const ext = cimg::split_filename(filename);
- short datatype = -1;
- if (!*ext) {
- cimg_snprintf(hname,hname._width,"%s.hdr",filename);
- cimg_snprintf(iname,iname._width,"%s.img",filename);
- }
- if (!cimg::strncasecmp(ext,"hdr",3)) {
- std::strcpy(hname,filename);
- std::strncpy(iname,filename,iname._width - 1);
- cimg_snprintf(iname._data + std::strlen(iname) - 3,4,"img");
- }
- if (!cimg::strncasecmp(ext,"img",3)) {
- std::strcpy(hname,filename);
- std::strncpy(iname,filename,iname._width - 1);
- cimg_snprintf(hname._data + std::strlen(iname) - 3,4,"hdr");
- }
- if (!cimg::strncasecmp(ext,"nii",3)) {
- std::strncpy(hname,filename,hname._width - 1); *iname = 0;
- }
- CImg<charT> header(*iname?348:352,1,1,1,0);
- int *const iheader = (int*)header._data;
- *iheader = 348;
- std::strcpy(header._data + 4,"CImg");
- std::strcpy(header._data + 14," ");
- ((short*)&(header[36]))[0] = 4096;
- ((char*)&(header[38]))[0] = 114;
- ((short*)&(header[40]))[0] = 4;
- ((short*)&(header[40]))[1] = (short)_width;
- ((short*)&(header[40]))[2] = (short)_height;
- ((short*)&(header[40]))[3] = (short)_depth;
- ((short*)&(header[40]))[4] = (short)_spectrum;
- if (!cimg::strcasecmp(pixel_type(),"bool") ||
- !cimg::strcasecmp(pixel_type(),"uint8") ||
- !cimg::strcasecmp(pixel_type(),"int8")) datatype = 2;
- if (!cimg::strcasecmp(pixel_type(),"uint16") ||
- !cimg::strcasecmp(pixel_type(),"int16")) datatype = 4;
- if (!cimg::strcasecmp(pixel_type(),"uint32") ||
- !cimg::strcasecmp(pixel_type(),"int32")) datatype = 8;
- if (!cimg::strcasecmp(pixel_type(),"uint64") ||
- !cimg::strcasecmp(pixel_type(),"int64")) datatype = 8;
- if (!cimg::strcasecmp(pixel_type(),"float32")) datatype = 16;
- if (!cimg::strcasecmp(pixel_type(),"float64")) datatype = 64;
- if (datatype<0)
- throw CImgIOException(_cimg_instance
- "save_analyze(): Unsupported pixel type '%s' for file '%s'.",
- cimg_instance,
- pixel_type(),filename);
- ((short*)&(header[70]))[0] = datatype;
- ((short*)&(header[72]))[0] = sizeof(T);
- ((float*)&(header[108]))[0] = (float)(*iname?0:header.width());
- ((float*)&(header[112]))[0] = 1;
- ((float*)&(header[76]))[0] = 0;
- if (voxel_size) {
- ((float*)&(header[76]))[1] = voxel_size[0];
- ((float*)&(header[76]))[2] = voxel_size[1];
- ((float*)&(header[76]))[3] = voxel_size[2];
- } else ((float*)&(header[76]))[1] = ((float*)&(header[76]))[2] = ((float*)&(header[76]))[3] = 1;
- file = cimg::fopen(hname,"wb");
- cimg::fwrite(header._data,header.width(),file);
- if (*iname) { cimg::fclose(file); file = cimg::fopen(iname,"wb"); }
- cimg::fwrite(_data,size(),file);
- cimg::fclose(file);
- return *this;
- }
- //! Save image as a .cimg file.
- /**
- \param filename Filename, as a C-string.
- \param is_compressed Tells if the file contains compressed image data.
- **/
- const CImg<T>& save_cimg(const char *const filename, const bool is_compressed=false) const {
- CImgList<T>(*this,true).save_cimg(filename,is_compressed);
- return *this;
- }
- //! Save image as a .cimg file \overloading.
- const CImg<T>& save_cimg(std::FILE *const file, const bool is_compressed=false) const {
- CImgList<T>(*this,true).save_cimg(file,is_compressed);
- return *this;
- }
- //! Save image as a sub-image into an existing .cimg file.
- /**
- \param filename Filename, as a C-string.
- \param n0 Index of the image inside the file.
- \param x0 X-coordinate of the sub-image location.
- \param y0 Y-coordinate of the sub-image location.
- \param z0 Z-coordinate of the sub-image location.
- \param c0 C-coordinate of the sub-image location.
- **/
- const CImg<T>& save_cimg(const char *const filename,
- const unsigned int n0,
- const unsigned int x0, const unsigned int y0,
- const unsigned int z0, const unsigned int c0) const {
- CImgList<T>(*this,true).save_cimg(filename,n0,x0,y0,z0,c0);
- return *this;
- }
- //! Save image as a sub-image into an existing .cimg file \overloading.
- const CImg<T>& save_cimg(std::FILE *const file,
- const unsigned int n0,
- const unsigned int x0, const unsigned int y0,
- const unsigned int z0, const unsigned int c0) const {
- CImgList<T>(*this,true).save_cimg(file,n0,x0,y0,z0,c0);
- return *this;
- }
- //! Save blank image as a .cimg file.
- /**
- \param filename Filename, as a C-string.
- \param dx Width of the image.
- \param dy Height of the image.
- \param dz Depth of the image.
- \param dc Number of channels of the image.
- \note
- - All pixel values of the saved image are set to \c 0.
- - Use this method to save large images without having to instantiate and allocate them.
- **/
- static void save_empty_cimg(const char *const filename,
- const unsigned int dx, const unsigned int dy=1,
- const unsigned int dz=1, const unsigned int dc=1) {
- return CImgList<T>::save_empty_cimg(filename,1,dx,dy,dz,dc);
- }
- //! Save blank image as a .cimg file \overloading.
- /**
- Same as save_empty_cimg(const char *,unsigned int,unsigned int,unsigned int,unsigned int)
- with a file stream argument instead of a filename string.
- **/
- static void save_empty_cimg(std::FILE *const file,
- const unsigned int dx, const unsigned int dy=1,
- const unsigned int dz=1, const unsigned int dc=1) {
- return CImgList<T>::save_empty_cimg(file,1,dx,dy,dz,dc);
- }
- //! Save image as an INRIMAGE-4 file.
- /**
- \param filename Filename, as a C-string.
- \param voxel_size Pointer to 3 values specifying the voxel sizes along the X,Y and Z dimensions.
- **/
- const CImg<T>& save_inr(const char *const filename, const float *const voxel_size=0) const {
- return _save_inr(0,filename,voxel_size);
- }
- //! Save image as an INRIMAGE-4 file \overloading.
- const CImg<T>& save_inr(std::FILE *const file, const float *const voxel_size=0) const {
- return _save_inr(file,0,voxel_size);
- }
- const CImg<T>& _save_inr(std::FILE *const file, const char *const filename, const float *const voxel_size) const {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "save_inr(): Specified filename is (null).",
- cimg_instance);
- if (is_empty()) { cimg::fempty(file,filename); return *this; }
- int inrpixsize = -1;
- const char *inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0";
- if (!cimg::strcasecmp(pixel_type(),"uint8")) {
- inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1;
- }
- if (!cimg::strcasecmp(pixel_type(),"int8")) {
- inrtype = "fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1;
- }
- if (!cimg::strcasecmp(pixel_type(),"uint16")) {
- inrtype = "unsigned fixed\nPIXSIZE=16 bits\nSCALE=2**0";inrpixsize = 2;
- }
- if (!cimg::strcasecmp(pixel_type(),"int16")) {
- inrtype = "fixed\nPIXSIZE=16 bits\nSCALE=2**0"; inrpixsize = 2;
- }
- if (!cimg::strcasecmp(pixel_type(),"uint32")) {
- inrtype = "unsigned fixed\nPIXSIZE=32 bits\nSCALE=2**0";inrpixsize = 4;
- }
- if (!cimg::strcasecmp(pixel_type(),"int32")) {
- inrtype = "fixed\nPIXSIZE=32 bits\nSCALE=2**0"; inrpixsize = 4;
- }
- if (!cimg::strcasecmp(pixel_type(),"float32")) {
- inrtype = "float\nPIXSIZE=32 bits"; inrpixsize = 4;
- }
- if (!cimg::strcasecmp(pixel_type(),"float64")) {
- inrtype = "float\nPIXSIZE=64 bits"; inrpixsize = 8;
- }
- if (inrpixsize<=0)
- throw CImgIOException(_cimg_instance
- "save_inr(): Unsupported pixel type '%s' for file '%s'",
- cimg_instance,
- pixel_type(),filename?filename:"(FILE*)");
- std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
- CImg<charT> header(257);
- int err = cimg_snprintf(header,header._width,"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n",
- _width,_height,_depth,_spectrum);
- if (voxel_size)
- err+=cimg_snprintf(header._data + err,128,"VX=%g\nVY=%g\nVZ=%g\n",
- voxel_size[0],voxel_size[1],voxel_size[2]);
- err+=cimg_snprintf(header._data + err,128,"TYPE=%s\nCPU=%s\n",
- inrtype,cimg::endianness()?"sun":"decm");
- std::memset(header._data + err,'\n',252 - err);
- std::memcpy(header._data + 252,"##}\n",4);
- cimg::fwrite(header._data,256,nfile);
- cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) cimg::fwrite(&((*this)(x,y,z,c)),1,nfile);
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Save image as an OpenEXR file.
- /**
- \param filename Filename, as a C-string.
- \note The OpenEXR file format is <a href="http://en.wikipedia.org/wiki/OpenEXR">described here</a>.
- **/
- const CImg<T>& save_exr(const char *const filename) const {
- if (!filename)
- throw CImgArgumentException(_cimg_instance
- "save_exr(): Specified filename is (null).",
- cimg_instance);
- if (is_empty()) { cimg::fempty(0,filename); return *this; }
- if (_depth>1)
- cimg::warn(_cimg_instance
- "save_exr(): Instance is volumetric, only the first slice will be saved in file '%s'.",
- cimg_instance,
- filename);
- #ifndef cimg_use_openexr
- return save_other(filename);
- #else
- Imf::Rgba *const ptrd0 = new Imf::Rgba[(size_t)_width*_height], *ptrd = ptrd0, rgba;
- switch (_spectrum) {
- case 1 : { // Grayscale image
- for (const T *ptr_r = data(), *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) {
- rgba.r = (half)(*(ptr_r));
- rgba.g = (half)(*(ptr_r));
- rgba.b = (half)(*(ptr_r++));
- rgba.a = (half)1;
- *(ptrd++) = rgba;
- }
- } break;
- case 2 : { // RG image
- for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1),
- *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e; ) {
- rgba.r = (half)(*(ptr_r++));
- rgba.g = (half)(*(ptr_g++));
- rgba.b = (half)0;
- rgba.a = (half)1;
- *(ptrd++) = rgba;
- }
- } break;
- case 3 : { // RGB image
- for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2),
- *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) {
- rgba.r = (half)(*(ptr_r++));
- rgba.g = (half)(*(ptr_g++));
- rgba.b = (half)(*(ptr_b++));
- rgba.a = (half)1;
- *(ptrd++) = rgba;
- }
- } break;
- default : { // RGBA image
- for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2), *ptr_a = data(0,0,0,3),
- *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) {
- rgba.r = (half)(*(ptr_r++));
- rgba.g = (half)(*(ptr_g++));
- rgba.b = (half)(*(ptr_b++));
- rgba.a = (half)(*(ptr_a++));
- *(ptrd++) = rgba;
- }
- } break;
- }
- Imf::RgbaOutputFile outFile(filename,_width,_height,
- _spectrum==1?Imf::WRITE_Y:_spectrum==2?Imf::WRITE_YA:_spectrum==3?
- Imf::WRITE_RGB:Imf::WRITE_RGBA);
- outFile.setFrameBuffer(ptrd0,1,_width);
- outFile.writePixels(_height);
- delete[] ptrd0;
- return *this;
- #endif
- }
- //! Save image as a Pandore-5 file.
- /**
- \param filename Filename, as a C-string.
- \param colorspace Colorspace data field in output file
- (see <a href="http://www.greyc.ensicaen.fr/~regis/Pandore">Pandore file specifications</a>
- for more information).
- **/
- const CImg<T>& save_pandore(const char *const filename, const unsigned int colorspace=0) const {
- return _save_pandore(0,filename,colorspace);
- }
- //! Save image as a Pandore-5 file \overloading.
- /**
- Same as save_pandore(const char *,unsigned int) const
- with a file stream argument instead of a filename string.
- **/
- const CImg<T>& save_pandore(std::FILE *const file, const unsigned int colorspace=0) const {
- return _save_pandore(file,0,colorspace);
- }
- unsigned int _save_pandore_header_length(unsigned int id, unsigned int *dims, const unsigned int colorspace) const {
- unsigned int nbdims = 0;
- if (id==2 || id==3 || id==4) {
- dims[0] = 1; dims[1] = _width; nbdims = 2;
- }
- if (id==5 || id==6 || id==7) {
- dims[0] = 1; dims[1] = _height; dims[2] = _width; nbdims=3;
- }
- if (id==8 || id==9 || id==10) {
- dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4;
- }
- if (id==16 || id==17 || id==18) {
- dims[0] = 3; dims[1] = _height; dims[2] = _width; dims[3] = colorspace; nbdims = 4;
- }
- if (id==19 || id==20 || id==21) {
- dims[0] = 3; dims[1] = _depth; dims[2] = _height; dims[3] = _width; dims[4] = colorspace; nbdims = 5;
- }
- if (id==22 || id==23 || id==25) {
- dims[0] = _spectrum; dims[1] = _width; nbdims = 2;
- }
- if (id==26 || id==27 || id==29) {
- dims[0] = _spectrum; dims[1] = _height; dims[2] = _width; nbdims=3;
- }
- if (id==30 || id==31 || id==33) {
- dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4;
- }
- return nbdims;
- }
- const CImg<T>& _save_pandore(std::FILE *const file, const char *const filename,
- const unsigned int colorspace) const {
- #define __cimg_save_pandore_case(dtype) \
- dtype *buffer = new dtype[size()]; \
- const T *ptrs = _data; \
- cimg_foroff(*this,off) *(buffer++) = (dtype)(*(ptrs++)); \
- buffer-=size(); \
- cimg::fwrite(buffer,size(),nfile); \
- delete[] buffer
- #define _cimg_save_pandore_case(sy,sz,sv,stype,id) \
- if (!saved && (sy?(sy==_height):true) && (sz?(sz==_depth):true) && \
- (sv?(sv==_spectrum):true) && !std::strcmp(stype,pixel_type())) { \
- unsigned int *iheader = (unsigned int*)(header + 12); \
- nbdims = _save_pandore_header_length((*iheader=id),dims,colorspace); \
- cimg::fwrite(header,36,nfile); \
- if (sizeof(unsigned long)==4) { CImg<ulongT> ndims(5); \
- for (int d = 0; d<5; ++d) ndims[d] = (unsigned long)dims[d]; \
- cimg::fwrite(ndims._data,nbdims,nfile); } \
- else if (sizeof(unsigned int)==4) { CImg<uintT> ndims(5); \
- for (int d = 0; d<5; ++d) ndims[d] = (unsigned int)dims[d]; \
- cimg::fwrite(ndims._data,nbdims,nfile); } \
- else if (sizeof(unsigned short)==4) { CImg<ushortT> ndims(5); \
- for (int d = 0; d<5; ++d) ndims[d] = (unsigned short)dims[d]; \
- cimg::fwrite(ndims._data,nbdims,nfile); } \
- else throw CImgIOException(_cimg_instance \
- "save_pandore(): Unsupported datatype for file '%s'.",\
- cimg_instance, \
- filename?filename:"(FILE*)"); \
- if (id==2 || id==5 || id==8 || id==16 || id==19 || id==22 || id==26 || id==30) { \
- __cimg_save_pandore_case(unsigned char); \
- } else if (id==3 || id==6 || id==9 || id==17 || id==20 || id==23 || id==27 || id==31) { \
- if (sizeof(unsigned long)==4) { __cimg_save_pandore_case(unsigned long); } \
- else if (sizeof(unsigned int)==4) { __cimg_save_pandore_case(unsigned int); } \
- else if (sizeof(unsigned short)==4) { __cimg_save_pandore_case(unsigned short); } \
- else throw CImgIOException(_cimg_instance \
- "save_pandore(): Unsupported datatype for file '%s'.",\
- cimg_instance, \
- filename?filename:"(FILE*)"); \
- } else if (id==4 || id==7 || id==10 || id==18 || id==21 || id==25 || id==29 || id==33) { \
- if (sizeof(double)==4) { __cimg_save_pandore_case(double); } \
- else if (sizeof(float)==4) { __cimg_save_pandore_case(float); } \
- else throw CImgIOException(_cimg_instance \
- "save_pandore(): Unsupported datatype for file '%s'.",\
- cimg_instance, \
- filename?filename:"(FILE*)"); \
- } \
- saved = true; \
- }
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "save_pandore(): Specified filename is (null).",
- cimg_instance);
- if (is_empty()) { cimg::fempty(file,filename); return *this; }
- std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
- unsigned char header[36] = { 'P','A','N','D','O','R','E','0','4',0,0,0,
- 0,0,0,0,'C','I','m','g',0,0,0,0,0,
- 'N','o',' ','d','a','t','e',0,0,0,0 };
- unsigned int nbdims, dims[5] = {};
- bool saved = false;
- _cimg_save_pandore_case(1,1,1,"uint8",2);
- _cimg_save_pandore_case(1,1,1,"int8",3);
- _cimg_save_pandore_case(1,1,1,"uint16",3);
- _cimg_save_pandore_case(1,1,1,"int16",3);
- _cimg_save_pandore_case(1,1,1,"uint32",3);
- _cimg_save_pandore_case(1,1,1,"int32",3);
- _cimg_save_pandore_case(1,1,1,"uint64",3);
- _cimg_save_pandore_case(1,1,1,"int64",3);
- _cimg_save_pandore_case(1,1,1,"float32",4);
- _cimg_save_pandore_case(1,1,1,"float64",4);
- _cimg_save_pandore_case(0,1,1,"uint8",5);
- _cimg_save_pandore_case(0,1,1,"int8",6);
- _cimg_save_pandore_case(0,1,1,"uint16",6);
- _cimg_save_pandore_case(0,1,1,"int16",6);
- _cimg_save_pandore_case(0,1,1,"uint32",6);
- _cimg_save_pandore_case(0,1,1,"int32",6);
- _cimg_save_pandore_case(0,1,1,"uint64",6);
- _cimg_save_pandore_case(0,1,1,"int64",6);
- _cimg_save_pandore_case(0,1,1,"float32",7);
- _cimg_save_pandore_case(0,1,1,"float64",7);
- _cimg_save_pandore_case(0,0,1,"uint8",8);
- _cimg_save_pandore_case(0,0,1,"int8",9);
- _cimg_save_pandore_case(0,0,1,"uint16",9);
- _cimg_save_pandore_case(0,0,1,"int16",9);
- _cimg_save_pandore_case(0,0,1,"uint32",9);
- _cimg_save_pandore_case(0,0,1,"int32",9);
- _cimg_save_pandore_case(0,0,1,"uint64",9);
- _cimg_save_pandore_case(0,0,1,"int64",9);
- _cimg_save_pandore_case(0,0,1,"float32",10);
- _cimg_save_pandore_case(0,0,1,"float64",10);
- _cimg_save_pandore_case(0,1,3,"uint8",16);
- _cimg_save_pandore_case(0,1,3,"int8",17);
- _cimg_save_pandore_case(0,1,3,"uint16",17);
- _cimg_save_pandore_case(0,1,3,"int16",17);
- _cimg_save_pandore_case(0,1,3,"uint32",17);
- _cimg_save_pandore_case(0,1,3,"int32",17);
- _cimg_save_pandore_case(0,1,3,"uint64",17);
- _cimg_save_pandore_case(0,1,3,"int64",17);
- _cimg_save_pandore_case(0,1,3,"float32",18);
- _cimg_save_pandore_case(0,1,3,"float64",18);
- _cimg_save_pandore_case(0,0,3,"uint8",19);
- _cimg_save_pandore_case(0,0,3,"int8",20);
- _cimg_save_pandore_case(0,0,3,"uint16",20);
- _cimg_save_pandore_case(0,0,3,"int16",20);
- _cimg_save_pandore_case(0,0,3,"uint32",20);
- _cimg_save_pandore_case(0,0,3,"int32",20);
- _cimg_save_pandore_case(0,0,3,"uint64",20);
- _cimg_save_pandore_case(0,0,3,"int64",20);
- _cimg_save_pandore_case(0,0,3,"float32",21);
- _cimg_save_pandore_case(0,0,3,"float64",21);
- _cimg_save_pandore_case(1,1,0,"uint8",22);
- _cimg_save_pandore_case(1,1,0,"int8",23);
- _cimg_save_pandore_case(1,1,0,"uint16",23);
- _cimg_save_pandore_case(1,1,0,"int16",23);
- _cimg_save_pandore_case(1,1,0,"uint32",23);
- _cimg_save_pandore_case(1,1,0,"int32",23);
- _cimg_save_pandore_case(1,1,0,"uint64",23);
- _cimg_save_pandore_case(1,1,0,"int64",23);
- _cimg_save_pandore_case(1,1,0,"float32",25);
- _cimg_save_pandore_case(1,1,0,"float64",25);
- _cimg_save_pandore_case(0,1,0,"uint8",26);
- _cimg_save_pandore_case(0,1,0,"int8",27);
- _cimg_save_pandore_case(0,1,0,"uint16",27);
- _cimg_save_pandore_case(0,1,0,"int16",27);
- _cimg_save_pandore_case(0,1,0,"uint32",27);
- _cimg_save_pandore_case(0,1,0,"int32",27);
- _cimg_save_pandore_case(0,1,0,"uint64",27);
- _cimg_save_pandore_case(0,1,0,"int64",27);
- _cimg_save_pandore_case(0,1,0,"float32",29);
- _cimg_save_pandore_case(0,1,0,"float64",29);
- _cimg_save_pandore_case(0,0,0,"uint8",30);
- _cimg_save_pandore_case(0,0,0,"int8",31);
- _cimg_save_pandore_case(0,0,0,"uint16",31);
- _cimg_save_pandore_case(0,0,0,"int16",31);
- _cimg_save_pandore_case(0,0,0,"uint32",31);
- _cimg_save_pandore_case(0,0,0,"int32",31);
- _cimg_save_pandore_case(0,0,0,"uint64",31);
- _cimg_save_pandore_case(0,0,0,"int64",31);
- _cimg_save_pandore_case(0,0,0,"float32",33);
- _cimg_save_pandore_case(0,0,0,"float64",33);
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Save image as a raw data file.
- /**
- \param filename Filename, as a C-string.
- \param is_multiplexed Tells if the image channels are stored in a multiplexed way (\c true) or not (\c false).
- \note The .raw format does not store the image dimensions in the output file,
- so you have to keep track of them somewhere to be able to read the file correctly afterwards.
- **/
- const CImg<T>& save_raw(const char *const filename, const bool is_multiplexed=false) const {
- return _save_raw(0,filename,is_multiplexed);
- }
- //! Save image as a raw data file \overloading.
- /**
- Same as save_raw(const char *,bool) const
- with a file stream argument instead of a filename string.
- **/
- const CImg<T>& save_raw(std::FILE *const file, const bool is_multiplexed=false) const {
- return _save_raw(file,0,is_multiplexed);
- }
- const CImg<T>& _save_raw(std::FILE *const file, const char *const filename, const bool is_multiplexed) const {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "save_raw(): Specified filename is (null).",
- cimg_instance);
- if (is_empty()) { cimg::fempty(file,filename); return *this; }
- std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
- if (pixel_type()==cimg::type<bool>::string()) { // Boolean data (bitwise)
- ulongT siz;
- const unsigned char *const buf = _bool2uchar(siz,is_multiplexed);
- cimg::fwrite(buf,siz,nfile);
- delete[] buf;
- } else { // Non boolean data
- if (!is_multiplexed || _spectrum==1) cimg::fwrite(_data,size(),nfile); // Non-multiplexed
- else { // Multiplexed
- CImg<T> buf(_spectrum);
- cimg_forXYZ(*this,x,y,z) {
- cimg_forC(*this,c) buf[c] = (*this)(x,y,z,c);
- cimg::fwrite(buf._data,_spectrum,nfile);
- }
- }
- }
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- // Return unsigned char buffer that encodes data of a CImg<bool> instance bitwise.
- // (buffer needs to be deallocated afterwards, with delete[]).
- const unsigned char *_bool2uchar(ulongT &siz, const bool is_multiplexed) const {
- const ulongT _siz = size();
- siz = _siz/8 + (_siz%8?1:0);
- unsigned char *const buf = new unsigned char[siz], *ptrd = buf, val = 0, bit = 0;
- if (!is_multiplexed || _spectrum==1) // Non-multiplexed
- cimg_for(*this,ptrs,T) { (val<<=1)|=(*ptrs?1:0); if (++bit==8) { *(ptrd++) = val; val = bit = 0; }}
- else // Multiplexed
- cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) {
- (val<<=1)|=((*this)(x,y,z,c)?1:0); if (++bit==8) { *(ptrd++) = val; val = bit = 0; }
- }
- if (bit) *ptrd = val;
- return buf;
- }
- // Fill CImg<T> instance from bitwise data encoded in an unsigned char buffer.
- void _uchar2bool(const unsigned char *buf, const ulongT siz, const bool is_multiplexed) {
- const ulongT S = std::min(siz*8,size());
- const unsigned char *ptrs = buf;
- unsigned char val = 0, mask = 0;
- T *ptrd = _data;
- if (S && (!is_multiplexed || _spectrum==1)) // Non-multiplexed
- for (ulongT off = 0; off<S; ++off) {
- if (!(mask>>=1)) { val = *(ptrs++); mask = 128; }
- *(ptrd++) = (T)((val&mask)?1:0);
- }
- else if (S) { // Multiplexed
- ulongT off = 0;
- for (int z = 0; z<depth() && off<=S; ++z)
- for (int y = 0; y<height() && off<=S; ++y)
- for (int x = 0; x<width() && off<=S; ++x)
- for (int c = 0; c<spectrum() && off<=S; ++c) {
- if (!(mask>>=1)) { val = *(ptrs++); ++off; mask = 128; }
- (*this)(x,y,z,c) = (T)((val&mask)?1:0);
- }
- }
- }
- //! Save image as a .yuv video file.
- /**
- \param filename Filename, as a C-string.
- \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
- \param is_rgb Tells if pixel values of the instance image are RGB-coded (\c true) or YUV-coded (\c false).
- \note Each slice of the instance image is considered to be a single frame of the output video file.
- **/
- const CImg<T>& save_yuv(const char *const filename,
- const unsigned int chroma_subsampling=444,
- const bool is_rgb=true) const {
- CImgList<T>(*this,true).save_yuv(filename,chroma_subsampling,is_rgb);
- return *this;
- }
- //! Save image as a .yuv video file \overloading.
- /**
- Same as save_yuv(const char*,const unsigned int,const bool) const
- with a file stream argument instead of a filename string.
- **/
- const CImg<T>& save_yuv(std::FILE *const file,
- const unsigned int chroma_subsampling=444,
- const bool is_rgb=true) const {
- CImgList<T>(*this,true).save_yuv(file,chroma_subsampling,is_rgb);
- return *this;
- }
- //! Save 3D object as an Object File Format (.off) file.
- /**
- \param filename Filename, as a C-string.
- \param primitives List of 3D object primitives.
- \param colors List of 3D object colors.
- \note
- - Instance image contains the vertices data of the 3D object.
- - Textured, transparent or sphere-shaped primitives cannot be managed by the .off file format.
- Such primitives will be lost or simplified during file saving.
- - The .off file format is <a href="http://people.sc.fsu.edu/~jburkardt/html/off_format.html">described here</a>.
- **/
- template<typename tf, typename tc>
- const CImg<T>& save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
- const char *const filename) const {
- return _save_off(primitives,colors,0,filename);
- }
- //! Save 3D object as an Object File Format (.off) file \overloading.
- /**
- Same as save_off(const CImgList<tf>&,const CImgList<tc>&,const char*) const
- with a file stream argument instead of a filename string.
- **/
- template<typename tf, typename tc>
- const CImg<T>& save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
- std::FILE *const file) const {
- return _save_off(primitives,colors,file,0);
- }
- template<typename tf, typename tc>
- const CImg<T>& _save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
- std::FILE *const file, const char *const filename) const {
- if (!file && !filename)
- throw CImgArgumentException(_cimg_instance
- "save_off(): Specified filename is (null).",
- cimg_instance);
- if (is_empty())
- throw CImgInstanceException(_cimg_instance
- "save_off(): Empty instance, for file '%s'.",
- cimg_instance,
- filename?filename:"(FILE*)");
- CImgList<T> opacities;
- CImg<charT> error_message(1024);
- if (!is_object3d(primitives,colors,opacities,true,error_message))
- throw CImgInstanceException(_cimg_instance
- "save_off(): Invalid specified 3D object, for file '%s' (%s).",
- cimg_instance,
- filename?filename:"(FILE*)",error_message.data());
- const CImg<tc> default_color(1,3,1,1,(tc)std::min((int)cimg::type<tc>::max(),200));
- std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
- unsigned int supported_primitives = 0;
- cimglist_for(primitives,l) if (primitives[l].size()!=5) ++supported_primitives;
- std::fprintf(nfile,"OFF\n%u %u %u\n",_width,supported_primitives,3*primitives._width);
- cimg_forX(*this,i) std::fprintf(nfile,"%f %f %f\n",
- (float)((*this)(i,0)),(float)((*this)(i,1)),(float)((*this)(i,2)));
- cimglist_for(primitives,l) {
- const CImg<tc>& color = l<colors.width()?colors[l]:default_color;
- const unsigned int psiz = primitives[l].size(), csiz = color.size();
- const float r = color[0]/255.f, g = (csiz>1?color[1]:r)/255.f, b = (csiz>2?color[2]:g)/255.f;
- switch (psiz) {
- case 1 : std::fprintf(nfile,"1 %u %f %f %f\n",
- (unsigned int)primitives(l,0),r,g,b); break;
- case 2 : std::fprintf(nfile,"2 %u %u %f %f %f\n",
- (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break;
- case 3 : std::fprintf(nfile,"3 %u %u %u %f %f %f\n",
- (unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
- (unsigned int)primitives(l,1),r,g,b); break;
- case 4 : std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",
- (unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
- (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),r,g,b); break;
- case 5 : std::fprintf(nfile,"2 %u %u %f %f %f\n",
- (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break;
- case 6 : {
- const unsigned int xt = (unsigned int)primitives(l,2), yt = (unsigned int)primitives(l,3);
- const float
- rt = color.atXY(xt,yt,0)/255.f,
- gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f,
- bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f;
- std::fprintf(nfile,"2 %u %u %f %f %f\n",
- (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),rt,gt,bt);
- } break;
- case 9 : {
- const unsigned int xt = (unsigned int)primitives(l,3), yt = (unsigned int)primitives(l,4);
- const float
- rt = color.atXY(xt,yt,0)/255.f,
- gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f,
- bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f;
- std::fprintf(nfile,"3 %u %u %u %f %f %f\n",
- (unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
- (unsigned int)primitives(l,1),rt,gt,bt);
- } break;
- case 12 : {
- const unsigned int xt = (unsigned int)primitives(l,4), yt = (unsigned int)primitives(l,5);
- const float
- rt = color.atXY(xt,yt,0)/255.f,
- gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f,
- bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f;
- std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",
- (unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
- (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),rt,gt,bt);
- } break;
- }
- }
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Save volumetric image as a video (using the OpenCV library when available).
- /**
- \param filename Filename to write data to.
- \param fps Number of frames per second.
- \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs).
- \param keep_open Tells if the video writer associated to the specified filename
- must be kept open or not (to allow frames to be added in the same file afterwards).
- **/
- const CImg<T>& save_video(const char *const filename, const unsigned int fps=25,
- const char *codec=0, const bool keep_open=false) const {
- if (is_empty()) { CImgList<T>().save_video(filename,fps,codec,keep_open); return *this; }
- CImgList<T> list;
- get_split('z').move_to(list);
- list.save_video(filename,fps,codec,keep_open);
- return *this;
- }
- //! Save volumetric image as a video, using ffmpeg external binary.
- /**
- \param filename Filename, as a C-string.
- \param fps Video framerate.
- \param codec Video codec, as a C-string.
- \param bitrate Video bitrate.
- \note
- - Each slice of the instance image is considered to be a single frame of the output video file.
- - This method uses \c ffmpeg, an external executable binary provided by
- <a href="http://www.ffmpeg.org">FFmpeg</a>.
- It must be installed for the method to succeed.
- **/
- const CImg<T>& save_ffmpeg_external(const char *const filename, const unsigned int fps=25,
- const char *const codec=0, const unsigned int bitrate=2048) const {
- if (!filename)
- throw CImgArgumentException(_cimg_instance
- "save_ffmpeg_external(): Specified filename is (null).",
- cimg_instance);
- if (is_empty()) { cimg::fempty(0,filename); return *this; }
- CImgList<T> list;
- get_split('z').move_to(list);
- list.save_ffmpeg_external(filename,fps,codec,bitrate);
- return *this;
- }
- //! Save image using gzip external binary.
- /**
- \param filename Filename, as a C-string.
- \note This method uses \c gzip, an external executable binary provided by
- <a href="//http://www.gzip.org">gzip</a>.
- It must be installed for the method to succeed.
- **/
- const CImg<T>& save_gzip_external(const char *const filename) const {
- if (!filename)
- throw CImgArgumentException(_cimg_instance
- "save_gzip_external(): Specified filename is (null).",
- cimg_instance);
- if (is_empty()) { cimg::fempty(0,filename); return *this; }
- CImg<charT> command(1024), filename_tmp(256), body(256);
- const char
- *ext = cimg::split_filename(filename,body),
- *ext2 = cimg::split_filename(body,0);
- std::FILE *file;
- do {
- if (!cimg::strcasecmp(ext,"gz")) {
- if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
- cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
- else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
- cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
- } else {
- if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
- cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
- else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
- cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
- }
- if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
- } while (file);
- save(filename_tmp);
- cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"",
- cimg::gzip_path(),
- CImg<charT>::string(filename_tmp)._system_strescape().data(),
- CImg<charT>::string(filename)._system_strescape().data());
- cimg::system(command,cimg::gzip_path());
- file = cimg::std_fopen(filename,"rb");
- if (!file)
- throw CImgIOException(_cimg_instance
- "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.",
- cimg_instance,
- filename);
- else cimg::fclose(file);
- std::remove(filename_tmp);
- return *this;
- }
- //! Save image using GraphicsMagick's external binary.
- /**
- \param filename Filename, as a C-string.
- \param quality Image quality (expressed in percent), when the file format supports it.
- \note This method uses \c gm, an external executable binary provided by
- <a href="http://www.graphicsmagick.org">GraphicsMagick</a>.
- It must be installed for the method to succeed.
- **/
- const CImg<T>& save_graphicsmagick_external(const char *const filename, const unsigned int quality=100) const {
- if (!filename)
- throw CImgArgumentException(_cimg_instance
- "save_graphicsmagick_external(): Specified filename is (null).",
- cimg_instance);
- if (is_empty()) { cimg::fempty(0,filename); return *this; }
- if (_depth>1)
- cimg::warn(_cimg_instance
- "save_other(): File '%s', saving a volumetric image with an external call to "
- "GraphicsMagick only writes the first image slice.",
- cimg_instance,filename);
- #ifdef cimg_use_png
- #define _cimg_sge_extension1 "png"
- #define _cimg_sge_extension2 "png"
- #else
- #define _cimg_sge_extension1 "pgm"
- #define _cimg_sge_extension2 "ppm"
- #endif
- CImg<charT> command(1024), filename_tmp(256);
- std::FILE *file;
- do {
- cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
- cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),
- _spectrum==1?_cimg_sge_extension1:_cimg_sge_extension2);
- if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
- } while (file);
- #ifdef cimg_use_png
- save_png(filename_tmp);
- #else
- save_pnm(filename_tmp);
- #endif
- cimg_snprintf(command,command._width,"\"%s\" convert -quality %u \"%s\" \"%s\"",
- cimg::graphicsmagick_path(),quality,
- CImg<charT>::string(filename_tmp)._system_strescape().data(),
- CImg<charT>::string(filename)._system_strescape().data());
- cimg::system(command,cimg::graphicsmagick_path());
- file = cimg::std_fopen(filename,"rb");
- if (!file)
- throw CImgIOException(_cimg_instance
- "save_graphicsmagick_external(): Failed to save file '%s' with external command 'gm'.",
- cimg_instance,
- filename);
- if (file) cimg::fclose(file);
- std::remove(filename_tmp);
- return *this;
- }
- //! Save image using ImageMagick's external binary.
- /**
- \param filename Filename, as a C-string.
- \param quality Image quality (expressed in percent), when the file format supports it.
- \note This method uses \c convert, an external executable binary provided by
- <a href="http://www.imagemagick.org">ImageMagick</a>.
- It must be installed for the method to succeed.
- **/
- const CImg<T>& save_imagemagick_external(const char *const filename, const unsigned int quality=100) const {
- if (!filename)
- throw CImgArgumentException(_cimg_instance
- "save_imagemagick_external(): Specified filename is (null).",
- cimg_instance);
- if (is_empty()) { cimg::fempty(0,filename); return *this; }
- if (_depth>1)
- cimg::warn(_cimg_instance
- "save_other(): File '%s', saving a volumetric image with an external call to "
- "ImageMagick only writes the first image slice.",
- cimg_instance,filename);
- #ifdef cimg_use_png
- #define _cimg_sie_extension1 "png"
- #define _cimg_sie_extension2 "png"
- #else
- #define _cimg_sie_extension1 "pgm"
- #define _cimg_sie_extension2 "ppm"
- #endif
- CImg<charT> command(1024), filename_tmp(256);
- std::FILE *file;
- do {
- cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",cimg::temporary_path(),
- cimg_file_separator,cimg::filenamerand(),_spectrum==1?_cimg_sie_extension1:_cimg_sie_extension2);
- if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
- } while (file);
- #ifdef cimg_use_png
- save_png(filename_tmp);
- #else
- save_pnm(filename_tmp);
- #endif
- cimg_snprintf(command,command._width,"\"%s\" -quality %u \"%s\" \"%s\"",
- cimg::imagemagick_path(),quality,
- CImg<charT>::string(filename_tmp)._system_strescape().data(),
- CImg<charT>::string(filename)._system_strescape().data());
- cimg::system(command,cimg::imagemagick_path());
- file = cimg::std_fopen(filename,"rb");
- if (!file)
- throw CImgIOException(_cimg_instance
- "save_imagemagick_external(): Failed to save file '%s' with "
- "external command 'magick/convert'.",
- cimg_instance,
- filename);
- if (file) cimg::fclose(file);
- std::remove(filename_tmp);
- return *this;
- }
- //! Save image as a Dicom file.
- /**
- \param filename Filename, as a C-string.
- \note This method uses \c medcon, an external executable binary provided by
- <a href="http://xmedcon.sourceforge.net">(X)Medcon</a>.
- It must be installed for the method to succeed.
- **/
- const CImg<T>& save_medcon_external(const char *const filename) const {
- if (!filename)
- throw CImgArgumentException(_cimg_instance
- "save_medcon_external(): Specified filename is (null).",
- cimg_instance);
- if (is_empty()) { cimg::fempty(0,filename); return *this; }
- CImg<charT> command(1024), filename_tmp(256), body(256);
- std::FILE *file;
- do {
- cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand());
- if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
- } while (file);
- save_analyze(filename_tmp);
- cimg_snprintf(command,command._width,"\"%s\" -w -c dicom -o \"%s\" -f \"%s\"",
- cimg::medcon_path(),
- CImg<charT>::string(filename)._system_strescape().data(),
- CImg<charT>::string(filename_tmp)._system_strescape().data());
- cimg::system(command,cimg::medcon_path());
- std::remove(filename_tmp);
- cimg::split_filename(filename_tmp,body);
- cimg_snprintf(filename_tmp,filename_tmp._width,"%s.img",body._data);
- std::remove(filename_tmp);
- file = cimg::std_fopen(filename,"rb");
- if (!file) {
- cimg_snprintf(command,command._width,"m000-%s",filename);
- file = cimg::std_fopen(command,"rb");
- if (!file) {
- cimg::fclose(cimg::fopen(filename,"r"));
- throw CImgIOException(_cimg_instance
- "save_medcon_external(): Failed to save file '%s' with external command 'medcon'.",
- cimg_instance,
- filename);
- }
- }
- cimg::fclose(file);
- std::rename(command,filename);
- return *this;
- }
- // Save image for non natively supported formats.
- /**
- \param filename Filename, as a C-string.
- \param quality Image quality (expressed in percent), when the file format supports it.
- \note
- - The filename extension tells about the desired file format.
- - This method tries to save the instance image as a file, using external tools from
- <a href="http://www.imagemagick.org">ImageMagick</a> or
- <a href="http://www.graphicsmagick.org">GraphicsMagick</a>.
- At least one of these tool must be installed for the method to succeed.
- - It is recommended to use the generic method save(const char*, int) const instead,
- as it can handle some file formats natively.
- **/
- const CImg<T>& save_other(const char *const filename, const unsigned int quality=100) const {
- if (!filename)
- throw CImgArgumentException(_cimg_instance
- "save_other(): Specified filename is (null).",
- cimg_instance);
- if (is_empty()) { cimg::fempty(0,filename); return *this; }
- if (_depth>1)
- cimg::warn(_cimg_instance
- "save_other(): File '%s', saving a volumetric image with an external call to "
- "ImageMagick or GraphicsMagick only writes the first image slice.",
- cimg_instance,filename);
- const unsigned int omode = cimg::exception_mode();
- bool is_saved = true;
- cimg::exception_mode(0);
- try { save_magick(filename); }
- catch (CImgException&) {
- try { save_imagemagick_external(filename,quality); }
- catch (CImgException&) {
- try { save_graphicsmagick_external(filename,quality); }
- catch (CImgException&) {
- is_saved = false;
- }
- }
- }
- cimg::exception_mode(omode);
- if (!is_saved)
- throw CImgIOException(_cimg_instance
- "save_other(): Failed to save file '%s'. Format is not natively supported, "
- "and no external commands succeeded.",
- cimg_instance,
- filename);
- return *this;
- }
- //! Serialize a CImg<T> instance into a raw CImg<unsigned char> buffer.
- /**
- \param is_compressed tells if zlib compression must be used for serialization
- (this requires 'cimg_use_zlib' been enabled).
- \param header_size Reserve empty bytes as a starting header.
- **/
- CImg<ucharT> get_serialize(const bool is_compressed=false, const unsigned int header_size=0) const {
- return CImgList<T>(*this,true).get_serialize(is_compressed,header_size);
- }
- // [internal] Return a 40x38 color logo of a 'danger' item.
- static CImg<T> _logo40x38() {
- CImg<T> res(40,38,1,3);
- const unsigned char *ptrs = cimg::logo40x38;
- T *ptr1 = res.data(0,0,0,0), *ptr2 = res.data(0,0,0,1), *ptr3 = res.data(0,0,0,2);
- for (ulongT off = 0; off<(ulongT)res._width*res._height;) {
- const unsigned char n = *(ptrs++), r = *(ptrs++), g = *(ptrs++), b = *(ptrs++);
- for (unsigned int l = 0; l<n; ++off, ++l) { *(ptr1++) = (T)r; *(ptr2++) = (T)g; *(ptr3++) = (T)b; }
- }
- return res;
- }
- //@}
- }; // struct CImg { ...
- /*
- #-----------------------------------------
- #
- #
- #
- # Definition of the CImgList<T> structure
- #
- #
- #
- #------------------------------------------
- */
- //! Represent a list of images CImg<T>.
- template<typename T>
- struct CImgList {
- unsigned int _width, _allocated_width;
- CImg<T> *_data;
- //! Simple iterator type, to loop through each image of a list.
- /**
- \note
- - The \c CImgList<T>::iterator type is defined as a <tt>CImg<T>*</tt>.
- - You may use it like this:
- \code
- CImgList<> list; // Assuming this image list is not empty
- for (CImgList<>::iterator it = list.begin(); it<list.end(); ++it) (*it).mirror('x');
- \endcode
- - Using the loop macro \c cimglist_for is another (more concise) alternative:
- \code
- cimglist_for(list,l) list[l].mirror('x');
- \endcode
- **/
- typedef CImg<T>* iterator;
- //! Simple const iterator type, to loop through each image of a \c const list instance.
- /**
- \note
- - The \c CImgList<T>::const_iterator type is defined to be a <tt>const CImg<T>*</tt>.
- - Similar to CImgList<T>::iterator, but for constant list instances.
- **/
- typedef const CImg<T>* const_iterator;
- //! Pixel value type.
- /**
- Refer to the pixels value type of the images in the list.
- \note
- - The \c CImgList<T>::value_type type of a \c CImgList<T> is defined to be a \c T.
- It is then similar to CImg<T>::value_type.
- - \c CImgList<T>::value_type is actually not used in %CImg methods. It has been mainly defined for
- compatibility with STL naming conventions.
- **/
- typedef T value_type;
- // Define common types related to template type T.
- typedef typename cimg::superset<T,bool>::type Tbool;
- typedef typename cimg::superset<T,unsigned char>::type Tuchar;
- typedef typename cimg::superset<T,char>::type Tchar;
- typedef typename cimg::superset<T,unsigned short>::type Tushort;
- typedef typename cimg::superset<T,short>::type Tshort;
- typedef typename cimg::superset<T,unsigned int>::type Tuint;
- typedef typename cimg::superset<T,int>::type Tint;
- typedef typename cimg::superset<T,cimg_ulong>::type Tulong;
- typedef typename cimg::superset<T,cimg_long>::type Tlong;
- typedef typename cimg::superset<T,float>::type Tfloat;
- typedef typename cimg::superset<T,double>::type Tdouble;
- typedef typename cimg::last<T,bool>::type boolT;
- typedef typename cimg::last<T,unsigned char>::type ucharT;
- typedef typename cimg::last<T,char>::type charT;
- typedef typename cimg::last<T,unsigned short>::type ushortT;
- typedef typename cimg::last<T,short>::type shortT;
- typedef typename cimg::last<T,unsigned int>::type uintT;
- typedef typename cimg::last<T,int>::type intT;
- typedef typename cimg::last<T,cimg_ulong>::type ulongT;
- typedef typename cimg::last<T,cimg_long>::type longT;
- typedef typename cimg::last<T,cimg_uint64>::type uint64T;
- typedef typename cimg::last<T,cimg_int64>::type int64T;
- typedef typename cimg::last<T,float>::type floatT;
- typedef typename cimg::last<T,double>::type doubleT;
- //@}
- //---------------------------
- //
- //! \name Plugins
- //@{
- //---------------------------
- #ifdef cimglist_plugin
- #include cimglist_plugin
- #endif
- #ifdef cimglist_plugin1
- #include cimglist_plugin1
- #endif
- #ifdef cimglist_plugin2
- #include cimglist_plugin2
- #endif
- #ifdef cimglist_plugin3
- #include cimglist_plugin3
- #endif
- #ifdef cimglist_plugin4
- #include cimglist_plugin4
- #endif
- #ifdef cimglist_plugin5
- #include cimglist_plugin5
- #endif
- #ifdef cimglist_plugin6
- #include cimglist_plugin6
- #endif
- #ifdef cimglist_plugin7
- #include cimglist_plugin7
- #endif
- #ifdef cimglist_plugin8
- #include cimglist_plugin8
- #endif
- //@}
- //--------------------------------------------------------
- //
- //! \name Constructors / Destructor / Instance Management
- //@{
- //--------------------------------------------------------
- //! Destructor.
- /**
- Destroy current list instance.
- \note
- - Any allocated buffer is deallocated.
- - Destroying an empty list does nothing actually.
- **/
- ~CImgList() {
- delete[] _data;
- }
- //! Default constructor.
- /**
- Construct a new empty list instance.
- \note
- - An empty list has no pixel data and its dimension width() is set to \c 0, as well as its
- image buffer pointer data().
- - An empty list may be reassigned afterwards, with the family of the assign() methods.
- In all cases, the type of pixels stays \c T.
- **/
- CImgList():
- _width(0),_allocated_width(0),_data(0) {}
- //! Construct list containing empty images.
- /**
- \param n Number of empty images.
- \note Useful when you know by advance the number of images you want to manage, as
- it will allocate the right amount of memory for the list, without needs for reallocation
- (that may occur when starting from an empty list and inserting several images in it).
- **/
- explicit CImgList(const unsigned int n):_width(n) {
- if (n) _data = new CImg<T>[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))];
- else { _allocated_width = 0; _data = 0; }
- }
- //! Construct list containing images of specified size.
- /**
- \param n Number of images.
- \param width Width of images.
- \param height Height of images.
- \param depth Depth of images.
- \param spectrum Number of channels of images.
- \note Pixel values are not initialized and may probably contain garbage.
- **/
- CImgList(const unsigned int n, const unsigned int width, const unsigned int height=1,
- const unsigned int depth=1, const unsigned int spectrum=1):
- _width(0),_allocated_width(0),_data(0) {
- assign(n);
- cimglist_apply(*this,assign)(width,height,depth,spectrum);
- }
- //! Construct list containing images of specified size, and initialize pixel values.
- /**
- \param n Number of images.
- \param width Width of images.
- \param height Height of images.
- \param depth Depth of images.
- \param spectrum Number of channels of images.
- \param val Initialization value for images pixels.
- **/
- CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
- const unsigned int depth, const unsigned int spectrum, const T& val):
- _width(0),_allocated_width(0),_data(0) {
- assign(n);
- cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
- }
- //! Construct list containing images of specified size, and initialize pixel values from a sequence of integers.
- /**
- \param n Number of images.
- \param width Width of images.
- \param height Height of images.
- \param depth Depth of images.
- \param spectrum Number of channels of images.
- \param val0 First value of the initializing integers sequence.
- \param val1 Second value of the initializing integers sequence.
- \warning You must specify at least <tt>width*height*depth*spectrum</tt> values in your argument list,
- or you will probably segfault.
- **/
- CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
- const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...):
- _width(0),_allocated_width(0),_data(0) {
- #define _CImgList_stdarg(t) { \
- assign(n,width,height,depth,spectrum); \
- const ulongT siz = (ulongT)width*height*depth*spectrum, nsiz = siz*n; \
- T *ptrd = _data->_data; \
- va_list ap; \
- va_start(ap,val1); \
- for (ulongT l = 0, s = 0, i = 0; i<nsiz; ++i) { \
- *(ptrd++) = (T)(i==0?val0:(i==1?val1:va_arg(ap,t))); \
- if ((++s)==siz) { ptrd = _data[++l]._data; s = 0; } \
- } \
- va_end(ap); \
- }
- _CImgList_stdarg(int);
- }
- //! Construct list containing images of specified size, and initialize pixel values from a sequence of doubles.
- /**
- \param n Number of images.
- \param width Width of images.
- \param height Height of images.
- \param depth Depth of images.
- \param spectrum Number of channels of images.
- \param val0 First value of the initializing doubles sequence.
- \param val1 Second value of the initializing doubles sequence.
- \warning You must specify at least <tt>width*height*depth*spectrum</tt> values in your argument list,
- or you will probably segfault.
- **/
- CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
- const unsigned int depth, const unsigned int spectrum, const double val0, const double val1, ...):
- _width(0),_allocated_width(0),_data(0) {
- _CImgList_stdarg(double);
- }
- //! Construct list containing copies of an input image.
- /**
- \param n Number of images.
- \param img Input image to copy in the constructed list.
- \param is_shared Tells if the elements of the list are shared or non-shared copies of \c img.
- **/
- template<typename t>
- CImgList(const unsigned int n, const CImg<t>& img, const bool is_shared=false):
- _width(0),_allocated_width(0),_data(0) {
- assign(n);
- cimglist_apply(*this,assign)(img,is_shared);
- }
- //! Construct list from one image.
- /**
- \param img Input image to copy in the constructed list.
- \param is_shared Tells if the element of the list is a shared or non-shared copy of \c img.
- **/
- template<typename t>
- explicit CImgList(const CImg<t>& img, const bool is_shared=false):
- _width(0),_allocated_width(0),_data(0) {
- assign(1);
- _data[0].assign(img,is_shared);
- }
- //! Construct list from two images.
- /**
- \param img1 First input image to copy in the constructed list.
- \param img2 Second input image to copy in the constructed list.
- \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
- **/
- template<typename t1, typename t2>
- CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false):
- _width(0),_allocated_width(0),_data(0) {
- assign(2);
- _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared);
- }
- //! Construct list from three images.
- /**
- \param img1 First input image to copy in the constructed list.
- \param img2 Second input image to copy in the constructed list.
- \param img3 Third input image to copy in the constructed list.
- \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
- **/
- template<typename t1, typename t2, typename t3>
- CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool is_shared=false):
- _width(0),_allocated_width(0),_data(0) {
- assign(3);
- _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
- }
- //! Construct list from four images.
- /**
- \param img1 First input image to copy in the constructed list.
- \param img2 Second input image to copy in the constructed list.
- \param img3 Third input image to copy in the constructed list.
- \param img4 Fourth input image to copy in the constructed list.
- \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
- **/
- template<typename t1, typename t2, typename t3, typename t4>
- CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
- const bool is_shared=false):
- _width(0),_allocated_width(0),_data(0) {
- assign(4);
- _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
- _data[3].assign(img4,is_shared);
- }
- //! Construct list from five images.
- /**
- \param img1 First input image to copy in the constructed list.
- \param img2 Second input image to copy in the constructed list.
- \param img3 Third input image to copy in the constructed list.
- \param img4 Fourth input image to copy in the constructed list.
- \param img5 Fifth input image to copy in the constructed list.
- \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
- **/
- template<typename t1, typename t2, typename t3, typename t4, typename t5>
- CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
- const CImg<t5>& img5, const bool is_shared=false):
- _width(0),_allocated_width(0),_data(0) {
- assign(5);
- _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
- _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared);
- }
- //! Construct list from six images.
- /**
- \param img1 First input image to copy in the constructed list.
- \param img2 Second input image to copy in the constructed list.
- \param img3 Third input image to copy in the constructed list.
- \param img4 Fourth input image to copy in the constructed list.
- \param img5 Fifth input image to copy in the constructed list.
- \param img6 Sixth input image to copy in the constructed list.
- \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
- **/
- template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
- CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
- const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false):
- _width(0),_allocated_width(0),_data(0) {
- assign(6);
- _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
- _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
- }
- //! Construct list from seven images.
- /**
- \param img1 First input image to copy in the constructed list.
- \param img2 Second input image to copy in the constructed list.
- \param img3 Third input image to copy in the constructed list.
- \param img4 Fourth input image to copy in the constructed list.
- \param img5 Fifth input image to copy in the constructed list.
- \param img6 Sixth input image to copy in the constructed list.
- \param img7 Seventh input image to copy in the constructed list.
- \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
- **/
- template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
- CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
- const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool is_shared=false):
- _width(0),_allocated_width(0),_data(0) {
- assign(7);
- _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
- _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
- _data[6].assign(img7,is_shared);
- }
- //! Construct list from eight images.
- /**
- \param img1 First input image to copy in the constructed list.
- \param img2 Second input image to copy in the constructed list.
- \param img3 Third input image to copy in the constructed list.
- \param img4 Fourth input image to copy in the constructed list.
- \param img5 Fifth input image to copy in the constructed list.
- \param img6 Sixth input image to copy in the constructed list.
- \param img7 Seventh input image to copy in the constructed list.
- \param img8 Eighth input image to copy in the constructed list.
- \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
- **/
- template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
- CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
- const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8,
- const bool is_shared=false):
- _width(0),_allocated_width(0),_data(0) {
- assign(8);
- _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
- _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
- _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared);
- }
- //! Construct list copy.
- /**
- \param list Input list to copy.
- \note The shared state of each element of the constructed list is kept the same as in \c list.
- **/
- template<typename t>
- CImgList(const CImgList<t>& list):_width(0),_allocated_width(0),_data(0) {
- assign(list._width);
- cimglist_for(*this,l) _data[l].assign(list[l],false);
- }
- //! Construct list copy \specialization.
- CImgList(const CImgList<T>& list):_width(0),_allocated_width(0),_data(0) {
- assign(list._width);
- cimglist_for(*this,l) _data[l].assign(list[l],list[l]._is_shared);
- }
- //! Construct list copy, and force the shared state of the list elements.
- /**
- \param list Input list to copy.
- \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
- **/
- template<typename t>
- CImgList(const CImgList<t>& list, const bool is_shared):_width(0),_allocated_width(0),_data(0) {
- assign(list._width);
- cimglist_for(*this,l) _data[l].assign(list[l],is_shared);
- }
- //! Construct list by reading the content of a file.
- /**
- \param filename Filename, as a C-string.
- **/
- explicit CImgList(const char *const filename):_width(0),_allocated_width(0),_data(0) {
- assign(filename);
- }
- //! Construct list from the content of a display window.
- /**
- \param disp Display window to get content from.
- \note Constructed list contains a single image only.
- **/
- explicit CImgList(const CImgDisplay& disp):_width(0),_allocated_width(0),_data(0) {
- assign(disp);
- }
- //! Return a list with elements being shared copies of images in the list instance.
- /**
- \note <tt>list2 = list1.get_shared()</tt> is equivalent to <tt>list2.assign(list1,true)</tt>.
- **/
- CImgList<T> get_shared() {
- CImgList<T> res(_width);
- cimglist_for(*this,l) res[l].assign(_data[l],true);
- return res;
- }
- //! Return a list with elements being shared copies of images in the list instance \const.
- const CImgList<T> get_shared() const {
- CImgList<T> res(_width);
- cimglist_for(*this,l) res[l].assign(_data[l],true);
- return res;
- }
- //! Destructor \inplace.
- /**
- \see CImgList().
- **/
- CImgList<T>& assign() {
- delete[] _data;
- _width = _allocated_width = 0;
- _data = 0;
- return *this;
- }
- //! Destructor \inplace.
- /**
- Equivalent to assign().
- \note Only here for compatibility with STL naming conventions.
- **/
- CImgList<T>& clear() {
- return assign();
- }
- //! Construct list containing empty images \inplace.
- /**
- \see CImgList(unsigned int).
- **/
- CImgList<T>& assign(const unsigned int n) {
- if (!n) return assign();
- if (_allocated_width<n || _allocated_width>(n<<2)) {
- delete[] _data;
- _data = new CImg<T>[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))];
- }
- _width = n;
- return *this;
- }
- //! Construct list containing images of specified size \inplace.
- /**
- \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int).
- **/
- CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height=1,
- const unsigned int depth=1, const unsigned int spectrum=1) {
- assign(n);
- cimglist_apply(*this,assign)(width,height,depth,spectrum);
- return *this;
- }
- //! Construct list containing images of specified size, and initialize pixel values \inplace.
- /**
- \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const T).
- **/
- CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
- const unsigned int depth, const unsigned int spectrum, const T& val) {
- assign(n);
- cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
- return *this;
- }
- //! Construct list with images of specified size, and initialize pixel values from a sequence of integers \inplace.
- /**
- \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const int, const int, ...).
- **/
- CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
- const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...) {
- _CImgList_stdarg(int);
- return *this;
- }
- //! Construct list with images of specified size, and initialize pixel values from a sequence of doubles \inplace.
- /**
- \see CImgList(unsigned int,unsigned int,unsigned int,unsigned int,unsigned int,const double,const double,...).
- **/
- CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
- const unsigned int depth, const unsigned int spectrum,
- const double val0, const double val1, ...) {
- _CImgList_stdarg(double);
- return *this;
- }
- //! Construct list containing copies of an input image \inplace.
- /**
- \see CImgList(unsigned int, const CImg<t>&, bool).
- **/
- template<typename t>
- CImgList<T>& assign(const unsigned int n, const CImg<t>& img, const bool is_shared=false) {
- assign(n);
- cimglist_apply(*this,assign)(img,is_shared);
- return *this;
- }
- //! Construct list from one image \inplace.
- /**
- \see CImgList(const CImg<t>&, bool).
- **/
- template<typename t>
- CImgList<T>& assign(const CImg<t>& img, const bool is_shared=false) {
- assign(1);
- _data[0].assign(img,is_shared);
- return *this;
- }
- //! Construct list from two images \inplace.
- /**
- \see CImgList(const CImg<t>&, const CImg<t>&, bool).
- **/
- template<typename t1, typename t2>
- CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false) {
- assign(2);
- _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared);
- return *this;
- }
- //! Construct list from three images \inplace.
- /**
- \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
- **/
- template<typename t1, typename t2, typename t3>
- CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool is_shared=false) {
- assign(3);
- _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
- return *this;
- }
- //! Construct list from four images \inplace.
- /**
- \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
- **/
- template<typename t1, typename t2, typename t3, typename t4>
- CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
- const bool is_shared=false) {
- assign(4);
- _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
- _data[3].assign(img4,is_shared);
- return *this;
- }
- //! Construct list from five images \inplace.
- /**
- \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
- **/
- template<typename t1, typename t2, typename t3, typename t4, typename t5>
- CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
- const CImg<t5>& img5, const bool is_shared=false) {
- assign(5);
- _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
- _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared);
- return *this;
- }
- //! Construct list from six images \inplace.
- /**
- \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&, bool).
- **/
- template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
- CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
- const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false) {
- assign(6);
- _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
- _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
- return *this;
- }
- //! Construct list from seven images \inplace.
- /**
- \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,
- const CImg<t>&, bool).
- **/
- template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
- CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
- const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool is_shared=false) {
- assign(7);
- _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
- _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
- _data[6].assign(img7,is_shared);
- return *this;
- }
- //! Construct list from eight images \inplace.
- /**
- \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,
- const CImg<t>&, const CImg<t>&, bool).
- **/
- template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
- CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
- const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8,
- const bool is_shared=false) {
- assign(8);
- _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
- _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
- _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared);
- return *this;
- }
- //! Construct list as a copy of an existing list and force the shared state of the list elements \inplace.
- /**
- \see CImgList(const CImgList<t>&, bool is_shared).
- **/
- template<typename t>
- CImgList<T>& assign(const CImgList<t>& list, const bool is_shared=false) {
- cimg::unused(is_shared);
- assign(list._width);
- cimglist_for(*this,l) _data[l].assign(list[l],false);
- return *this;
- }
- //! Construct list as a copy of an existing list and force shared state of elements \inplace \specialization.
- CImgList<T>& assign(const CImgList<T>& list, const bool is_shared=false) {
- if (this==&list) return *this;
- CImgList<T> res(list._width);
- cimglist_for(res,l) res[l].assign(list[l],is_shared);
- return res.move_to(*this);
- }
- //! Construct list by reading the content of a file \inplace.
- /**
- \see CImgList(const char *const).
- **/
- CImgList<T>& assign(const char *const filename) {
- return load(filename);
- }
- //! Construct list from the content of a display window \inplace.
- /**
- \see CImgList(const CImgDisplay&).
- **/
- CImgList<T>& assign(const CImgDisplay &disp) {
- return assign(CImg<T>(disp));
- }
- //! Transfer the content of the list instance to another list.
- /**
- \param list Destination list.
- \note When returning, the current list instance is empty and the initial content of \c list is destroyed.
- **/
- template<typename t>
- CImgList<t>& move_to(CImgList<t>& list) {
- list.assign(_width);
- bool is_one_shared_element = false;
- cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
- if (is_one_shared_element) cimglist_for(*this,l) list[l].assign(_data[l]);
- else cimglist_for(*this,l) _data[l].move_to(list[l]);
- assign();
- return list;
- }
- //! Transfer the content of the list instance at a specified position in another list.
- /**
- \param list Destination list.
- \param pos Index of the insertion in the list.
- \note When returning, the list instance is empty and the initial content of \c list is preserved
- (only images indexes may be modified).
- **/
- template<typename t>
- CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos) {
- if (is_empty()) return list;
- const unsigned int npos = pos>list._width?list._width:pos;
- list.insert(_width,npos);
- bool is_one_shared_element = false;
- cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
- if (is_one_shared_element) cimglist_for(*this,l) list[npos + l].assign(_data[l]);
- else cimglist_for(*this,l) _data[l].move_to(list[npos + l]);
- assign();
- return list;
- }
- //! Swap all fields between two list instances.
- /**
- \param list List to swap fields with.
- \note Can be used to exchange the content of two lists in a fast way.
- **/
- CImgList<T>& swap(CImgList<T>& list) {
- cimg::swap(_width,list._width,_allocated_width,list._allocated_width);
- cimg::swap(_data,list._data);
- return list;
- }
- //! Return a reference to an empty list.
- /**
- \note Can be used to define default values in a function taking a CImgList<T> as an argument.
- \code
- void f(const CImgList<char>& list=CImgList<char>::empty());
- \endcode
- **/
- static CImgList<T>& empty() {
- static CImgList<T> _empty;
- return _empty.assign();
- }
- //! Return a reference to an empty list \const.
- static const CImgList<T>& const_empty() {
- static const CImgList<T> _empty;
- return _empty;
- }
- //@}
- //------------------------------------------
- //
- //! \name Overloaded Operators
- //@{
- //------------------------------------------
- //! Return a reference to one image element of the list.
- /**
- \param pos Index of the image element.
- **/
- CImg<T>& operator()(const unsigned int pos) {
- #if cimg_verbosity>=3
- if (pos>=_width) {
- cimg::warn(_cimglist_instance
- "operator(): Invalid image request, at position [%u].",
- cimglist_instance,
- pos);
- return *_data;
- }
- #endif
- return _data[pos];
- }
- //! Return a reference to one image of the list.
- /**
- \param pos Index of the image element.
- **/
- const CImg<T>& operator()(const unsigned int pos) const {
- return const_cast<CImgList<T>*>(this)->operator()(pos);
- }
- //! Return a reference to one pixel value of one image of the list.
- /**
- \param pos Index of the image element.
- \param x X-coordinate of the pixel value.
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- \param c C-coordinate of the pixel value.
- \note <tt>list(n,x,y,z,c)</tt> is equivalent to <tt>list[n](x,y,z,c)</tt>.
- **/
- T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
- const unsigned int z=0, const unsigned int c=0) {
- return (*this)[pos](x,y,z,c);
- }
- //! Return a reference to one pixel value of one image of the list \const.
- const T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
- const unsigned int z=0, const unsigned int c=0) const {
- return (*this)[pos](x,y,z,c);
- }
- //! Return pointer to the first image of the list.
- /**
- \note Images in a list are stored as a buffer of \c CImg<T>.
- **/
- operator CImg<T>*() {
- return _data;
- }
- //! Return pointer to the first image of the list \const.
- operator const CImg<T>*() const {
- return _data;
- }
- //! Construct list from one image \inplace.
- /**
- \param img Input image to copy in the constructed list.
- \note <tt>list = img;</tt> is equivalent to <tt>list.assign(img);</tt>.
- **/
- template<typename t>
- CImgList<T>& operator=(const CImg<t>& img) {
- return assign(img);
- }
- //! Construct list from another list.
- /**
- \param list Input list to copy.
- \note <tt>list1 = list2</tt> is equivalent to <tt>list1.assign(list2);</tt>.
- **/
- template<typename t>
- CImgList<T>& operator=(const CImgList<t>& list) {
- return assign(list);
- }
- //! Construct list from another list \specialization.
- CImgList<T>& operator=(const CImgList<T>& list) {
- return assign(list);
- }
- //! Construct list by reading the content of a file \inplace.
- /**
- \see CImgList(const char *const).
- **/
- CImgList<T>& operator=(const char *const filename) {
- return assign(filename);
- }
- //! Construct list from the content of a display window \inplace.
- /**
- \see CImgList(const CImgDisplay&).
- **/
- CImgList<T>& operator=(const CImgDisplay& disp) {
- return assign(disp);
- }
- //! Return a non-shared copy of a list.
- /**
- \note <tt>+list</tt> is equivalent to <tt>CImgList<T>(list,false)</tt>.
- It forces the copy to have non-shared elements.
- **/
- CImgList<T> operator+() const {
- return CImgList<T>(*this,false);
- }
- //! Return a copy of the list instance, where image \c img has been inserted at the end.
- /**
- \param img Image inserted at the end of the instance copy.
- \note Define a convenient way to create temporary lists of images, as in the following code:
- \code
- (img1,img2,img3,img4).display("My four images");
- \endcode
- **/
- template<typename t>
- CImgList<T>& operator,(const CImg<t>& img) {
- return insert(img);
- }
- //! Return a copy of the list instance, where image \c img has been inserted at the end \const.
- template<typename t>
- CImgList<T> operator,(const CImg<t>& img) const {
- return (+*this).insert(img);
- }
- //! Return a copy of the list instance, where all elements of input list \c list have been inserted at the end.
- /**
- \param list List inserted at the end of the instance copy.
- **/
- template<typename t>
- CImgList<T>& operator,(const CImgList<t>& list) {
- return insert(list);
- }
- //! Return a copy of the list instance, where all elements of input \c list have been inserted at the end \const.
- template<typename t>
- CImgList<T>& operator,(const CImgList<t>& list) const {
- return (+*this).insert(list);
- }
- //! Return image corresponding to the appending of all images of the instance list along specified axis.
- /**
- \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
- \note <tt>list>'x'</tt> is equivalent to <tt>list.get_append('x')</tt>.
- **/
- CImg<T> operator>(const char axis) const {
- return get_append(axis,0);
- }
- //! Return list corresponding to the splitting of all images of the instance list along specified axis.
- /**
- \param axis Axis used for image splitting.
- \note <tt>list<'x'</tt> is equivalent to <tt>list.get_split('x')</tt>.
- **/
- CImgList<T> operator<(const char axis) const {
- return get_split(axis);
- }
- //@}
- //-------------------------------------
- //
- //! \name Instance Characteristics
- //@{
- //-------------------------------------
- //! Return the type of image pixel values as a C string.
- /**
- Return a \c char* string containing the usual type name of the image pixel values
- (i.e. a stringified version of the template parameter \c T).
- \note
- - The returned string does not contain any spaces.
- - If the pixel type \c T does not correspond to a registered type, the string <tt>"unknown"</tt> is returned.
- **/
- static const char* pixel_type() {
- return cimg::type<T>::string();
- }
- //! Return the size of the list, i.e. the number of images contained in it.
- /**
- \note Similar to size() but returns result as a (signed) integer.
- **/
- int width() const {
- return (int)_width;
- }
- //! Return the size of the list, i.e. the number of images contained in it.
- /**
- \note Similar to width() but returns result as an unsigned integer.
- **/
- unsigned int size() const {
- return _width;
- }
- //! Return pointer to the first image of the list.
- /**
- \note Images in a list are stored as a buffer of \c CImg<T>.
- **/
- CImg<T> *data() {
- return _data;
- }
- //! Return pointer to the first image of the list \const.
- const CImg<T> *data() const {
- return _data;
- }
- //! Return pointer to the pos-th image of the list.
- /**
- \param pos Index of the image element to access.
- \note <tt>list.data(n);</tt> is equivalent to <tt>list.data + n;</tt>.
- **/
- #if cimg_verbosity>=3
- CImg<T> *data(const unsigned int pos) {
- if (pos>=size())
- cimg::warn(_cimglist_instance
- "data(): Invalid pointer request, at position [%u].",
- cimglist_instance,
- pos);
- return _data + pos;
- }
- const CImg<T> *data(const unsigned int l) const {
- return const_cast<CImgList<T>*>(this)->data(l);
- }
- #else
- CImg<T> *data(const unsigned int l) {
- return _data + l;
- }
- //! Return pointer to the pos-th image of the list \const.
- const CImg<T> *data(const unsigned int l) const {
- return _data + l;
- }
- #endif
- //! Return iterator to the first image of the list.
- /**
- **/
- iterator begin() {
- return _data;
- }
- //! Return iterator to the first image of the list \const.
- const_iterator begin() const {
- return _data;
- }
- //! Return iterator to one position after the last image of the list.
- /**
- **/
- iterator end() {
- return _data + _width;
- }
- //! Return iterator to one position after the last image of the list \const.
- const_iterator end() const {
- return _data + _width;
- }
- //! Return reference to the first image of the list.
- /**
- **/
- CImg<T>& front() {
- return *_data;
- }
- //! Return reference to the first image of the list \const.
- const CImg<T>& front() const {
- return *_data;
- }
- //! Return a reference to the last image of the list.
- /**
- **/
- const CImg<T>& back() const {
- return *(_data + _width - 1);
- }
- //! Return a reference to the last image of the list \const.
- CImg<T>& back() {
- return *(_data + _width - 1);
- }
- //! Return pos-th image of the list.
- /**
- \param pos Index of the image element to access.
- **/
- CImg<T>& at(const int pos) {
- if (is_empty())
- throw CImgInstanceException(_cimglist_instance
- "at(): Empty instance.",
- cimglist_instance);
- return _data[cimg::cut(pos,0,width() - 1)];
- }
- //! Access to pixel value with Dirichlet boundary conditions.
- /**
- \param pos Index of the image element to access.
- \param x X-coordinate of the pixel value.
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- \param c C-coordinate of the pixel value.
- \param out_value Default value returned if \c offset is outside image bounds.
- \note <tt>list.atNXYZC(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZC(x,y,z,c);</tt>.
- **/
- T& atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
- return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atXYZC(x,y,z,c,out_value);
- }
- //! Access to pixel value with Dirichlet boundary conditions \const.
- T atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
- return (pos<0 || pos>=width())?out_value:_data[pos].atXYZC(x,y,z,c,out_value);
- }
- //! Access to pixel value with Neumann boundary conditions.
- /**
- \param pos Index of the image element to access.
- \param x X-coordinate of the pixel value.
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- \param c C-coordinate of the pixel value.
- \note <tt>list.atNXYZC(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZC(x,y,z,c);</tt>.
- **/
- T& atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
- if (is_empty())
- throw CImgInstanceException(_cimglist_instance
- "atNXYZC(): Empty instance.",
- cimglist_instance);
- return _atNXYZC(pos,x,y,z,c);
- }
- //! Access to pixel value with Neumann boundary conditions \const.
- T atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
- if (is_empty())
- throw CImgInstanceException(_cimglist_instance
- "atNXYZC(): Empty instance.",
- cimglist_instance);
- return _atNXYZC(pos,x,y,z,c);
- }
- T& _atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
- return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c);
- }
- T _atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
- return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c);
- }
- //! Access pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y,\c z).
- /**
- \param pos Index of the image element to access.
- \param x X-coordinate of the pixel value.
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- \param c C-coordinate of the pixel value.
- \param out_value Default value returned if \c offset is outside image bounds.
- \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
- **/
- T& atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
- return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atXYZ(x,y,z,c,out_value);
- }
- //! Access pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y,\c z) \const.
- T atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
- return (pos<0 || pos>=width())?out_value:_data[pos].atXYZ(x,y,z,c,out_value);
- }
- //! Access to pixel value with Neumann boundary conditions for the 4 coordinates (\c pos, \c x,\c y,\c z).
- /**
- \param pos Index of the image element to access.
- \param x X-coordinate of the pixel value.
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- \param c C-coordinate of the pixel value.
- \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
- **/
- T& atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
- if (is_empty())
- throw CImgInstanceException(_cimglist_instance
- "atNXYZ(): Empty instance.",
- cimglist_instance);
- return _atNXYZ(pos,x,y,z,c);
- }
- //! Access to pixel value with Neumann boundary conditions for the 4 coordinates (\c pos, \c x,\c y,\c z) \const.
- T atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
- if (is_empty())
- throw CImgInstanceException(_cimglist_instance
- "atNXYZ(): Empty instance.",
- cimglist_instance);
- return _atNXYZ(pos,x,y,z,c);
- }
- T& _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
- return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c);
- }
- T _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
- return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c);
- }
- //! Access to pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y).
- /**
- \param pos Index of the image element to access.
- \param x X-coordinate of the pixel value.
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- \param c C-coordinate of the pixel value.
- \param out_value Default value returned if \c offset is outside image bounds.
- \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
- **/
- T& atNXY(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
- return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atXY(x,y,z,c,out_value);
- }
- //! Access to pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y) \const.
- T atNXY(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
- return (pos<0 || pos>=width())?out_value:_data[pos].atXY(x,y,z,c,out_value);
- }
- //! Access to pixel value with Neumann boundary conditions for the 3 coordinates (\c pos, \c x,\c y).
- /**
- \param pos Index of the image element to access.
- \param x X-coordinate of the pixel value.
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- \param c C-coordinate of the pixel value.
- \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
- **/
- T& atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
- if (is_empty())
- throw CImgInstanceException(_cimglist_instance
- "atNXY(): Empty instance.",
- cimglist_instance);
- return _atNXY(pos,x,y,z,c);
- }
- //! Access to pixel value with Neumann boundary conditions for the 3 coordinates (\c pos, \c x,\c y) \const.
- T atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
- if (is_empty())
- throw CImgInstanceException(_cimglist_instance
- "atNXY(): Empty instance.",
- cimglist_instance);
- return _atNXY(pos,x,y,z,c);
- }
- T& _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
- return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c);
- }
- T _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
- return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c);
- }
- //! Access to pixel value with Dirichlet boundary conditions for the 2 coordinates (\c pos,\c x).
- /**
- \param pos Index of the image element to access.
- \param x X-coordinate of the pixel value.
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- \param c C-coordinate of the pixel value.
- \param out_value Default value returned if \c offset is outside image bounds.
- \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
- **/
- T& atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
- return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atX(x,y,z,c,out_value);
- }
- //! Access to pixel value with Dirichlet boundary conditions for the 2 coordinates (\c pos,\c x) \const.
- T atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
- return (pos<0 || pos>=width())?out_value:_data[pos].atX(x,y,z,c,out_value);
- }
- //! Access to pixel value with Neumann boundary conditions for the 2 coordinates (\c pos, \c x).
- /**
- \param pos Index of the image element to access.
- \param x X-coordinate of the pixel value.
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- \param c C-coordinate of the pixel value.
- \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
- **/
- T& atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
- if (is_empty())
- throw CImgInstanceException(_cimglist_instance
- "atNX(): Empty instance.",
- cimglist_instance);
- return _atNX(pos,x,y,z,c);
- }
- //! Access to pixel value with Neumann boundary conditions for the 2 coordinates (\c pos, \c x) \const.
- T atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
- if (is_empty())
- throw CImgInstanceException(_cimglist_instance
- "atNX(): Empty instance.",
- cimglist_instance);
- return _atNX(pos,x,y,z,c);
- }
- T& _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
- return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c);
- }
- T _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
- return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c);
- }
- //! Access to pixel value with Dirichlet boundary conditions for the coordinate (\c pos).
- /**
- \param pos Index of the image element to access.
- \param x X-coordinate of the pixel value.
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- \param c C-coordinate of the pixel value.
- \param out_value Default value returned if \c offset is outside image bounds.
- \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
- **/
- T& atN(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
- return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):(*this)(pos,x,y,z,c);
- }
- //! Access to pixel value with Dirichlet boundary conditions for the coordinate (\c pos) \const.
- T atN(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
- return (pos<0 || pos>=width())?out_value:(*this)(pos,x,y,z,c);
- }
- //! Return pixel value with Neumann boundary conditions for the coordinate (\c pos).
- /**
- \param pos Index of the image element to access.
- \param x X-coordinate of the pixel value.
- \param y Y-coordinate of the pixel value.
- \param z Z-coordinate of the pixel value.
- \param c C-coordinate of the pixel value.
- \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
- **/
- T& atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
- if (is_empty())
- throw CImgInstanceException(_cimglist_instance
- "atN(): Empty instance.",
- cimglist_instance);
- return _atN(pos,x,y,z,c);
- }
- //! Return pixel value with Neumann boundary conditions for the coordinate (\c pos) \const.
- T atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
- if (is_empty())
- throw CImgInstanceException(_cimglist_instance
- "atN(): Empty instance.",
- cimglist_instance);
- return _atN(pos,x,y,z,c);
- }
- T& _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
- return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c);
- }
- T _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
- return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c);
- }
- //@}
- //-------------------------------------
- //
- //! \name Instance Checking
- //@{
- //-------------------------------------
- //! Return \c true if list is empty.
- /**
- **/
- bool is_empty() const {
- return (!_data || !_width);
- }
- //! Test if number of image elements is equal to specified value.
- /**
- \param size_n Number of image elements to test.
- **/
- bool is_sameN(const unsigned int size_n) const {
- return _width==size_n;
- }
- //! Test if number of image elements is equal between two images lists.
- /**
- \param list Input list to compare with.
- **/
- template<typename t>
- bool is_sameN(const CImgList<t>& list) const {
- return is_sameN(list._width);
- }
- // Define useful functions to check list dimensions.
- // (cannot be documented because macro-generated).
- #define _cimglist_def_is_same1(axis) \
- bool is_same##axis(const unsigned int val) const { \
- bool res = true; \
- for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(val); \
- return res; \
- } \
- bool is_sameN##axis(const unsigned int n, const unsigned int val) const { \
- return is_sameN(n) && is_same##axis(val); \
- } \
- #define _cimglist_def_is_same2(axis1,axis2) \
- bool is_same##axis1##axis2(const unsigned int val1, const unsigned int val2) const { \
- bool res = true; \
- for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2(val1,val2); \
- return res; \
- } \
- bool is_sameN##axis1##axis2(const unsigned int n, const unsigned int val1, const unsigned int val2) const { \
- return is_sameN(n) && is_same##axis1##axis2(val1,val2); \
- } \
- #define _cimglist_def_is_same3(axis1,axis2,axis3) \
- bool is_same##axis1##axis2##axis3(const unsigned int val1, const unsigned int val2, \
- const unsigned int val3) const { \
- bool res = true; \
- for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2##axis3(val1,val2,val3); \
- return res; \
- } \
- bool is_sameN##axis1##axis2##axis3(const unsigned int n, const unsigned int val1, \
- const unsigned int val2, const unsigned int val3) const { \
- return is_sameN(n) && is_same##axis1##axis2##axis3(val1,val2,val3); \
- } \
- #define _cimglist_def_is_same(axis) \
- template<typename t> bool is_same##axis(const CImg<t>& img) const { \
- bool res = true; \
- for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(img); \
- return res; \
- } \
- template<typename t> bool is_same##axis(const CImgList<t>& list) const { \
- const unsigned int lmin = std::min(_width,list._width); \
- bool res = true; \
- for (unsigned int l = 0; l<lmin && res; ++l) res = _data[l].is_same##axis(list[l]); \
- return res; \
- } \
- template<typename t> bool is_sameN##axis(const unsigned int n, const CImg<t>& img) const { \
- return (is_sameN(n) && is_same##axis(img)); \
- } \
- template<typename t> bool is_sameN##axis(const CImgList<t>& list) const { \
- return (is_sameN(list) && is_same##axis(list)); \
- }
- _cimglist_def_is_same(XY)
- _cimglist_def_is_same(XZ)
- _cimglist_def_is_same(XC)
- _cimglist_def_is_same(YZ)
- _cimglist_def_is_same(YC)
- _cimglist_def_is_same(XYZ)
- _cimglist_def_is_same(XYC)
- _cimglist_def_is_same(YZC)
- _cimglist_def_is_same(XYZC)
- _cimglist_def_is_same1(X)
- _cimglist_def_is_same1(Y)
- _cimglist_def_is_same1(Z)
- _cimglist_def_is_same1(C)
- _cimglist_def_is_same2(X,Y)
- _cimglist_def_is_same2(X,Z)
- _cimglist_def_is_same2(X,C)
- _cimglist_def_is_same2(Y,Z)
- _cimglist_def_is_same2(Y,C)
- _cimglist_def_is_same2(Z,C)
- _cimglist_def_is_same3(X,Y,Z)
- _cimglist_def_is_same3(X,Y,C)
- _cimglist_def_is_same3(X,Z,C)
- _cimglist_def_is_same3(Y,Z,C)
- //! Test if dimensions of each image of the list match specified arguments.
- /**
- \param dx Checked image width.
- \param dy Checked image height.
- \param dz Checked image depth.
- \param dc Checked image spectrum.
- **/
- bool is_sameXYZC(const unsigned int dx, const unsigned int dy,
- const unsigned int dz, const unsigned int dc) const {
- bool res = true;
- for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_sameXYZC(dx,dy,dz,dc);
- return res;
- }
- //! Test if list dimensions match specified arguments.
- /**
- \param n Number of images in the list.
- \param dx Checked image width.
- \param dy Checked image height.
- \param dz Checked image depth.
- \param dc Checked image spectrum.
- **/
- bool is_sameNXYZC(const unsigned int n,
- const unsigned int dx, const unsigned int dy,
- const unsigned int dz, const unsigned int dc) const {
- return is_sameN(n) && is_sameXYZC(dx,dy,dz,dc);
- }
- //! Test if list contains one particular pixel location.
- /**
- \param n Index of the image whom checked pixel value belong to.
- \param x X-coordinate of the checked pixel value.
- \param y Y-coordinate of the checked pixel value.
- \param z Z-coordinate of the checked pixel value.
- \param c C-coordinate of the checked pixel value.
- **/
- bool containsNXYZC(const int n, const int x=0, const int y=0, const int z=0, const int c=0) const {
- if (is_empty()) return false;
- return n>=0 && n<width() && x>=0 && x<_data[n].width() && y>=0 && y<_data[n].height() &&
- z>=0 && z<_data[n].depth() && c>=0 && c<_data[n].spectrum();
- }
- //! Test if list contains image with specified index.
- /**
- \param n Index of the checked image.
- **/
- bool containsN(const int n) const {
- if (is_empty()) return false;
- return n>=0 && n<width();
- }
- //! Test if one image of the list contains the specified referenced value.
- /**
- \param pixel Reference to pixel value to test.
- \param[out] n Index of image containing the pixel value, if test succeeds.
- \param[out] x X-coordinate of the pixel value, if test succeeds.
- \param[out] y Y-coordinate of the pixel value, if test succeeds.
- \param[out] z Z-coordinate of the pixel value, if test succeeds.
- \param[out] c C-coordinate of the pixel value, if test succeeds.
- \note If true, set coordinates (n,x,y,z,c).
- **/
- template<typename t>
- bool contains(const T& pixel, t& n, t& x, t&y, t& z, t& c) const {
- if (is_empty()) return false;
- cimglist_for(*this,l) if (_data[l].contains(pixel,x,y,z,c)) { n = (t)l; return true; }
- return false;
- }
- //! Test if one of the image list contains the specified referenced value.
- /**
- \param pixel Reference to pixel value to test.
- \param[out] n Index of image containing the pixel value, if test succeeds.
- \param[out] x X-coordinate of the pixel value, if test succeeds.
- \param[out] y Y-coordinate of the pixel value, if test succeeds.
- \param[out] z Z-coordinate of the pixel value, if test succeeds.
- \note If true, set coordinates (n,x,y,z).
- **/
- template<typename t>
- bool contains(const T& pixel, t& n, t& x, t&y, t& z) const {
- t c;
- return contains(pixel,n,x,y,z,c);
- }
- //! Test if one of the image list contains the specified referenced value.
- /**
- \param pixel Reference to pixel value to test.
- \param[out] n Index of image containing the pixel value, if test succeeds.
- \param[out] x X-coordinate of the pixel value, if test succeeds.
- \param[out] y Y-coordinate of the pixel value, if test succeeds.
- \note If true, set coordinates (n,x,y).
- **/
- template<typename t>
- bool contains(const T& pixel, t& n, t& x, t&y) const {
- t z, c;
- return contains(pixel,n,x,y,z,c);
- }
- //! Test if one of the image list contains the specified referenced value.
- /**
- \param pixel Reference to pixel value to test.
- \param[out] n Index of image containing the pixel value, if test succeeds.
- \param[out] x X-coordinate of the pixel value, if test succeeds.
- \note If true, set coordinates (n,x).
- **/
- template<typename t>
- bool contains(const T& pixel, t& n, t& x) const {
- t y, z, c;
- return contains(pixel,n,x,y,z,c);
- }
- //! Test if one of the image list contains the specified referenced value.
- /**
- \param pixel Reference to pixel value to test.
- \param[out] n Index of image containing the pixel value, if test succeeds.
- \note If true, set coordinates (n).
- **/
- template<typename t>
- bool contains(const T& pixel, t& n) const {
- t x, y, z, c;
- return contains(pixel,n,x,y,z,c);
- }
- //! Test if one of the image list contains the specified referenced value.
- /**
- \param pixel Reference to pixel value to test.
- **/
- bool contains(const T& pixel) const {
- unsigned int n, x, y, z, c;
- return contains(pixel,n,x,y,z,c);
- }
- //! Test if the list contains the image 'img'.
- /**
- \param img Reference to image to test.
- \param[out] n Index of image in the list, if test succeeds.
- \note If true, returns the position (n) of the image in the list.
- **/
- template<typename t>
- bool contains(const CImg<T>& img, t& n) const {
- if (is_empty()) return false;
- const CImg<T> *const ptr = &img;
- cimglist_for(*this,i) if (_data + i==ptr) { n = (t)i; return true; }
- return false;
- }
- //! Test if the list contains the image img.
- /**
- \param img Reference to image to test.
- **/
- bool contains(const CImg<T>& img) const {
- unsigned int n;
- return contains(img,n);
- }
- //@}
- //-------------------------------------
- //
- //! \name Mathematical Functions
- //@{
- //-------------------------------------
- //! Return a reference to the minimum pixel value of the instance list.
- /**
- **/
- T& min() {
- bool is_all_empty = true;
- T *ptr_min = 0;
- cimglist_for(*this,l) if (!_data[l].is_empty()) {
- ptr_min = _data[l]._data;
- is_all_empty = false;
- break;
- }
- if (is_all_empty)
- throw CImgInstanceException(_cimglist_instance
- "min(): %s.",
- _data?"List of empty images":"Empty instance",
- cimglist_instance);
- T min_value = *ptr_min;
- cimglist_for(*this,l) {
- const CImg<T>& img = _data[l];
- cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
- }
- return *ptr_min;
- }
- //! Return a reference to the minimum pixel value of the instance list \const.
- const T& min() const {
- bool is_all_empty = true;
- T *ptr_min = 0;
- cimglist_for(*this,l) if (!_data[l].is_empty()) {
- ptr_min = _data[l]._data;
- is_all_empty = false;
- break;
- }
- if (is_all_empty)
- throw CImgInstanceException(_cimglist_instance
- "min(): %s.",
- _data?"List of empty images":"Empty instance",
- cimglist_instance);
- T min_value = *ptr_min;
- cimglist_for(*this,l) {
- const CImg<T>& img = _data[l];
- cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
- }
- return *ptr_min;
- }
- //! Return a reference to the maximum pixel value of the instance list.
- /**
- **/
- T& max() {
- bool is_all_empty = true;
- T *ptr_max = 0;
- cimglist_for(*this,l) if (!_data[l].is_empty()) {
- ptr_max = _data[l]._data;
- is_all_empty = false;
- break;
- }
- if (is_all_empty)
- throw CImgInstanceException(_cimglist_instance
- "max(): %s.",
- _data?"List of empty images":"Empty instance",
- cimglist_instance);
- T max_value = *ptr_max;
- cimglist_for(*this,l) {
- const CImg<T>& img = _data[l];
- cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
- }
- return *ptr_max;
- }
- //! Return a reference to the maximum pixel value of the instance list \const.
- const T& max() const {
- bool is_all_empty = true;
- T *ptr_max = 0;
- cimglist_for(*this,l) if (!_data[l].is_empty()) {
- ptr_max = _data[l]._data;
- is_all_empty = false;
- break;
- }
- if (is_all_empty)
- throw CImgInstanceException(_cimglist_instance
- "max(): %s.",
- _data?"List of empty images":"Empty instance",
- cimglist_instance);
- T max_value = *ptr_max;
- cimglist_for(*this,l) {
- const CImg<T>& img = _data[l];
- cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
- }
- return *ptr_max;
- }
- //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well.
- /**
- \param[out] max_val Value of the maximum value found.
- **/
- template<typename t>
- T& min_max(t& max_val) {
- bool is_all_empty = true;
- T *ptr_min = 0;
- cimglist_for(*this,l) if (!_data[l].is_empty()) {
- ptr_min = _data[l]._data;
- is_all_empty = false;
- break;
- }
- if (is_all_empty)
- throw CImgInstanceException(_cimglist_instance
- "min_max(): %s.",
- _data?"List of empty images":"Empty instance",
- cimglist_instance);
- T min_value = *ptr_min, max_value = min_value;
- cimglist_for(*this,l) {
- const CImg<T>& img = _data[l];
- cimg_for(img,ptrs,T) {
- const T val = *ptrs;
- if (val<min_value) { min_value = val; ptr_min = ptrs; }
- if (val>max_value) max_value = val;
- }
- }
- max_val = (t)max_value;
- return *ptr_min;
- }
- //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well \const.
- /**
- \param[out] max_val Value of the maximum value found.
- **/
- template<typename t>
- const T& min_max(t& max_val) const {
- bool is_all_empty = true;
- T *ptr_min = 0;
- cimglist_for(*this,l) if (!_data[l].is_empty()) {
- ptr_min = _data[l]._data;
- is_all_empty = false;
- break;
- }
- if (is_all_empty)
- throw CImgInstanceException(_cimglist_instance
- "min_max(): %s.",
- _data?"List of empty images":"Empty instance",
- cimglist_instance);
- T min_value = *ptr_min, max_value = min_value;
- cimglist_for(*this,l) {
- const CImg<T>& img = _data[l];
- cimg_for(img,ptrs,T) {
- const T val = *ptrs;
- if (val<min_value) { min_value = val; ptr_min = ptrs; }
- if (val>max_value) max_value = val;
- }
- }
- max_val = (t)max_value;
- return *ptr_min;
- }
- //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well.
- /**
- \param[out] min_val Value of the minimum value found.
- **/
- template<typename t>
- T& max_min(t& min_val) {
- bool is_all_empty = true;
- T *ptr_max = 0;
- cimglist_for(*this,l) if (!_data[l].is_empty()) {
- ptr_max = _data[l]._data;
- is_all_empty = false;
- break;
- }
- if (is_all_empty)
- throw CImgInstanceException(_cimglist_instance
- "max_min(): %s.",
- _data?"List of empty images":"Empty instance",
- cimglist_instance);
- T min_value = *ptr_max, max_value = min_value;
- cimglist_for(*this,l) {
- const CImg<T>& img = _data[l];
- cimg_for(img,ptrs,T) {
- const T val = *ptrs;
- if (val>max_value) { max_value = val; ptr_max = ptrs; }
- if (val<min_value) min_value = val;
- }
- }
- min_val = (t)min_value;
- return *ptr_max;
- }
- //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well \const.
- template<typename t>
- const T& max_min(t& min_val) const {
- bool is_all_empty = true;
- T *ptr_max = 0;
- cimglist_for(*this,l) if (!_data[l].is_empty()) {
- ptr_max = _data[l]._data;
- is_all_empty = false;
- break;
- }
- if (is_all_empty)
- throw CImgInstanceException(_cimglist_instance
- "max_min(): %s.",
- _data?"List of empty images":"Empty instance",
- cimglist_instance);
- T min_value = *ptr_max, max_value = min_value;
- cimglist_for(*this,l) {
- const CImg<T>& img = _data[l];
- cimg_for(img,ptrs,T) {
- const T val = *ptrs;
- if (val>max_value) { max_value = val; ptr_max = ptrs; }
- if (val<min_value) min_value = val;
- }
- }
- min_val = (t)min_value;
- return *ptr_max;
- }
- //@}
- //---------------------------
- //
- //! \name List Manipulation
- //@{
- //---------------------------
- //! Insert a copy of the image \c img into the current image list, at position \c pos.
- /**
- \param img Image to insert a copy to the list.
- \param pos Index of the insertion.
- \param is_shared Tells if the inserted image is a shared copy of \c img or not.
- **/
- template<typename t>
- CImgList<T>& insert(const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) {
- const unsigned int npos = pos==~0U?_width:pos;
- if (npos>_width)
- throw CImgArgumentException(_cimglist_instance
- "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) "
- "at position %u.",
- cimglist_instance,
- img._width,img._height,img._depth,img._spectrum,img._data,npos);
- if (is_shared)
- throw CImgArgumentException(_cimglist_instance
- "insert(): Invalid insertion request of specified shared image "
- "CImg<%s>(%u,%u,%u,%u,%p) at position %u (pixel types are different).",
- cimglist_instance,
- img.pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data,npos);
- CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):
- (_allocated_width=16)]:0;
- if (!_data) { // Insert new element into empty list
- _data = new_data;
- *_data = img;
- } else {
- if (new_data) { // Insert with re-allocation
- if (npos) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg<T>)*npos);
- if (npos!=_width - 1)
- std::memcpy((void*)(new_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
- std::memset((void*)_data,0,sizeof(CImg<T>)*(_width - 1));
- delete[] _data;
- _data = new_data;
- } else if (npos!=_width - 1) // Insert without re-allocation
- std::memmove((void*)(_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
- _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0;
- _data[npos]._data = 0;
- _data[npos] = img;
- }
- return *this;
- }
- //! Insert a copy of the image \c img into the current image list, at position \c pos \specialization.
- CImgList<T>& insert(const CImg<T>& img, const unsigned int pos=~0U, const bool is_shared=false) {
- const unsigned int npos = pos==~0U?_width:pos;
- if (npos>_width)
- throw CImgArgumentException(_cimglist_instance
- "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) "
- "at position %u.",
- cimglist_instance,
- img._width,img._height,img._depth,img._spectrum,img._data,npos);
- CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):
- (_allocated_width=16)]:0;
- if (!_data) { // Insert new element into empty list
- _data = new_data;
- if (is_shared && img) {
- _data->_width = img._width;
- _data->_height = img._height;
- _data->_depth = img._depth;
- _data->_spectrum = img._spectrum;
- _data->_is_shared = true;
- _data->_data = img._data;
- } else *_data = img;
- }
- else {
- if (new_data) { // Insert with re-allocation
- if (npos) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg<T>)*npos);
- if (npos!=_width - 1)
- std::memcpy((void*)(new_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
- if (is_shared && img) {
- new_data[npos]._width = img._width;
- new_data[npos]._height = img._height;
- new_data[npos]._depth = img._depth;
- new_data[npos]._spectrum = img._spectrum;
- new_data[npos]._is_shared = true;
- new_data[npos]._data = img._data;
- } else {
- new_data[npos]._width = new_data[npos]._height = new_data[npos]._depth = new_data[npos]._spectrum = 0;
- new_data[npos]._data = 0;
- new_data[npos] = img;
- }
- std::memset((void*)_data,0,sizeof(CImg<T>)*(_width - 1));
- delete[] _data;
- _data = new_data;
- } else { // Insert without re-allocation
- if (npos!=_width - 1)
- std::memmove((void*)(_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
- if (is_shared && img) {
- _data[npos]._width = img._width;
- _data[npos]._height = img._height;
- _data[npos]._depth = img._depth;
- _data[npos]._spectrum = img._spectrum;
- _data[npos]._is_shared = true;
- _data[npos]._data = img._data;
- } else {
- _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0;
- _data[npos]._data = 0;
- _data[npos] = img;
- }
- }
- }
- return *this;
- }
- //! Insert a copy of the image \c img into the current image list, at position \c pos \newinstance.
- template<typename t>
- CImgList<T> get_insert(const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) const {
- return (+*this).insert(img,pos,is_shared);
- }
- //! Insert n empty images img into the current image list, at position \p pos.
- /**
- \param n Number of empty images to insert.
- \param pos Index of the insertion.
- **/
- CImgList<T>& insert(const unsigned int n, const unsigned int pos=~0U) {
- CImg<T> empty;
- if (!n) return *this;
- const unsigned int npos = pos==~0U?_width:pos;
- for (unsigned int i = 0; i<n; ++i) insert(empty,npos+i);
- return *this;
- }
- //! Insert n empty images img into the current image list, at position \p pos \newinstance.
- CImgList<T> get_insert(const unsigned int n, const unsigned int pos=~0U) const {
- return (+*this).insert(n,pos);
- }
- //! Insert \c n copies of the image \c img into the current image list, at position \c pos.
- /**
- \param n Number of image copies to insert.
- \param img Image to insert by copy.
- \param pos Index of the insertion.
- \param is_shared Tells if inserted images are shared copies of \c img or not.
- **/
- template<typename t>
- CImgList<T>& insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U,
- const bool is_shared=false) {
- if (!n) return *this;
- const unsigned int npos = pos==~0U?_width:pos;
- insert(img,npos,is_shared);
- for (unsigned int i = 1; i<n; ++i) insert(_data[npos],npos + i,is_shared);
- return *this;
- }
- //! Insert \c n copies of the image \c img into the current image list, at position \c pos \newinstance.
- template<typename t>
- CImgList<T> get_insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U,
- const bool is_shared=false) const {
- return (+*this).insert(n,img,pos,is_shared);
- }
- //! Insert a copy of the image list \c list into the current image list, starting from position \c pos.
- /**
- \param list Image list to insert.
- \param pos Index of the insertion.
- \param is_shared Tells if inserted images are shared copies of images of \c list or not.
- **/
- template<typename t>
- CImgList<T>& insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) {
- const unsigned int npos = pos==~0U?_width:pos;
- if ((void*)this!=(void*)&list) cimglist_for(list,l) insert(list[l],npos + l,is_shared);
- else insert(CImgList<T>(list),npos,is_shared);
- return *this;
- }
- //! Insert a copy of the image list \c list into the current image list, starting from position \c pos \newinstance.
- template<typename t>
- CImgList<T> get_insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) const {
- return (+*this).insert(list,pos,is_shared);
- }
- //! Insert n copies of the list \c list at position \c pos of the current list.
- /**
- \param n Number of list copies to insert.
- \param list Image list to insert.
- \param pos Index of the insertion.
- \param is_shared Tells if inserted images are shared copies of images of \c list or not.
- **/
- template<typename t>
- CImgList<T>& insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U,
- const bool is_shared=false) {
- if (!n) return *this;
- const unsigned int npos = pos==~0U?_width:pos;
- for (unsigned int i = 0; i<n; ++i) insert(list,npos,is_shared);
- return *this;
- }
- //! Insert n copies of the list \c list at position \c pos of the current list \newinstance.
- template<typename t>
- CImgList<T> get_insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U,
- const bool is_shared=false) const {
- return (+*this).insert(n,list,pos,is_shared);
- }
- //! Remove all images between from indexes.
- /**
- \param pos1 Starting index of the removal.
- \param pos2 Ending index of the removal.
- **/
- CImgList<T>& remove(const unsigned int pos1, const unsigned int pos2) {
- const unsigned int
- npos1 = pos1<pos2?pos1:pos2,
- tpos2 = pos1<pos2?pos2:pos1,
- npos2 = tpos2<_width?tpos2:_width - 1;
- if (npos1>=_width)
- throw CImgArgumentException(_cimglist_instance
- "remove(): Invalid remove request at positions %u->%u.",
- cimglist_instance,
- npos1,tpos2);
- else {
- if (tpos2>=_width)
- throw CImgArgumentException(_cimglist_instance
- "remove(): Invalid remove request at positions %u->%u.",
- cimglist_instance,
- npos1,tpos2);
- for (unsigned int k = npos1; k<=npos2; ++k) _data[k].assign();
- const unsigned int nb = 1 + npos2 - npos1;
- if (!(_width-=nb)) return assign();
- if (_width>(_allocated_width>>4) || _allocated_width<=16) { // Removing items without reallocation
- if (npos1!=_width)
- std::memmove((void*)(_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg<T>)*(_width - npos1));
- std::memset((void*)(_data + _width),0,sizeof(CImg<T>)*nb);
- } else { // Removing items with reallocation
- _allocated_width>>=4;
- while (_allocated_width>16 && _width<(_allocated_width>>1)) _allocated_width>>=1;
- CImg<T> *const new_data = new CImg<T>[_allocated_width];
- if (npos1) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg<T>)*npos1);
- if (npos1!=_width)
- std::memcpy((void*)(new_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg<T>)*(_width - npos1));
- if (_width!=_allocated_width)
- std::memset((void*)(new_data + _width),0,sizeof(CImg<T>)*(_allocated_width - _width));
- std::memset((void*)_data,0,sizeof(CImg<T>)*(_width + nb));
- delete[] _data;
- _data = new_data;
- }
- }
- return *this;
- }
- //! Remove all images between from indexes \newinstance.
- CImgList<T> get_remove(const unsigned int pos1, const unsigned int pos2) const {
- return (+*this).remove(pos1,pos2);
- }
- //! Remove image at index \c pos from the image list.
- /**
- \param pos Index of the image to remove.
- **/
- CImgList<T>& remove(const unsigned int pos) {
- return remove(pos,pos);
- }
- //! Remove image at index \c pos from the image list \newinstance.
- CImgList<T> get_remove(const unsigned int pos) const {
- return (+*this).remove(pos);
- }
- //! Remove last image.
- /**
- **/
- CImgList<T>& remove() {
- return remove(_width - 1);
- }
- //! Remove last image \newinstance.
- CImgList<T> get_remove() const {
- return (+*this).remove();
- }
- //! Reverse list order.
- CImgList<T>& reverse() {
- for (unsigned int l = 0; l<_width/2; ++l) (*this)[l].swap((*this)[_width - 1 - l]);
- return *this;
- }
- //! Reverse list order \newinstance.
- CImgList<T> get_reverse() const {
- return (+*this).reverse();
- }
- //! Return a sublist.
- /**
- \param pos0 Starting index of the sublist.
- \param pos1 Ending index of the sublist.
- **/
- CImgList<T>& images(const unsigned int pos0, const unsigned int pos1) {
- return get_images(pos0,pos1).move_to(*this);
- }
- //! Return a sublist \newinstance.
- CImgList<T> get_images(const unsigned int pos0, const unsigned int pos1) const {
- if (pos0>pos1 || pos1>=_width)
- throw CImgArgumentException(_cimglist_instance
- "images(): Specified sub-list indices (%u->%u) are out of bounds.",
- cimglist_instance,
- pos0,pos1);
- CImgList<T> res(pos1 - pos0 + 1);
- cimglist_for(res,l) res[l].assign(_data[pos0 + l]);
- return res;
- }
- //! Return a shared sublist.
- /**
- \param pos0 Starting index of the sublist.
- \param pos1 Ending index of the sublist.
- **/
- CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) {
- if (pos0>pos1 || pos1>=_width)
- throw CImgArgumentException(_cimglist_instance
- "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.",
- cimglist_instance,
- pos0,pos1);
- CImgList<T> res(pos1 - pos0 + 1);
- cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false);
- return res;
- }
- //! Return a shared sublist \newinstance.
- const CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) const {
- if (pos0>pos1 || pos1>=_width)
- throw CImgArgumentException(_cimglist_instance
- "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.",
- cimglist_instance,
- pos0,pos1);
- CImgList<T> res(pos1 - pos0 + 1);
- cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false);
- return res;
- }
- //! Return a single image which is the appending of all images of the current CImgList instance.
- /**
- \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
- \param align Appending alignment.
- **/
- CImg<T> get_append(const char axis, const float align=0) const {
- if (is_empty()) return CImg<T>();
- if (_width==1) return +((*this)[0]);
- unsigned int dx = 0, dy = 0, dz = 0, dc = 0, pos = 0;
- CImg<T> res;
- switch (cimg::lowercase(axis)) {
- case 'x' : { // Along the X-axis
- cimglist_for(*this,l) {
- const CImg<T>& img = (*this)[l];
- if (img) {
- dx+=img._width;
- dy = std::max(dy,img._height);
- dz = std::max(dz,img._depth);
- dc = std::max(dc,img._spectrum);
- }
- }
- res.assign(dx,dy,dz,dc,(T)0);
- if (res) cimglist_for(*this,l) {
- const CImg<T>& img = (*this)[l];
- if (img) {
- if (img._height==1 && img._depth==1 && img._spectrum==1 &&
- res._height==1 && res._depth==1 && res._spectrum==1)
- std::memcpy(&res[pos],img._data,sizeof(T)*img._width);
- else
- res.draw_image(pos,
- (int)(align*(dy - img._height)),
- (int)(align*(dz - img._depth)),
- (int)(align*(dc - img._spectrum)),
- img);
- }
- pos+=img._width;
- }
- } break;
- case 'y' : { // Along the Y-axis
- cimglist_for(*this,l) {
- const CImg<T>& img = (*this)[l];
- if (img) {
- dx = std::max(dx,img._width);
- dy+=img._height;
- dz = std::max(dz,img._depth);
- dc = std::max(dc,img._spectrum);
- }
- }
- res.assign(dx,dy,dz,dc,(T)0);
- if (res) cimglist_for(*this,l) {
- const CImg<T>& img = (*this)[l];
- if (img) {
- if (img._width==1 && img._depth==1 && img._spectrum==1 &&
- res._width==1 && res._depth==1 && res._spectrum==1)
- std::memcpy(&res[pos],img._data,sizeof(T)*img._height);
- else
- res.draw_image((int)(align*(dx - img._width)),
- pos,
- (int)(align*(dz - img._depth)),
- (int)(align*(dc - img._spectrum)),
- img);
- }
- pos+=img._height;
- }
- } break;
- case 'z' : { // Along the Z-axis
- cimglist_for(*this,l) {
- const CImg<T>& img = (*this)[l];
- if (img) {
- dx = std::max(dx,img._width);
- dy = std::max(dy,img._height);
- dz+=img._depth;
- dc = std::max(dc,img._spectrum);
- }
- }
- res.assign(dx,dy,dz,dc,(T)0);
- if (res) cimglist_for(*this,l) {
- const CImg<T>& img = (*this)[l];
- if (img) {
- if (img._width==1 && img._height==1 && img._spectrum==1 &&
- res._width==1 && res._height==1 && res._spectrum==1)
- std::memcpy(&res[pos],img._data,sizeof(T)*img._depth);
- else
- res.draw_image((int)(align*(dx - img._width)),
- (int)(align*(dy - img._height)),
- pos,
- (int)(align*(dc - img._spectrum)),
- img);
- }
- pos+=img._depth;
- }
- } break;
- default : { // Along the C-axis
- cimglist_for(*this,l) {
- const CImg<T>& img = (*this)[l];
- if (img) {
- dx = std::max(dx,img._width);
- dy = std::max(dy,img._height);
- dz = std::max(dz,img._depth);
- dc+=img._spectrum;
- }
- }
- res.assign(dx,dy,dz,dc,(T)0);
- if (res) cimglist_for(*this,l) {
- const CImg<T>& img = (*this)[l];
- if (img) {
- if (img._width==1 && img._height==1 && img._depth==1 &&
- res._width==1 && res._height==1 && res._depth==1)
- std::memcpy(&res[pos],img._data,sizeof(T)*img._spectrum);
- else
- res.draw_image((int)(align*(dx - img._width)),
- (int)(align*(dy - img._height)),
- (int)(align*(dz - img._depth)),
- pos,
- img);
- }
- pos+=img._spectrum;
- }
- }
- }
- return res;
- }
- //! Return a list where each image has been split along the specified axis.
- /**
- \param axis Axis to split images along.
- \param nb Number of split parts for each image.
- **/
- CImgList<T>& split(const char axis, const int nb=-1) {
- return get_split(axis,nb).move_to(*this);
- }
- //! Return a list where each image has been split along the specified axis \newinstance.
- CImgList<T> get_split(const char axis, const int nb=-1) const {
- CImgList<T> res;
- cimglist_for(*this,l) _data[l].get_split(axis,nb).move_to(res,~0U);
- return res;
- }
- //! Insert image at the end of the list.
- /**
- \param img Image to insert.
- **/
- template<typename t>
- CImgList<T>& push_back(const CImg<t>& img) {
- return insert(img);
- }
- //! Insert image at the front of the list.
- /**
- \param img Image to insert.
- **/
- template<typename t>
- CImgList<T>& push_front(const CImg<t>& img) {
- return insert(img,0);
- }
- //! Insert list at the end of the current list.
- /**
- \param list List to insert.
- **/
- template<typename t>
- CImgList<T>& push_back(const CImgList<t>& list) {
- return insert(list);
- }
- //! Insert list at the front of the current list.
- /**
- \param list List to insert.
- **/
- template<typename t>
- CImgList<T>& push_front(const CImgList<t>& list) {
- return insert(list,0);
- }
- //! Remove last image.
- /**
- **/
- CImgList<T>& pop_back() {
- return remove(_width - 1);
- }
- //! Remove first image.
- /**
- **/
- CImgList<T>& pop_front() {
- return remove(0);
- }
- //! Remove image pointed by iterator.
- /**
- \param iter Iterator pointing to the image to remove.
- **/
- CImgList<T>& erase(const iterator iter) {
- return remove(iter - _data);
- }
- //@}
- //----------------------------------
- //
- //! \name Data Input
- //@{
- //----------------------------------
- //! Display a simple interactive interface to select images or sublists.
- /**
- \param disp Window instance to display selection and user interface.
- \param feature_type Can be \c false to select a single image, or \c true to select a sublist.
- \param axis Axis along whom images are appended for visualization.
- \param align Alignment setting when images have not all the same size.
- \param exit_on_anykey Exit function when any key is pressed.
- \return A one-column vector containing the selected image indexes.
- **/
- CImg<intT> get_select(CImgDisplay &disp, const bool feature_type=true,
- const char axis='x', const float align=0,
- const bool exit_on_anykey=false) const {
- return _select(disp,0,feature_type,axis,align,exit_on_anykey,0,false,false,false);
- }
- //! Display a simple interactive interface to select images or sublists.
- /**
- \param title Title of a new window used to display selection and user interface.
- \param feature_type Can be \c false to select a single image, or \c true to select a sublist.
- \param axis Axis along whom images are appended for visualization.
- \param align Alignment setting when images have not all the same size.
- \param exit_on_anykey Exit function when any key is pressed.
- \return A one-column vector containing the selected image indexes.
- **/
- CImg<intT> get_select(const char *const title, const bool feature_type=true,
- const char axis='x', const float align=0,
- const bool exit_on_anykey=false) const {
- CImgDisplay disp;
- return _select(disp,title,feature_type,axis,align,exit_on_anykey,0,false,false,false);
- }
- CImg<intT> _select(CImgDisplay &disp, const char *const title, const bool feature_type,
- const char axis, const float align, const bool exit_on_anykey,
- const unsigned int orig, const bool resize_disp,
- const bool exit_on_rightbutton, const bool exit_on_wheel) const {
- if (is_empty())
- throw CImgInstanceException(_cimglist_instance
- "select(): Empty instance.",
- cimglist_instance);
- // Create image correspondence table and get list dimensions for visualization.
- CImgList<uintT> _indices;
- unsigned int max_width = 0, max_height = 0, sum_width = 0, sum_height = 0;
- cimglist_for(*this,l) {
- const CImg<T>& img = _data[l];
- const unsigned int
- w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
- h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
- if (w>max_width) max_width = w;
- if (h>max_height) max_height = h;
- sum_width+=w; sum_height+=h;
- if (axis=='x') CImg<uintT>(w,1,1,1,(unsigned int)l).move_to(_indices);
- else CImg<uintT>(h,1,1,1,(unsigned int)l).move_to(_indices);
- }
- const CImg<uintT> indices0 = _indices>'x';
- // Create display window.
- if (!disp) {
- if (axis=='x') disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:0,1);
- else disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:0,1);
- if (!title) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
- } else {
- if (title) disp.set_title("%s",title);
- disp.move_inside_screen();
- }
- if (resize_disp) {
- if (axis=='x') disp.resize(cimg_fitscreen(sum_width,max_height,1),false);
- else disp.resize(cimg_fitscreen(max_width,sum_height,1),false);
- }
- const unsigned int old_normalization = disp.normalization();
- bool old_is_resized = disp.is_resized();
- disp._normalization = 0;
- disp.show().set_key(0).show_mouse();
- static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
- // Enter event loop.
- CImg<ucharT> visu0, visu;
- CImg<uintT> indices;
- CImg<intT> positions(_width,4,1,1,-1);
- int oindex0 = -1, oindex1 = -1, index0 = -1, index1 = -1;
- bool is_clicked = false, is_selected = false, text_down = false, update_display = true;
- unsigned int key = 0, font_size = 32;
- while (!is_selected && !disp.is_closed() && !key) {
- // Create background image.
- if (!visu0) {
- visu0.assign(disp._width,disp._height,1,3,0); visu.assign();
- (indices0.get_resize(axis=='x'?visu0._width:visu0._height,1)).move_to(indices);
- unsigned int _ind = 0;
- const CImg<T> onexone(1,1,1,1,(T)0);
- if (axis=='x')
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width,4))
- cimglist_for(*this,ind) {
- unsigned int x0 = 0;
- while (x0<visu0._width && indices[x0++]!=(unsigned int)ind) {}
- unsigned int x1 = x0;
- while (x1<visu0._width && indices[x1++]==(unsigned int)ind) {}
- const CImg<T> &src = _data[ind]?_data[ind]:onexone;
- CImg<ucharT> res;
- src._get_select(disp,old_normalization,src._width/2,src._height/2,src._depth/2).
- move_to(res);
- const unsigned int h = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,true);
- res.resize(x1 - x0,std::max(32U,h*disp._height/max_height),1,res._spectrum==1?3:-100);
- positions(ind,0) = positions(ind,2) = (int)x0;
- positions(ind,1) = positions(ind,3) = (int)(align*(visu0.height() - res.height()));
- positions(ind,2)+=res._width;
- positions(ind,3)+=res._height - 1;
- visu0.draw_image(positions(ind,0),positions(ind,1),res);
- }
- else
- cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width,4))
- cimglist_for(*this,ind) {
- unsigned int y0 = 0;
- while (y0<visu0._height && indices[y0++]!=(unsigned int)ind) {}
- unsigned int y1 = y0;
- while (y1<visu0._height && indices[y1++]==(unsigned int)ind) {}
- const CImg<T> &src = _data[ind]?_data[ind]:onexone;
- CImg<ucharT> res;
- src._get_select(disp,old_normalization,(src._width - 1)/2,(src._height - 1)/2,(src._depth - 1)/2).
- move_to(res);
- const unsigned int w = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,false);
- res.resize(std::max(32U,w*disp._width/max_width),y1 - y0,1,res._spectrum==1?3:-100);
- positions(ind,0) = positions(ind,2) = (int)(align*(visu0.width() - res.width()));
- positions(ind,1) = positions(ind,3) = (int)y0;
- positions(ind,2)+=res._width - 1;
- positions(ind,3)+=res._height;
- visu0.draw_image(positions(ind,0),positions(ind,1),res);
- }
- if (axis=='x') --positions(_ind,2); else --positions(_ind,3);
- update_display = true;
- }
- if (!visu || oindex0!=index0 || oindex1!=index1) {
- if (index0>=0 && index1>=0) {
- visu.assign(visu0,false);
- const int indm = std::min(index0,index1), indM = std::max(index0,index1);
- for (int ind = indm; ind<=indM; ++ind) if (positions(ind,0)>=0) {
- visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),
- background_color,0.2f);
- if ((axis=='x' && positions(ind,2) - positions(ind,0)>=8) ||
- (axis!='x' && positions(ind,3) - positions(ind,1)>=8))
- visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),
- foreground_color,0.9f,0xAAAAAAAA);
- }
- if (is_clicked) visu.__draw_text(" Images #%u - #%u, Size = %u ",font_size,(int)text_down,
- orig + indm,orig + indM,indM - indm + 1);
- else visu.__draw_text(" Image #%u (%u,%u,%u,%u) ",font_size,(int)text_down,
- orig + index0,
- _data[index0]._width,
- _data[index0]._height,
- _data[index0]._depth,
- _data[index0]._spectrum);
- update_display = true;
- } else visu.assign();
- }
- if (!visu) { visu.assign(visu0,true); update_display = true; }
- if (update_display) { visu.display(disp); update_display = false; }
- disp.wait();
- // Manage user events.
- const int xm = disp.mouse_x(), ym = disp.mouse_y();
- int index = -1;
- if (xm>=0) {
- index = (int)indices(axis=='x'?xm:ym);
- if (disp.button()&1) {
- if (!is_clicked) { is_clicked = true; oindex0 = index0; index0 = index; }
- oindex1 = index1; index1 = index;
- if (!feature_type) is_selected = true;
- } else {
- if (!is_clicked) { oindex0 = oindex1 = index0; index0 = index1 = index; }
- else is_selected = true;
- }
- } else {
- if (is_clicked) {
- if (!(disp.button()&1)) { is_clicked = is_selected = false; index0 = index1 = -1; }
- else index1 = -1;
- } else index0 = index1 = -1;
- }
- if (disp.button()&4) { is_clicked = is_selected = false; index0 = index1 = -1; }
- if (disp.button()&2 && exit_on_rightbutton) { is_selected = true; index1 = index0 = -1; }
- if (disp.wheel() && exit_on_wheel) is_selected = true;
- CImg<charT> filename(32);
- switch (key = disp.key()) {
- #if cimg_OS!=2
- case cimg::keyCTRLRIGHT :
- #endif
- case 0 : case cimg::keyCTRLLEFT : key = 0; break;
- case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- disp.set_fullscreen(false).
- resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
- CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
- _is_resized = true;
- disp.set_key(key,false); key = 0; visu0.assign();
- } break;
- case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- disp.set_fullscreen(false).
- resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
- disp.set_key(key,false); key = 0; visu0.assign();
- } break;
- case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- disp.set_fullscreen(false).
- resize(cimg_fitscreen(axis=='x'?sum_width:max_width,axis=='x'?max_height:sum_height,1),false).
- _is_resized = true;
- disp.set_key(key,false); key = 0; visu0.assign();
- } break;
- case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
- disp.set_key(key,false); key = 0; visu0.assign();
- } break;
- case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- static unsigned int snap_number = 0;
- std::FILE *file;
- do {
- cimg_snprintf(filename,filename._width,cimg_appname "_%.6u."
- #ifdef cimg_use_png
- "png",
- #else
- "bmp",
- #endif
- snap_number++);
- if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
- } while (file);
- if (visu0) {
- (+visu0).__draw_text(" Saving snapshot... ",font_size,(int)text_down).display(disp);
- visu0.save(filename);
- (+visu0).__draw_text(" Snapshot '%s' saved. ",font_size,(int)text_down,filename._data).display(disp);
- }
- disp.set_key(key,false).wait(); key = 0;
- } break;
- case cimg::keyO :
- if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
- static unsigned int snap_number = 0;
- std::FILE *file;
- do {
- #ifdef cimg_use_zlib
- cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimgz",snap_number++);
- #else
- cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimg",snap_number++);
- #endif
- if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
- } while (file);
- (+visu0).__draw_text(" Saving instance... ",font_size,(int)text_down).display(disp);
- save(filename);
- (+visu0).__draw_text(" Instance '%s' saved. ",font_size,(int)text_down,filename._data).display(disp);
- disp.set_key(key,false).wait(); key = 0;
- } break;
- }
- if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
- if (ym>=0 && ym<13) { if (!text_down) { visu.assign(); text_down = true; }}
- else if (ym>=visu.height() - 13) { if (text_down) { visu.assign(); text_down = false; }}
- if (!exit_on_anykey && key && key!=cimg::keyESC &&
- (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
- key = 0;
- }
- }
- CImg<intT> res(1,2,1,1,-1);
- if (is_selected) {
- if (feature_type) res.fill(std::min(index0,index1),std::max(index0,index1));
- else res.fill(index0);
- }
- if (!(disp.button()&2)) disp.set_button();
- disp._normalization = old_normalization;
- disp._is_resized = old_is_resized;
- disp.set_key(key);
- return res;
- }
- //! Load a list from a file.
- /**
- \param filename Filename to read data from.
- **/
- CImgList<T>& load(const char *const filename) {
- if (!filename)
- throw CImgArgumentException(_cimglist_instance
- "load(): Specified filename is (null).",
- cimglist_instance);
- if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) {
- CImg<charT> filename_local(256);
- load(cimg::load_network(filename,filename_local));
- std::remove(filename_local);
- return *this;
- }
- const bool is_stdin = *filename=='-' && (!filename[1] || filename[1]=='.');
- const char *const ext = cimg::split_filename(filename);
- const unsigned int omode = cimg::exception_mode();
- cimg::exception_mode(0);
- bool is_loaded = true;
- try {
- #ifdef cimglist_load_plugin
- cimglist_load_plugin(filename);
- #endif
- #ifdef cimglist_load_plugin1
- cimglist_load_plugin1(filename);
- #endif
- #ifdef cimglist_load_plugin2
- cimglist_load_plugin2(filename);
- #endif
- #ifdef cimglist_load_plugin3
- cimglist_load_plugin3(filename);
- #endif
- #ifdef cimglist_load_plugin4
- cimglist_load_plugin4(filename);
- #endif
- #ifdef cimglist_load_plugin5
- cimglist_load_plugin5(filename);
- #endif
- #ifdef cimglist_load_plugin6
- cimglist_load_plugin6(filename);
- #endif
- #ifdef cimglist_load_plugin7
- cimglist_load_plugin7(filename);
- #endif
- #ifdef cimglist_load_plugin8
- cimglist_load_plugin8(filename);
- #endif
- if (!cimg::strcasecmp(ext,"tif") ||
- !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
- else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename);
- else if (!cimg::strcasecmp(ext,"cimg") ||
- !cimg::strcasecmp(ext,"cimgz") ||
- !*ext) load_cimg(filename);
- else if (!cimg::strcasecmp(ext,"rec") ||
- !cimg::strcasecmp(ext,"par")) load_parrec(filename);
- else if (!cimg::strcasecmp(ext,"avi") ||
- !cimg::strcasecmp(ext,"mov") ||
- !cimg::strcasecmp(ext,"asf") ||
- !cimg::strcasecmp(ext,"divx") ||
- !cimg::strcasecmp(ext,"flv") ||
- !cimg::strcasecmp(ext,"mpg") ||
- !cimg::strcasecmp(ext,"m1v") ||
- !cimg::strcasecmp(ext,"m2v") ||
- !cimg::strcasecmp(ext,"m4v") ||
- !cimg::strcasecmp(ext,"mjp") ||
- !cimg::strcasecmp(ext,"mp4") ||
- !cimg::strcasecmp(ext,"mkv") ||
- !cimg::strcasecmp(ext,"mpe") ||
- !cimg::strcasecmp(ext,"movie") ||
- !cimg::strcasecmp(ext,"ogm") ||
- !cimg::strcasecmp(ext,"ogg") ||
- !cimg::strcasecmp(ext,"ogv") ||
- !cimg::strcasecmp(ext,"qt") ||
- !cimg::strcasecmp(ext,"rm") ||
- !cimg::strcasecmp(ext,"vob") ||
- !cimg::strcasecmp(ext,"webm") ||
- !cimg::strcasecmp(ext,"wmv") ||
- !cimg::strcasecmp(ext,"xvid") ||
- !cimg::strcasecmp(ext,"mpeg")) load_video(filename);
- else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
- else is_loaded = false;
- } catch (CImgIOException&) { is_loaded = false; }
- // If nothing loaded, try to guess file format from magic number in file.
- if (!is_loaded && !is_stdin) {
- std::FILE *const file = cimg::std_fopen(filename,"rb");
- if (!file) {
- cimg::exception_mode(omode);
- throw CImgIOException(_cimglist_instance
- "load(): Failed to open file '%s'.",
- cimglist_instance,
- filename);
- }
- const char *const f_type = cimg::ftype(file,filename);
- cimg::fclose(file);
- is_loaded = true;
- try {
- if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename);
- else if (!cimg::strcasecmp(f_type,"tif") &&
- cimg::strcasecmp(ext,"nef") &&
- cimg::strcasecmp(ext,"dng")) load_tiff(filename);
- else is_loaded = false;
- } catch (CImgIOException&) { is_loaded = false; }
- }
- // If nothing loaded, try to load file as a single image.
- if (!is_loaded) {
- assign(1);
- try {
- _data->load(filename);
- } catch (CImgIOException&) {
- cimg::exception_mode(omode);
- throw CImgIOException(_cimglist_instance
- "load(): Failed to recognize format of file '%s'.",
- cimglist_instance,
- filename);
- }
- }
- cimg::exception_mode(omode);
- return *this;
- }
- //! Load a list from a file \newinstance.
- static CImgList<T> get_load(const char *const filename) {
- return CImgList<T>().load(filename);
- }
- //! Load a list from a .cimg file.
- /**
- \param filename Filename to read data from.
- **/
- CImgList<T>& load_cimg(const char *const filename) {
- return _load_cimg(0,filename);
- }
- //! Load a list from a .cimg file \newinstance.
- static CImgList<T> get_load_cimg(const char *const filename) {
- return CImgList<T>().load_cimg(filename);
- }
- //! Load a list from a .cimg file.
- /**
- \param file File to read data from.
- **/
- CImgList<T>& load_cimg(std::FILE *const file) {
- return _load_cimg(file,0);
- }
- //! Load a list from a .cimg file \newinstance.
- static CImgList<T> get_load_cimg(std::FILE *const file) {
- return CImgList<T>().load_cimg(file);
- }
- CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename) {
- #ifdef cimg_use_zlib
- #define _cimgz_load_cimg_case(Tss) { \
- Bytef *const cbuf = new Bytef[csiz]; \
- cimg::fread(cbuf,(size_t)csiz,nfile); \
- if (is_bool) { \
- CImg<ucharT> raw(W*H*D*C/8); \
- uLongf destlen = (uLongf)raw.size(); \
- uncompress((Bytef*)raw._data,&destlen,cbuf,(uLong)csiz); \
- img.assign(W,H,D,C); \
- img._uchar2bool(raw,raw.size(),false); \
- } else { \
- CImg<Tss> raw(W,H,D,C); \
- uLongf destlen = (uLongf)(raw.size()*sizeof(Tss)); \
- uncompress((Bytef*)raw._data,&destlen,cbuf,(uLong)csiz); \
- if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
- raw.move_to(img); \
- } \
- delete[] cbuf; \
- }
- #else
- #define _cimgz_load_cimg_case(Tss) \
- throw CImgIOException(_cimglist_instance \
- "load_cimg(): Unable to load compressed data from file '%s' unless zlib is enabled.", \
- cimglist_instance, \
- filename?filename:"(FILE*)");
- #endif
- #define _cimg_load_cimg_case(Ts1,Ts2,Ts3,Tss) \
- if (!loaded && ((Ts1 && !cimg::strcasecmp(Ts1,str_pixeltype)) || \
- (Ts2 && !cimg::strcasecmp(Ts2,str_pixeltype)) || \
- (Ts3 && !cimg::strcasecmp(Ts3,str_pixeltype)))) { \
- const bool is_bool = cimg::type<Tss>::string()==cimg::type<bool>::string(); \
- for (unsigned int l = 0; l<N; ++l) { \
- j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0; \
- W = H = D = C = 0; csiz = 0; \
- if ((err = cimg_sscanf(tmp,"%u %u %u %u #" cimg_fuint64,&W,&H,&D,&C,&csiz))<4) \
- throw CImgIOException(_cimglist_instance \
- "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'.", \
- cimglist_instance, \
- W,H,D,C,l,filename?filename:("(FILE*)")); \
- if (W*H*D*C>0) { \
- CImg<T> &img = _data[l]; \
- if (err==5) _cimgz_load_cimg_case(Tss) \
- else { \
- img.assign(W,H,D,C); \
- T *ptrd = img._data; \
- if (is_bool) { \
- CImg<ucharT> raw; \
- for (ulongT to_read = img.size(); to_read; ) { \
- raw.assign((unsigned int)std::min(to_read,cimg_iobuffer)); \
- cimg::fread(raw._data,raw._width,nfile); \
- CImg<T>(ptrd,std::min(8*raw._width,(unsigned int)(img.end() - ptrd)),1,1,1,true).\
- _uchar2bool(raw,raw._width,false); \
- to_read-=raw._width; \
- } \
- } else { \
- CImg<Tss> raw; \
- for (ulongT to_read = img.size(); to_read; ) { \
- raw.assign((unsigned int)std::min(to_read,cimg_iobuffer)); \
- cimg::fread(raw._data,raw._width,nfile); \
- if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
- const Tss *ptrs = raw._data; \
- for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
- to_read-=raw._width; \
- } \
- } \
- } \
- } \
- } \
- loaded = true; \
- }
- if (!filename && !file)
- throw CImgArgumentException(_cimglist_instance
- "load_cimg(): Specified filename is (null).",
- cimglist_instance);
- const ulongT cimg_iobuffer = (ulongT)24*1024*1024;
- std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
- bool loaded = false, endian = cimg::endianness();
- CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
- *tmp = *str_pixeltype = *str_endian = 0;
- unsigned int j, N = 0, W, H, D, C;
- cimg_uint64 csiz;
- int i, err;
- do {
- j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0;
- } while (*tmp=='#' && i>=0);
- err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z123468_]%*c%255[sA-Za-z_ ]",
- &N,str_pixeltype._data,str_endian._data);
- if (err<2) {
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimglist_instance
- "load_cimg(): File or CImg header not found in file '%s'.",
- cimglist_instance,
- filename?filename:"(FILE*)");
- }
- if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
- else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
- assign(N);
- _cimg_load_cimg_case("bool",0,0,cimg_uint8);
- _cimg_load_cimg_case("uint8","unsigned_char","uchar",cimg_uint8);
- _cimg_load_cimg_case("int8",0,0,cimg_int8);
- _cimg_load_cimg_case("char",0,0,char);
- _cimg_load_cimg_case("uint16","unsigned_short","ushort",cimg_uint16);
- _cimg_load_cimg_case("int16","short",0,cimg_int16);
- _cimg_load_cimg_case("uint32","unsigned_int","uint",cimg_uint32);
- _cimg_load_cimg_case("int32","int",0,cimg_int32);
- _cimg_load_cimg_case("unsigned_long","ulong",0,cimg_ulong);
- _cimg_load_cimg_case("long",0,0,cimg_long);
- _cimg_load_cimg_case("uint64","unsigned_int64",0,cimg_uint64);
- _cimg_load_cimg_case("int64",0,0,cimg_int64);
- _cimg_load_cimg_case("float32","float",0,cimg_float32);
- _cimg_load_cimg_case("float64","double",0,cimg_float64);
- if (!loaded) {
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimglist_instance
- "load_cimg(): Unsupported pixel type '%s' for file '%s'.",
- cimglist_instance,
- str_pixeltype._data,filename?filename:"(FILE*)");
- }
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Load a sublist list from a (non compressed) .cimg file.
- /**
- \param filename Filename to read data from.
- \param n0 Starting index of images to read (~0U for max).
- \param n1 Ending index of images to read (~0U for max).
- \param x0 Starting X-coordinates of image regions to read.
- \param y0 Starting Y-coordinates of image regions to read.
- \param z0 Starting Z-coordinates of image regions to read.
- \param c0 Starting C-coordinates of image regions to read.
- \param x1 Ending X-coordinates of image regions to read (~0U for max).
- \param y1 Ending Y-coordinates of image regions to read (~0U for max).
- \param z1 Ending Z-coordinates of image regions to read (~0U for max).
- \param c1 Ending C-coordinates of image regions to read (~0U for max).
- **/
- CImgList<T>& load_cimg(const char *const filename,
- const unsigned int n0, const unsigned int n1,
- const unsigned int x0, const unsigned int y0,
- const unsigned int z0, const unsigned int c0,
- const unsigned int x1, const unsigned int y1,
- const unsigned int z1, const unsigned int c1) {
- return _load_cimg(0,filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
- }
- //! Load a sublist list from a (non compressed) .cimg file \newinstance.
- static CImgList<T> get_load_cimg(const char *const filename,
- const unsigned int n0, const unsigned int n1,
- const unsigned int x0, const unsigned int y0,
- const unsigned int z0, const unsigned int c0,
- const unsigned int x1, const unsigned int y1,
- const unsigned int z1, const unsigned int c1) {
- return CImgList<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
- }
- //! Load a sub-image list from a (non compressed) .cimg file \overloading.
- CImgList<T>& load_cimg(std::FILE *const file,
- const unsigned int n0, const unsigned int n1,
- const unsigned int x0, const unsigned int y0,
- const unsigned int z0, const unsigned int c0,
- const unsigned int x1, const unsigned int y1,
- const unsigned int z1, const unsigned int c1) {
- return _load_cimg(file,0,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
- }
- //! Load a sub-image list from a (non compressed) .cimg file \newinstance.
- static CImgList<T> get_load_cimg(std::FILE *const file,
- const unsigned int n0, const unsigned int n1,
- const unsigned int x0, const unsigned int y0,
- const unsigned int z0, const unsigned int c0,
- const unsigned int x1, const unsigned int y1,
- const unsigned int z1, const unsigned int c1) {
- return CImgList<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
- }
- CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename,
- const unsigned int n0, const unsigned int n1,
- const unsigned int x0, const unsigned int y0,
- const unsigned int z0, const unsigned int c0,
- const unsigned int x1, const unsigned int y1,
- const unsigned int z1, const unsigned int c1) {
- #define _cimg_load_cimg_case2(Ts1,Ts2,Ts3,Tss) \
- if (!loaded && ((Ts1 && !cimg::strcasecmp(Ts1,str_pixeltype)) || \
- (Ts2 && !cimg::strcasecmp(Ts2,str_pixeltype)) || \
- (Ts3 && !cimg::strcasecmp(Ts3,str_pixeltype)))) { \
- for (unsigned int l = 0; l<=nn1; ++l) { \
- j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \
- W = H = D = C = 0; \
- if (cimg_sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
- throw CImgIOException(_cimglist_instance \
- "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \
- cimglist_instance, \
- W,H,D,C,l,filename?filename:"(FILE*)"); \
- if (W*H*D*C>0) { \
- if (l<nn0 || nx0>=W || ny0>=H || nz0>=D || nc0>=C) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
- else { \
- const unsigned int \
- _nx1 = nx1==~0U?W - 1:nx1, \
- _ny1 = ny1==~0U?H - 1:ny1, \
- _nz1 = nz1==~0U?D - 1:nz1, \
- _nc1 = nc1==~0U?C - 1:nc1; \
- if (_nx1>=W || _ny1>=H || _nz1>=D || _nc1>=C) \
- throw CImgArgumentException(_cimglist_instance \
- "load_cimg(): Invalid specified coordinates " \
- "[%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) " \
- "because image [%u] in file '%s' has size (%u,%u,%u,%u).", \
- cimglist_instance, \
- n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,l,filename?filename:"(FILE*)",W,H,D,C); \
- CImg<Tss> raw(1 + _nx1 - nx0); \
- CImg<T> &img = _data[l - nn0]; \
- img.assign(1 + _nx1 - nx0,1 + _ny1 - ny0,1 + _nz1 - nz0,1 + _nc1 - nc0); \
- T *ptrd = img._data; \
- ulongT skipvb = nc0*W*H*D*sizeof(Tss); \
- if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \
- for (unsigned int c = 1 + _nc1 - nc0; c; --c) { \
- const ulongT skipzb = nz0*W*H*sizeof(Tss); \
- if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \
- for (unsigned int z = 1 + _nz1 - nz0; z; --z) { \
- const ulongT skipyb = ny0*W*sizeof(Tss); \
- if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \
- for (unsigned int y = 1 + _ny1 - ny0; y; --y) { \
- const ulongT skipxb = nx0*sizeof(Tss); \
- if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \
- cimg::fread(raw._data,raw._width,nfile); \
- if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \
- const Tss *ptrs = raw._data; \
- for (unsigned int off = raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
- const ulongT skipxe = (W - 1 - _nx1)*sizeof(Tss); \
- if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \
- } \
- const ulongT skipye = (H - 1 - _ny1)*W*sizeof(Tss); \
- if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \
- } \
- const ulongT skipze = (D - 1 - _nz1)*W*H*sizeof(Tss); \
- if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \
- } \
- const ulongT skipve = (C - 1 - _nc1)*W*H*D*sizeof(Tss); \
- if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \
- } \
- } \
- } \
- loaded = true; \
- }
- if (!filename && !file)
- throw CImgArgumentException(_cimglist_instance
- "load_cimg(): Specified filename is (null).",
- cimglist_instance);
- unsigned int
- nn0 = std::min(n0,n1), nn1 = std::max(n0,n1),
- nx0 = std::min(x0,x1), nx1 = std::max(x0,x1),
- ny0 = std::min(y0,y1), ny1 = std::max(y0,y1),
- nz0 = std::min(z0,z1), nz1 = std::max(z0,z1),
- nc0 = std::min(c0,c1), nc1 = std::max(c0,c1);
- std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
- bool loaded = false, endian = cimg::endianness();
- CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
- *tmp = *str_pixeltype = *str_endian = 0;
- unsigned int j, N, W, H, D, C;
- int i, err;
- j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
- err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z123468_]%*c%255[sA-Za-z_ ]",
- &N,str_pixeltype._data,str_endian._data);
- if (err<2) {
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimglist_instance
- "load_cimg(): CImg header not found in file '%s'.",
- cimglist_instance,
- filename?filename:"(FILE*)");
- }
- if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
- else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
- nn1 = n1==~0U?N - 1:n1;
- if (nn1>=N)
- throw CImgArgumentException(_cimglist_instance
- "load_cimg(): Invalid specified coordinates [%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) "
- "because file '%s' contains only %u images.",
- cimglist_instance,
- n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,filename?filename:"(FILE*)",N);
- assign(1 + nn1 - n0);
- _cimg_load_cimg_case2("bool",0,0,cimg_uint8);
- _cimg_load_cimg_case2("uint8","unsigned char","uchar",cimg_uint8);
- _cimg_load_cimg_case2("int8",0,0,cimg_int8);
- _cimg_load_cimg_case2("char",0,0,char);
- _cimg_load_cimg_case2("uint16","unsigned_short","ushort",cimg_uint16);
- _cimg_load_cimg_case2("int16","short",0,cimg_int16);
- _cimg_load_cimg_case2("uint32","unsigned_int","uint",cimg_uint32);
- _cimg_load_cimg_case2("int32","int",0,cimg_int32);
- _cimg_load_cimg_case2("unsigned_long","ulong",0,cimg_ulong);
- _cimg_load_cimg_case2("long",0,0,cimg_long);
- _cimg_load_cimg_case2("uint64","unsigned_int64",0,cimg_uint64);
- _cimg_load_cimg_case2("int64",0,0,cimg_int64);
- _cimg_load_cimg_case2("float32","float",0,cimg_float32);
- _cimg_load_cimg_case2("float64","double",0,cimg_float64);
- if (!loaded) {
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimglist_instance
- "load_cimg(): Unsupported pixel type '%s' for file '%s'.",
- cimglist_instance,
- str_pixeltype._data,filename?filename:"(FILE*)");
- }
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Load a list from a PAR/REC (Philips) file.
- /**
- \param filename Filename to read data from.
- **/
- CImgList<T>& load_parrec(const char *const filename) {
- if (!filename)
- throw CImgArgumentException(_cimglist_instance
- "load_parrec(): Specified filename is (null).",
- cimglist_instance);
- CImg<charT> body(1024), filenamepar(1024), filenamerec(1024);
- *body = *filenamepar = *filenamerec = 0;
- const char *const ext = cimg::split_filename(filename,body);
- if (!std::strcmp(ext,"par")) {
- std::strncpy(filenamepar,filename,filenamepar._width - 1);
- cimg_snprintf(filenamerec,filenamerec._width,"%s.rec",body._data);
- }
- if (!std::strcmp(ext,"PAR")) {
- std::strncpy(filenamepar,filename,filenamepar._width - 1);
- cimg_snprintf(filenamerec,filenamerec._width,"%s.REC",body._data);
- }
- if (!std::strcmp(ext,"rec")) {
- std::strncpy(filenamerec,filename,filenamerec._width - 1);
- cimg_snprintf(filenamepar,filenamepar._width,"%s.par",body._data);
- }
- if (!std::strcmp(ext,"REC")) {
- std::strncpy(filenamerec,filename,filenamerec._width - 1);
- cimg_snprintf(filenamepar,filenamepar._width,"%s.PAR",body._data);
- }
- std::FILE *file = cimg::fopen(filenamepar,"r");
- // Parse header file
- CImgList<floatT> st_slices;
- CImgList<uintT> st_global;
- CImg<charT> line(256); *line = 0;
- int err;
- do { err = std::fscanf(file,"%255[^\n]%*c",line._data); } while (err!=EOF && (*line=='#' || *line=='.'));
- do {
- unsigned int sn,size_x,size_y,pixsize;
- float rs,ri,ss;
- err = std::fscanf(file,"%u%*u%*u%*u%*u%*u%*u%u%*u%u%u%g%g%g%*[^\n]",&sn,&pixsize,&size_x,&size_y,&ri,&rs,&ss);
- if (err==7) {
- CImg<floatT>::vector((float)sn,(float)pixsize,(float)size_x,(float)size_y,ri,rs,ss,0).move_to(st_slices);
- unsigned int i; for (i = 0; i<st_global._width && sn<=st_global[i][2]; ++i) {}
- if (i==st_global._width) CImg<uintT>::vector(size_x,size_y,sn).move_to(st_global);
- else {
- CImg<uintT> &vec = st_global[i];
- if (size_x>vec[0]) vec[0] = size_x;
- if (size_y>vec[1]) vec[1] = size_y;
- vec[2] = sn;
- }
- st_slices[st_slices._width - 1][7] = (float)i;
- }
- } while (err==7);
- // Read data
- std::FILE *file2 = cimg::fopen(filenamerec,"rb");
- cimglist_for(st_global,l) {
- const CImg<uintT>& vec = st_global[l];
- CImg<T>(vec[0],vec[1],vec[2]).move_to(*this);
- }
- cimglist_for(st_slices,l) {
- const CImg<floatT>& vec = st_slices[l];
- const unsigned int
- sn = (unsigned int)vec[0] - 1,
- pixsize = (unsigned int)vec[1],
- size_x = (unsigned int)vec[2],
- size_y = (unsigned int)vec[3],
- imn = (unsigned int)vec[7];
- const float ri = vec[4], rs = vec[5], ss = vec[6];
- switch (pixsize) {
- case 8 : {
- CImg<ucharT> buf(size_x,size_y);
- cimg::fread(buf._data,size_x*size_y,file2);
- if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
- CImg<T>& img = (*this)[imn];
- cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
- } break;
- case 16 : {
- CImg<ushortT> buf(size_x,size_y);
- cimg::fread(buf._data,size_x*size_y,file2);
- if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
- CImg<T>& img = (*this)[imn];
- cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
- } break;
- case 32 : {
- CImg<uintT> buf(size_x,size_y);
- cimg::fread(buf._data,size_x*size_y,file2);
- if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
- CImg<T>& img = (*this)[imn];
- cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
- } break;
- default :
- cimg::fclose(file);
- cimg::fclose(file2);
- throw CImgIOException(_cimglist_instance
- "load_parrec(): Unsupported %d-bits pixel type for file '%s'.",
- cimglist_instance,
- pixsize,filename);
- }
- }
- cimg::fclose(file);
- cimg::fclose(file2);
- if (!_width)
- throw CImgIOException(_cimglist_instance
- "load_parrec(): Failed to recognize valid PAR-REC data in file '%s'.",
- cimglist_instance,
- filename);
- return *this;
- }
- //! Load a list from a PAR/REC (Philips) file \newinstance.
- static CImgList<T> get_load_parrec(const char *const filename) {
- return CImgList<T>().load_parrec(filename);
- }
- //! Load a list from a YUV image sequence file.
- /**
- \param filename Filename to read data from.
- \param size_x Width of the images.
- \param size_y Height of the images.
- \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
- \param first_frame Index of first image frame to read.
- \param last_frame Index of last image frame to read.
- \param step_frame Step applied between each frame.
- \param yuv2rgb Apply YUV to RGB transformation during reading.
- **/
- CImgList<T>& load_yuv(const char *const filename,
- const unsigned int size_x, const unsigned int size_y,
- const unsigned int chroma_subsampling=444,
- const unsigned int first_frame=0, const unsigned int last_frame=~0U,
- const unsigned int step_frame=1, const bool yuv2rgb=true) {
- return _load_yuv(0,filename,size_x,size_y,chroma_subsampling,
- first_frame,last_frame,step_frame,yuv2rgb);
- }
- //! Load a list from a YUV image sequence file \newinstance.
- static CImgList<T> get_load_yuv(const char *const filename,
- const unsigned int size_x, const unsigned int size_y=1,
- const unsigned int chroma_subsampling=444,
- const unsigned int first_frame=0, const unsigned int last_frame=~0U,
- const unsigned int step_frame=1, const bool yuv2rgb=true) {
- return CImgList<T>().load_yuv(filename,size_x,size_y,chroma_subsampling,
- first_frame,last_frame,step_frame,yuv2rgb);
- }
- //! Load a list from an image sequence YUV file \overloading.
- CImgList<T>& load_yuv(std::FILE *const file,
- const unsigned int size_x, const unsigned int size_y,
- const unsigned int chroma_subsampling=444,
- const unsigned int first_frame=0, const unsigned int last_frame=~0U,
- const unsigned int step_frame=1, const bool yuv2rgb=true) {
- return _load_yuv(file,0,size_x,size_y,chroma_subsampling,
- first_frame,last_frame,step_frame,yuv2rgb);
- }
- //! Load a list from an image sequence YUV file \newinstance.
- static CImgList<T> get_load_yuv(std::FILE *const file,
- const unsigned int size_x, const unsigned int size_y=1,
- const unsigned int chroma_subsampling=444,
- const unsigned int first_frame=0, const unsigned int last_frame=~0U,
- const unsigned int step_frame=1, const bool yuv2rgb=true) {
- return CImgList<T>().load_yuv(file,size_x,size_y,chroma_subsampling,
- first_frame,last_frame,step_frame,yuv2rgb);
- }
- CImgList<T>& _load_yuv(std::FILE *const file, const char *const filename,
- const unsigned int size_x, const unsigned int size_y,
- const unsigned int chroma_subsampling,
- const unsigned int first_frame, const unsigned int last_frame,
- const unsigned int step_frame, const bool yuv2rgb) {
- if (!filename && !file)
- throw CImgArgumentException(_cimglist_instance
- "load_yuv(): Specified filename is (null).",
- cimglist_instance);
- if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444)
- throw CImgArgumentException(_cimglist_instance
- "load_yuv(): Specified chroma subsampling %u is invalid, for file '%s'.",
- cimglist_instance,
- chroma_subsampling,filename?filename:"(FILE*)");
- const unsigned int
- cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1,
- cfy = chroma_subsampling==420?2:1,
- nfirst_frame = first_frame<last_frame?first_frame:last_frame,
- nlast_frame = first_frame<last_frame?last_frame:first_frame,
- nstep_frame = step_frame?step_frame:1;
- if (!size_x || !size_y || size_x%cfx || size_y%cfy)
- throw CImgArgumentException(_cimglist_instance
- "load_yuv(): Specified dimensions (%u,%u) are invalid, for file '%s'.",
- cimglist_instance,
- size_x,size_y,filename?filename:"(FILE*)");
- CImg<ucharT> YUV(size_x,size_y,1,3), UV(size_x/cfx,size_y/cfy,1,2);
- std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
- bool stop_flag = false;
- int err;
- if (nfirst_frame) {
- err = cimg::fseek(nfile,(uint64T)nfirst_frame*(YUV._width*YUV._height + 2*UV._width*UV._height),SEEK_CUR);
- if (err) {
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimglist_instance
- "load_yuv(): File '%s' doesn't contain frame number %u.",
- cimglist_instance,
- filename?filename:"(FILE*)",nfirst_frame);
- }
- }
- unsigned int frame;
- for (frame = nfirst_frame; !stop_flag && frame<=nlast_frame; frame+=nstep_frame) {
- YUV.get_shared_channel(0).fill(0);
- // *TRY* to read the luminance part, do not replace by cimg::fread!
- err = (int)std::fread((void*)(YUV._data),1,(size_t)YUV._width*YUV._height,nfile);
- if (err!=(int)(YUV._width*YUV._height)) {
- stop_flag = true;
- if (err>0)
- cimg::warn(_cimglist_instance
- "load_yuv(): File '%s' contains incomplete data or given image dimensions "
- "(%u,%u) are incorrect.",
- cimglist_instance,
- filename?filename:"(FILE*)",size_x,size_y);
- } else {
- UV.fill(0);
- // *TRY* to read the luminance part, do not replace by cimg::fread!
- err = (int)std::fread((void*)(UV._data),1,(size_t)UV.size(),nfile);
- if (err!=(int)(UV.size())) {
- stop_flag = true;
- if (err>0)
- cimg::warn(_cimglist_instance
- "load_yuv(): File '%s' contains incomplete data or given image dimensions "
- "(%u,%u) are incorrect.",
- cimglist_instance,
- filename?filename:"(FILE*)",size_x,size_y);
- } else {
- const ucharT *ptrs1 = UV._data, *ptrs2 = UV.data(0,0,0,1);
- ucharT *ptrd1 = YUV.data(0,0,0,1), *ptrd2 = YUV.data(0,0,0,2);
- const unsigned int wd = YUV._width;
- switch (chroma_subsampling) {
- case 420 :
- cimg_forY(UV,y) {
- cimg_forX(UV,x) {
- const ucharT U = *(ptrs1++), V = *(ptrs2++);
- ptrd1[wd] = U; *(ptrd1)++ = U;
- ptrd1[wd] = U; *(ptrd1)++ = U;
- ptrd2[wd] = V; *(ptrd2)++ = V;
- ptrd2[wd] = V; *(ptrd2)++ = V;
- }
- ptrd1+=wd; ptrd2+=wd;
- }
- break;
- case 422 :
- cimg_forXY(UV,x,y) {
- const ucharT U = *(ptrs1++), V = *(ptrs2++);
- *(ptrd1++) = U; *(ptrd1++) = U;
- *(ptrd2++) = V; *(ptrd2++) = V;
- }
- break;
- default :
- YUV.draw_image(0,0,0,1,UV);
- }
- if (yuv2rgb) YUV.YCbCrtoRGB();
- insert(YUV);
- if (nstep_frame>1) cimg::fseek(nfile,(uint64T)(nstep_frame - 1)*(size_x*size_y + size_x*size_y/2),SEEK_CUR);
- }
- }
- }
- if (is_empty())
- throw CImgIOException(_cimglist_instance
- "load_yuv() : Missing data in file '%s'.",
- cimglist_instance,
- filename?filename:"(FILE*)");
- if (stop_flag && nlast_frame!=~0U && frame!=nlast_frame)
- cimg::warn(_cimglist_instance
- "load_yuv(): Frame %d not reached since only %u frames were found in file '%s'.",
- cimglist_instance,
- nlast_frame,frame - 1,filename?filename:"(FILE*)");
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Load an image from a video file, using OpenCV library.
- /**
- \param filename Filename, as a C-string.
- \param first_frame Index of the first frame to read.
- \param last_frame Index of the last frame to read (can be higher than the actual number of frames, e.g. '~0U').
- \param step_frame Step value for frame reading.
- \note If step_frame==0, the current video stream is forced to be released (without any frames read).
- **/
- CImgList<T>& load_video(const char *const filename,
- const unsigned int first_frame=0, const unsigned int last_frame=~0U,
- const unsigned int step_frame=1) {
- #ifndef cimg_use_opencv
- if (first_frame || last_frame!=~0U || step_frame>1)
- throw CImgArgumentException(_cimglist_instance
- "load_video() : File '%s', arguments 'first_frame', 'last_frame' "
- "and 'step_frame' requires features from the OpenCV library "
- "('-Dcimg_use_opencv' must be defined).",
- cimglist_instance,filename);
- return load_ffmpeg_external(filename);
- #else
- static cv::VideoCapture *captures[32] = {};
- static CImgList<charT> filenames(32);
- static CImg<uintT> positions(32,1,1,1,0);
- static int last_used_index = -1;
- // Detect if a video capture already exists for the specified filename.
- cimg::mutex(9);
- int index = -1;
- if (filename) {
- if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) {
- index = last_used_index;
- } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) {
- index = l; break;
- }
- } else index = last_used_index;
- cimg::mutex(9,0);
- // Release stream if needed.
- if (!step_frame || (index>=0 && positions[index]>first_frame)) {
- if (index>=0) {
- cimg::mutex(9);
- captures[index]->release();
- delete captures[index];
- captures[index] = 0;
- positions[index] = 0;
- filenames[index].assign();
- if (last_used_index==index) last_used_index = -1;
- index = -1;
- cimg::mutex(9,0);
- } else
- if (filename)
- cimg::warn(_cimglist_instance
- "load_video() : File '%s', no opened video stream associated with filename found.",
- cimglist_instance,filename);
- else
- cimg::warn(_cimglist_instance
- "load_video() : No opened video stream found.",
- cimglist_instance,filename);
- if (!step_frame) return *this;
- }
- // Find empty slot for capturing video stream.
- if (index<0) {
- if (!filename)
- throw CImgArgumentException(_cimglist_instance
- "load_video(): No already open video reader found. You must specify a "
- "non-(null) filename argument for the first call.",
- cimglist_instance);
- else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); }
- if (index<0)
- throw CImgIOException(_cimglist_instance
- "load_video(): File '%s', no video reader slots available. "
- "You have to release some of your previously opened videos.",
- cimglist_instance,filename);
- cimg::mutex(9);
- captures[index] = new cv::VideoCapture(filename);
- positions[index] = 0;
- if (!captures[index]->isOpened()) {
- delete captures[index];
- captures[index] = 0;
- cimg::mutex(9,0);
- cimg::fclose(cimg::fopen(filename,"rb")); // Check file availability
- throw CImgIOException(_cimglist_instance
- "load_video(): File '%s', unable to detect format of video file.",
- cimglist_instance,filename);
- }
- CImg<charT>::string(filename).move_to(filenames[index]);
- cimg::mutex(9,0);
- }
- cimg::mutex(9);
- const unsigned int nb_frames = (unsigned int)std::max(0.,captures[index]->get(_cimg_cap_prop_frame_count));
- cimg::mutex(9,0);
- assign();
- // Skip frames if requested.
- bool go_on = true;
- unsigned int &pos = positions[index];
- while (pos<first_frame) {
- cimg::mutex(9);
- if (!captures[index]->grab()) { cimg::mutex(9,0); go_on = false; break; }
- cimg::mutex(9,0);
- ++pos;
- }
- // Read and convert frames.
- const unsigned int _last_frame = std::min(nb_frames?nb_frames - 1:~0U,last_frame);
- while (go_on && pos<=_last_frame) {
- cv::Mat cvimg;
- cimg::mutex(9);
- if (captures[index]->read(cvimg)) { CImg<T>::_cvmat2cimg(cvimg).move_to(*this); ++pos; }
- else go_on = false;
- cimg::mutex(9,0);
- if (go_on)
- for (unsigned int i = 1; go_on && i<step_frame && pos<=_last_frame; ++i, ++pos) {
- cimg::mutex(9);
- if (!captures[index]->grab()) go_on = false;
- cimg::mutex(9,0);
- }
- }
- if (!go_on || (nb_frames && pos>=nb_frames)) { // Close video stream when necessary
- cimg::mutex(9);
- captures[index]->release();
- delete captures[index];
- captures[index] = 0;
- filenames[index].assign();
- positions[index] = 0;
- index = -1;
- cimg::mutex(9,0);
- }
- cimg::mutex(9);
- last_used_index = index;
- cimg::mutex(9,0);
- if (is_empty())
- throw CImgIOException(_cimglist_instance
- "load_video(): File '%s', unable to locate frame %u.",
- cimglist_instance,filename,first_frame);
- return *this;
- #endif
- }
- //! Load an image from a video file, using OpenCV library \newinstance.
- static CImgList<T> get_load_video(const char *const filename,
- const unsigned int first_frame=0, const unsigned int last_frame=~0U,
- const unsigned int step_frame=1) {
- return CImgList<T>().load_video(filename,first_frame,last_frame,step_frame);
- }
- //! Load an image from a video file using the external tool 'ffmpeg'.
- /**
- \param filename Filename to read data from.
- **/
- CImgList<T>& load_ffmpeg_external(const char *const filename) {
- if (!filename)
- throw CImgArgumentException(_cimglist_instance
- "load_ffmpeg_external(): Specified filename is (null).",
- cimglist_instance);
- cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
- CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
- std::FILE *file = 0;
- do {
- cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
- cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
- cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data);
- if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
- } while (file);
- cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%%6d.ppm",filename_tmp._data);
- cimg_snprintf(command,command._width,"\"%s\" -v -8 -i \"%s\" \"%s\"",
- cimg::ffmpeg_path(),
- CImg<charT>::string(filename)._system_strescape().data(),
- CImg<charT>::string(filename_tmp2)._system_strescape().data());
- cimg::system(command,cimg::ffmpeg_path());
- const unsigned int omode = cimg::exception_mode();
- cimg::exception_mode(0);
- assign();
- unsigned int i = 1;
- for (bool stop_flag = false; !stop_flag; ++i) {
- cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,i);
- CImg<T> img;
- try { img.load_pnm(filename_tmp2); }
- catch (CImgException&) { stop_flag = true; }
- if (img) { img.move_to(*this); std::remove(filename_tmp2); }
- }
- cimg::exception_mode(omode);
- if (is_empty())
- throw CImgIOException(_cimglist_instance
- "load_ffmpeg_external(): Failed to open file '%s' with external command 'ffmpeg'.",
- cimglist_instance,
- filename);
- return *this;
- }
- //! Load an image from a video file using the external tool 'ffmpeg' \newinstance.
- static CImgList<T> get_load_ffmpeg_external(const char *const filename) {
- return CImgList<T>().load_ffmpeg_external(filename);
- }
- //! Load gif file, using ImageMagick or GraphicsMagick's external tools.
- /**
- \param filename Filename to read data from.
- **/
- CImgList<T>& load_gif_external(const char *const filename) {
- if (!filename)
- throw CImgArgumentException(_cimglist_instance
- "load_gif_external(): Specified filename is (null).",
- cimglist_instance);
- cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
- if (!_load_gif_external(filename,false))
- if (!_load_gif_external(filename,true))
- try { assign(CImg<T>().load_other(filename)); } catch (CImgException&) { assign(); }
- if (is_empty())
- throw CImgIOException(_cimglist_instance
- "load_gif_external(): Failed to open file '%s'.",
- cimglist_instance,filename);
- return *this;
- }
- CImgList<T>& _load_gif_external(const char *const filename, const bool use_graphicsmagick=false) {
- CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
- std::FILE *file = 0;
- do {
- cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
- cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
- if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.0",filename_tmp._data);
- else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-0.png",filename_tmp._data);
- if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
- } while (file);
- if (use_graphicsmagick) cimg_snprintf(command,command._width,"%s convert \"%s\" \"%s.png\"",
- cimg::graphicsmagick_path(),
- CImg<charT>::string(filename)._system_strescape().data(),
- CImg<charT>::string(filename_tmp)._system_strescape().data());
- else cimg_snprintf(command,command._width,"\"%s\" -coalesce \"%s\" \"%s.png\"",
- cimg::imagemagick_path(),
- CImg<charT>::string(filename)._system_strescape().data(),
- CImg<charT>::string(filename_tmp)._system_strescape().data());
- cimg::system(command,cimg::imagemagick_path());
- const unsigned int omode = cimg::exception_mode();
- cimg::exception_mode(0);
- assign();
- // Try to read a single frame gif.
- cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png",filename_tmp._data);
- CImg<T> img;
- try { img.load_png(filename_tmp2); }
- catch (CImgException&) { }
- if (img) { img.move_to(*this); std::remove(filename_tmp2); }
- else { // Try to read animated gif
- unsigned int i = 0;
- for (bool stop_flag = false; !stop_flag; ++i) {
- if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.%u",filename_tmp._data,i);
- else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-%u.png",filename_tmp._data,i);
- try { img.load_png(filename_tmp2); }
- catch (CImgException&) { stop_flag = true; }
- if (img) { img.move_to(*this); std::remove(filename_tmp2); }
- }
- }
- cimg::exception_mode(omode);
- return *this;
- }
- //! Load gif file, using ImageMagick or GraphicsMagick's external tools \newinstance.
- static CImgList<T> get_load_gif_external(const char *const filename) {
- return CImgList<T>().load_gif_external(filename);
- }
- //! Load a gzipped list, using external tool 'gunzip'.
- /**
- \param filename Filename to read data from.
- **/
- CImgList<T>& load_gzip_external(const char *const filename) {
- if (!filename)
- throw CImgIOException(_cimglist_instance
- "load_gzip_external(): Specified filename is (null).",
- cimglist_instance);
- cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
- CImg<charT> command(1024), filename_tmp(256), body(256);
- const char
- *ext = cimg::split_filename(filename,body),
- *ext2 = cimg::split_filename(body,0);
- std::FILE *file = 0;
- do {
- if (!cimg::strcasecmp(ext,"gz")) {
- if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
- cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
- else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
- cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
- } else {
- if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
- cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
- else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
- cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
- }
- if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
- } while (file);
- cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"",
- cimg::gunzip_path(),
- CImg<charT>::string(filename)._system_strescape().data(),
- CImg<charT>::string(filename_tmp)._system_strescape().data());
- cimg::system(command,cimg::gunzip_path());
- if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
- cimg::fclose(cimg::fopen(filename,"r"));
- throw CImgIOException(_cimglist_instance
- "load_gzip_external(): Failed to open file '%s'.",
- cimglist_instance,
- filename);
- } else cimg::fclose(file);
- load(filename_tmp);
- std::remove(filename_tmp);
- return *this;
- }
- //! Load a gzipped list, using external tool 'gunzip' \newinstance.
- static CImgList<T> get_load_gzip_external(const char *const filename) {
- return CImgList<T>().load_gzip_external(filename);
- }
- //! Load images from a TIFF file.
- /**
- \param filename Filename to read data from.
- \param first_frame Index of first image frame to read.
- \param last_frame Index of last image frame to read.
- \param step_frame Step applied between each frame.
- \param[out] bits_per_value Number of bits used to store a scalar value in the image file.
- \param[out] voxel_size Voxel size, as stored in the filename.
- \param[out] description Description, as stored in the filename.
- **/
- CImgList<T>& load_tiff(const char *const filename,
- const unsigned int first_frame=0, const unsigned int last_frame=~0U,
- const unsigned int step_frame=1, unsigned int *const bits_per_value=0,
- float *const voxel_size=0, CImg<charT> *const description=0) {
- const unsigned int
- nfirst_frame = first_frame<last_frame?first_frame:last_frame,
- nstep_frame = step_frame?step_frame:1;
- unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
- #ifndef cimg_use_tiff
- cimg::unused(bits_per_value,voxel_size,description);
- if (nfirst_frame || nlast_frame!=~0U || nstep_frame!=1)
- throw CImgArgumentException(_cimglist_instance
- "load_tiff(): Unable to load sub-images from file '%s' unless libtiff is enabled.",
- cimglist_instance,
- filename);
- return assign(CImg<T>::get_load_tiff(filename));
- #else
- #if cimg_verbosity<3
- TIFFSetWarningHandler(0);
- TIFFSetErrorHandler(0);
- #endif
- TIFF *tif = TIFFOpen(filename,"r");
- if (tif) {
- unsigned int nb_images = 0;
- do ++nb_images; while (TIFFReadDirectory(tif));
- if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
- cimg::warn(_cimglist_instance
- "load_tiff(): Invalid specified frame range is [%u,%u] (step %u) since "
- "file '%s' contains %u image(s).",
- cimglist_instance,
- nfirst_frame,nlast_frame,nstep_frame,filename,nb_images);
- if (nfirst_frame>=nb_images) return assign();
- if (nlast_frame>=nb_images) nlast_frame = nb_images - 1;
- assign(1 + (nlast_frame - nfirst_frame)/nstep_frame);
- TIFFSetDirectory(tif,0);
- cimglist_for(*this,l)
- _data[l]._load_tiff(tif,nfirst_frame + l*nstep_frame,bits_per_value,voxel_size,description);
- TIFFClose(tif);
- } else throw CImgIOException(_cimglist_instance
- "load_tiff(): Failed to open file '%s'.",
- cimglist_instance,
- filename);
- return *this;
- #endif
- }
- //! Load a multi-page TIFF file \newinstance.
- static CImgList<T> get_load_tiff(const char *const filename,
- const unsigned int first_frame=0, const unsigned int last_frame=~0U,
- const unsigned int step_frame=1, unsigned int *const bits_per_value=0,
- float *const voxel_size=0, CImg<charT> *const description=0) {
- return CImgList<T>().load_tiff(filename,first_frame,last_frame,step_frame,bits_per_value,voxel_size,description);
- }
- //@}
- //----------------------------------
- //
- //! \name Data Output
- //@{
- //----------------------------------
- //! Print information about the list on the standard output.
- /**
- \param title Label set to the information displayed.
- \param display_stats Tells if image statistics must be computed and displayed.
- **/
- const CImgList<T>& print(const char *const title=0, const bool display_stats=true) const {
- unsigned int msiz = 0;
- cimglist_for(*this,l) msiz+=_data[l].size();
- msiz*=sizeof(T);
- const unsigned int mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U;
- CImg<charT> _title(64);
- if (!title) cimg_snprintf(_title,_title._width,"CImgList<%s>",pixel_type());
- std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = %u/%u [%u %s], %sdata%s = (CImg<%s>*)%p",
- cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal,
- cimg::t_bold,cimg::t_normal,(void*)this,
- cimg::t_bold,cimg::t_normal,_width,_allocated_width,
- mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)),
- mdisp==0?"b":(mdisp==1?"Kio":"Mio"),
- cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin());
- if (_data) std::fprintf(cimg::output(),"..%p.\n",(void*)((char*)end() - 1));
- else std::fprintf(cimg::output(),".\n");
- char tmp[16] = {};
- cimglist_for(*this,ll) {
- cimg_snprintf(tmp,sizeof(tmp),"[%d]",ll);
- std::fprintf(cimg::output()," ");
- _data[ll].print(tmp,display_stats);
- if (ll==3 && width()>8) { ll = width() - 5; std::fprintf(cimg::output()," ...\n"); }
- }
- std::fflush(cimg::output());
- return *this;
- }
- //! Display the current CImgList instance in an existing CImgDisplay window (by reference).
- /**
- \param disp Reference to an existing CImgDisplay instance, where the current image list will be displayed.
- \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
- \param align Appending alignment.
- \note This function displays the list images of the current CImgList instance into an existing
- CImgDisplay window.
- Images of the list are appended in a single temporary image for visualization purposes.
- The function returns immediately.
- **/
- const CImgList<T>& display(CImgDisplay &disp, const char axis='x', const float align=0) const {
- disp.display(*this,axis,align);
- return *this;
- }
- //! Display the current CImgList instance in a new display window.
- /**
- \param disp Display window.
- \param display_info Tells if image information are displayed on the standard output.
- \param axis Alignment axis for images viewing.
- \param align Appending alignment.
- \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
- \param exit_on_anykey Exit function when any key is pressed.
- \note This function opens a new window with a specific title and displays the list images of the
- current CImgList instance into it.
- Images of the list are appended in a single temporary image for visualization purposes.
- The function returns when a key is pressed or the display window is closed by the user.
- **/
- const CImgList<T>& display(CImgDisplay &disp, const bool display_info,
- const char axis='x', const float align=0,
- unsigned int *const XYZ=0, const bool exit_on_anykey=false) const {
- bool is_exit = false;
- return _display(disp,0,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit);
- }
- //! Display the current CImgList instance in a new display window.
- /**
- \param title Title of the opening display window.
- \param display_info Tells if list information must be written on standard output.
- \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
- \param align Appending alignment.
- \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
- \param exit_on_anykey Exit function when any key is pressed.
- **/
- const CImgList<T>& display(const char *const title=0, const bool display_info=true,
- const char axis='x', const float align=0,
- unsigned int *const XYZ=0, const bool exit_on_anykey=false) const {
- CImgDisplay disp;
- bool is_exit = false;
- return _display(disp,title,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit);
- }
- const CImgList<T>& _display(CImgDisplay &disp, const char *const title, const CImgList<charT> *const titles,
- const bool display_info, const char axis, const float align, unsigned int *const XYZ,
- const bool exit_on_anykey, const unsigned int orig, const bool is_first_call,
- bool &is_exit) const {
- if (is_empty())
- throw CImgInstanceException(_cimglist_instance
- "display(): Empty instance.",
- cimglist_instance);
- if (!disp) {
- if (axis=='x') {
- unsigned int sum_width = 0, max_height = 0;
- cimglist_for(*this,l) {
- const CImg<T> &img = _data[l];
- const unsigned int
- w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
- h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
- sum_width+=w;
- if (h>max_height) max_height = h;
- }
- disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:titles?titles->__display()._data:0,1);
- } else {
- unsigned int max_width = 0, sum_height = 0;
- cimglist_for(*this,l) {
- const CImg<T> &img = _data[l];
- const unsigned int
- w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
- h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
- if (w>max_width) max_width = w;
- sum_height+=h;
- }
- disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:titles?titles->__display()._data:0,1);
- }
- if (!title && !titles) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
- } else if (title) disp.set_title("%s",title);
- else if (titles) disp.set_title("%s",titles->__display()._data);
- const CImg<char> dtitle = CImg<char>::string(disp.title());
- if (display_info) print(disp.title());
- disp.show().flush();
- if (_width==1) {
- const unsigned int dw = disp._width, dh = disp._height;
- if (!is_first_call)
- disp.resize(cimg_fitscreen(_data[0]._width,_data[0]._height,_data[0]._depth),false);
- disp.set_title("%s (%ux%ux%ux%u)",
- dtitle.data(),_data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum);
- _data[0]._display(disp,0,false,XYZ,exit_on_anykey,!is_first_call);
- if (disp.key()) is_exit = true;
- disp.resize(cimg_fitscreen(dw,dh,1),false).set_title("%s",dtitle.data());
- } else {
- bool disp_resize = !is_first_call;
- while (!disp.is_closed() && !is_exit) {
- const CImg<intT> s = _select(disp,0,true,axis,align,exit_on_anykey,orig,disp_resize,!is_first_call,true);
- disp_resize = true;
- if (s[0]<0 && !disp.wheel()) { // No selections done
- if (disp.button()&2) { disp.flush(); break; }
- is_exit = true;
- } else if (disp.wheel()) { // Zoom in/out
- const int wheel = disp.wheel();
- disp.set_wheel();
- if (!is_first_call && wheel<0) break;
- if (wheel>0 && _width>=4) {
- const unsigned int
- delta = std::max(1U,(unsigned int)cimg::round(0.3*_width)),
- ind0 = (unsigned int)std::max(0,s[0] - (int)delta),
- ind1 = (unsigned int)std::min(width() - 1,s[0] + (int)delta);
- if ((ind0!=0 || ind1!=_width - 1) && ind1 - ind0>=3) {
- const CImgList<T> sublist = get_shared_images(ind0,ind1);
- CImgList<charT> t_sublist;
- if (titles) t_sublist = titles->get_shared_images(ind0,ind1);
- sublist._display(disp,0,titles?&t_sublist:0,false,axis,align,XYZ,exit_on_anykey,
- orig + ind0,false,is_exit);
- }
- }
- } else if (s[0]!=0 || s[1]!=width() - 1) {
- const CImgList<T> sublist = get_shared_images(s[0],s[1]);
- CImgList<charT> t_sublist;
- if (titles) t_sublist = titles->get_shared_images(s[0],s[1]);
- sublist._display(disp,0,titles?&t_sublist:0,false,axis,align,XYZ,exit_on_anykey,
- orig + s[0],false,is_exit);
- }
- disp.set_title("%s",dtitle.data());
- }
- }
- return *this;
- }
- // [internal] Return string to describe display title.
- CImg<charT> __display() const {
- CImg<charT> res, str;
- cimglist_for(*this,l) {
- CImg<charT>::string((char*)_data[l]._data).move_to(str);
- if (l!=width() - 1) {
- str.resize(str._width + 1,1,1,1,0);
- str[str._width - 2] = ',';
- str[str._width - 1] = ' ';
- }
- res.append(str,'x');
- }
- if (!res) return CImg<charT>(1,1,1,1,0).move_to(res);
- cimg::strellipsize(res,128,false);
- if (_width>1) {
- const unsigned int l = (unsigned int)std::strlen(res);
- if (res._width<=l + 16) res.resize(l + 16,1,1,1,0);
- cimg_snprintf(res._data + l,16," (#%u)",_width);
- }
- return res;
- }
- //! Save list into a file.
- /**
- \param filename Filename to write data to.
- \param number When positive, represents an index added to the filename. Otherwise, no number is added.
- \param digits Number of digits used for adding the number to the filename.
- **/
- const CImgList<T>& save(const char *const filename, const int number=-1, const unsigned int digits=6) const {
- if (!filename)
- throw CImgArgumentException(_cimglist_instance
- "save(): Specified filename is (null).",
- cimglist_instance);
- // Do not test for empty instances, since .cimg format is able to manage empty instances.
- const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.');
- const char *const ext = cimg::split_filename(filename);
- CImg<charT> nfilename(1024);
- const char *const fn = is_stdout?filename:number>=0?cimg::number_filename(filename,number,digits,nfilename):
- filename;
- #ifdef cimglist_save_plugin
- cimglist_save_plugin(fn);
- #endif
- #ifdef cimglist_save_plugin1
- cimglist_save_plugin1(fn);
- #endif
- #ifdef cimglist_save_plugin2
- cimglist_save_plugin2(fn);
- #endif
- #ifdef cimglist_save_plugin3
- cimglist_save_plugin3(fn);
- #endif
- #ifdef cimglist_save_plugin4
- cimglist_save_plugin4(fn);
- #endif
- #ifdef cimglist_save_plugin5
- cimglist_save_plugin5(fn);
- #endif
- #ifdef cimglist_save_plugin6
- cimglist_save_plugin6(fn);
- #endif
- #ifdef cimglist_save_plugin7
- cimglist_save_plugin7(fn);
- #endif
- #ifdef cimglist_save_plugin8
- cimglist_save_plugin8(fn);
- #endif
- if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
- else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false);
- else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true);
- else if (!cimg::strcasecmp(ext,"avi") ||
- !cimg::strcasecmp(ext,"mov") ||
- !cimg::strcasecmp(ext,"asf") ||
- !cimg::strcasecmp(ext,"divx") ||
- !cimg::strcasecmp(ext,"flv") ||
- !cimg::strcasecmp(ext,"mpg") ||
- !cimg::strcasecmp(ext,"m1v") ||
- !cimg::strcasecmp(ext,"m2v") ||
- !cimg::strcasecmp(ext,"m4v") ||
- !cimg::strcasecmp(ext,"mjp") ||
- !cimg::strcasecmp(ext,"mp4") ||
- !cimg::strcasecmp(ext,"mkv") ||
- !cimg::strcasecmp(ext,"mpe") ||
- !cimg::strcasecmp(ext,"movie") ||
- !cimg::strcasecmp(ext,"ogm") ||
- !cimg::strcasecmp(ext,"ogg") ||
- !cimg::strcasecmp(ext,"ogv") ||
- !cimg::strcasecmp(ext,"qt") ||
- !cimg::strcasecmp(ext,"rm") ||
- !cimg::strcasecmp(ext,"vob") ||
- !cimg::strcasecmp(ext,"webm") ||
- !cimg::strcasecmp(ext,"wmv") ||
- !cimg::strcasecmp(ext,"xvid") ||
- !cimg::strcasecmp(ext,"mpeg")) return save_video(fn);
- #ifdef cimg_use_tiff
- else if (!cimg::strcasecmp(ext,"tif") ||
- !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
- #endif
- else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
- else {
- if (_width==1) _data[0].save(fn,-1);
- else cimglist_for(*this,l) { _data[l].save(fn,is_stdout?-1:l); if (is_stdout) std::fputc(EOF,cimg::_stdout()); }
- }
- return *this;
- }
- //! Tell if an image list can be saved as one single file.
- /**
- \param filename Filename, as a C-string.
- \return \c true if the file format supports multiple images, \c false otherwise.
- **/
- static bool is_saveable(const char *const filename) {
- const char *const ext = cimg::split_filename(filename);
- if (!cimg::strcasecmp(ext,"cimgz") ||
- #ifdef cimg_use_tiff
- !cimg::strcasecmp(ext,"tif") ||
- !cimg::strcasecmp(ext,"tiff") ||
- #endif
- !cimg::strcasecmp(ext,"yuv") ||
- !cimg::strcasecmp(ext,"avi") ||
- !cimg::strcasecmp(ext,"mov") ||
- !cimg::strcasecmp(ext,"asf") ||
- !cimg::strcasecmp(ext,"divx") ||
- !cimg::strcasecmp(ext,"flv") ||
- !cimg::strcasecmp(ext,"mpg") ||
- !cimg::strcasecmp(ext,"m1v") ||
- !cimg::strcasecmp(ext,"m2v") ||
- !cimg::strcasecmp(ext,"m4v") ||
- !cimg::strcasecmp(ext,"mjp") ||
- !cimg::strcasecmp(ext,"mp4") ||
- !cimg::strcasecmp(ext,"mkv") ||
- !cimg::strcasecmp(ext,"mpe") ||
- !cimg::strcasecmp(ext,"movie") ||
- !cimg::strcasecmp(ext,"ogm") ||
- !cimg::strcasecmp(ext,"ogg") ||
- !cimg::strcasecmp(ext,"ogv") ||
- !cimg::strcasecmp(ext,"qt") ||
- !cimg::strcasecmp(ext,"rm") ||
- !cimg::strcasecmp(ext,"vob") ||
- !cimg::strcasecmp(ext,"webm") ||
- !cimg::strcasecmp(ext,"wmv") ||
- !cimg::strcasecmp(ext,"xvid") ||
- !cimg::strcasecmp(ext,"mpeg")) return true;
- return false;
- }
- //! Save image sequence as a GIF animated file.
- /**
- \param filename Filename to write data to.
- \param fps Number of desired frames per second.
- \param nb_loops Number of loops (\c 0 for infinite looping).
- **/
- const CImgList<T>& save_gif_external(const char *const filename, const float fps=25,
- const unsigned int nb_loops=0) {
- CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
- CImgList<charT> filenames;
- std::FILE *file = 0;
- #ifdef cimg_use_png
- #define _cimg_save_gif_extension "png"
- #else
- #define _cimg_save_gif_extension "ppm"
- #endif
- do {
- cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
- cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
- cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001." _cimg_save_gif_extension,filename_tmp._data);
- if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
- } while (file);
- cimglist_for(*this,l) {
- cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u." _cimg_save_gif_extension,filename_tmp._data,l + 1);
- CImg<charT>::string(filename_tmp2).move_to(filenames);
- CImg<T> frame;
- if (_data[l]._depth>1) _data[l].get_slice(0).move_to(frame); else frame.assign(_data[l],true);
- if (frame._spectrum>4) frame.assign(frame.get_channels(0,3),false);
- else if (frame._spectrum==1) frame.assign(frame.get_resize(-100,-100,1,3),false);
- else if (frame._spectrum==2)
- frame.assign(frame.get_resize(-100,-100,1,4).draw_image(0,0,0,2,frame.get_shared_channel(0)),false);
- frame.save(filename_tmp2);
- }
- cimg_snprintf(command,command._width,"\"%s\" -delay %u -loop %u -dispose previous",
- cimg::imagemagick_path(),
- (unsigned int)std::max(0.f,cimg::round(100/fps)),
- nb_loops);
- CImg<ucharT>::string(command).move_to(filenames,0);
- cimg_snprintf(command,command._width,"\"%s\"",
- CImg<charT>::string(filename)._system_strescape().data());
- CImg<ucharT>::string(command).move_to(filenames);
- CImg<charT> _command = filenames>'x';
- cimg_for(_command,p,char) if (!*p) *p = ' ';
- _command.back() = 0;
- cimg::system(_command,cimg::imagemagick_path());
- file = cimg::std_fopen(filename,"rb");
- if (!file)
- throw CImgIOException(_cimglist_instance
- "save_gif_external(): Failed to save file '%s' with external command 'magick/convert'.",
- cimglist_instance,
- filename);
- else cimg::fclose(file);
- cimglist_for_in(*this,1,filenames._width - 1,l) std::remove(filenames[l]);
- return *this;
- }
- //! Save list as a YUV image sequence file.
- /**
- \param filename Filename to write data to.
- \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
- \param is_rgb Tells if the RGB to YUV conversion must be done for saving.
- **/
- const CImgList<T>& save_yuv(const char *const filename=0,
- const unsigned int chroma_subsampling=444,
- const bool is_rgb=true) const {
- return _save_yuv(0,filename,chroma_subsampling,is_rgb);
- }
- //! Save image sequence into a YUV file.
- /**
- \param file File to write data to.
- \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
- \param is_rgb Tells if the RGB to YUV conversion must be done for saving.
- **/
- const CImgList<T>& save_yuv(std::FILE *const file,
- const unsigned int chroma_subsampling=444,
- const bool is_rgb=true) const {
- return _save_yuv(file,0,chroma_subsampling,is_rgb);
- }
- const CImgList<T>& _save_yuv(std::FILE *const file, const char *const filename,
- const unsigned int chroma_subsampling,
- const bool is_rgb) const {
- if (!file && !filename)
- throw CImgArgumentException(_cimglist_instance
- "save_yuv(): Specified filename is (null).",
- cimglist_instance);
- if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444)
- throw CImgArgumentException(_cimglist_instance
- "save_yuv(): Specified chroma subsampling %u is invalid, for file '%s'.",
- cimglist_instance,
- chroma_subsampling,filename?filename:"(FILE*)");
- if (is_empty()) { cimg::fempty(file,filename); return *this; }
- const unsigned int
- cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1,
- cfy = chroma_subsampling==420?2:1,
- w0 = (*this)[0]._width, h0 = (*this)[0]._height,
- width0 = w0 + (w0%cfx), height0 = h0 + (h0%cfy);
- std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
- cimglist_for(*this,l) {
- const CImg<T> &frame = (*this)[l];
- cimg_forZ(frame,z) {
- CImg<ucharT> YUV;
- if (sizeof(T)==1 && !is_rgb &&
- frame._width==width0 && frame._height==height0 && frame._depth==1 && frame._spectrum==3)
- YUV.assign((unsigned char*)frame._data,width0,height0,1,3,true);
- else {
- YUV = frame.get_slice(z);
- if (YUV._width!=width0 || YUV._height!=height0) YUV.resize(width0,height0,1,-100,0);
- if (YUV._spectrum!=3) YUV.resize(-100,-100,1,3,YUV._spectrum==1?1:0);
- if (is_rgb) YUV.RGBtoYCbCr();
- }
- if (chroma_subsampling==444)
- cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height*3,nfile);
- else {
- cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height,nfile);
- CImg<ucharT> UV = YUV.get_channels(1,2);
- UV.resize(UV._width/cfx,UV._height/cfy,1,2,2);
- cimg::fwrite(UV._data,(size_t)UV._width*UV._height*2,nfile);
- }
- }
- }
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Save list into a .cimg file.
- /**
- \param filename Filename to write data to.
- \param is_compressed Tells if data compression must be enabled.
- **/
- const CImgList<T>& save_cimg(const char *const filename, const bool is_compressed=false) const {
- return _save_cimg(0,filename,is_compressed);
- }
- const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename, const bool is_compressed) const {
- if (!file && !filename)
- throw CImgArgumentException(_cimglist_instance
- "save_cimg(): Specified filename is (null).",
- cimglist_instance);
- #ifndef cimg_use_zlib
- if (is_compressed)
- cimg::warn(_cimglist_instance
- "save_cimg(): Unable to save compressed data in file '%s' unless zlib is enabled, "
- "saving them uncompressed.",
- cimglist_instance,
- filename?filename:"(FILE*)");
- #endif
- const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little";
- std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
- const bool is_bool = ptype==cimg::type<bool>::string();
- std::fprintf(nfile,"%u %s %s_endian\n",_width,ptype,etype);
- cimglist_for(*this,l) {
- const CImg<T>& img = _data[l];
- std::fprintf(nfile,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum);
- if (img._data) {
- CImg<T> tmp;
- if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); }
- const CImg<T>& ref = cimg::endianness()?tmp:img;
- bool failed_to_compress = true;
- if (is_compressed) {
- #ifdef cimg_use_zlib
- Bytef *cbuf = 0;
- uLongf csiz = 0;
- if (is_bool) { // Boolean data (bitwise)
- ulongT siz;
- const unsigned char *const buf = ref._bool2uchar(siz,false);
- csiz = siz + siz/100 + 16;
- cbuf = new Bytef[csiz];
- failed_to_compress = (bool)compress(cbuf,&csiz,(Bytef*)buf,siz);
- if (!failed_to_compress) {
- std::fprintf(nfile," #%lu\n",csiz);
- cimg::fwrite(cbuf,csiz,nfile);
- }
- delete[] buf;
- } else { // Non-boolean data
- const ulongT siz = sizeof(T)*ref.size();
- csiz = siz + siz/100 + 16;
- cbuf = new Bytef[csiz];
- failed_to_compress = (bool)compress(cbuf,&csiz,(Bytef*)ref._data,siz);
- if (!failed_to_compress) {
- std::fprintf(nfile," #%lu\n",csiz);
- cimg::fwrite(cbuf,csiz,nfile);
- }
- }
- if (failed_to_compress)
- cimg::warn(_cimglist_instance
- "save_cimg(): Failed to save compressed data for file '%s', saving them uncompressed.",
- cimglist_instance,
- filename?filename:"(FILE*)");
- delete[] cbuf;
- #endif
- }
- if (failed_to_compress) { // Write non-compressed
- std::fputc('\n',nfile);
- if (is_bool) { // Boolean data (bitwise)
- ulongT siz;
- const unsigned char *const buf = ref._bool2uchar(siz,false);
- cimg::fwrite(buf,siz,nfile);
- delete[] buf;
- } else cimg::fwrite(ref._data,ref.size(),nfile); // Non-boolean data
- }
- } else std::fputc('\n',nfile);
- }
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Save list into a .cimg file.
- /**
- \param file File to write data to.
- \param is_compressed Tells if data compression must be enabled.
- **/
- const CImgList<T>& save_cimg(std::FILE *file, const bool is_compressed=false) const {
- return _save_cimg(file,0,is_compressed);
- }
- const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename,
- const unsigned int n0,
- const unsigned int x0, const unsigned int y0,
- const unsigned int z0, const unsigned int c0) const {
- #define _cimg_save_cimg_case(Ts1,Ts2,Ts3,Tss) \
- if (!saved && ((Ts1 && !cimg::strcasecmp(Ts1,str_pixeltype)) || \
- (Ts2 && !cimg::strcasecmp(Ts2,str_pixeltype)) || \
- (Ts3 && !cimg::strcasecmp(Ts3,str_pixeltype)))) { \
- for (unsigned int l = 0; l<lmax; ++l) { \
- j = 0; while ((i=std::fgetc(nfile))!='\n') tmp[j++]=(char)i; tmp[j] = 0; \
- W = H = D = C = 0; \
- if (cimg_sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
- throw CImgIOException(_cimglist_instance \
- "save_cimg(): Invalid size (%u,%u,%u,%u) of image[%u], for file '%s'.", \
- cimglist_instance, \
- W,H,D,C,l,filename?filename:"(FILE*)"); \
- if (W*H*D*C>0) { \
- if (l<n0 || x0>=W || y0>=H || z0>=D || c0>=D) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
- else { \
- const CImg<T>& img = (*this)[l - n0]; \
- const T *ptrs = img._data; \
- const unsigned int \
- x1 = x0 + img._width - 1, \
- y1 = y0 + img._height - 1, \
- z1 = z0 + img._depth - 1, \
- c1 = c0 + img._spectrum - 1, \
- nx1 = x1>=W?W - 1:x1, \
- ny1 = y1>=H?H - 1:y1, \
- nz1 = z1>=D?D - 1:z1, \
- nc1 = c1>=C?C - 1:c1; \
- CImg<Tss> raw(1 + nx1 - x0); \
- const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \
- if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \
- for (unsigned int v = 1 + nc1 - c0; v; --v) { \
- const unsigned int skipzb = z0*W*H*sizeof(Tss); \
- if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \
- for (unsigned int z = 1 + nz1 - z0; z; --z) { \
- const unsigned int skipyb = y0*W*sizeof(Tss); \
- if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \
- for (unsigned int y = 1 + ny1 - y0; y; --y) { \
- const unsigned int skipxb = x0*sizeof(Tss); \
- if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \
- raw.assign(ptrs, raw._width); \
- ptrs+=img._width; \
- if (endian) cimg::invert_endianness(raw._data,raw._width); \
- cimg::fwrite(raw._data,raw._width,nfile); \
- const unsigned int skipxe = (W - 1 - nx1)*sizeof(Tss); \
- if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \
- } \
- const unsigned int skipye = (H - 1 - ny1)*W*sizeof(Tss); \
- if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \
- } \
- const unsigned int skipze = (D - 1 - nz1)*W*H*sizeof(Tss); \
- if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \
- } \
- const unsigned int skipve = (C - 1 - nc1)*W*H*D*sizeof(Tss); \
- if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \
- } \
- } \
- } \
- saved = true; \
- }
- if (!file && !filename)
- throw CImgArgumentException(_cimglist_instance
- "save_cimg(): Specified filename is (null).",
- cimglist_instance);
- if (is_empty())
- throw CImgInstanceException(_cimglist_instance
- "save_cimg(): Empty instance, for file '%s'.",
- cimglist_instance,
- filename?filename:"(FILE*)");
- std::FILE *const nfile = file?file:cimg::fopen(filename,"rb+");
- bool saved = false, endian = cimg::endianness();
- CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
- *tmp = *str_pixeltype = *str_endian = 0;
- unsigned int j, N, W, H, D, C;
- int i, err;
- j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
- err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z123468_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype._data,str_endian._data);
- if (err<2) {
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimglist_instance
- "save_cimg(): CImg header not found in file '%s'.",
- cimglist_instance,
- filename?filename:"(FILE*)");
- }
- if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
- else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
- const unsigned int lmax = std::min(N,n0 + _width);
- _cimg_save_cimg_case("bool",0,0,cimg_uint8);
- _cimg_save_cimg_case("uint8","unsigned_char","uchar",cimg_uint8);
- _cimg_save_cimg_case("int8",0,0,cimg_int8);
- _cimg_save_cimg_case("char",0,0,char);
- _cimg_save_cimg_case("uint16","unsigned_short","ushort",cimg_uint16);
- _cimg_save_cimg_case("int16","short",0,cimg_int16);
- _cimg_save_cimg_case("uint32","unsigned_int","uint",cimg_uint32);
- _cimg_save_cimg_case("int32","int",0,cimg_int32);
- _cimg_save_cimg_case("uint64","unsigned_int64",0,cimg_uint64);
- _cimg_save_cimg_case("int64",0,0,cimg_int64);
- _cimg_save_cimg_case("float","float32",0,cimg_float32);
- _cimg_save_cimg_case("float64","double",0,cimg_float64);
- if (!saved) {
- if (!file) cimg::fclose(nfile);
- throw CImgIOException(_cimglist_instance
- "save_cimg(): Unsupported data type '%s' for file '%s'.",
- cimglist_instance,
- filename?filename:"(FILE*)",str_pixeltype._data);
- }
- if (!file) cimg::fclose(nfile);
- return *this;
- }
- //! Insert the image instance into into an existing .cimg file, at specified coordinates.
- /**
- \param filename Filename to write data to.
- \param n0 Starting index of images to write.
- \param x0 Starting X-coordinates of image regions to write.
- \param y0 Starting Y-coordinates of image regions to write.
- \param z0 Starting Z-coordinates of image regions to write.
- \param c0 Starting C-coordinates of image regions to write.
- **/
- const CImgList<T>& save_cimg(const char *const filename,
- const unsigned int n0,
- const unsigned int x0, const unsigned int y0,
- const unsigned int z0, const unsigned int c0) const {
- return _save_cimg(0,filename,n0,x0,y0,z0,c0);
- }
- //! Insert the image instance into into an existing .cimg file, at specified coordinates.
- /**
- \param file File to write data to.
- \param n0 Starting index of images to write.
- \param x0 Starting X-coordinates of image regions to write.
- \param y0 Starting Y-coordinates of image regions to write.
- \param z0 Starting Z-coordinates of image regions to write.
- \param c0 Starting C-coordinates of image regions to write.
- **/
- const CImgList<T>& save_cimg(std::FILE *const file,
- const unsigned int n0,
- const unsigned int x0, const unsigned int y0,
- const unsigned int z0, const unsigned int c0) const {
- return _save_cimg(file,0,n0,x0,y0,z0,c0);
- }
- static void _save_empty_cimg(std::FILE *const file, const char *const filename,
- const unsigned int nb,
- const unsigned int dx, const unsigned int dy,
- const unsigned int dz, const unsigned int dc) {
- std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
- const ulongT siz = (ulongT)dx*dy*dz*dc*sizeof(T);
- std::fprintf(nfile,"%u %s\n",nb,pixel_type());
- for (unsigned int i=nb; i; --i) {
- std::fprintf(nfile,"%u %u %u %u\n",dx,dy,dz,dc);
- for (ulongT off = siz; off; --off) std::fputc(0,nfile);
- }
- if (!file) cimg::fclose(nfile);
- }
- //! Save empty (non-compressed) .cimg file with specified dimensions.
- /**
- \param filename Filename to write data to.
- \param nb Number of images to write.
- \param dx Width of images in the written file.
- \param dy Height of images in the written file.
- \param dz Depth of images in the written file.
- \param dc Spectrum of images in the written file.
- **/
- static void save_empty_cimg(const char *const filename,
- const unsigned int nb,
- const unsigned int dx, const unsigned int dy=1,
- const unsigned int dz=1, const unsigned int dc=1) {
- return _save_empty_cimg(0,filename,nb,dx,dy,dz,dc);
- }
- //! Save empty .cimg file with specified dimensions.
- /**
- \param file File to write data to.
- \param nb Number of images to write.
- \param dx Width of images in the written file.
- \param dy Height of images in the written file.
- \param dz Depth of images in the written file.
- \param dc Spectrum of images in the written file.
- **/
- static void save_empty_cimg(std::FILE *const file,
- const unsigned int nb,
- const unsigned int dx, const unsigned int dy=1,
- const unsigned int dz=1, const unsigned int dc=1) {
- return _save_empty_cimg(file,0,nb,dx,dy,dz,dc);
- }
- //! Save list as a TIFF file.
- /**
- \param filename Filename to write data to.
- \param compression_type Compression mode used to write data.
- \param voxel_size Voxel size, to be stored in the filename.
- \param description Description, to be stored in the filename.
- \param use_bigtiff Allow to save big tiff files (>4Gb).
- **/
- const CImgList<T>& save_tiff(const char *const filename, const unsigned int compression_type=0,
- const float *const voxel_size=0, const char *const description=0,
- const bool use_bigtiff=true) const {
- if (!filename)
- throw CImgArgumentException(_cimglist_instance
- "save_tiff(): Specified filename is (null).",
- cimglist_instance);
- if (is_empty()) { cimg::fempty(0,filename); return *this; }
- #ifndef cimg_use_tiff
- if (_width==1) _data[0].save_tiff(filename,compression_type,voxel_size,description,use_bigtiff);
- else cimglist_for(*this,l) {
- CImg<charT> nfilename(1024);
- cimg::number_filename(filename,l,6,nfilename);
- _data[l].save_tiff(nfilename,compression_type,voxel_size,description,use_bigtiff);
- }
- #else
- ulongT siz = 0;
- cimglist_for(*this,l) siz+=_data[l].size();
- const bool _use_bigtiff = use_bigtiff && sizeof(siz)>=8 && siz*sizeof(T)>=1UL<<31; // No bigtiff for small images
- TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4");
- if (tif) {
- for (unsigned int dir = 0, l = 0; l<_width; ++l) {
- const CImg<T>& img = (*this)[l];
- cimg_forZ(img,z) img._save_tiff(tif,dir++,z,compression_type,voxel_size,description);
- }
- TIFFClose(tif);
- } else
- throw CImgIOException(_cimglist_instance
- "save_tiff(): Failed to open stream for file '%s'.",
- cimglist_instance,
- filename);
- #endif
- return *this;
- }
- //! Save list as a gzipped file, using external tool 'gzip'.
- /**
- \param filename Filename to write data to.
- **/
- const CImgList<T>& save_gzip_external(const char *const filename) const {
- if (!filename)
- throw CImgIOException(_cimglist_instance
- "save_gzip_external(): Specified filename is (null).",
- cimglist_instance);
- CImg<charT> command(1024), filename_tmp(256), body(256);
- const char
- *ext = cimg::split_filename(filename,body),
- *ext2 = cimg::split_filename(body,0);
- std::FILE *file;
- do {
- if (!cimg::strcasecmp(ext,"gz")) {
- if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
- cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
- else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
- cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
- } else {
- if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
- cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
- else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
- cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
- }
- if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
- } while (file);
- if (is_saveable(body)) {
- save(filename_tmp);
- cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"",
- cimg::gzip_path(),
- CImg<charT>::string(filename_tmp)._system_strescape().data(),
- CImg<charT>::string(filename)._system_strescape().data());
- cimg::system(command,cimg::gzip_path());
- file = cimg::std_fopen(filename,"rb");
- if (!file)
- throw CImgIOException(_cimglist_instance
- "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.",
- cimglist_instance,
- filename);
- else cimg::fclose(file);
- std::remove(filename_tmp);
- } else {
- CImg<charT> nfilename(1024);
- cimglist_for(*this,l) {
- cimg::number_filename(body,l,6,nfilename);
- if (*ext) cimg_snprintf(nfilename._data + std::strlen(nfilename),64,".%s",ext);
- _data[l].save_gzip_external(nfilename);
- }
- }
- return *this;
- }
- //! Save image sequence (using the OpenCV library when available).
- /**
- \param filename Filename to write data to.
- \param fps Number of frames per second.
- \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs).
- \param keep_open Tells if the video writer associated to the specified filename
- must be kept open or not (to allow frames to be added in the same file afterwards).
- **/
- const CImgList<T>& save_video(const char *const filename, const unsigned int fps=25,
- const char *codec=0, const bool keep_open=false) const {
- #ifndef cimg_use_opencv
- cimg::unused(codec,keep_open);
- if (keep_open) cimg::warn(_cimglist_instance
- "save_video(): Cannot output streamed video, as this requires features from the "
- "OpenCV library ('-Dcimg_use_opencv') must be defined).",
- cimglist_instance);
- if (!is_empty()) return save_ffmpeg_external(filename,fps);
- return *this;
- #else
- try {
- static cv::VideoWriter *writers[32] = {};
- static CImgList<charT> filenames(32);
- static CImg<intT> sizes(32,2,1,1,0);
- static int last_used_index = -1;
- // Detect if a video writer already exists for the specified filename.
- cimg::mutex(9);
- int index = -1;
- if (filename) {
- if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) {
- index = last_used_index;
- } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) {
- index = l; break;
- }
- } else index = last_used_index;
- cimg::mutex(9,0);
- // Find empty slot for capturing video stream.
- if (index<0) {
- if (!filename)
- throw CImgArgumentException(_cimglist_instance
- "save_video(): No already open video writer found. You must specify a "
- "non-(null) filename argument for the first call.",
- cimglist_instance);
- else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); }
- if (index<0)
- throw CImgIOException(_cimglist_instance
- "save_video(): File '%s', no video writer slots available. "
- "You have to release some of your previously opened videos.",
- cimglist_instance,filename);
- if (is_empty())
- throw CImgInstanceException(_cimglist_instance
- "save_video(): Instance list is empty.",
- cimglist_instance);
- const unsigned int W = _data?_data[0]._width:0, H = _data?_data[0]._height:0;
- if (!W || !H)
- throw CImgInstanceException(_cimglist_instance
- "save_video(): Frame [0] is an empty image.",
- cimglist_instance);
- const char
- *const _codec = codec && *codec?codec:"h264",
- codec0 = cimg::uppercase(_codec[0]),
- codec1 = _codec[0]?cimg::uppercase(_codec[1]):0,
- codec2 = _codec[1]?cimg::uppercase(_codec[2]):0,
- codec3 = _codec[2]?cimg::uppercase(_codec[3]):0;
- cimg::mutex(9);
- writers[index] = new cv::VideoWriter(filename,_cimg_fourcc(codec0,codec1,codec2,codec3),fps,cv::Size(W,H));
- if (!writers[index]->isOpened()) {
- delete writers[index];
- writers[index] = 0;
- cimg::mutex(9,0);
- throw CImgIOException(_cimglist_instance
- "save_video(): File '%s', unable to initialize video writer with codec '%c%c%c%c'.",
- cimglist_instance,filename,
- codec0,codec1,codec2,codec3);
- }
- CImg<charT>::string(filename).move_to(filenames[index]);
- sizes(index,0) = W;
- sizes(index,1) = H;
- cimg::mutex(9,0);
- }
- if (!is_empty()) {
- const unsigned int W = sizes(index,0), H = sizes(index,1);
- cimg::mutex(9);
- cimglist_for(*this,l) {
- CImg<T> &src = _data[l];
- if (src.is_empty())
- cimg::warn(_cimglist_instance
- "save_video(): Skip empty frame %d for file '%s'.",
- cimglist_instance,l,filename);
- if (src._spectrum>3)
- cimg::warn(_cimglist_instance
- "save_video(): Frame %u has incompatible dimension (%u,%u,%u,%u). "
- "Some image data may be ignored when writing frame into video file '%s'.",
- cimglist_instance,l,src._width,src._height,src._depth,src._spectrum,filename);
- cimg_forZ(src,z) {
- CImg<T> _src = src._depth>1?src.get_slice(z):src.get_shared();
- if (_src._width==W && _src._height==H && _src._spectrum==3)
- writers[index]->write(CImg<ucharT>(_src)._cimg2cvmat());
- else {
- CImg<ucharT> __src(_src,false);
- __src.channels(0,std::min(__src._spectrum - 1,2U)).resize(W,H);
- __src.resize(W,H,1,3,__src._spectrum==1);
- writers[index]->write(__src._cimg2cvmat());
- }
- }
- }
- cimg::mutex(9,0);
- }
- cimg::mutex(9);
- if (!keep_open) {
- delete writers[index];
- writers[index] = 0;
- filenames[index].assign();
- sizes(index,0) = sizes(index,1) = 0;
- last_used_index = -1;
- } else last_used_index = index;
- cimg::mutex(9,0);
- } catch (CImgIOException &e) {
- if (!keep_open) return save_ffmpeg_external(filename,fps);
- throw e;
- }
- return *this;
- #endif
- }
- //! Save image sequence, using the external tool 'ffmpeg'.
- /**
- \param filename Filename to write data to.
- \param fps Number of frames per second.
- \param codec Type of compression.
- \param bitrate Output bitrate
- **/
- const CImgList<T>& save_ffmpeg_external(const char *const filename, const unsigned int fps=25,
- const char *const codec=0, const unsigned int bitrate=2048) const {
- if (!filename)
- throw CImgArgumentException(_cimglist_instance
- "save_ffmpeg_external(): Specified filename is (null).",
- cimglist_instance);
- if (is_empty()) { cimg::fempty(0,filename); return *this; }
- const char
- *const ext = cimg::split_filename(filename),
- *const _codec = codec?codec:
- !cimg::strcasecmp(ext,"flv")?"flv":
- !cimg::strcasecmp(ext,"mp4")?"h264":"mpeg2video";
- CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
- CImgList<charT> filenames;
- std::FILE *file = 0;
- cimglist_for(*this,l) if (!_data[l].is_sameXYZ(_data[0]))
- throw CImgInstanceException(_cimglist_instance
- "save_ffmpeg_external(): Invalid instance dimensions for file '%s'.",
- cimglist_instance,
- filename);
- do {
- cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
- cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
- cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data);
- if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
- } while (file);
- unsigned int frame = 1;
- cimglist_for(*this,l) {
- CImg<T>& src = _data[l];
- cimg_forZ(src,z) {
- cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,frame);
- CImg<charT>::string(filename_tmp2).move_to(filenames);
- CImg<T> _src = src._depth>1?src.get_slice(z):src.get_shared();
- if (_src._width%2 || _src._height%2) // Force output to have an even number of columns and rows
- _src.assign(_src.get_resize(_src._width + (_src._width%2),_src._height + (_src._height%2),1,-100,0),false);
- if (_src._spectrum!=3) // Force output to be one slice, in color
- _src.assign(_src.get_resize(-100,-100,1,3),false);
- _src.save_pnm(filename_tmp2);
- ++frame;
- }
- }
- cimg_snprintf(command,command._width,
- "\"%s\" -framerate %u -v -8 -y -i \"%s_%%6d.ppm\" -pix_fmt yuv420p -vcodec %s -b %uk -r %u \"%s\"",
- cimg::ffmpeg_path(),
- fps,CImg<charT>::string(filename_tmp)._system_strescape().data(),
- _codec,bitrate,fps,
- CImg<charT>::string(filename)._system_strescape().data());
- cimg::system(command,cimg::ffmpeg_path());
- file = cimg::std_fopen(filename,"rb");
- if (!file)
- throw CImgIOException(_cimglist_instance
- "save_ffmpeg_external(): Failed to save file '%s' with external command 'ffmpeg'.",
- cimglist_instance,
- filename);
- else cimg::fclose(file);
- cimglist_for(*this,l) std::remove(filenames[l]);
- return *this;
- }
- //! Serialize a CImgList<T> instance into a raw CImg<unsigned char> buffer.
- /**
- \param is_compressed tells if zlib compression must be used for serialization
- (this requires 'cimg_use_zlib' been enabled).
- \param header_size Reserve empty bytes as a starting header.
- **/
- CImg<ucharT> get_serialize(const bool is_compressed=false, const unsigned int header_size=0) const {
- #ifndef cimg_use_zlib
- if (is_compressed)
- cimg::warn(_cimglist_instance
- "get_serialize(): Unable to compress data unless zlib is enabled, "
- "storing them uncompressed.",
- cimglist_instance);
- #endif
- CImgList<ucharT> stream;
- if (header_size) CImg<ucharT>(1,header_size,1,1,0).move_to(stream);
- CImg<charT> tmpstr(128);
- const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little";
- cimg_snprintf(tmpstr,tmpstr._width,"%u %s %s_endian\n",_width,ptype,etype);
- CImg<ucharT>::string(tmpstr,false).move_to(stream);
- cimglist_for(*this,l) {
- const CImg<T>& img = _data[l];
- cimg_snprintf(tmpstr,tmpstr._width,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum);
- CImg<ucharT>::string(tmpstr,false).move_to(stream);
- if (img._data) {
- CImg<T> tmp;
- if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); }
- const CImg<T>& ref = cimg::endianness()?tmp:img;
- bool failed_to_compress = true;
- if (is_compressed) {
- #ifdef cimg_use_zlib
- const ulongT siz = sizeof(T)*ref.size();
- uLongf csiz = (ulongT)compressBound(siz);
- Bytef *const cbuf = new Bytef[csiz];
- if (compress(cbuf,&csiz,(Bytef*)ref._data,siz))
- cimg::warn(_cimglist_instance
- "get_serialize(): Failed to save compressed data, saving them uncompressed.",
- cimglist_instance);
- else {
- cimg_snprintf(tmpstr,tmpstr._width," #%lu\n",csiz);
- CImg<ucharT>::string(tmpstr,false).move_to(stream);
- CImg<ucharT>(cbuf,csiz).move_to(stream);
- delete[] cbuf;
- failed_to_compress = false;
- }
- #endif
- }
- if (failed_to_compress) { // Write in a non-compressed way
- CImg<charT>::string("\n",false).move_to(stream);
- stream.insert(1);
- stream.back().
- assign((unsigned char*)ref._data,ref._width,ref._height,ref._depth,ref._spectrum*sizeof(T),true);
- }
- } else CImg<charT>::string("\n",false).move_to(stream);
- }
- // Determine best serialized image dimensions to store the whole buffer.
- ulongT siz = 0;
- cimglist_for(stream,l) siz+=stream[l].size();
- const ulongT max_siz = (ulongT)cimg::type<int>::max();
- const unsigned int
- nw = (unsigned int)(siz/max_siz + ((siz%max_siz)?1:0)),
- nh = (unsigned int)(siz/nw + (siz%nw?1:0));
- CImg<ucharT> res(nw,nh,1,1,0);
- unsigned char *ptr = res.data();
- cimglist_for(stream,l) { siz = stream[l].size(); std::memcpy(ptr,stream[l]._data,siz); ptr+=siz; }
- return res;
- }
- //! Unserialize a CImg<unsigned char> serialized buffer into a CImgList<T> list.
- template<typename t>
- static CImgList<T> get_unserialize(const CImg<t>& buffer, const unsigned int header_size=0) {
- #ifdef cimg_use_zlib
- #define _cimgz_unserialize_case(Tss) { \
- Bytef *cbuf = 0; \
- if (sizeof(t)!=1 || buffer.pixel_type()==cimg::type<bool>::string()) { \
- cbuf = new Bytef[csiz]; Bytef *_cbuf = cbuf; \
- for (ulongT k = 0; k<csiz && stream<estream; ++k) *(_cbuf++) = (Bytef)*(stream++); \
- is_bytef = false; \
- } else { cbuf = (Bytef*)stream; stream+=csiz; is_bytef = true; } \
- raw.assign(W,H,D,C); \
- uLongf destlen = raw.size()*sizeof(Tss); \
- uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \
- if (!is_bytef) delete[] cbuf; \
- }
- #else
- #define _cimgz_unserialize_case(Tss) \
- throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unable to unserialize compressed data " \
- "unless zlib is enabled.", \
- pixel_type());
- #endif
- #define _cimg_unserialize_case(Ts1,Ts2,Ts3,Tss) \
- if (!loaded && ((Ts1 && !cimg::strcasecmp(Ts1,str_pixeltype)) || \
- (Ts2 && !cimg::strcasecmp(Ts2,str_pixeltype)) || \
- (Ts3 && !cimg::strcasecmp(Ts3,str_pixeltype)))) { \
- for (unsigned int l = 0; l<N; ++l) { \
- j = 0; while ((i=(int)*stream)!='\n' && stream<estream && j<255) { ++stream; tmp[j++] = (char)i; } \
- ++stream; tmp[j] = 0; \
- W = H = D = C = 0; csiz = 0; \
- if ((err = cimg_sscanf(tmp,"%u %u %u %u #" cimg_fuint64,&W,&H,&D,&C,&csiz))<4) \
- throw CImgArgumentException("CImgList<%s>::unserialize(): Invalid specified size (%u,%u,%u,%u) for " \
- "image #%u in serialized buffer.", \
- pixel_type(),W,H,D,C,l); \
- if (W*H*D*C>0) { \
- CImg<Tss> raw; \
- CImg<T> &img = res._data[l]; \
- if (err==5) _cimgz_unserialize_case(Tss) \
- else { \
- raw.assign(W,H,D,C); \
- CImg<ucharT> _raw((unsigned char*)raw._data,W*sizeof(Tss),H,D,C,true); \
- if (sizeof(t)==1) { std::memcpy(_raw,stream,_raw.size()); stream+=_raw.size(); } \
- else cimg_for(_raw,p,unsigned char) *p = (unsigned char)*(stream++); \
- } \
- if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
- raw.move_to(img); \
- } \
- } \
- loaded = true; \
- }
- if (buffer.is_empty())
- throw CImgArgumentException("CImgList<%s>::get_unserialize(): Specified serialized buffer is (null).",
- pixel_type());
- CImgList<T> res;
- const t *stream = buffer._data + header_size, *const estream = buffer._data + buffer.size();
- bool loaded = false, endian = cimg::endianness(), is_bytef = false;
- CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
- *tmp = *str_pixeltype = *str_endian = 0;
- unsigned int j, N = 0, W, H, D, C;
- uint64T csiz;
- int i, err;
- cimg::unused(is_bytef);
- do {
- j = 0; while ((i=(int)*stream)!='\n' && stream<estream && j<255) { ++stream; tmp[j++] = (char)i; }
- ++stream; tmp[j] = 0;
- } while (*tmp=='#' && stream<estream);
- err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z123468_]%*c%255[sA-Za-z_ ]",
- &N,str_pixeltype._data,str_endian._data);
- if (err<2)
- throw CImgArgumentException("CImgList<%s>::get_unserialize(): CImg header not found in serialized buffer.",
- pixel_type());
- if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
- else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
- res.assign(N);
- _cimg_unserialize_case("bool",0,0,cimg_uint8);
- _cimg_unserialize_case("uint8","unsigned_char","uchar",cimg_uint8);
- _cimg_unserialize_case("int8",0,0,cimg_int8);
- _cimg_unserialize_case("char",0,0,char);
- _cimg_unserialize_case("uint16","unsigned_short","ushort",cimg_uint16);
- _cimg_unserialize_case("int16","short",0,cimg_int16);
- _cimg_unserialize_case("uint32","unsigned_int","uint",cimg_uint32);
- _cimg_unserialize_case("int32","int",0,cimg_int32);
- _cimg_unserialize_case("uint64","unsigned_int64",0,cimg_uint64);
- _cimg_unserialize_case("int64",0,0,cimg_int64);
- _cimg_unserialize_case("float32","float",0,cimg_float32);
- _cimg_unserialize_case("float64","double",0,cimg_float64);
- if (!loaded)
- throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unsupported pixel type '%s' defined "
- "in serialized buffer.",
- pixel_type(),str_pixeltype._data);
- return res;
- }
- //@}
- //----------------------------------
- //
- //! \name Others
- //@{
- //----------------------------------
- //! Return a CImg pre-defined font with requested height.
- /**
- \param font_height Height of the desired font (exact match for 13,23,53,103).
- \param is_variable_width Decide if the font has a variable (\c true) or fixed (\c false) width.
- **/
- static const CImgList<ucharT>& font(const unsigned int requested_height, const bool is_variable_width=true) {
- if (!requested_height) return CImgList<ucharT>::const_empty();
- cimg::mutex(11);
- static const unsigned char font_resizemap[] = {
- 0, 4, 7, 9, 11, 13, 15, 17, 19, 21, 22, 24, 26, 27, 29, 30,
- 32, 33, 35, 36, 38, 39, 41, 42, 43, 45, 46, 47, 49, 50, 51, 52,
- 54, 55, 56, 58, 59, 60, 61, 62, 64, 65, 66, 67, 68, 69, 71, 72,
- 73, 74, 75, 76, 77, 78, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
- 90, 91, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
- 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122,
- 123, 124, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137,
- 138, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 148, 149, 150, 151,
- 152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 162, 163, 164, 164, 165,
- 166, 167, 168, 169, 170, 170, 171, 172, 173, 174, 175, 176, 176, 177, 178, 179,
- 180, 181, 181, 182, 183, 184, 185, 186, 186, 187, 188, 189, 190, 191, 191, 192,
- 193, 194, 195, 196, 196, 197, 198, 199, 200, 200, 201, 202, 203, 204, 205, 205,
- 206, 207, 208, 209, 209, 210, 211, 212, 213, 213, 214, 215, 216, 216, 217, 218,
- 219, 220, 220, 221, 222, 223, 224, 224, 225, 226, 227, 227, 228, 229, 230, 231,
- 231, 232, 233, 234, 234, 235, 236, 237, 238, 238, 239, 240, 241, 241, 242, 243,
- 244, 244, 245, 246, 247, 247, 248, 249, 250, 250, 251, 252, 253, 253, 254, 255 };
- static const char *const *font_data[] = {
- cimg::data_font_small,
- cimg::data_font_normal,
- cimg::data_font_large,
- cimg::data_font_huge };
- static const unsigned int
- font_width[] = { 10,26,52,104 },
- font_height[] = { 13,32,64,128 },
- font_M[] = { 86,91,91,47 },
- font_chunk[] = { sizeof(cimg::data_font_small)/sizeof(char*),
- sizeof(cimg::data_font_normal)/sizeof(char*),
- sizeof(cimg::data_font_large)/sizeof(char*),
- sizeof(cimg::data_font_huge)/sizeof(char*) };
- static const unsigned char font_is_binary[] = { 1,0,0,1 };
- static CImg<ucharT> font_base[4];
- unsigned int ind =
- requested_height<=font_height[0]?0U:
- requested_height<=font_height[1]?1U:
- requested_height<=font_height[2]?2U:3U;
- // Decompress nearest base font data if needed.
- CImg<ucharT> &basef = font_base[ind];
- if (!basef) {
- basef.assign(256*font_width[ind],font_height[ind]);
- unsigned char *ptrd = basef;
- const unsigned char *const ptrde = basef.end();
- // Recompose font data from several chunks, to deal with MS compiler limit with big strings (64 Kb).
- CImg<char> dataf;
- for (unsigned int k = 0; k<font_chunk[ind]; ++k)
- dataf.append(CImg<char>::string(font_data[ind][k],k==font_chunk[ind] - 1,true),'x');
- // Uncompress font data (decode RLE).
- const unsigned int M = font_M[ind];
- if (font_is_binary[ind])
- for (const char *ptrs = dataf; *ptrs; ++ptrs) {
- const int _n = (int)(*ptrs - M - 32), v = _n>=0?255:0, n = _n>=0?_n:-_n;
- if (ptrd + n<=ptrde) { std::memset(ptrd,v,n); ptrd+=n; }
- else { std::memset(ptrd,v,ptrde - ptrd); break; }
- }
- else
- for (const char *ptrs = dataf; *ptrs; ++ptrs) {
- int n = (int)*ptrs - M - 32, v = 0;
- if (n>=0) { v = 85*n; n = 1; }
- else {
- n = -n;
- v = (int)*(++ptrs) - M - 32;
- if (v<0) { v = 0; --ptrs; } else v*=85;
- }
- if (ptrd + n<=ptrde) { std::memset(ptrd,v,n); ptrd+=n; }
- else { std::memset(ptrd,v,ptrde - ptrd); break; }
- }
- }
- // Find optimal font cache location to return.
- static CImgList<ucharT> fonts[16];
- static bool is_variable_widths[16] = {};
- ind = ~0U;
- for (int i = 0; i<16; ++i)
- if (!fonts[i] || (is_variable_widths[i]==is_variable_width && requested_height==fonts[i][0]._height)) {
- ind = (unsigned int)i; break; // Found empty slot or cached font
- }
- if (ind==~0U) { // No empty slots nor existing font in cache
- fonts->assign();
- std::memmove((void*)fonts,(void*)(fonts + 1),15*sizeof(CImgList<ucharT>));
- std::memmove(is_variable_widths,is_variable_widths + 1,15*sizeof(bool));
- std::memset((void*)(fonts + (ind=15)),0,sizeof(CImgList<ucharT>)); // Free a slot in cache for new font
- }
- CImgList<ucharT> &font = fonts[ind];
- // Render requested font.
- if (!font) {
- is_variable_widths[ind] = is_variable_width;
- basef.get_split('x',256).move_to(font);
- if (requested_height!=font[0]._height)
- cimglist_for(font,l) {
- font[l].resize(std::max(1U,font[l]._width*requested_height/font[l]._height),requested_height,-100,-100,5);
- cimg_for(font[l],ptr,ucharT) *ptr = font_resizemap[*ptr];
- }
- if (is_variable_width) { // Crop font
- cimglist_for(font,l) {
- CImg<ucharT>& letter = font[l];
- int xmin = letter.width(), xmax = 0;
- cimg_forX(letter,x) { // Find xmin
- cimg_forY(letter,y) if (letter(x,y)) { xmin = x; break; }
- if (xmin!=letter.width()) break;
- }
- cimg_rofX(letter,x) { // Find xmax
- cimg_forY(letter,y) if (letter(x,y)) { xmax = x; break; }
- if (xmax) break;
- }
- if (xmin<=xmax) letter.crop(xmin,0,xmax,letter._height - 1);
- }
- font[(int)' '].resize(font[(int)'f']._width,-100,-100,-100,0);
- if (' ' + 256<font.size()) font[' ' + 256].resize(font[(int)'f']._width,-100,-100,-100,0);
- }
- font.insert(256,0);
- cimglist_for_in(font,0,255,l) font[l].assign(font[l + 256]._width,font[l + 256]._height,1,1,255);
- }
- cimg::mutex(11,0);
- return font;
- }
- //! Compute a 1D Fast Fourier Transform, along specified axis.
- /**
- \param axis Axis along which the Fourier transform is computed.
- \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed.
- **/
- CImgList<T>& FFT(const char axis, const bool invert=false) {
- if (is_empty()) return *this;
- if (_width==1) insert(1);
- if (_width>2)
- cimg::warn(_cimglist_instance
- "FFT(): Instance has more than 2 images",
- cimglist_instance);
- CImg<T>::FFT(_data[0],_data[1],axis,invert);
- return *this;
- }
- //! Compute a 1-D Fast Fourier Transform, along specified axis \newinstance.
- CImgList<Tfloat> get_FFT(const char axis, const bool invert=false) const {
- return CImgList<Tfloat>(*this,false).FFT(axis,invert);
- }
- //! Compute n-D Fast Fourier Transform.
- /**
- \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed.
- **/
- CImgList<T>& FFT(const bool invert=false) {
- if (is_empty()) return *this;
- if (_width==1) insert(1);
- if (_width>2)
- cimg::warn(_cimglist_instance
- "FFT(): Instance has more than 2 images",
- cimglist_instance);
- CImg<T>::FFT(_data[0],_data[1],invert);
- return *this;
- }
- //! Compute n-D Fast Fourier Transform \newinstance.
- CImgList<Tfloat> get_FFT(const bool invert=false) const {
- return CImgList<Tfloat>(*this,false).FFT(invert);
- }
- //! Reverse primitives orientations of a 3D object.
- /**
- **/
- CImgList<T>& reverse_object3d() {
- cimglist_for(*this,l) {
- CImg<T>& p = _data[l];
- switch (p.size()) {
- case 2 : case 3: cimg::swap(p[0],p[1]); break;
- case 6 : cimg::swap(p[0],p[1],p[2],p[4],p[3],p[5]); break;
- case 9 : cimg::swap(p[0],p[1],p[3],p[5],p[4],p[6]); break;
- case 4 : cimg::swap(p[0],p[1],p[2],p[3]); break;
- case 12 : cimg::swap(p[0],p[1],p[2],p[3],p[4],p[6],p[5],p[7],p[8],p[10],p[9],p[11]); break;
- }
- }
- return *this;
- }
- //! Reverse primitives orientations of a 3D object \newinstance.
- CImgList<T> get_reverse_object3d() const {
- return (+*this).reverse_object3d();
- }
- //@}
- }; // struct CImgList { ...
- // Completion of previously declared functions
- //--------------------------------------------
- namespace cimg {
- // Functions to return standard streams 'stdin', 'stdout' and 'stderr'.
- // (throw a CImgIOException when macro 'cimg_use_r' is defined).
- inline FILE* _stdin(const bool throw_exception) {
- #ifndef cimg_use_r
- cimg::unused(throw_exception);
- return stdin;
- #else
- if (throw_exception) {
- cimg::exception_mode(0);
- throw CImgIOException("cimg::stdin(): Reference to 'stdin' stream not allowed in R mode "
- "('cimg_use_r' is defined).");
- }
- return 0;
- #endif
- }
- inline FILE* _stdout(const bool throw_exception) {
- #ifndef cimg_use_r
- cimg::unused(throw_exception);
- return stdout;
- #else
- if (throw_exception) {
- cimg::exception_mode(0);
- throw CImgIOException("cimg::stdout(): Reference to 'stdout' stream not allowed in R mode "
- "('cimg_use_r' is defined).");
- }
- return 0;
- #endif
- }
- inline FILE* _stderr(const bool throw_exception) {
- #ifndef cimg_use_r
- cimg::unused(throw_exception);
- return stderr;
- #else
- if (throw_exception) {
- cimg::exception_mode(0);
- throw CImgIOException("cimg::stderr(): Reference to 'stderr' stream not allowed in R mode "
- "('cimg_use_r' is defined).");
- }
- return 0;
- #endif
- }
- // Open a file (similar to std:: fopen(), but with wide character support on Windows).
- inline std::FILE *std_fopen(const char *const path, const char *const mode) {
- std::FILE *const res = std::fopen(path,mode);
- if (res) return res;
- #if cimg_OS==2
- // Try alternative method, with wide-character string.
- int err = MultiByteToWideChar(CP_UTF8,0,path,-1,0,0);
- if (err) {
- CImg<wchar_t> wpath((unsigned int)err);
- err = MultiByteToWideChar(CP_UTF8,0,path,-1,wpath,err);
- if (err) { // Convert 'mode' to a wide-character string
- err = MultiByteToWideChar(CP_UTF8,0,mode,-1,0,0);
- if (err) {
- CImg<wchar_t> wmode((unsigned int)err);
- if (MultiByteToWideChar(CP_UTF8,0,mode,-1,wmode,err))
- return _wfopen(wpath,wmode);
- }
- }
- }
- #endif
- return 0;
- }
- //! Search path of an executable (Windows only).
- #if cimg_OS==2
- inline bool win_searchpath(const char *const exec_name, char *const res, const unsigned int size_res) {
- char *ptr = 0;
- DWORD err = SearchPathA(0,exec_name,0,size_res,res,&ptr);
- return err!=0;
- }
- #endif
- //! Get the file or directory attributes with support for UTF-8 paths (Windows only).
- #if cimg_OS==2
- inline DWORD win_getfileattributes(const char *const path) {
- DWORD res = GetFileAttributesA(path);
- if (res==INVALID_FILE_ATTRIBUTES) {
- // Try alternative method, with wide-character string.
- int err = MultiByteToWideChar(CP_UTF8,0,path,-1,0,0);
- if (err) {
- CImg<wchar_t> wpath((unsigned int)err);
- if (MultiByteToWideChar(CP_UTF8,0,path,-1,wpath,err)) res = GetFileAttributesW(wpath);
- }
- }
- return res;
- }
- #endif
- //! Get/set path to the <i>Program Files/</i> directory (Windows only).
- /**
- \param user_path Specified path, or \c 0 to get the path currently used.
- \param reinit_path Force path to be recalculated (may take some time).
- \return Path containing the program files.
- **/
- #if cimg_OS==2
- inline const char* win_programfiles_path(const char *const user_path=0, const bool reinit_path=false) {
- static CImg<char> s_path;
- cimg::mutex(7);
- if (reinit_path) s_path.assign();
- if (user_path) {
- if (!s_path) s_path.assign(1024);
- std::strncpy(s_path,user_path,1023);
- } else if (!s_path) {
- s_path.assign(MAX_PATH);
- *s_path = 0;
- // Note: in the following line, 0x26 = CSIDL_PROGRAM_FILES (not defined on every compiler).
- #if !defined(__INTEL_COMPILER)
- if (!SHGetSpecialFolderPathA(0,s_path,0x0026,false)) {
- const char *const pfPath = std::getenv("PROGRAMFILES");
- if (pfPath) std::strncpy(s_path,pfPath,MAX_PATH - 1);
- else std::strcpy(s_path,"C:\\PROGRA~1");
- }
- #else
- std::strcpy(s_path,"C:\\PROGRA~1");
- #endif
- }
- cimg::mutex(7,0);
- return s_path;
- }
- #endif
- //! Get/set path to the \c curl binary.
- /**
- \param user_path Specified path, or \c 0 to get the path currently used.
- \param reinit_path Force path to be recalculated (may take some time).
- \return Path containing the \c curl binary.
- **/
- inline const char *curl_path(const char *const user_path, const bool reinit_path) {
- static CImg<char> s_path;
- cimg::mutex(7);
- if (reinit_path) s_path.assign();
- if (user_path) {
- if (!s_path) s_path.assign(1024);
- std::strncpy(s_path,user_path,1023);
- } else if (!s_path) {
- s_path.assign(1024);
- bool path_found = false;
- std::FILE *file = 0;
- #if cimg_OS==2
- if (win_searchpath("curl.exe",s_path,s_path._width)) path_found = true;
- if (!path_found) {
- std::strcpy(s_path,".\\curl.exe");
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- if (!path_found) std::strcpy(s_path,"curl.exe");
- #else
- if (!path_found) {
- std::strcpy(s_path,"./curl");
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- if (!path_found) std::strcpy(s_path,"curl");
- #endif
- winformat_string(s_path);
- }
- cimg::mutex(7,0);
- return s_path;
- }
- //! Get/set path to the \c dcraw binary.
- /**
- \param user_path Specified path, or \c 0 to get the path currently used.
- \param reinit_path Force path to be recalculated (may take some time).
- \return Path containing the \c dcraw binary.
- **/
- inline const char *dcraw_path(const char *const user_path, const bool reinit_path) {
- static CImg<char> s_path;
- cimg::mutex(7);
- if (reinit_path) s_path.assign();
- if (user_path) {
- if (!s_path) s_path.assign(1024);
- std::strncpy(s_path,user_path,1023);
- } else if (!s_path) {
- s_path.assign(1024);
- bool path_found = false;
- std::FILE *file = 0;
- #if cimg_OS==2
- if (win_searchpath("dcraw.exe",s_path,s_path._width)) path_found = true;
- if (!path_found) {
- std::strcpy(s_path,".\\dcraw.exe");
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- if (!path_found) std::strcpy(s_path,"dcraw.exe");
- #else
- if (!path_found) {
- std::strcpy(s_path,"./dcraw");
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- if (!path_found) std::strcpy(s_path,"dcraw");
- #endif
- winformat_string(s_path);
- }
- cimg::mutex(7,0);
- return s_path;
- }
- //! Get/set path to the FFMPEG's \c ffmpeg binary.
- /**
- \param user_path Specified path, or \c 0 to get the path currently used.
- \param reinit_path Force path to be recalculated (may take some time).
- \return Path containing the \c ffmpeg binary.
- **/
- inline const char *ffmpeg_path(const char *const user_path, const bool reinit_path) {
- static CImg<char> s_path;
- cimg::mutex(7);
- if (reinit_path) s_path.assign();
- if (user_path) {
- if (!s_path) s_path.assign(1024);
- std::strncpy(s_path,user_path,1023);
- } else if (!s_path) {
- s_path.assign(1024);
- bool path_found = false;
- std::FILE *file = 0;
- #if cimg_OS==2
- if (win_searchpath("ffmpeg.exe",s_path,s_path._width)) path_found = true;
- if (!path_found) {
- std::strcpy(s_path,".\\ffmpeg.exe");
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- if (!path_found) std::strcpy(s_path,"ffmpeg.exe");
- #else
- if (!path_found) {
- std::strcpy(s_path,"./ffmpeg");
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- if (!path_found) std::strcpy(s_path,"ffmpeg");
- #endif
- winformat_string(s_path);
- }
- cimg::mutex(7,0);
- return s_path;
- }
- //! Get/set path to the GraphicsMagick's \c gm binary.
- /**
- \param user_path Specified path, or \c 0 to get the path currently used.
- \param reinit_path Force path to be recalculated (may take some time).
- \return Path containing the \c gm binary.
- **/
- inline const char* graphicsmagick_path(const char *const user_path, const bool reinit_path) {
- static CImg<char> s_path;
- cimg::mutex(7);
- if (reinit_path) s_path.assign();
- if (user_path) {
- if (!s_path) s_path.assign(1024);
- std::strncpy(s_path,user_path,1023);
- } else if (!s_path) {
- s_path.assign(1024);
- bool path_found = false;
- std::FILE *file = 0;
- #if cimg_OS==2
- if (win_searchpath("gm.exe",s_path,s_path._width)) path_found = true;
- const char *const pf_path = win_programfiles_path();
- if (!path_found) {
- std::strcpy(s_path,".\\gm.exe");
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 32; k>=10 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%.2d-\\gm.exe",pf_path,k);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 9; k>=0 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d-Q\\gm.exe",pf_path,k);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 32; k>=0 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d\\gm.exe",pf_path,k);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 32; k>=10 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",pf_path,k);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 9; k>=0 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",pf_path,k);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 32; k>=0 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",pf_path,k);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 32; k>=10 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%.2d-\\gm.exe",k);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 9; k>=0 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d-Q\\gm.exe",k);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 32; k>=0 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d\\gm.exe",k);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 32; k>=10 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 9; k>=0 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 32; k>=0 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 32; k>=10 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%.2d-\\gm.exe",k);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 9; k>=0 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d-Q\\gm.exe",k);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 32; k>=0 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d\\gm.exe",k);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 32; k>=10 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 9; k>=0 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 32; k>=0 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- if (!path_found) std::strcpy(s_path,"gm.exe");
- #else
- if (!path_found) {
- std::strcpy(s_path,"./gm");
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- if (!path_found) std::strcpy(s_path,"gm");
- #endif
- winformat_string(s_path);
- }
- cimg::mutex(7,0);
- return s_path;
- }
- //! Get/set path to the \c gunzip binary.
- /**
- \param user_path Specified path, or \c 0 to get the path currently used.
- \param reinit_path Force path to be recalculated (may take some time).
- \return Path containing the \c gunzip binary.
- **/
- inline const char *gunzip_path(const char *const user_path, const bool reinit_path) {
- static CImg<char> s_path;
- cimg::mutex(7);
- if (reinit_path) s_path.assign();
- if (user_path) {
- if (!s_path) s_path.assign(1024);
- std::strncpy(s_path,user_path,1023);
- } else if (!s_path) {
- s_path.assign(1024);
- bool path_found = false;
- std::FILE *file = 0;
- #if cimg_OS==2
- if (win_searchpath("gunzip.exe",s_path,s_path._width)) path_found = true;
- if (!path_found) {
- std::strcpy(s_path,".\\gunzip.exe");
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- if (!path_found) std::strcpy(s_path,"gunzip.exe");
- #else
- if (!path_found) {
- std::strcpy(s_path,"./gunzip");
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- if (!path_found) std::strcpy(s_path,"gunzip");
- #endif
- winformat_string(s_path);
- }
- cimg::mutex(7,0);
- return s_path;
- }
- //! Get/set path to the \c gzip binary.
- /**
- \param user_path Specified path, or \c 0 to get the path currently used.
- \param reinit_path Force path to be recalculated (may take some time).
- \return Path containing the \c gzip binary.
- **/
- inline const char *gzip_path(const char *const user_path, const bool reinit_path) {
- static CImg<char> s_path;
- cimg::mutex(7);
- if (reinit_path) s_path.assign();
- if (user_path) {
- if (!s_path) s_path.assign(1024);
- std::strncpy(s_path,user_path,1023);
- } else if (!s_path) {
- s_path.assign(1024);
- bool path_found = false;
- std::FILE *file = 0;
- #if cimg_OS==2
- if (win_searchpath("gzip.exe",s_path,s_path._width)) path_found = true;
- if (!path_found) {
- std::strcpy(s_path,".\\gzip.exe");
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- if (!path_found) std::strcpy(s_path,"gzip.exe");
- #else
- if (!path_found) {
- std::strcpy(s_path,"./gzip");
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- if (!path_found) std::strcpy(s_path,"gzip");
- #endif
- winformat_string(s_path);
- }
- cimg::mutex(7,0);
- return s_path;
- }
- //! Get/set path to the ImageMagick's \c convert binary.
- /**
- \param user_path Specified path, or \c 0 to get the path currently used.
- \param reinit_path Force path to be recalculated (may take some time).
- \return Path containing the \c convert binary.
- **/
- inline const char* imagemagick_path(const char *const user_path, const bool reinit_path) {
- static CImg<char> s_path;
- cimg::mutex(7);
- if (reinit_path) s_path.assign();
- if (user_path) {
- if (!s_path) s_path.assign(1024);
- std::strncpy(s_path,user_path,1023);
- } else if (!s_path) {
- s_path.assign(1024);
- bool path_found = false;
- std::FILE *file = 0;
- #if cimg_OS==2
- if (win_searchpath("magick.exe",s_path,s_path._width)) path_found = true;
- const char *const pf_path = win_programfiles_path();
- for (int l = 0; l<2 && !path_found; ++l) {
- const char *const s_exe = l?"convert":"magick";
- cimg_snprintf(s_path,s_path._width,".\\%s.exe",s_exe);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- for (int k = 32; k>=10 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\%s.exe",pf_path,k,s_exe);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 9; k>=0 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\%s.exe",pf_path,k,s_exe);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 32; k>=0 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\%s.exe",pf_path,k,s_exe);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 32; k>=10 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 9; k>=0 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 32; k>=0 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 32; k>=10 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 9; k>=0 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 32; k>=0 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\%s.exe",k,s_exe);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 32; k>=10 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 9; k>=0 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 32; k>=0 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 32; k>=10 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 9; k>=0 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 32; k>=0 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\%s.exe",k,s_exe);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 32; k>=10 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 9; k>=0 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- for (int k = 32; k>=0 && !path_found; --k) {
- cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- if (!path_found) cimg_snprintf(s_path,s_path._width,"%s.exe",s_exe);
- }
- #else
- std::strcpy(s_path,"./magick");
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- if (!path_found) {
- std::strcpy(s_path,"./convert");
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- if (!path_found) std::strcpy(s_path,"convert");
- #endif
- winformat_string(s_path);
- }
- cimg::mutex(7,0);
- return s_path;
- }
- //! Get/set path to the Medcon's \c medcon binary.
- /**
- \param user_path Specified path, or \c 0 to get the path currently used.
- \param reinit_path Force path to be recalculated (may take some time).
- \return Path containing the \c medcon binary.
- **/
- inline const char* medcon_path(const char *const user_path, const bool reinit_path) {
- static CImg<char> s_path;
- cimg::mutex(7);
- if (reinit_path) s_path.assign();
- if (user_path) {
- if (!s_path) s_path.assign(1024);
- std::strncpy(s_path,user_path,1023);
- } else if (!s_path) {
- s_path.assign(1024);
- bool path_found = false;
- std::FILE *file = 0;
- #if cimg_OS==2
- if (win_searchpath("medcon.exe",s_path,s_path._width)) path_found = true;
- const char *const pf_path = win_programfiles_path();
- if (!path_found) {
- std::strcpy(s_path,".\\medcon.exe");
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- if (!path_found) {
- cimg_snprintf(s_path,s_path._width,"%s\\XMedCon\\bin\\medcon.bat",pf_path);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- if (!path_found) {
- cimg_snprintf(s_path,s_path._width,"%s\\XMedCon\\bin\\medcon.exe",pf_path);
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- if (!path_found) {
- std::strcpy(s_path,"C:\\XMedCon\\bin\\medcon.exe");
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- if (!path_found) std::strcpy(s_path,"medcon.exe");
- #else
- if (!path_found) {
- std::strcpy(s_path,"./medcon");
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- if (!path_found) std::strcpy(s_path,"medcon");
- #endif
- winformat_string(s_path);
- }
- cimg::mutex(7,0);
- return s_path;
- }
- //! Get/set path to store temporary files.
- /**
- \param user_path Specified path, or \c 0 to get the path currently used.
- \param reinit_path Force path to be recalculated (may take some time).
- \return Path where temporary files can be saved.
- **/
- inline const char* temporary_path(const char *const user_path, const bool reinit_path) {
- #define _cimg_test_temporary_path(p) \
- if (!path_found) { \
- cimg_snprintf(s_path,s_path._width,"%s",p); \
- cimg_snprintf(tmp,tmp._width,"%s%c%s",s_path.data(),cimg_file_separator,filename_tmp._data); \
- if ((file=cimg::std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } \
- }
- static CImg<char> s_path;
- cimg::mutex(7);
- if (reinit_path) s_path.assign();
- if (user_path) {
- if (!s_path) s_path.assign(1024);
- std::strncpy(s_path,user_path,1023);
- } else if (!s_path) {
- s_path.assign(1024);
- bool path_found = false;
- CImg<char> tmp(1024), filename_tmp(256);
- std::FILE *file = 0;
- cimg_snprintf(filename_tmp,filename_tmp._width,"%s.tmp",cimg::filenamerand());
- char *tmpPath = std::getenv("TMP");
- if (!tmpPath) { tmpPath = std::getenv("TEMP"); winformat_string(tmpPath); }
- if (tmpPath) _cimg_test_temporary_path(tmpPath);
- #if cimg_OS==2
- _cimg_test_temporary_path("C:\\WINNT\\Temp");
- _cimg_test_temporary_path("C:\\WINDOWS\\Temp");
- _cimg_test_temporary_path("C:\\Temp");
- _cimg_test_temporary_path("C:");
- _cimg_test_temporary_path("D:\\WINNT\\Temp");
- _cimg_test_temporary_path("D:\\WINDOWS\\Temp");
- _cimg_test_temporary_path("D:\\Temp");
- _cimg_test_temporary_path("D:");
- #else
- _cimg_test_temporary_path("/tmp");
- _cimg_test_temporary_path("/var/tmp");
- #endif
- if (!path_found) {
- *s_path = 0;
- std::strncpy(tmp,filename_tmp,tmp._width - 1);
- if ((file=cimg::std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; }
- }
- if (!path_found) {
- cimg::mutex(7,0);
- throw CImgIOException("cimg::temporary_path(): Failed to locate path for writing temporary files.\n");
- }
- }
- cimg::mutex(7,0);
- return s_path;
- }
- //! Get/set path to the \c wget binary.
- /**
- \param user_path Specified path, or \c 0 to get the path currently used.
- \param reinit_path Force path to be recalculated (may take some time).
- \return Path containing the \c wget binary.
- **/
- inline const char *wget_path(const char *const user_path, const bool reinit_path) {
- static CImg<char> s_path;
- cimg::mutex(7);
- if (reinit_path) s_path.assign();
- if (user_path) {
- if (!s_path) s_path.assign(1024);
- std::strncpy(s_path,user_path,1023);
- } else if (!s_path) {
- s_path.assign(1024);
- bool path_found = false;
- std::FILE *file = 0;
- #if cimg_OS==2
- if (win_searchpath("wget.exe",s_path,s_path._width)) path_found = true;
- if (!path_found) {
- std::strcpy(s_path,".\\wget.exe");
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- if (!path_found) std::strcpy(s_path,"wget.exe");
- #else
- if (!path_found) {
- std::strcpy(s_path,"./wget");
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- if (!path_found) std::strcpy(s_path,"wget");
- #endif
- winformat_string(s_path);
- }
- cimg::mutex(7,0);
- return s_path;
- }
- #if cimg_OS==2
- //! Get/set path to the \c powershell binary.
- /**
- \param user_path Specified path, or \c 0 to get the path currently used.
- \param reinit_path Force path to be recalculated (may take some time).
- \return Path containing the \c wget binary.
- **/
- inline const char *powershell_path(const char *const user_path, const bool reinit_path) {
- static CImg<char> s_path;
- cimg::mutex(7);
- if (reinit_path) s_path.assign();
- if (user_path) {
- if (!s_path) s_path.assign(1024);
- std::strncpy(s_path,user_path,1023);
- } else if (!s_path) {
- s_path.assign(1024);
- bool path_found = false;
- std::FILE *file = 0;
- if (win_searchpath("powershell.exe",s_path,s_path._width)) path_found = true;
- if (!path_found) {
- std::strcpy(s_path,".\\powershell.exe");
- if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
- }
- if (!path_found) std::strcpy(s_path,"powershell.exe");
- winformat_string(s_path);
- }
- cimg::mutex(7,0);
- return s_path;
- }
- #endif
- // [internal] Sorting function, used by cimg::files().
- inline int _sort_files(const void* a, const void* b) {
- const CImg<char> &sa = *(CImg<char>*)a, &sb = *(CImg<char>*)b;
- return std::strcmp(sa._data,sb._data);
- }
- //! Generate a numbered version of a filename.
- inline char* number_filename(const char *const filename, const int number,
- const unsigned int digits, char *const str) {
- if (!filename) { if (str) *str = 0; return 0; }
- const unsigned int siz = (unsigned int)std::strlen(filename);
- CImg<char> format(16), body(siz + 32);
- const char *const ext = cimg::split_filename(filename,body);
- if (*ext) cimg_snprintf(format,format._width,"%%s_%%.%ud.%%s",digits);
- else cimg_snprintf(format,format._width,"%%s_%%.%ud",digits);
- cimg_snprintf(str,1024,format._data,body._data,number,ext);
- return str;
- }
- //! Return list of files/directories in specified directory.
- /**
- \param path Path to the directory. Set to 0 for current directory.
- \param is_pattern Tell if specified path has a matching pattern in it.
- \param mode Output type, can be primary { 0=files only | 1=folders only | 2=files + folders }.
- \param include_path Tell if \c path must be included in resulting filenames.
- \return A list of filenames.
- **/
- inline CImgList<char> files(const char *const path, const bool is_pattern=false,
- const unsigned int mode=2, const bool include_path=false) {
- if (!path || !*path) return files("*",true,mode,include_path);
- CImgList<char> res;
- // If path is a valid folder name, ignore argument 'is_pattern'.
- const bool _is_pattern = is_pattern && !cimg::is_directory(path);
- bool is_root = false, is_current = false;
- cimg::unused(is_root,is_current);
- // Clean format of input path.
- CImg<char> pattern, _path = CImg<char>::string(path);
- #if cimg_OS==2
- for (char *ps = _path; *ps; ++ps) if (*ps=='\\') *ps='/';
- #endif
- char *pd = _path;
- for (char *ps = pd; *ps; ++ps) { if (*ps!='/' || *ps!=*(ps+1)) *(pd++) = *ps; }
- *pd = 0;
- unsigned int lp = (unsigned int)std::strlen(_path);
- if (!_is_pattern && lp && _path[lp - 1]=='/') {
- _path[lp - 1] = 0; --lp;
- #if cimg_OS!=2
- is_root = !*_path;
- #endif
- }
- // Separate folder path and matching pattern.
- if (_is_pattern) {
- const unsigned int bpos = (unsigned int)(cimg::basename(_path,'/') - _path.data());
- CImg<char>::string(_path).move_to(pattern);
- if (bpos) {
- _path[bpos - 1] = 0; // End 'path' at last slash
- #if cimg_OS!=2
- is_root = !*_path;
- #endif
- } else { // No path to folder specified, assuming current folder
- is_current = true; *_path = 0;
- }
- lp = (unsigned int)std::strlen(_path);
- }
- // Windows version.
- #if cimg_OS==2
- if (!_is_pattern) {
- pattern.assign(lp + 3);
- std::memcpy(pattern,_path,lp);
- pattern[lp] = '/'; pattern[lp + 1] = '*'; pattern[lp + 2] = 0;
- }
- WIN32_FIND_DATAA file_data;
- const HANDLE dir = FindFirstFileA(pattern.data(),&file_data);
- if (dir==INVALID_HANDLE_VALUE) return CImgList<char>::const_empty();
- do {
- const char *const filename = file_data.cFileName;
- if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) {
- const unsigned int lf = (unsigned int)std::strlen(filename);
- const bool is_directory = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!=0;
- if ((!mode && !is_directory) || (mode==1 && is_directory) || mode>=2) {
- if (include_path) {
- CImg<char> full_filename((lp?lp+1:0) + lf + 1);
- if (lp) { std::memcpy(full_filename,_path,lp); full_filename[lp] = '/'; }
- std::memcpy(full_filename._data + (lp?lp + 1:0),filename,lf + 1);
- full_filename.move_to(res);
- } else CImg<char>(filename,lf + 1).move_to(res);
- }
- }
- } while (FindNextFileA(dir,&file_data));
- FindClose(dir);
- // Unix version (posix).
- #elif cimg_OS == 1
- DIR *const dir = opendir(is_root?"/":is_current?".":_path.data());
- if (!dir) return CImgList<char>::const_empty();
- struct dirent *ent;
- while ((ent=readdir(dir))!=0) {
- const char *const filename = ent->d_name;
- if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) {
- const unsigned int lf = (unsigned int)std::strlen(filename);
- CImg<char> full_filename(lp + lf + 2);
- if (!is_current) {
- full_filename.assign(lp + lf + 2);
- if (lp) std::memcpy(full_filename,_path,lp);
- full_filename[lp] = '/';
- std::memcpy(full_filename._data + lp + 1,filename,lf + 1);
- } else full_filename.assign(filename,lf + 1);
- struct stat st;
- if (stat(full_filename,&st)==-1) continue;
- const bool is_directory = (st.st_mode & S_IFDIR)!=0;
- if ((!mode && !is_directory) || (mode==1 && is_directory) || mode==2) {
- if (include_path) {
- if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0)))
- full_filename.move_to(res);
- } else {
- if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0)))
- CImg<char>(filename,lf + 1).move_to(res);
- }
- }
- }
- }
- closedir(dir);
- #endif
- // Sort resulting list by lexicographic order.
- if (res._width>=2) std::qsort(res._data,res._width,sizeof(CImg<char>),_sort_files);
- return res;
- }
- //! Try to guess format from an image file.
- /**
- \param file Input file (can be \c 0 if \c filename is set).
- \param filename Filename, as a C-string (can be \c 0 if \c file is set).
- \return C-string containing the guessed file format, or \c 0 if nothing has been guessed.
- **/
- inline const char *ftype(std::FILE *const file, const char *const filename) {
- if (!file && !filename)
- throw CImgArgumentException("cimg::ftype(): Specified filename is (null).");
- static const char
- *const _bmp = "bmp",
- *const _cr2 = "cr2",
- *const _dcm = "dcm",
- *const _gif = "gif",
- *const _inr = "inr",
- *const _jpg = "jpg",
- *const _off = "off",
- *const _pan = "pan",
- *const _pfm = "pfm",
- *const _png = "png",
- *const _pnm = "pnm",
- *const _tif = "tif";
- const char *f_type = 0;
- CImg<char> header;
- const unsigned int omode = cimg::exception_mode();
- cimg::exception_mode(0);
- try {
- header._load_raw(file,filename,512,1,1,1,false,false,0);
- const unsigned char *const uheader = (unsigned char*)header._data;
- if (!std::strncmp(header,"OFF\n",4)) f_type = _off; // OFF
- else if (!std::strncmp(header,"#INRIMAGE",9)) // INRIMAGE
- f_type = _inr;
- else if (!std::strncmp(header,"PANDORE",7)) // PANDORE
- f_type = _pan;
- else if (!std::strncmp(header.data() + 128,"DICM",4)) // DICOM
- f_type = _dcm;
- else if (uheader[0]==0xFF && uheader[1]==0xD8 && uheader[2]==0xFF) // JPEG
- f_type = _jpg;
- else if (header[0]=='B' && header[1]=='M') // BMP
- f_type = _bmp;
- else if (header[0]=='G' && header[1]=='I' && header[2]=='F' && header[3]=='8' && header[5]=='a' &&
- (header[4]=='7' || header[4]=='9')) // GIF
- f_type = _gif;
- else if (uheader[0]==0x89 && uheader[1]==0x50 && uheader[2]==0x4E && uheader[3]==0x47 &&
- uheader[4]==0x0D && uheader[5]==0x0A && uheader[6]==0x1A && uheader[7]==0x0A) // PNG
- f_type = _png;
- else if (uheader[0]==0x49 && uheader[1]==0x49 && uheader[2]==0x2A && uheader[3]==0x00 && // CR2
- uheader[4]==0x10 && uheader[5]==0x00 && uheader[6]==0x00 && uheader[7]==0x00 &&
- uheader[8]==0x43 && uheader[9]==0x52)
- f_type = _cr2;
- else if ((uheader[0]==0x49 && uheader[1]==0x49 && uheader[2]==0x2A && uheader[3]==0x00) ||
- (uheader[0]==0x4D && uheader[1]==0x4D && uheader[2]==0x00 && uheader[3]==0x2A)) // TIFF
- f_type = _tif;
- else { // PNM or PFM
- CImgList<char> _header = header.get_split(CImg<char>::vector('\n'),0,false);
- cimglist_for(_header,l) {
- if (_header(l,0)=='#') continue;
- if (_header[l]._width==2 && _header(l,0)=='P') {
- const char c = _header(l,1);
- if (c=='f' || c=='F') { f_type = _pfm; break; }
- if (c>='1' && c<='9') { f_type = _pnm; break; }
- }
- f_type = 0; break;
- }
- }
- } catch (CImgIOException&) { }
- cimg::exception_mode(omode);
- return f_type;
- }
- //! Load file from network as a local temporary file.
- /**
- \param url URL of the filename, as a C-string.
- \param[out] filename_local C-string containing the path to a local copy of \c filename.
- \param timeout Maximum time (in seconds) authorized for downloading the file from the URL.
- \param try_fallback When using libcurl, tells using system calls as fallbacks in case of libcurl failure.
- \param referer Referer used, as a C-string.
- \param user_agent User agent used, as a C-string.
- \return Value of \c filename_local.
- \note Use the \c libcurl library, or the external binaries \c wget or \c curl to perform the download.
- **/
- inline char *load_network(const char *const url, char *const filename_local,
- const unsigned int timeout, const bool try_fallback,
- const char *const referer, const char *const user_agent) {
- if (!url)
- throw CImgArgumentException("cimg::load_network(): Specified URL is (null).");
- if (!filename_local)
- throw CImgArgumentException("cimg::load_network(): Specified destination string is (null).");
- if (!network_mode())
- throw CImgIOException("cimg::load_network(): Loading files from network is disabled.");
- const char *const __ext = cimg::split_filename(url), *const _ext = (*__ext && __ext>url)?__ext - 1:__ext;
- CImg<char> ext = CImg<char>::string(_ext);
- std::FILE *file = 0;
- *filename_local = 0;
- if (ext._width>16 || !cimg::strncasecmp(ext,"cgi",3)) *ext = 0;
- else cimg::strwindows_reserved(ext);
- do {
- cimg_snprintf(filename_local,256,"%s%c%s%s",
- cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext._data);
- if ((file = cimg::std_fopen(filename_local,"rb"))!=0) cimg::fclose(file);
- } while (file);
- #ifdef cimg_use_curl
- const unsigned int omode = cimg::exception_mode();
- cimg::exception_mode(0);
- try {
- CURL *curl = 0;
- CURLcode res;
- curl = curl_easy_init();
- if (curl) {
- file = cimg::fopen(filename_local,"wb");
- curl_easy_setopt(curl,CURLOPT_URL,url);
- curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,0);
- curl_easy_setopt(curl,CURLOPT_WRITEDATA,file);
- curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,0L);
- curl_easy_setopt(curl,CURLOPT_SSL_VERIFYHOST,0L);
- curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1L);
- curl_easy_setopt(curl,CURLOPT_MAXREDIRS,20L);
- if (timeout) curl_easy_setopt(curl,CURLOPT_TIMEOUT,(long)timeout);
- if (std::strchr(url,'?')) curl_easy_setopt(curl,CURLOPT_HTTPGET,1L);
- if (referer) curl_easy_setopt(curl,CURLOPT_REFERER,referer);
- if (user_agent) curl_easy_setopt(curl,CURLOPT_USERAGENT,user_agent);
- res = curl_easy_perform(curl);
- curl_easy_cleanup(curl);
- cimg::fseek(file,0,SEEK_END); // Check if file size is 0
- const cimg_ulong siz = cimg::ftell(file);
- cimg::fclose(file);
- if (siz>0 && res==CURLE_OK) {
- cimg::exception_mode(omode);
- return filename_local;
- } else std::remove(filename_local);
- }
- } catch (...) { }
- cimg::exception_mode(omode);
- if (!try_fallback) throw CImgIOException("cimg::load_network(): Failed to load file '%s' with libcurl.",url);
- #endif
- CImg<char> command((unsigned int)std::strlen(url) + 1024), s_referer, s_user_agent, s_timeout;
- cimg::unused(try_fallback);
- // Try with 'curl' first.
- if (timeout) cimg_snprintf(s_timeout.assign(64),64,"-m %u ",timeout);
- else s_timeout.assign(1,1,1,1,0);
- if (referer) cimg_snprintf(s_referer.assign(1024),1024,"-e %s ",referer);
- else s_referer.assign(1,1,1,1,0);
- if (user_agent) cimg_snprintf(s_user_agent.assign(1024),1024,"-A \"%s\" ",user_agent);
- else s_user_agent.assign(1,1,1,1,0);
- cimg_snprintf(command,command._width,
- "\"%s\" -L --max-redirs 20 %s%s%s-f --silent --compressed -o \"%s\" \"%s\"",
- cimg::curl_path(),s_timeout._data,s_referer._data,s_user_agent._data,filename_local,
- CImg<char>::string(url)._system_strescape().data());
- cimg::system(command,cimg::curl_path());
- #if cimg_OS==2
- if (cimg::fsize(filename_local)<=0) { // Try with 'powershell' otherwise.
- if (timeout) cimg_snprintf(s_timeout.assign(64),64,"-TimeoutSec %u ",timeout);
- else s_timeout.assign(1,1,1,1,0);
- if (referer) cimg_snprintf(s_referer.assign(1024),1024,"-Headers @{'Referer'='%s'} ",referer);
- else s_referer.assign(1,1,1,1,0);
- if (user_agent) cimg_snprintf(s_user_agent.assign(1024),1024,"-UserAgent \"%s\" ",user_agent);
- else s_user_agent.assign(1,1,1,1,0);
- cimg_snprintf(command,command._width,
- "\"%s\" -NonInteractive -Command Invoke-WebRequest %s%s%s-OutFile \"%s\" -Uri \"%s\"",
- cimg::powershell_path(),s_timeout._data,s_referer._data,s_user_agent._data,filename_local,
- CImg<char>::string(url)._system_strescape().data());
- cimg::system(command,cimg::powershell_path());
- }
- #endif
- if (cimg::fsize(filename_local)<=0) { // Try with 'wget' otherwise.
- if (timeout) cimg_snprintf(s_timeout.assign(64),64,"-T %u ",timeout);
- else s_timeout.assign(1,1,1,1,0);
- if (referer) cimg_snprintf(s_referer.assign(1024),1024,"--referer=%s ",referer);
- else s_referer.assign(1,1,1,1,0);
- if (user_agent) cimg_snprintf(s_user_agent.assign(1024),1024,"--user-agent=\"%s\" ",user_agent);
- else s_user_agent.assign(1,1,1,1,0);
- cimg_snprintf(command,command._width,
- "\"%s\" --max-redirect=20 %s%s%s-q -r -l 0 --no-cache -O \"%s\" \"%s\"",
- cimg::wget_path(),s_timeout._data,s_referer._data,s_user_agent._data,filename_local,
- CImg<char>::string(url)._system_strescape().data());
- cimg::system(command,cimg::wget_path());
- if (cimg::fsize(filename_local)<=0)
- throw CImgIOException("cimg::load_network(): Failed to load file '%s' with external commands "
- #if cimg_OS==2
- "'wget', 'curl', or 'powershell'.",url);
- #else
- "'wget' or 'curl'.",url);
- #endif
- cimg::fclose(file);
- // Try gunzip it.
- cimg_snprintf(command,command._width,"%s.gz",filename_local);
- std::rename(filename_local,command);
- cimg_snprintf(command,command._width,"\"%s\" --quiet \"%s.gz\"",
- gunzip_path(),filename_local);
- cimg::system(command,gunzip_path());
- file = cimg::std_fopen(filename_local,"rb");
- if (!file) {
- cimg_snprintf(command,command._width,"%s.gz",filename_local);
- std::rename(command,filename_local);
- file = cimg::std_fopen(filename_local,"rb");
- }
- }
- if (file) cimg::fclose(file);
- return filename_local;
- }
- // Implement a tic/toc mechanism to display elapsed time of algorithms.
- inline cimg_uint64 tictoc(const bool is_tic) {
- cimg::mutex(2);
- static CImg<cimg_uint64> times(64);
- static unsigned int pos = 0;
- const cimg_uint64 t1 = cimg::time();
- if (is_tic) {
- // Tic
- times[pos++] = t1;
- if (pos>=times._width)
- throw CImgArgumentException("cimg::tic(): Too much calls to 'cimg::tic()' without calls to 'cimg::toc()'.");
- cimg::mutex(2,0);
- return t1;
- }
- // Toc
- if (!pos)
- throw CImgArgumentException("cimg::toc(): No previous call to 'cimg::tic()' has been made.");
- const cimg_uint64
- t0 = times[--pos],
- dt = t1>=t0?(t1 - t0):cimg::type<cimg_uint64>::max();
- const unsigned int
- edays = (unsigned int)(dt/86400000.),
- ehours = (unsigned int)((dt - edays*86400000.)/3600000.),
- emin = (unsigned int)((dt - edays*86400000. - ehours*3600000.)/60000.),
- esec = (unsigned int)((dt - edays*86400000. - ehours*3600000. - emin*60000.)/1000.),
- ems = (unsigned int)(dt - edays*86400000. - ehours*3600000. - emin*60000. - esec*1000.);
- if (!edays && !ehours && !emin && !esec)
- std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u ms%s\n",
- cimg::t_red,1 + 2*pos,"",ems,cimg::t_normal);
- else {
- if (!edays && !ehours && !emin)
- std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u sec %u ms%s\n",
- cimg::t_red,1 + 2*pos,"",esec,ems,cimg::t_normal);
- else {
- if (!edays && !ehours)
- std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u min %u sec %u ms%s\n",
- cimg::t_red,1 + 2*pos,"",emin,esec,ems,cimg::t_normal);
- else{
- if (!edays)
- std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u hours %u min %u sec %u ms%s\n",
- cimg::t_red,1 + 2*pos,"",ehours,emin,esec,ems,cimg::t_normal);
- else{
- std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u days %u hours %u min %u sec %u ms%s\n",
- cimg::t_red,1 + 2*pos,"",edays,ehours,emin,esec,ems,cimg::t_normal);
- }
- }
- }
- }
- cimg::mutex(2,0);
- return dt;
- }
- // Return a temporary string describing the size of a memory buffer.
- inline const char *strbuffersize(const cimg_ulong size) {
- static CImg<char> res(256);
- cimg::mutex(5);
- if (size<1024LU) cimg_snprintf(res,res._width,"%lu byte%s",(unsigned long)size,size>1?"s":"");
- else if (size<1024*1024LU) { const float nsize = size/1024.f; cimg_snprintf(res,res._width,"%.1f Kio",nsize); }
- else if (size<1024*1024*1024LU) {
- const float nsize = size/(1024*1024.f); cimg_snprintf(res,res._width,"%.1f Mio",nsize);
- } else { const float nsize = size/(1024*1024*1024.f); cimg_snprintf(res,res._width,"%.1f Gio",nsize); }
- cimg::mutex(5,0);
- return res;
- }
- //! Display a simple dialog box, and wait for the user's response.
- /**
- \param title Title of the dialog window.
- \param msg Main message displayed inside the dialog window.
- \param button1_label Label of the 1st button.
- \param button2_label Label of the 2nd button (\c 0 to hide button).
- \param button3_label Label of the 3rd button (\c 0 to hide button).
- \param button4_label Label of the 4th button (\c 0 to hide button).
- \param button5_label Label of the 5th button (\c 0 to hide button).
- \param button6_label Label of the 6th button (\c 0 to hide button).
- \param logo Image logo displayed at the left of the main message.
- \param is_centered Tells if the dialog window must be centered on the screen.
- \return Index of clicked button (from \c 0 to \c 5), or \c -1 if the dialog window has been closed by the user.
- \note
- - Up to 6 buttons can be defined in the dialog window.
- - The function returns when a user clicked one of the button or closed the dialog window.
- - If a button text is set to 0, the corresponding button (and the following) will not appear in the dialog box.
- At least one button must be specified.
- **/
- template<typename t>
- inline int dialog(const char *const title, const char *const msg,
- const char *const button1_label, const char *const button2_label,
- const char *const button3_label, const char *const button4_label,
- const char *const button5_label, const char *const button6_label,
- const CImg<t>& logo, const bool is_centered=false) {
- #if cimg_display==0
- cimg::unused(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,
- logo._data,is_centered);
- throw CImgIOException("cimg::dialog(): No display available.");
- #else
- static const unsigned char
- black[] = { 0,0,0 }, white[] = { 255,255,255 }, gray[] = { 200,200,200 }, gray2[] = { 150,150,150 };
- // Create buttons and canvas graphics
- CImgList<unsigned char> buttons, cbuttons, sbuttons;
- if (button1_label) {
- CImg<unsigned char>().draw_text(0,0,button1_label,black,gray,1,13).move_to(buttons);
- if (button2_label) {
- CImg<unsigned char>().draw_text(0,0,button2_label,black,gray,1,13).move_to(buttons);
- if (button3_label) {
- CImg<unsigned char>().draw_text(0,0,button3_label,black,gray,1,13).move_to(buttons);
- if (button4_label) {
- CImg<unsigned char>().draw_text(0,0,button4_label,black,gray,1,13).move_to(buttons);
- if (button5_label) {
- CImg<unsigned char>().draw_text(0,0,button5_label,black,gray,1,13).move_to(buttons);
- if (button6_label) {
- CImg<unsigned char>().draw_text(0,0,button6_label,black,gray,1,13).move_to(buttons);
- }}}}}}
- if (!buttons._width)
- throw CImgArgumentException("cimg::dialog(): No buttons have been defined.");
- cimglist_for(buttons,l) buttons[l].resize(-100,-100,1,3);
- unsigned int bw = 0, bh = 0;
- cimglist_for(buttons,l) { bw = std::max(bw,buttons[l]._width); bh = std::max(bh,buttons[l]._height); }
- bw+=8; bh+=8;
- if (bw<64) bw = 64;
- if (bw>128) bw = 128;
- if (bh<24) bh = 24;
- if (bh>48) bh = 48;
- CImg<unsigned char> button(bw,bh,1,3);
- button.draw_rectangle(0,0,bw - 1,bh - 1,gray);
- button.draw_line(0,0,bw - 1,0,white).draw_line(0,bh - 1,0,0,white);
- button.draw_line(bw - 1,0,bw - 1,bh - 1,black).draw_line(bw - 1,bh - 1,0,bh - 1,black);
- button.draw_line(1,bh - 2,bw - 2,bh - 2,gray2).draw_line(bw - 2,bh - 2,bw - 2,1,gray2);
- CImg<unsigned char> sbutton(bw,bh,1,3);
- sbutton.draw_rectangle(0,0,bw - 1,bh - 1,gray);
- sbutton.draw_line(0,0,bw - 1,0,black).draw_line(bw - 1,0,bw - 1,bh - 1,black);
- sbutton.draw_line(bw - 1,bh - 1,0,bh - 1,black).draw_line(0,bh - 1,0,0,black);
- sbutton.draw_line(1,1,bw - 2,1,white).draw_line(1,bh - 2,1,1,white);
- sbutton.draw_line(bw - 2,1,bw - 2,bh - 2,black).draw_line(bw - 2,bh - 2,1,bh - 2,black);
- sbutton.draw_line(2,bh - 3,bw - 3,bh - 3,gray2).draw_line(bw - 3,bh - 3,bw - 3,2,gray2);
- sbutton.draw_line(4,4,bw - 5,4,black,1,0xAAAAAAAA,true).
- draw_line(bw - 5,4,bw - 5,bh - 5,black,1,0xAAAAAAAA,false);
- sbutton.draw_line(bw - 5,bh - 5,4,bh - 5,black,1,0xAAAAAAAA,false).
- draw_line(4,bh - 5,4,4,black,1,0xAAAAAAAA,false);
- CImg<unsigned char> cbutton(bw,bh,1,3);
- cbutton.draw_rectangle(0,0,bw - 1,bh - 1,black).draw_rectangle(1,1,bw - 2,bh - 2,gray2).
- draw_rectangle(2,2,bw - 3,bh - 3,gray);
- cbutton.draw_line(4,4,bw - 5,4,black,1,0xAAAAAAAA,true).
- draw_line(bw - 5,4,bw - 5,bh - 5,black,1,0xAAAAAAAA,false);
- cbutton.draw_line(bw - 5,bh - 5,4,bh - 5,black,1,0xAAAAAAAA,false).
- draw_line(4,bh - 5,4,4,black,1,0xAAAAAAAA,false);
- cimglist_for(buttons,ll) {
- CImg<unsigned char>(cbutton).
- draw_image(1 + (bw -buttons[ll].width())/2,1 + (bh - buttons[ll].height())/2,buttons[ll]).
- move_to(cbuttons);
- CImg<unsigned char>(sbutton).
- draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]).
- move_to(sbuttons);
- CImg<unsigned char>(button).
- draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]).
- move_to(buttons[ll]);
- }
- CImg<unsigned char> canvas;
- if (msg)
- ((CImg<unsigned char>().draw_text(0,0,"%s",gray,0,1,13,msg)*=-1)+=200).resize(-100,-100,1,3).move_to(canvas);
- const unsigned int
- bwall = (buttons._width - 1)*(12 + bw) + bw,
- w = cimg::max(196U,36 + logo._width + canvas._width,24 + bwall),
- h = cimg::max(96U,36 + canvas._height + bh,36 + logo._height + bh),
- lx = 12 + (canvas._data?0:((w - 24 - logo._width)/2)),
- ly = (h - 12 - bh - logo._height)/2,
- tx = lx + logo._width + 12,
- ty = (h - 12 - bh - canvas._height)/2,
- bx = (w - bwall)/2,
- by = h - 12 - bh;
- if (canvas._data)
- canvas = CImg<unsigned char>(w,h,1,3).
- draw_rectangle(0,0,w - 1,h - 1,gray).
- draw_line(0,0,w - 1,0,white).draw_line(0,h - 1,0,0,white).
- draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black).
- draw_image(tx,ty,canvas);
- else
- canvas = CImg<unsigned char>(w,h,1,3).
- draw_rectangle(0,0,w - 1,h - 1,gray).
- draw_line(0,0,w - 1,0,white).draw_line(0,h - 1,0,0,white).
- draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black);
- if (logo._data) canvas.draw_image(lx,ly,logo);
- unsigned int xbuttons[6] = {};
- cimglist_for(buttons,lll) {
- xbuttons[lll] = bx + (bw + 12)*lll;
- canvas.draw_image(xbuttons[lll],by,buttons[lll]);
- }
- // Open window and enter events loop
- CImgDisplay disp(canvas,title?title:" ",0,false,is_centered?true:false);
- if (is_centered) disp.move((CImgDisplay::screen_width() - disp.width())/2,
- (CImgDisplay::screen_height() - disp.height())/2);
- bool stop_flag = false, refresh = false;
- int oselected = -1, oclicked = -1, selected = -1, clicked = -1;
- while (!disp.is_closed() && !stop_flag) {
- if (refresh) {
- if (clicked>=0)
- CImg<unsigned char>(canvas).draw_image(xbuttons[clicked],by,cbuttons[clicked]).display(disp);
- else {
- if (selected>=0)
- CImg<unsigned char>(canvas).draw_image(xbuttons[selected],by,sbuttons[selected]).display(disp);
- else canvas.display(disp);
- }
- refresh = false;
- }
- disp.wait(15);
- if (disp.is_resized()) disp.resize(disp,false);
- if (disp.button()&1) {
- oclicked = clicked;
- clicked = -1;
- cimglist_for(buttons,l)
- if (disp.mouse_y()>=(int)by && disp.mouse_y()<(int)(by + bh) &&
- disp.mouse_x()>=(int)xbuttons[l] && disp.mouse_x()<(int)(xbuttons[l] + bw)) {
- clicked = selected = l;
- refresh = true;
- }
- if (clicked!=oclicked) refresh = true;
- } else if (clicked>=0) stop_flag = true;
- if (disp.key()) {
- oselected = selected;
- switch (disp.key()) {
- case cimg::keyESC : selected = -1; stop_flag = true; break;
- case cimg::keyENTER : if (selected<0) selected = 0; stop_flag = true; break;
- case cimg::keyTAB :
- case cimg::keyARROWRIGHT :
- case cimg::keyARROWDOWN : selected = (selected + 1)%buttons.width(); break;
- case cimg::keyARROWLEFT :
- case cimg::keyARROWUP : selected = (selected + buttons.width() - 1)%buttons.width(); break;
- }
- disp.set_key();
- if (selected!=oselected) refresh = true;
- }
- }
- if (!disp) selected = -1;
- return selected;
- #endif
- }
- //! Display a simple dialog box, and wait for the user's response \specialization.
- inline int dialog(const char *const title, const char *const msg,
- const char *const button1_label, const char *const button2_label,
- const char *const button3_label, const char *const button4_label,
- const char *const button5_label, const char *const button6_label,
- const bool is_centered) {
- return dialog(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,
- CImg<unsigned char>::_logo40x38(),is_centered);
- }
- //! Evaluate math expression.
- /**
- \param expression C-string describing the formula to evaluate.
- \param x Value of the pre-defined variable \c x.
- \param y Value of the pre-defined variable \c y.
- \param z Value of the pre-defined variable \c z.
- \param c Value of the pre-defined variable \c c.
- \return Result of the formula evaluation.
- \note Set \c expression to \c 0 to keep evaluating the last specified \c expression.
- \par Example
- \code
- const double
- res1 = cimg::eval("cos(x)^2 + sin(y)^2",2,2), // will return '1'
- res2 = cimg::eval(0,1,1); // will return '1' too
- \endcode
- **/
- inline double eval(const char *const expression, const double x, const double y, const double z, const double c) {
- static const CImg<float> empty;
- return empty.eval(expression,x,y,z,c);
- }
- template<typename t>
- inline CImg<typename cimg::superset<double,t>::type> eval(const char *const expression, const CImg<t>& xyzc) {
- static const CImg<float> empty;
- return empty.eval(expression,xyzc);
- }
- } // namespace cimg { ...
- } // namespace cimg_library { ...
- //! Short alias name.
- namespace cil = cimg_library;
- #ifdef _cimg_redefine_False
- #define False 0
- #endif
- #ifdef _cimg_redefine_True
- #define True 1
- #endif
- #ifdef _cimg_redefine_Status
- #define Status int
- #endif
- #ifdef _cimg_redefine_Success
- #define Success 0
- #endif
- #ifdef _cimg_redefine_min
- #define min(a,b) (((a)<(b))?(a):(b))
- #endif
- #ifdef _cimg_redefine_max
- #define max(a,b) (((a)>(b))?(a):(b))
- #endif
- #ifdef _cimg_redefine_PI
- #define PI 3.141592653589793238462643383
- #endif
- #ifdef _MSC_VER
- #pragma warning(pop)
- #endif
- #endif
- // Local Variables:
- // mode: c++
- // End:
|