CImg.h 3.2 MB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975149761497714978149791498014981149821498314984149851498614987149881498914990149911499214993149941499514996149971499814999150001500115002150031500415005150061500715008150091501015011150121501315014150151501615017150181501915020150211502215023150241502515026150271502815029150301503115032150331503415035150361503715038150391504015041150421504315044150451504615047150481504915050150511505215053150541505515056150571505815059150601506115062150631506415065150661506715068150691507015071150721507315074150751507615077150781507915080150811508215083150841508515086150871508815089150901509115092150931509415095150961509715098150991510015101151021510315104151051510615107151081510915110151111511215113151141511515116151171511815119151201512115122151231512415125151261512715128151291513015131151321513315134151351513615137151381513915140151411514215143151441514515146151471514815149151501515115152151531515415155151561515715158151591516015161151621516315164151651516615167151681516915170151711517215173151741517515176151771517815179151801518115182151831518415185151861518715188151891519015191151921519315194151951519615197151981519915200152011520215203152041520515206152071520815209152101521115212152131521415215152161521715218152191522015221152221522315224152251522615227152281522915230152311523215233152341523515236152371523815239152401524115242152431524415245152461524715248152491525015251152521525315254152551525615257152581525915260152611526215263152641526515266152671526815269152701527115272152731527415275152761527715278152791528015281152821528315284152851528615287152881528915290152911529215293152941529515296152971529815299153001530115302153031530415305153061530715308153091531015311153121531315314153151531615317153181531915320153211532215323153241532515326153271532815329153301533115332153331533415335153361533715338153391534015341153421534315344153451534615347153481534915350153511535215353153541535515356153571535815359153601536115362153631536415365153661536715368153691537015371153721537315374153751537615377153781537915380153811538215383153841538515386153871538815389153901539115392153931539415395153961539715398153991540015401154021540315404154051540615407154081540915410154111541215413154141541515416154171541815419154201542115422154231542415425154261542715428154291543015431154321543315434154351543615437154381543915440154411544215443154441544515446154471544815449154501545115452154531545415455154561545715458154591546015461154621546315464154651546615467154681546915470154711547215473154741547515476154771547815479154801548115482154831548415485154861548715488154891549015491154921549315494154951549615497154981549915500155011550215503155041550515506155071550815509155101551115512155131551415515155161551715518155191552015521155221552315524155251552615527155281552915530155311553215533155341553515536155371553815539155401554115542155431554415545155461554715548155491555015551155521555315554155551555615557155581555915560155611556215563155641556515566155671556815569155701557115572155731557415575155761557715578155791558015581155821558315584155851558615587155881558915590155911559215593155941559515596155971559815599156001560115602156031560415605156061560715608156091561015611156121561315614156151561615617156181561915620156211562215623156241562515626156271562815629156301563115632156331563415635156361563715638156391564015641156421564315644156451564615647156481564915650156511565215653156541565515656156571565815659156601566115662156631566415665156661566715668156691567015671156721567315674156751567615677156781567915680156811568215683156841568515686156871568815689156901569115692156931569415695156961569715698156991570015701157021570315704157051570615707157081570915710157111571215713157141571515716157171571815719157201572115722157231572415725157261572715728157291573015731157321573315734157351573615737157381573915740157411574215743157441574515746157471574815749157501575115752157531575415755157561575715758157591576015761157621576315764157651576615767157681576915770157711577215773157741577515776157771577815779157801578115782157831578415785157861578715788157891579015791157921579315794157951579615797157981579915800158011580215803158041580515806158071580815809158101581115812158131581415815158161581715818158191582015821158221582315824158251582615827158281582915830158311583215833158341583515836158371583815839158401584115842158431584415845158461584715848158491585015851158521585315854158551585615857158581585915860158611586215863158641586515866158671586815869158701587115872158731587415875158761587715878158791588015881158821588315884158851588615887158881588915890158911589215893158941589515896158971589815899159001590115902159031590415905159061590715908159091591015911159121591315914159151591615917159181591915920159211592215923159241592515926159271592815929159301593115932159331593415935159361593715938159391594015941159421594315944159451594615947159481594915950159511595215953159541595515956159571595815959159601596115962159631596415965159661596715968159691597015971159721597315974159751597615977159781597915980159811598215983159841598515986159871598815989159901599115992159931599415995159961599715998159991600016001160021600316004160051600616007160081600916010160111601216013160141601516016160171601816019160201602116022160231602416025160261602716028160291603016031160321603316034160351603616037160381603916040160411604216043160441604516046160471604816049160501605116052160531605416055160561605716058160591606016061160621606316064160651606616067160681606916070160711607216073160741607516076160771607816079160801608116082160831608416085160861608716088160891609016091160921609316094160951609616097160981609916100161011610216103161041610516106161071610816109161101611116112161131611416115161161611716118161191612016121161221612316124161251612616127161281612916130161311613216133161341613516136161371613816139161401614116142161431614416145161461614716148161491615016151161521615316154161551615616157161581615916160161611616216163161641616516166161671616816169161701617116172161731617416175161761617716178161791618016181161821618316184161851618616187161881618916190161911619216193161941619516196161971619816199162001620116202162031620416205162061620716208162091621016211162121621316214162151621616217162181621916220162211622216223162241622516226162271622816229162301623116232162331623416235162361623716238162391624016241162421624316244162451624616247162481624916250162511625216253162541625516256162571625816259162601626116262162631626416265162661626716268162691627016271162721627316274162751627616277162781627916280162811628216283162841628516286162871628816289162901629116292162931629416295162961629716298162991630016301163021630316304163051630616307163081630916310163111631216313163141631516316163171631816319163201632116322163231632416325163261632716328163291633016331163321633316334163351633616337163381633916340163411634216343163441634516346163471634816349163501635116352163531635416355163561635716358163591636016361163621636316364163651636616367163681636916370163711637216373163741637516376163771637816379163801638116382163831638416385163861638716388163891639016391163921639316394163951639616397163981639916400164011640216403164041640516406164071640816409164101641116412164131641416415164161641716418164191642016421164221642316424164251642616427164281642916430164311643216433164341643516436164371643816439164401644116442164431644416445164461644716448164491645016451164521645316454164551645616457164581645916460164611646216463164641646516466164671646816469164701647116472164731647416475164761647716478164791648016481164821648316484164851648616487164881648916490164911649216493164941649516496164971649816499165001650116502165031650416505165061650716508165091651016511165121651316514165151651616517165181651916520165211652216523165241652516526165271652816529165301653116532165331653416535165361653716538165391654016541165421654316544165451654616547165481654916550165511655216553165541655516556165571655816559165601656116562165631656416565165661656716568165691657016571165721657316574165751657616577165781657916580165811658216583165841658516586165871658816589165901659116592165931659416595165961659716598165991660016601166021660316604166051660616607166081660916610166111661216613166141661516616166171661816619166201662116622166231662416625166261662716628166291663016631166321663316634166351663616637166381663916640166411664216643166441664516646166471664816649166501665116652166531665416655166561665716658166591666016661166621666316664166651666616667166681666916670166711667216673166741667516676166771667816679166801668116682166831668416685166861668716688166891669016691166921669316694166951669616697166981669916700167011670216703167041670516706167071670816709167101671116712167131671416715167161671716718167191672016721167221672316724167251672616727167281672916730167311673216733167341673516736167371673816739167401674116742167431674416745167461674716748167491675016751167521675316754167551675616757167581675916760167611676216763167641676516766167671676816769167701677116772167731677416775167761677716778167791678016781167821678316784167851678616787167881678916790167911679216793167941679516796167971679816799168001680116802168031680416805168061680716808168091681016811168121681316814168151681616817168181681916820168211682216823168241682516826168271682816829168301683116832168331683416835168361683716838168391684016841168421684316844168451684616847168481684916850168511685216853168541685516856168571685816859168601686116862168631686416865168661686716868168691687016871168721687316874168751687616877168781687916880168811688216883168841688516886168871688816889168901689116892168931689416895168961689716898168991690016901169021690316904169051690616907169081690916910169111691216913169141691516916169171691816919169201692116922169231692416925169261692716928169291693016931169321693316934169351693616937169381693916940169411694216943169441694516946169471694816949169501695116952169531695416955169561695716958169591696016961169621696316964169651696616967169681696916970169711697216973169741697516976169771697816979169801698116982169831698416985169861698716988169891699016991169921699316994169951699616997169981699917000170011700217003170041700517006170071700817009170101701117012170131701417015170161701717018170191702017021170221702317024170251702617027170281702917030170311703217033170341703517036170371703817039170401704117042170431704417045170461704717048170491705017051170521705317054170551705617057170581705917060170611706217063170641706517066170671706817069170701707117072170731707417075170761707717078170791708017081170821708317084170851708617087170881708917090170911709217093170941709517096170971709817099171001710117102171031710417105171061710717108171091711017111171121711317114171151711617117171181711917120171211712217123171241712517126171271712817129171301713117132171331713417135171361713717138171391714017141171421714317144171451714617147171481714917150171511715217153171541715517156171571715817159171601716117162171631716417165171661716717168171691717017171171721717317174171751717617177171781717917180171811718217183171841718517186171871718817189171901719117192171931719417195171961719717198171991720017201172021720317204172051720617207172081720917210172111721217213172141721517216172171721817219172201722117222172231722417225172261722717228172291723017231172321723317234172351723617237172381723917240172411724217243172441724517246172471724817249172501725117252172531725417255172561725717258172591726017261172621726317264172651726617267172681726917270172711727217273172741727517276172771727817279172801728117282172831728417285172861728717288172891729017291172921729317294172951729617297172981729917300173011730217303173041730517306173071730817309173101731117312173131731417315173161731717318173191732017321173221732317324173251732617327173281732917330173311733217333173341733517336173371733817339173401734117342173431734417345173461734717348173491735017351173521735317354173551735617357173581735917360173611736217363173641736517366173671736817369173701737117372173731737417375173761737717378173791738017381173821738317384173851738617387173881738917390173911739217393173941739517396173971739817399174001740117402174031740417405174061740717408174091741017411174121741317414174151741617417174181741917420174211742217423174241742517426174271742817429174301743117432174331743417435174361743717438174391744017441174421744317444174451744617447174481744917450174511745217453174541745517456174571745817459174601746117462174631746417465174661746717468174691747017471174721747317474174751747617477174781747917480174811748217483174841748517486174871748817489174901749117492174931749417495174961749717498174991750017501175021750317504175051750617507175081750917510175111751217513175141751517516175171751817519175201752117522175231752417525175261752717528175291753017531175321753317534175351753617537175381753917540175411754217543175441754517546175471754817549175501755117552175531755417555175561755717558175591756017561175621756317564175651756617567175681756917570175711757217573175741757517576175771757817579175801758117582175831758417585175861758717588175891759017591175921759317594175951759617597175981759917600176011760217603176041760517606176071760817609176101761117612176131761417615176161761717618176191762017621176221762317624176251762617627176281762917630176311763217633176341763517636176371763817639176401764117642176431764417645176461764717648176491765017651176521765317654176551765617657176581765917660176611766217663176641766517666176671766817669176701767117672176731767417675176761767717678176791768017681176821768317684176851768617687176881768917690176911769217693176941769517696176971769817699177001770117702177031770417705177061770717708177091771017711177121771317714177151771617717177181771917720177211772217723177241772517726177271772817729177301773117732177331773417735177361773717738177391774017741177421774317744177451774617747177481774917750177511775217753177541775517756177571775817759177601776117762177631776417765177661776717768177691777017771177721777317774177751777617777177781777917780177811778217783177841778517786177871778817789177901779117792177931779417795177961779717798177991780017801178021780317804178051780617807178081780917810178111781217813178141781517816178171781817819178201782117822178231782417825178261782717828178291783017831178321783317834178351783617837178381783917840178411784217843178441784517846178471784817849178501785117852178531785417855178561785717858178591786017861178621786317864178651786617867178681786917870178711787217873178741787517876178771787817879178801788117882178831788417885178861788717888178891789017891178921789317894178951789617897178981789917900179011790217903179041790517906179071790817909179101791117912179131791417915179161791717918179191792017921179221792317924179251792617927179281792917930179311793217933179341793517936179371793817939179401794117942179431794417945179461794717948179491795017951179521795317954179551795617957179581795917960179611796217963179641796517966179671796817969179701797117972179731797417975179761797717978179791798017981179821798317984179851798617987179881798917990179911799217993179941799517996179971799817999180001800118002180031800418005180061800718008180091801018011180121801318014180151801618017180181801918020180211802218023180241802518026180271802818029180301803118032180331803418035180361803718038180391804018041180421804318044180451804618047180481804918050180511805218053180541805518056180571805818059180601806118062180631806418065180661806718068180691807018071180721807318074180751807618077180781807918080180811808218083180841808518086180871808818089180901809118092180931809418095180961809718098180991810018101181021810318104181051810618107181081810918110181111811218113181141811518116181171811818119181201812118122181231812418125181261812718128181291813018131181321813318134181351813618137181381813918140181411814218143181441814518146181471814818149181501815118152181531815418155181561815718158181591816018161181621816318164181651816618167181681816918170181711817218173181741817518176181771817818179181801818118182181831818418185181861818718188181891819018191181921819318194181951819618197181981819918200182011820218203182041820518206182071820818209182101821118212182131821418215182161821718218182191822018221182221822318224182251822618227182281822918230182311823218233182341823518236182371823818239182401824118242182431824418245182461824718248182491825018251182521825318254182551825618257182581825918260182611826218263182641826518266182671826818269182701827118272182731827418275182761827718278182791828018281182821828318284182851828618287182881828918290182911829218293182941829518296182971829818299183001830118302183031830418305183061830718308183091831018311183121831318314183151831618317183181831918320183211832218323183241832518326183271832818329183301833118332183331833418335183361833718338183391834018341183421834318344183451834618347183481834918350183511835218353183541835518356183571835818359183601836118362183631836418365183661836718368183691837018371183721837318374183751837618377183781837918380183811838218383183841838518386183871838818389183901839118392183931839418395183961839718398183991840018401184021840318404184051840618407184081840918410184111841218413184141841518416184171841818419184201842118422184231842418425184261842718428184291843018431184321843318434184351843618437184381843918440184411844218443184441844518446184471844818449184501845118452184531845418455184561845718458184591846018461184621846318464184651846618467184681846918470184711847218473184741847518476184771847818479184801848118482184831848418485184861848718488184891849018491184921849318494184951849618497184981849918500185011850218503185041850518506185071850818509185101851118512185131851418515185161851718518185191852018521185221852318524185251852618527185281852918530185311853218533185341853518536185371853818539185401854118542185431854418545185461854718548185491855018551185521855318554185551855618557185581855918560185611856218563185641856518566185671856818569185701857118572185731857418575185761857718578185791858018581185821858318584185851858618587185881858918590185911859218593185941859518596185971859818599186001860118602186031860418605186061860718608186091861018611186121861318614186151861618617186181861918620186211862218623186241862518626186271862818629186301863118632186331863418635186361863718638186391864018641186421864318644186451864618647186481864918650186511865218653186541865518656186571865818659186601866118662186631866418665186661866718668186691867018671186721867318674186751867618677186781867918680186811868218683186841868518686186871868818689186901869118692186931869418695186961869718698186991870018701187021870318704187051870618707187081870918710187111871218713187141871518716187171871818719187201872118722187231872418725187261872718728187291873018731187321873318734187351873618737187381873918740187411874218743187441874518746187471874818749187501875118752187531875418755187561875718758187591876018761187621876318764187651876618767187681876918770187711877218773187741877518776187771877818779187801878118782187831878418785187861878718788187891879018791187921879318794187951879618797187981879918800188011880218803188041880518806188071880818809188101881118812188131881418815188161881718818188191882018821188221882318824188251882618827188281882918830188311883218833188341883518836188371883818839188401884118842188431884418845188461884718848188491885018851188521885318854188551885618857188581885918860188611886218863188641886518866188671886818869188701887118872188731887418875188761887718878188791888018881188821888318884188851888618887188881888918890188911889218893188941889518896188971889818899189001890118902189031890418905189061890718908189091891018911189121891318914189151891618917189181891918920189211892218923189241892518926189271892818929189301893118932189331893418935189361893718938189391894018941189421894318944189451894618947189481894918950189511895218953189541895518956189571895818959189601896118962189631896418965189661896718968189691897018971189721897318974189751897618977189781897918980189811898218983189841898518986189871898818989189901899118992189931899418995189961899718998189991900019001190021900319004190051900619007190081900919010190111901219013190141901519016190171901819019190201902119022190231902419025190261902719028190291903019031190321903319034190351903619037190381903919040190411904219043190441904519046190471904819049190501905119052190531905419055190561905719058190591906019061190621906319064190651906619067190681906919070190711907219073190741907519076190771907819079190801908119082190831908419085190861908719088190891909019091190921909319094190951909619097190981909919100191011910219103191041910519106191071910819109191101911119112191131911419115191161911719118191191912019121191221912319124191251912619127191281912919130191311913219133191341913519136191371913819139191401914119142191431914419145191461914719148191491915019151191521915319154191551915619157191581915919160191611916219163191641916519166191671916819169191701917119172191731917419175191761917719178191791918019181191821918319184191851918619187191881918919190191911919219193191941919519196191971919819199192001920119202192031920419205192061920719208192091921019211192121921319214192151921619217192181921919220192211922219223192241922519226192271922819229192301923119232192331923419235192361923719238192391924019241192421924319244192451924619247192481924919250192511925219253192541925519256192571925819259192601926119262192631926419265192661926719268192691927019271192721927319274192751927619277192781927919280192811928219283192841928519286192871928819289192901929119292192931929419295192961929719298192991930019301193021930319304193051930619307193081930919310193111931219313193141931519316193171931819319193201932119322193231932419325193261932719328193291933019331193321933319334193351933619337193381933919340193411934219343193441934519346193471934819349193501935119352193531935419355193561935719358193591936019361193621936319364193651936619367193681936919370193711937219373193741937519376193771937819379193801938119382193831938419385193861938719388193891939019391193921939319394193951939619397193981939919400194011940219403194041940519406194071940819409194101941119412194131941419415194161941719418194191942019421194221942319424194251942619427194281942919430194311943219433194341943519436194371943819439194401944119442194431944419445194461944719448194491945019451194521945319454194551945619457194581945919460194611946219463194641946519466194671946819469194701947119472194731947419475194761947719478194791948019481194821948319484194851948619487194881948919490194911949219493194941949519496194971949819499195001950119502195031950419505195061950719508195091951019511195121951319514195151951619517195181951919520195211952219523195241952519526195271952819529195301953119532195331953419535195361953719538195391954019541195421954319544195451954619547195481954919550195511955219553195541955519556195571955819559195601956119562195631956419565195661956719568195691957019571195721957319574195751957619577195781957919580195811958219583195841958519586195871958819589195901959119592195931959419595195961959719598195991960019601196021960319604196051960619607196081960919610196111961219613196141961519616196171961819619196201962119622196231962419625196261962719628196291963019631196321963319634196351963619637196381963919640196411964219643196441964519646196471964819649196501965119652196531965419655196561965719658196591966019661196621966319664196651966619667196681966919670196711967219673196741967519676196771967819679196801968119682196831968419685196861968719688196891969019691196921969319694196951969619697196981969919700197011970219703197041970519706197071970819709197101971119712197131971419715197161971719718197191972019721197221972319724197251972619727197281972919730197311973219733197341973519736197371973819739197401974119742197431974419745197461974719748197491975019751197521975319754197551975619757197581975919760197611976219763197641976519766197671976819769197701977119772197731977419775197761977719778197791978019781197821978319784197851978619787197881978919790197911979219793197941979519796197971979819799198001980119802198031980419805198061980719808198091981019811198121981319814198151981619817198181981919820198211982219823198241982519826198271982819829198301983119832198331983419835198361983719838198391984019841198421984319844198451984619847198481984919850198511985219853198541985519856198571985819859198601986119862198631986419865198661986719868198691987019871198721987319874198751987619877198781987919880198811988219883198841988519886198871988819889198901989119892198931989419895198961989719898198991990019901199021990319904199051990619907199081990919910199111991219913199141991519916199171991819919199201992119922199231992419925199261992719928199291993019931199321993319934199351993619937199381993919940199411994219943199441994519946199471994819949199501995119952199531995419955199561995719958199591996019961199621996319964199651996619967199681996919970199711997219973199741997519976199771997819979199801998119982199831998419985199861998719988199891999019991199921999319994199951999619997199981999920000200012000220003200042000520006200072000820009200102001120012200132001420015200162001720018200192002020021200222002320024200252002620027200282002920030200312003220033200342003520036200372003820039200402004120042200432004420045200462004720048200492005020051200522005320054200552005620057200582005920060200612006220063200642006520066200672006820069200702007120072200732007420075200762007720078200792008020081200822008320084200852008620087200882008920090200912009220093200942009520096200972009820099201002010120102201032010420105201062010720108201092011020111201122011320114201152011620117201182011920120201212012220123201242012520126201272012820129201302013120132201332013420135201362013720138201392014020141201422014320144201452014620147201482014920150201512015220153201542015520156201572015820159201602016120162201632016420165201662016720168201692017020171201722017320174201752017620177201782017920180201812018220183201842018520186201872018820189201902019120192201932019420195201962019720198201992020020201202022020320204202052020620207202082020920210202112021220213202142021520216202172021820219202202022120222202232022420225202262022720228202292023020231202322023320234202352023620237202382023920240202412024220243202442024520246202472024820249202502025120252202532025420255202562025720258202592026020261202622026320264202652026620267202682026920270202712027220273202742027520276202772027820279202802028120282202832028420285202862028720288202892029020291202922029320294202952029620297202982029920300203012030220303203042030520306203072030820309203102031120312203132031420315203162031720318203192032020321203222032320324203252032620327203282032920330203312033220333203342033520336203372033820339203402034120342203432034420345203462034720348203492035020351203522035320354203552035620357203582035920360203612036220363203642036520366203672036820369203702037120372203732037420375203762037720378203792038020381203822038320384203852038620387203882038920390203912039220393203942039520396203972039820399204002040120402204032040420405204062040720408204092041020411204122041320414204152041620417204182041920420204212042220423204242042520426204272042820429204302043120432204332043420435204362043720438204392044020441204422044320444204452044620447204482044920450204512045220453204542045520456204572045820459204602046120462204632046420465204662046720468204692047020471204722047320474204752047620477204782047920480204812048220483204842048520486204872048820489204902049120492204932049420495204962049720498204992050020501205022050320504205052050620507205082050920510205112051220513205142051520516205172051820519205202052120522205232052420525205262052720528205292053020531205322053320534205352053620537205382053920540205412054220543205442054520546205472054820549205502055120552205532055420555205562055720558205592056020561205622056320564205652056620567205682056920570205712057220573205742057520576205772057820579205802058120582205832058420585205862058720588205892059020591205922059320594205952059620597205982059920600206012060220603206042060520606206072060820609206102061120612206132061420615206162061720618206192062020621206222062320624206252062620627206282062920630206312063220633206342063520636206372063820639206402064120642206432064420645206462064720648206492065020651206522065320654206552065620657206582065920660206612066220663206642066520666206672066820669206702067120672206732067420675206762067720678206792068020681206822068320684206852068620687206882068920690206912069220693206942069520696206972069820699207002070120702207032070420705207062070720708207092071020711207122071320714207152071620717207182071920720207212072220723207242072520726207272072820729207302073120732207332073420735207362073720738207392074020741207422074320744207452074620747207482074920750207512075220753207542075520756207572075820759207602076120762207632076420765207662076720768207692077020771207722077320774207752077620777207782077920780207812078220783207842078520786207872078820789207902079120792207932079420795207962079720798207992080020801208022080320804208052080620807208082080920810208112081220813208142081520816208172081820819208202082120822208232082420825208262082720828208292083020831208322083320834208352083620837208382083920840208412084220843208442084520846208472084820849208502085120852208532085420855208562085720858208592086020861208622086320864208652086620867208682086920870208712087220873208742087520876208772087820879208802088120882208832088420885208862088720888208892089020891208922089320894208952089620897208982089920900209012090220903209042090520906209072090820909209102091120912209132091420915209162091720918209192092020921209222092320924209252092620927209282092920930209312093220933209342093520936209372093820939209402094120942209432094420945209462094720948209492095020951209522095320954209552095620957209582095920960209612096220963209642096520966209672096820969209702097120972209732097420975209762097720978209792098020981209822098320984209852098620987209882098920990209912099220993209942099520996209972099820999210002100121002210032100421005210062100721008210092101021011210122101321014210152101621017210182101921020210212102221023210242102521026210272102821029210302103121032210332103421035210362103721038210392104021041210422104321044210452104621047210482104921050210512105221053210542105521056210572105821059210602106121062210632106421065210662106721068210692107021071210722107321074210752107621077210782107921080210812108221083210842108521086210872108821089210902109121092210932109421095210962109721098210992110021101211022110321104211052110621107211082110921110211112111221113211142111521116211172111821119211202112121122211232112421125211262112721128211292113021131211322113321134211352113621137211382113921140211412114221143211442114521146211472114821149211502115121152211532115421155211562115721158211592116021161211622116321164211652116621167211682116921170211712117221173211742117521176211772117821179211802118121182211832118421185211862118721188211892119021191211922119321194211952119621197211982119921200212012120221203212042120521206212072120821209212102121121212212132121421215212162121721218212192122021221212222122321224212252122621227212282122921230212312123221233212342123521236212372123821239212402124121242212432124421245212462124721248212492125021251212522125321254212552125621257212582125921260212612126221263212642126521266212672126821269212702127121272212732127421275212762127721278212792128021281212822128321284212852128621287212882128921290212912129221293212942129521296212972129821299213002130121302213032130421305213062130721308213092131021311213122131321314213152131621317213182131921320213212132221323213242132521326213272132821329213302133121332213332133421335213362133721338213392134021341213422134321344213452134621347213482134921350213512135221353213542135521356213572135821359213602136121362213632136421365213662136721368213692137021371213722137321374213752137621377213782137921380213812138221383213842138521386213872138821389213902139121392213932139421395213962139721398213992140021401214022140321404214052140621407214082140921410214112141221413214142141521416214172141821419214202142121422214232142421425214262142721428214292143021431214322143321434214352143621437214382143921440214412144221443214442144521446214472144821449214502145121452214532145421455214562145721458214592146021461214622146321464214652146621467214682146921470214712147221473214742147521476214772147821479214802148121482214832148421485214862148721488214892149021491214922149321494214952149621497214982149921500215012150221503215042150521506215072150821509215102151121512215132151421515215162151721518215192152021521215222152321524215252152621527215282152921530215312153221533215342153521536215372153821539215402154121542215432154421545215462154721548215492155021551215522155321554215552155621557215582155921560215612156221563215642156521566215672156821569215702157121572215732157421575215762157721578215792158021581215822158321584215852158621587215882158921590215912159221593215942159521596215972159821599216002160121602216032160421605216062160721608216092161021611216122161321614216152161621617216182161921620216212162221623216242162521626216272162821629216302163121632216332163421635216362163721638216392164021641216422164321644216452164621647216482164921650216512165221653216542165521656216572165821659216602166121662216632166421665216662166721668216692167021671216722167321674216752167621677216782167921680216812168221683216842168521686216872168821689216902169121692216932169421695216962169721698216992170021701217022170321704217052170621707217082170921710217112171221713217142171521716217172171821719217202172121722217232172421725217262172721728217292173021731217322173321734217352173621737217382173921740217412174221743217442174521746217472174821749217502175121752217532175421755217562175721758217592176021761217622176321764217652176621767217682176921770217712177221773217742177521776217772177821779217802178121782217832178421785217862178721788217892179021791217922179321794217952179621797217982179921800218012180221803218042180521806218072180821809218102181121812218132181421815218162181721818218192182021821218222182321824218252182621827218282182921830218312183221833218342183521836218372183821839218402184121842218432184421845218462184721848218492185021851218522185321854218552185621857218582185921860218612186221863218642186521866218672186821869218702187121872218732187421875218762187721878218792188021881218822188321884218852188621887218882188921890218912189221893218942189521896218972189821899219002190121902219032190421905219062190721908219092191021911219122191321914219152191621917219182191921920219212192221923219242192521926219272192821929219302193121932219332193421935219362193721938219392194021941219422194321944219452194621947219482194921950219512195221953219542195521956219572195821959219602196121962219632196421965219662196721968219692197021971219722197321974219752197621977219782197921980219812198221983219842198521986219872198821989219902199121992219932199421995219962199721998219992200022001220022200322004220052200622007220082200922010220112201222013220142201522016220172201822019220202202122022220232202422025220262202722028220292203022031220322203322034220352203622037220382203922040220412204222043220442204522046220472204822049220502205122052220532205422055220562205722058220592206022061220622206322064220652206622067220682206922070220712207222073220742207522076220772207822079220802208122082220832208422085220862208722088220892209022091220922209322094220952209622097220982209922100221012210222103221042210522106221072210822109221102211122112221132211422115221162211722118221192212022121221222212322124221252212622127221282212922130221312213222133221342213522136221372213822139221402214122142221432214422145221462214722148221492215022151221522215322154221552215622157221582215922160221612216222163221642216522166221672216822169221702217122172221732217422175221762217722178221792218022181221822218322184221852218622187221882218922190221912219222193221942219522196221972219822199222002220122202222032220422205222062220722208222092221022211222122221322214222152221622217222182221922220222212222222223222242222522226222272222822229222302223122232222332223422235222362223722238222392224022241222422224322244222452224622247222482224922250222512225222253222542225522256222572225822259222602226122262222632226422265222662226722268222692227022271222722227322274222752227622277222782227922280222812228222283222842228522286222872228822289222902229122292222932229422295222962229722298222992230022301223022230322304223052230622307223082230922310223112231222313223142231522316223172231822319223202232122322223232232422325223262232722328223292233022331223322233322334223352233622337223382233922340223412234222343223442234522346223472234822349223502235122352223532235422355223562235722358223592236022361223622236322364223652236622367223682236922370223712237222373223742237522376223772237822379223802238122382223832238422385223862238722388223892239022391223922239322394223952239622397223982239922400224012240222403224042240522406224072240822409224102241122412224132241422415224162241722418224192242022421224222242322424224252242622427224282242922430224312243222433224342243522436224372243822439224402244122442224432244422445224462244722448224492245022451224522245322454224552245622457224582245922460224612246222463224642246522466224672246822469224702247122472224732247422475224762247722478224792248022481224822248322484224852248622487224882248922490224912249222493224942249522496224972249822499225002250122502225032250422505225062250722508225092251022511225122251322514225152251622517225182251922520225212252222523225242252522526225272252822529225302253122532225332253422535225362253722538225392254022541225422254322544225452254622547225482254922550225512255222553225542255522556225572255822559225602256122562225632256422565225662256722568225692257022571225722257322574225752257622577225782257922580225812258222583225842258522586225872258822589225902259122592225932259422595225962259722598225992260022601226022260322604226052260622607226082260922610226112261222613226142261522616226172261822619226202262122622226232262422625226262262722628226292263022631226322263322634226352263622637226382263922640226412264222643226442264522646226472264822649226502265122652226532265422655226562265722658226592266022661226622266322664226652266622667226682266922670226712267222673226742267522676226772267822679226802268122682226832268422685226862268722688226892269022691226922269322694226952269622697226982269922700227012270222703227042270522706227072270822709227102271122712227132271422715227162271722718227192272022721227222272322724227252272622727227282272922730227312273222733227342273522736227372273822739227402274122742227432274422745227462274722748227492275022751227522275322754227552275622757227582275922760227612276222763227642276522766227672276822769227702277122772227732277422775227762277722778227792278022781227822278322784227852278622787227882278922790227912279222793227942279522796227972279822799228002280122802228032280422805228062280722808228092281022811228122281322814228152281622817228182281922820228212282222823228242282522826228272282822829228302283122832228332283422835228362283722838228392284022841228422284322844228452284622847228482284922850228512285222853228542285522856228572285822859228602286122862228632286422865228662286722868228692287022871228722287322874228752287622877228782287922880228812288222883228842288522886228872288822889228902289122892228932289422895228962289722898228992290022901229022290322904229052290622907229082290922910229112291222913229142291522916229172291822919229202292122922229232292422925229262292722928229292293022931229322293322934229352293622937229382293922940229412294222943229442294522946229472294822949229502295122952229532295422955229562295722958229592296022961229622296322964229652296622967229682296922970229712297222973229742297522976229772297822979229802298122982229832298422985229862298722988229892299022991229922299322994229952299622997229982299923000230012300223003230042300523006230072300823009230102301123012230132301423015230162301723018230192302023021230222302323024230252302623027230282302923030230312303223033230342303523036230372303823039230402304123042230432304423045230462304723048230492305023051230522305323054230552305623057230582305923060230612306223063230642306523066230672306823069230702307123072230732307423075230762307723078230792308023081230822308323084230852308623087230882308923090230912309223093230942309523096230972309823099231002310123102231032310423105231062310723108231092311023111231122311323114231152311623117231182311923120231212312223123231242312523126231272312823129231302313123132231332313423135231362313723138231392314023141231422314323144231452314623147231482314923150231512315223153231542315523156231572315823159231602316123162231632316423165231662316723168231692317023171231722317323174231752317623177231782317923180231812318223183231842318523186231872318823189231902319123192231932319423195231962319723198231992320023201232022320323204232052320623207232082320923210232112321223213232142321523216232172321823219232202322123222232232322423225232262322723228232292323023231232322323323234232352323623237232382323923240232412324223243232442324523246232472324823249232502325123252232532325423255232562325723258232592326023261232622326323264232652326623267232682326923270232712327223273232742327523276232772327823279232802328123282232832328423285232862328723288232892329023291232922329323294232952329623297232982329923300233012330223303233042330523306233072330823309233102331123312233132331423315233162331723318233192332023321233222332323324233252332623327233282332923330233312333223333233342333523336233372333823339233402334123342233432334423345233462334723348233492335023351233522335323354233552335623357233582335923360233612336223363233642336523366233672336823369233702337123372233732337423375233762337723378233792338023381233822338323384233852338623387233882338923390233912339223393233942339523396233972339823399234002340123402234032340423405234062340723408234092341023411234122341323414234152341623417234182341923420234212342223423234242342523426234272342823429234302343123432234332343423435234362343723438234392344023441234422344323444234452344623447234482344923450234512345223453234542345523456234572345823459234602346123462234632346423465234662346723468234692347023471234722347323474234752347623477234782347923480234812348223483234842348523486234872348823489234902349123492234932349423495234962349723498234992350023501235022350323504235052350623507235082350923510235112351223513235142351523516235172351823519235202352123522235232352423525235262352723528235292353023531235322353323534235352353623537235382353923540235412354223543235442354523546235472354823549235502355123552235532355423555235562355723558235592356023561235622356323564235652356623567235682356923570235712357223573235742357523576235772357823579235802358123582235832358423585235862358723588235892359023591235922359323594235952359623597235982359923600236012360223603236042360523606236072360823609236102361123612236132361423615236162361723618236192362023621236222362323624236252362623627236282362923630236312363223633236342363523636236372363823639236402364123642236432364423645236462364723648236492365023651236522365323654236552365623657236582365923660236612366223663236642366523666236672366823669236702367123672236732367423675236762367723678236792368023681236822368323684236852368623687236882368923690236912369223693236942369523696236972369823699237002370123702237032370423705237062370723708237092371023711237122371323714237152371623717237182371923720237212372223723237242372523726237272372823729237302373123732237332373423735237362373723738237392374023741237422374323744237452374623747237482374923750237512375223753237542375523756237572375823759237602376123762237632376423765237662376723768237692377023771237722377323774237752377623777237782377923780237812378223783237842378523786237872378823789237902379123792237932379423795237962379723798237992380023801238022380323804238052380623807238082380923810238112381223813238142381523816238172381823819238202382123822238232382423825238262382723828238292383023831238322383323834238352383623837238382383923840238412384223843238442384523846238472384823849238502385123852238532385423855238562385723858238592386023861238622386323864238652386623867238682386923870238712387223873238742387523876238772387823879238802388123882238832388423885238862388723888238892389023891238922389323894238952389623897238982389923900239012390223903239042390523906239072390823909239102391123912239132391423915239162391723918239192392023921239222392323924239252392623927239282392923930239312393223933239342393523936239372393823939239402394123942239432394423945239462394723948239492395023951239522395323954239552395623957239582395923960239612396223963239642396523966239672396823969239702397123972239732397423975239762397723978239792398023981239822398323984239852398623987239882398923990239912399223993239942399523996239972399823999240002400124002240032400424005240062400724008240092401024011240122401324014240152401624017240182401924020240212402224023240242402524026240272402824029240302403124032240332403424035240362403724038240392404024041240422404324044240452404624047240482404924050240512405224053240542405524056240572405824059240602406124062240632406424065240662406724068240692407024071240722407324074240752407624077240782407924080240812408224083240842408524086240872408824089240902409124092240932409424095240962409724098240992410024101241022410324104241052410624107241082410924110241112411224113241142411524116241172411824119241202412124122241232412424125241262412724128241292413024131241322413324134241352413624137241382413924140241412414224143241442414524146241472414824149241502415124152241532415424155241562415724158241592416024161241622416324164241652416624167241682416924170241712417224173241742417524176241772417824179241802418124182241832418424185241862418724188241892419024191241922419324194241952419624197241982419924200242012420224203242042420524206242072420824209242102421124212242132421424215242162421724218242192422024221242222422324224242252422624227242282422924230242312423224233242342423524236242372423824239242402424124242242432424424245242462424724248242492425024251242522425324254242552425624257242582425924260242612426224263242642426524266242672426824269242702427124272242732427424275242762427724278242792428024281242822428324284242852428624287242882428924290242912429224293242942429524296242972429824299243002430124302243032430424305243062430724308243092431024311243122431324314243152431624317243182431924320243212432224323243242432524326243272432824329243302433124332243332433424335243362433724338243392434024341243422434324344243452434624347243482434924350243512435224353243542435524356243572435824359243602436124362243632436424365243662436724368243692437024371243722437324374243752437624377243782437924380243812438224383243842438524386243872438824389243902439124392243932439424395243962439724398243992440024401244022440324404244052440624407244082440924410244112441224413244142441524416244172441824419244202442124422244232442424425244262442724428244292443024431244322443324434244352443624437244382443924440244412444224443244442444524446244472444824449244502445124452244532445424455244562445724458244592446024461244622446324464244652446624467244682446924470244712447224473244742447524476244772447824479244802448124482244832448424485244862448724488244892449024491244922449324494244952449624497244982449924500245012450224503245042450524506245072450824509245102451124512245132451424515245162451724518245192452024521245222452324524245252452624527245282452924530245312453224533245342453524536245372453824539245402454124542245432454424545245462454724548245492455024551245522455324554245552455624557245582455924560245612456224563245642456524566245672456824569245702457124572245732457424575245762457724578245792458024581245822458324584245852458624587245882458924590245912459224593245942459524596245972459824599246002460124602246032460424605246062460724608246092461024611246122461324614246152461624617246182461924620246212462224623246242462524626246272462824629246302463124632246332463424635246362463724638246392464024641246422464324644246452464624647246482464924650246512465224653246542465524656246572465824659246602466124662246632466424665246662466724668246692467024671246722467324674246752467624677246782467924680246812468224683246842468524686246872468824689246902469124692246932469424695246962469724698246992470024701247022470324704247052470624707247082470924710247112471224713247142471524716247172471824719247202472124722247232472424725247262472724728247292473024731247322473324734247352473624737247382473924740247412474224743247442474524746247472474824749247502475124752247532475424755247562475724758247592476024761247622476324764247652476624767247682476924770247712477224773247742477524776247772477824779247802478124782247832478424785247862478724788247892479024791247922479324794247952479624797247982479924800248012480224803248042480524806248072480824809248102481124812248132481424815248162481724818248192482024821248222482324824248252482624827248282482924830248312483224833248342483524836248372483824839248402484124842248432484424845248462484724848248492485024851248522485324854248552485624857248582485924860248612486224863248642486524866248672486824869248702487124872248732487424875248762487724878248792488024881248822488324884248852488624887248882488924890248912489224893248942489524896248972489824899249002490124902249032490424905249062490724908249092491024911249122491324914249152491624917249182491924920249212492224923249242492524926249272492824929249302493124932249332493424935249362493724938249392494024941249422494324944249452494624947249482494924950249512495224953249542495524956249572495824959249602496124962249632496424965249662496724968249692497024971249722497324974249752497624977249782497924980249812498224983249842498524986249872498824989249902499124992249932499424995249962499724998249992500025001250022500325004250052500625007250082500925010250112501225013250142501525016250172501825019250202502125022250232502425025250262502725028250292503025031250322503325034250352503625037250382503925040250412504225043250442504525046250472504825049250502505125052250532505425055250562505725058250592506025061250622506325064250652506625067250682506925070250712507225073250742507525076250772507825079250802508125082250832508425085250862508725088250892509025091250922509325094250952509625097250982509925100251012510225103251042510525106251072510825109251102511125112251132511425115251162511725118251192512025121251222512325124251252512625127251282512925130251312513225133251342513525136251372513825139251402514125142251432514425145251462514725148251492515025151251522515325154251552515625157251582515925160251612516225163251642516525166251672516825169251702517125172251732517425175251762517725178251792518025181251822518325184251852518625187251882518925190251912519225193251942519525196251972519825199252002520125202252032520425205252062520725208252092521025211252122521325214252152521625217252182521925220252212522225223252242522525226252272522825229252302523125232252332523425235252362523725238252392524025241252422524325244252452524625247252482524925250252512525225253252542525525256252572525825259252602526125262252632526425265252662526725268252692527025271252722527325274252752527625277252782527925280252812528225283252842528525286252872528825289252902529125292252932529425295252962529725298252992530025301253022530325304253052530625307253082530925310253112531225313253142531525316253172531825319253202532125322253232532425325253262532725328253292533025331253322533325334253352533625337253382533925340253412534225343253442534525346253472534825349253502535125352253532535425355253562535725358253592536025361253622536325364253652536625367253682536925370253712537225373253742537525376253772537825379253802538125382253832538425385253862538725388253892539025391253922539325394253952539625397253982539925400254012540225403254042540525406254072540825409254102541125412254132541425415254162541725418254192542025421254222542325424254252542625427254282542925430254312543225433254342543525436254372543825439254402544125442254432544425445254462544725448254492545025451254522545325454254552545625457254582545925460254612546225463254642546525466254672546825469254702547125472254732547425475254762547725478254792548025481254822548325484254852548625487254882548925490254912549225493254942549525496254972549825499255002550125502255032550425505255062550725508255092551025511255122551325514255152551625517255182551925520255212552225523255242552525526255272552825529255302553125532255332553425535255362553725538255392554025541255422554325544255452554625547255482554925550255512555225553255542555525556255572555825559255602556125562255632556425565255662556725568255692557025571255722557325574255752557625577255782557925580255812558225583255842558525586255872558825589255902559125592255932559425595255962559725598255992560025601256022560325604256052560625607256082560925610256112561225613256142561525616256172561825619256202562125622256232562425625256262562725628256292563025631256322563325634256352563625637256382563925640256412564225643256442564525646256472564825649256502565125652256532565425655256562565725658256592566025661256622566325664256652566625667256682566925670256712567225673256742567525676256772567825679256802568125682256832568425685256862568725688256892569025691256922569325694256952569625697256982569925700257012570225703257042570525706257072570825709257102571125712257132571425715257162571725718257192572025721257222572325724257252572625727257282572925730257312573225733257342573525736257372573825739257402574125742257432574425745257462574725748257492575025751257522575325754257552575625757257582575925760257612576225763257642576525766257672576825769257702577125772257732577425775257762577725778257792578025781257822578325784257852578625787257882578925790257912579225793257942579525796257972579825799258002580125802258032580425805258062580725808258092581025811258122581325814258152581625817258182581925820258212582225823258242582525826258272582825829258302583125832258332583425835258362583725838258392584025841258422584325844258452584625847258482584925850258512585225853258542585525856258572585825859258602586125862258632586425865258662586725868258692587025871258722587325874258752587625877258782587925880258812588225883258842588525886258872588825889258902589125892258932589425895258962589725898258992590025901259022590325904259052590625907259082590925910259112591225913259142591525916259172591825919259202592125922259232592425925259262592725928259292593025931259322593325934259352593625937259382593925940259412594225943259442594525946259472594825949259502595125952259532595425955259562595725958259592596025961259622596325964259652596625967259682596925970259712597225973259742597525976259772597825979259802598125982259832598425985259862598725988259892599025991259922599325994259952599625997259982599926000260012600226003260042600526006260072600826009260102601126012260132601426015260162601726018260192602026021260222602326024260252602626027260282602926030260312603226033260342603526036260372603826039260402604126042260432604426045260462604726048260492605026051260522605326054260552605626057260582605926060260612606226063260642606526066260672606826069260702607126072260732607426075260762607726078260792608026081260822608326084260852608626087260882608926090260912609226093260942609526096260972609826099261002610126102261032610426105261062610726108261092611026111261122611326114261152611626117261182611926120261212612226123261242612526126261272612826129261302613126132261332613426135261362613726138261392614026141261422614326144261452614626147261482614926150261512615226153261542615526156261572615826159261602616126162261632616426165261662616726168261692617026171261722617326174261752617626177261782617926180261812618226183261842618526186261872618826189261902619126192261932619426195261962619726198261992620026201262022620326204262052620626207262082620926210262112621226213262142621526216262172621826219262202622126222262232622426225262262622726228262292623026231262322623326234262352623626237262382623926240262412624226243262442624526246262472624826249262502625126252262532625426255262562625726258262592626026261262622626326264262652626626267262682626926270262712627226273262742627526276262772627826279262802628126282262832628426285262862628726288262892629026291262922629326294262952629626297262982629926300263012630226303263042630526306263072630826309263102631126312263132631426315263162631726318263192632026321263222632326324263252632626327263282632926330263312633226333263342633526336263372633826339263402634126342263432634426345263462634726348263492635026351263522635326354263552635626357263582635926360263612636226363263642636526366263672636826369263702637126372263732637426375263762637726378263792638026381263822638326384263852638626387263882638926390263912639226393263942639526396263972639826399264002640126402264032640426405264062640726408264092641026411264122641326414264152641626417264182641926420264212642226423264242642526426264272642826429264302643126432264332643426435264362643726438264392644026441264422644326444264452644626447264482644926450264512645226453264542645526456264572645826459264602646126462264632646426465264662646726468264692647026471264722647326474264752647626477264782647926480264812648226483264842648526486264872648826489264902649126492264932649426495264962649726498264992650026501265022650326504265052650626507265082650926510265112651226513265142651526516265172651826519265202652126522265232652426525265262652726528265292653026531265322653326534265352653626537265382653926540265412654226543265442654526546265472654826549265502655126552265532655426555265562655726558265592656026561265622656326564265652656626567265682656926570265712657226573265742657526576265772657826579265802658126582265832658426585265862658726588265892659026591265922659326594265952659626597265982659926600266012660226603266042660526606266072660826609266102661126612266132661426615266162661726618266192662026621266222662326624266252662626627266282662926630266312663226633266342663526636266372663826639266402664126642266432664426645266462664726648266492665026651266522665326654266552665626657266582665926660266612666226663266642666526666266672666826669266702667126672266732667426675266762667726678266792668026681266822668326684266852668626687266882668926690266912669226693266942669526696266972669826699267002670126702267032670426705267062670726708267092671026711267122671326714267152671626717267182671926720267212672226723267242672526726267272672826729267302673126732267332673426735267362673726738267392674026741267422674326744267452674626747267482674926750267512675226753267542675526756267572675826759267602676126762267632676426765267662676726768267692677026771267722677326774267752677626777267782677926780267812678226783267842678526786267872678826789267902679126792267932679426795267962679726798267992680026801268022680326804268052680626807268082680926810268112681226813268142681526816268172681826819268202682126822268232682426825268262682726828268292683026831268322683326834268352683626837268382683926840268412684226843268442684526846268472684826849268502685126852268532685426855268562685726858268592686026861268622686326864268652686626867268682686926870268712687226873268742687526876268772687826879268802688126882268832688426885268862688726888268892689026891268922689326894268952689626897268982689926900269012690226903269042690526906269072690826909269102691126912269132691426915269162691726918269192692026921269222692326924269252692626927269282692926930269312693226933269342693526936269372693826939269402694126942269432694426945269462694726948269492695026951269522695326954269552695626957269582695926960269612696226963269642696526966269672696826969269702697126972269732697426975269762697726978269792698026981269822698326984269852698626987269882698926990269912699226993269942699526996269972699826999270002700127002270032700427005270062700727008270092701027011270122701327014270152701627017270182701927020270212702227023270242702527026270272702827029270302703127032270332703427035270362703727038270392704027041270422704327044270452704627047270482704927050270512705227053270542705527056270572705827059270602706127062270632706427065270662706727068270692707027071270722707327074270752707627077270782707927080270812708227083270842708527086270872708827089270902709127092270932709427095270962709727098270992710027101271022710327104271052710627107271082710927110271112711227113271142711527116271172711827119271202712127122271232712427125271262712727128271292713027131271322713327134271352713627137271382713927140271412714227143271442714527146271472714827149271502715127152271532715427155271562715727158271592716027161271622716327164271652716627167271682716927170271712717227173271742717527176271772717827179271802718127182271832718427185271862718727188271892719027191271922719327194271952719627197271982719927200272012720227203272042720527206272072720827209272102721127212272132721427215272162721727218272192722027221272222722327224272252722627227272282722927230272312723227233272342723527236272372723827239272402724127242272432724427245272462724727248272492725027251272522725327254272552725627257272582725927260272612726227263272642726527266272672726827269272702727127272272732727427275272762727727278272792728027281272822728327284272852728627287272882728927290272912729227293272942729527296272972729827299273002730127302273032730427305273062730727308273092731027311273122731327314273152731627317273182731927320273212732227323273242732527326273272732827329273302733127332273332733427335273362733727338273392734027341273422734327344273452734627347273482734927350273512735227353273542735527356273572735827359273602736127362273632736427365273662736727368273692737027371273722737327374273752737627377273782737927380273812738227383273842738527386273872738827389273902739127392273932739427395273962739727398273992740027401274022740327404274052740627407274082740927410274112741227413274142741527416274172741827419274202742127422274232742427425274262742727428274292743027431274322743327434274352743627437274382743927440274412744227443274442744527446274472744827449274502745127452274532745427455274562745727458274592746027461274622746327464274652746627467274682746927470274712747227473274742747527476274772747827479274802748127482274832748427485274862748727488274892749027491274922749327494274952749627497274982749927500275012750227503275042750527506275072750827509275102751127512275132751427515275162751727518275192752027521275222752327524275252752627527275282752927530275312753227533275342753527536275372753827539275402754127542275432754427545275462754727548275492755027551275522755327554275552755627557275582755927560275612756227563275642756527566275672756827569275702757127572275732757427575275762757727578275792758027581275822758327584275852758627587275882758927590275912759227593275942759527596275972759827599276002760127602276032760427605276062760727608276092761027611276122761327614276152761627617276182761927620276212762227623276242762527626276272762827629276302763127632276332763427635276362763727638276392764027641276422764327644276452764627647276482764927650276512765227653276542765527656276572765827659276602766127662276632766427665276662766727668276692767027671276722767327674276752767627677276782767927680276812768227683276842768527686276872768827689276902769127692276932769427695276962769727698276992770027701277022770327704277052770627707277082770927710277112771227713277142771527716277172771827719277202772127722277232772427725277262772727728277292773027731277322773327734277352773627737277382773927740277412774227743277442774527746277472774827749277502775127752277532775427755277562775727758277592776027761277622776327764277652776627767277682776927770277712777227773277742777527776277772777827779277802778127782277832778427785277862778727788277892779027791277922779327794277952779627797277982779927800278012780227803278042780527806278072780827809278102781127812278132781427815278162781727818278192782027821278222782327824278252782627827278282782927830278312783227833278342783527836278372783827839278402784127842278432784427845278462784727848278492785027851278522785327854278552785627857278582785927860278612786227863278642786527866278672786827869278702787127872278732787427875278762787727878278792788027881278822788327884278852788627887278882788927890278912789227893278942789527896278972789827899279002790127902279032790427905279062790727908279092791027911279122791327914279152791627917279182791927920279212792227923279242792527926279272792827929279302793127932279332793427935279362793727938279392794027941279422794327944279452794627947279482794927950279512795227953279542795527956279572795827959279602796127962279632796427965279662796727968279692797027971279722797327974279752797627977279782797927980279812798227983279842798527986279872798827989279902799127992279932799427995279962799727998279992800028001280022800328004280052800628007280082800928010280112801228013280142801528016280172801828019280202802128022280232802428025280262802728028280292803028031280322803328034280352803628037280382803928040280412804228043280442804528046280472804828049280502805128052280532805428055280562805728058280592806028061280622806328064280652806628067280682806928070280712807228073280742807528076280772807828079280802808128082280832808428085280862808728088280892809028091280922809328094280952809628097280982809928100281012810228103281042810528106281072810828109281102811128112281132811428115281162811728118281192812028121281222812328124281252812628127281282812928130281312813228133281342813528136281372813828139281402814128142281432814428145281462814728148281492815028151281522815328154281552815628157281582815928160281612816228163281642816528166281672816828169281702817128172281732817428175281762817728178281792818028181281822818328184281852818628187281882818928190281912819228193281942819528196281972819828199282002820128202282032820428205282062820728208282092821028211282122821328214282152821628217282182821928220282212822228223282242822528226282272822828229282302823128232282332823428235282362823728238282392824028241282422824328244282452824628247282482824928250282512825228253282542825528256282572825828259282602826128262282632826428265282662826728268282692827028271282722827328274282752827628277282782827928280282812828228283282842828528286282872828828289282902829128292282932829428295282962829728298282992830028301283022830328304283052830628307283082830928310283112831228313283142831528316283172831828319283202832128322283232832428325283262832728328283292833028331283322833328334283352833628337283382833928340283412834228343283442834528346283472834828349283502835128352283532835428355283562835728358283592836028361283622836328364283652836628367283682836928370283712837228373283742837528376283772837828379283802838128382283832838428385283862838728388283892839028391283922839328394283952839628397283982839928400284012840228403284042840528406284072840828409284102841128412284132841428415284162841728418284192842028421284222842328424284252842628427284282842928430284312843228433284342843528436284372843828439284402844128442284432844428445284462844728448284492845028451284522845328454284552845628457284582845928460284612846228463284642846528466284672846828469284702847128472284732847428475284762847728478284792848028481284822848328484284852848628487284882848928490284912849228493284942849528496284972849828499285002850128502285032850428505285062850728508285092851028511285122851328514285152851628517285182851928520285212852228523285242852528526285272852828529285302853128532285332853428535285362853728538285392854028541285422854328544285452854628547285482854928550285512855228553285542855528556285572855828559285602856128562285632856428565285662856728568285692857028571285722857328574285752857628577285782857928580285812858228583285842858528586285872858828589285902859128592285932859428595285962859728598285992860028601286022860328604286052860628607286082860928610286112861228613286142861528616286172861828619286202862128622286232862428625286262862728628286292863028631286322863328634286352863628637286382863928640286412864228643286442864528646286472864828649286502865128652286532865428655286562865728658286592866028661286622866328664286652866628667286682866928670286712867228673286742867528676286772867828679286802868128682286832868428685286862868728688286892869028691286922869328694286952869628697286982869928700287012870228703287042870528706287072870828709287102871128712287132871428715287162871728718287192872028721287222872328724287252872628727287282872928730287312873228733287342873528736287372873828739287402874128742287432874428745287462874728748287492875028751287522875328754287552875628757287582875928760287612876228763287642876528766287672876828769287702877128772287732877428775287762877728778287792878028781287822878328784287852878628787287882878928790287912879228793287942879528796287972879828799288002880128802288032880428805288062880728808288092881028811288122881328814288152881628817288182881928820288212882228823288242882528826288272882828829288302883128832288332883428835288362883728838288392884028841288422884328844288452884628847288482884928850288512885228853288542885528856288572885828859288602886128862288632886428865288662886728868288692887028871288722887328874288752887628877288782887928880288812888228883288842888528886288872888828889288902889128892288932889428895288962889728898288992890028901289022890328904289052890628907289082890928910289112891228913289142891528916289172891828919289202892128922289232892428925289262892728928289292893028931289322893328934289352893628937289382893928940289412894228943289442894528946289472894828949289502895128952289532895428955289562895728958289592896028961289622896328964289652896628967289682896928970289712897228973289742897528976289772897828979289802898128982289832898428985289862898728988289892899028991289922899328994289952899628997289982899929000290012900229003290042900529006290072900829009290102901129012290132901429015290162901729018290192902029021290222902329024290252902629027290282902929030290312903229033290342903529036290372903829039290402904129042290432904429045290462904729048290492905029051290522905329054290552905629057290582905929060290612906229063290642906529066290672906829069290702907129072290732907429075290762907729078290792908029081290822908329084290852908629087290882908929090290912909229093290942909529096290972909829099291002910129102291032910429105291062910729108291092911029111291122911329114291152911629117291182911929120291212912229123291242912529126291272912829129291302913129132291332913429135291362913729138291392914029141291422914329144291452914629147291482914929150291512915229153291542915529156291572915829159291602916129162291632916429165291662916729168291692917029171291722917329174291752917629177291782917929180291812918229183291842918529186291872918829189291902919129192291932919429195291962919729198291992920029201292022920329204292052920629207292082920929210292112921229213292142921529216292172921829219292202922129222292232922429225292262922729228292292923029231292322923329234292352923629237292382923929240292412924229243292442924529246292472924829249292502925129252292532925429255292562925729258292592926029261292622926329264292652926629267292682926929270292712927229273292742927529276292772927829279292802928129282292832928429285292862928729288292892929029291292922929329294292952929629297292982929929300293012930229303293042930529306293072930829309293102931129312293132931429315293162931729318293192932029321293222932329324293252932629327293282932929330293312933229333293342933529336293372933829339293402934129342293432934429345293462934729348293492935029351293522935329354293552935629357293582935929360293612936229363293642936529366293672936829369293702937129372293732937429375293762937729378293792938029381293822938329384293852938629387293882938929390293912939229393293942939529396293972939829399294002940129402294032940429405294062940729408294092941029411294122941329414294152941629417294182941929420294212942229423294242942529426294272942829429294302943129432294332943429435294362943729438294392944029441294422944329444294452944629447294482944929450294512945229453294542945529456294572945829459294602946129462294632946429465294662946729468294692947029471294722947329474294752947629477294782947929480294812948229483294842948529486294872948829489294902949129492294932949429495294962949729498294992950029501295022950329504295052950629507295082950929510295112951229513295142951529516295172951829519295202952129522295232952429525295262952729528295292953029531295322953329534295352953629537295382953929540295412954229543295442954529546295472954829549295502955129552295532955429555295562955729558295592956029561295622956329564295652956629567295682956929570295712957229573295742957529576295772957829579295802958129582295832958429585295862958729588295892959029591295922959329594295952959629597295982959929600296012960229603296042960529606296072960829609296102961129612296132961429615296162961729618296192962029621296222962329624296252962629627296282962929630296312963229633296342963529636296372963829639296402964129642296432964429645296462964729648296492965029651296522965329654296552965629657296582965929660296612966229663296642966529666296672966829669296702967129672296732967429675296762967729678296792968029681296822968329684296852968629687296882968929690296912969229693296942969529696296972969829699297002970129702297032970429705297062970729708297092971029711297122971329714297152971629717297182971929720297212972229723297242972529726297272972829729297302973129732297332973429735297362973729738297392974029741297422974329744297452974629747297482974929750297512975229753297542975529756297572975829759297602976129762297632976429765297662976729768297692977029771297722977329774297752977629777297782977929780297812978229783297842978529786297872978829789297902979129792297932979429795297962979729798297992980029801298022980329804298052980629807298082980929810298112981229813298142981529816298172981829819298202982129822298232982429825298262982729828298292983029831298322983329834298352983629837298382983929840298412984229843298442984529846298472984829849298502985129852298532985429855298562985729858298592986029861298622986329864298652986629867298682986929870298712987229873298742987529876298772987829879298802988129882298832988429885298862988729888298892989029891298922989329894298952989629897298982989929900299012990229903299042990529906299072990829909299102991129912299132991429915299162991729918299192992029921299222992329924299252992629927299282992929930299312993229933299342993529936299372993829939299402994129942299432994429945299462994729948299492995029951299522995329954299552995629957299582995929960299612996229963299642996529966299672996829969299702997129972299732997429975299762997729978299792998029981299822998329984299852998629987299882998929990299912999229993299942999529996299972999829999300003000130002300033000430005300063000730008300093001030011300123001330014300153001630017300183001930020300213002230023300243002530026300273002830029300303003130032300333003430035300363003730038300393004030041300423004330044300453004630047300483004930050300513005230053300543005530056300573005830059300603006130062300633006430065300663006730068300693007030071300723007330074300753007630077300783007930080300813008230083300843008530086300873008830089300903009130092300933009430095300963009730098300993010030101301023010330104301053010630107301083010930110301113011230113301143011530116301173011830119301203012130122301233012430125301263012730128301293013030131301323013330134301353013630137301383013930140301413014230143301443014530146301473014830149301503015130152301533015430155301563015730158301593016030161301623016330164301653016630167301683016930170301713017230173301743017530176301773017830179301803018130182301833018430185301863018730188301893019030191301923019330194301953019630197301983019930200302013020230203302043020530206302073020830209302103021130212302133021430215302163021730218302193022030221302223022330224302253022630227302283022930230302313023230233302343023530236302373023830239302403024130242302433024430245302463024730248302493025030251302523025330254302553025630257302583025930260302613026230263302643026530266302673026830269302703027130272302733027430275302763027730278302793028030281302823028330284302853028630287302883028930290302913029230293302943029530296302973029830299303003030130302303033030430305303063030730308303093031030311303123031330314303153031630317303183031930320303213032230323303243032530326303273032830329303303033130332303333033430335303363033730338303393034030341303423034330344303453034630347303483034930350303513035230353303543035530356303573035830359303603036130362303633036430365303663036730368303693037030371303723037330374303753037630377303783037930380303813038230383303843038530386303873038830389303903039130392303933039430395303963039730398303993040030401304023040330404304053040630407304083040930410304113041230413304143041530416304173041830419304203042130422304233042430425304263042730428304293043030431304323043330434304353043630437304383043930440304413044230443304443044530446304473044830449304503045130452304533045430455304563045730458304593046030461304623046330464304653046630467304683046930470304713047230473304743047530476304773047830479304803048130482304833048430485304863048730488304893049030491304923049330494304953049630497304983049930500305013050230503305043050530506305073050830509305103051130512305133051430515305163051730518305193052030521305223052330524305253052630527305283052930530305313053230533305343053530536305373053830539305403054130542305433054430545305463054730548305493055030551305523055330554305553055630557305583055930560305613056230563305643056530566305673056830569305703057130572305733057430575305763057730578305793058030581305823058330584305853058630587305883058930590305913059230593305943059530596305973059830599306003060130602306033060430605306063060730608306093061030611306123061330614306153061630617306183061930620306213062230623306243062530626306273062830629306303063130632306333063430635306363063730638306393064030641306423064330644306453064630647306483064930650306513065230653306543065530656306573065830659306603066130662306633066430665306663066730668306693067030671306723067330674306753067630677306783067930680306813068230683306843068530686306873068830689306903069130692306933069430695306963069730698306993070030701307023070330704307053070630707307083070930710307113071230713307143071530716307173071830719307203072130722307233072430725307263072730728307293073030731307323073330734307353073630737307383073930740307413074230743307443074530746307473074830749307503075130752307533075430755307563075730758307593076030761307623076330764307653076630767307683076930770307713077230773307743077530776307773077830779307803078130782307833078430785307863078730788307893079030791307923079330794307953079630797307983079930800308013080230803308043080530806308073080830809308103081130812308133081430815308163081730818308193082030821308223082330824308253082630827308283082930830308313083230833308343083530836308373083830839308403084130842308433084430845308463084730848308493085030851308523085330854308553085630857308583085930860308613086230863308643086530866308673086830869308703087130872308733087430875308763087730878308793088030881308823088330884308853088630887308883088930890308913089230893308943089530896308973089830899309003090130902309033090430905309063090730908309093091030911309123091330914309153091630917309183091930920309213092230923309243092530926309273092830929309303093130932309333093430935309363093730938309393094030941309423094330944309453094630947309483094930950309513095230953309543095530956309573095830959309603096130962309633096430965309663096730968309693097030971309723097330974309753097630977309783097930980309813098230983309843098530986309873098830989309903099130992309933099430995309963099730998309993100031001310023100331004310053100631007310083100931010310113101231013310143101531016310173101831019310203102131022310233102431025310263102731028310293103031031310323103331034310353103631037310383103931040310413104231043310443104531046310473104831049310503105131052310533105431055310563105731058310593106031061310623106331064310653106631067310683106931070310713107231073310743107531076310773107831079310803108131082310833108431085310863108731088310893109031091310923109331094310953109631097310983109931100311013110231103311043110531106311073110831109311103111131112311133111431115311163111731118311193112031121311223112331124311253112631127311283112931130311313113231133311343113531136311373113831139311403114131142311433114431145311463114731148311493115031151311523115331154311553115631157311583115931160311613116231163311643116531166311673116831169311703117131172311733117431175311763117731178311793118031181311823118331184311853118631187311883118931190311913119231193311943119531196311973119831199312003120131202312033120431205312063120731208312093121031211312123121331214312153121631217312183121931220312213122231223312243122531226312273122831229312303123131232312333123431235312363123731238312393124031241312423124331244312453124631247312483124931250312513125231253312543125531256312573125831259312603126131262312633126431265312663126731268312693127031271312723127331274312753127631277312783127931280312813128231283312843128531286312873128831289312903129131292312933129431295312963129731298312993130031301313023130331304313053130631307313083130931310313113131231313313143131531316313173131831319313203132131322313233132431325313263132731328313293133031331313323133331334313353133631337313383133931340313413134231343313443134531346313473134831349313503135131352313533135431355313563135731358313593136031361313623136331364313653136631367313683136931370313713137231373313743137531376313773137831379313803138131382313833138431385313863138731388313893139031391313923139331394313953139631397313983139931400314013140231403314043140531406314073140831409314103141131412314133141431415314163141731418314193142031421314223142331424314253142631427314283142931430314313143231433314343143531436314373143831439314403144131442314433144431445314463144731448314493145031451314523145331454314553145631457314583145931460314613146231463314643146531466314673146831469314703147131472314733147431475314763147731478314793148031481314823148331484314853148631487314883148931490314913149231493314943149531496314973149831499315003150131502315033150431505315063150731508315093151031511315123151331514315153151631517315183151931520315213152231523315243152531526315273152831529315303153131532315333153431535315363153731538315393154031541315423154331544315453154631547315483154931550315513155231553315543155531556315573155831559315603156131562315633156431565315663156731568315693157031571315723157331574315753157631577315783157931580315813158231583315843158531586315873158831589315903159131592315933159431595315963159731598315993160031601316023160331604316053160631607316083160931610316113161231613316143161531616316173161831619316203162131622316233162431625316263162731628316293163031631316323163331634316353163631637316383163931640316413164231643316443164531646316473164831649316503165131652316533165431655316563165731658316593166031661316623166331664316653166631667316683166931670316713167231673316743167531676316773167831679316803168131682316833168431685316863168731688316893169031691316923169331694316953169631697316983169931700317013170231703317043170531706317073170831709317103171131712317133171431715317163171731718317193172031721317223172331724317253172631727317283172931730317313173231733317343173531736317373173831739317403174131742317433174431745317463174731748317493175031751317523175331754317553175631757317583175931760317613176231763317643176531766317673176831769317703177131772317733177431775317763177731778317793178031781317823178331784317853178631787317883178931790317913179231793317943179531796317973179831799318003180131802318033180431805318063180731808318093181031811318123181331814318153181631817318183181931820318213182231823318243182531826318273182831829318303183131832318333183431835318363183731838318393184031841318423184331844318453184631847318483184931850318513185231853318543185531856318573185831859318603186131862318633186431865318663186731868318693187031871318723187331874318753187631877318783187931880318813188231883318843188531886318873188831889318903189131892318933189431895318963189731898318993190031901319023190331904319053190631907319083190931910319113191231913319143191531916319173191831919319203192131922319233192431925319263192731928319293193031931319323193331934319353193631937319383193931940319413194231943319443194531946319473194831949319503195131952319533195431955319563195731958319593196031961319623196331964319653196631967319683196931970319713197231973319743197531976319773197831979319803198131982319833198431985319863198731988319893199031991319923199331994319953199631997319983199932000320013200232003320043200532006320073200832009320103201132012320133201432015320163201732018320193202032021320223202332024320253202632027320283202932030320313203232033320343203532036320373203832039320403204132042320433204432045320463204732048320493205032051320523205332054320553205632057320583205932060320613206232063320643206532066320673206832069320703207132072320733207432075320763207732078320793208032081320823208332084320853208632087320883208932090320913209232093320943209532096320973209832099321003210132102321033210432105321063210732108321093211032111321123211332114321153211632117321183211932120321213212232123321243212532126321273212832129321303213132132321333213432135321363213732138321393214032141321423214332144321453214632147321483214932150321513215232153321543215532156321573215832159321603216132162321633216432165321663216732168321693217032171321723217332174321753217632177321783217932180321813218232183321843218532186321873218832189321903219132192321933219432195321963219732198321993220032201322023220332204322053220632207322083220932210322113221232213322143221532216322173221832219322203222132222322233222432225322263222732228322293223032231322323223332234322353223632237322383223932240322413224232243322443224532246322473224832249322503225132252322533225432255322563225732258322593226032261322623226332264322653226632267322683226932270322713227232273322743227532276322773227832279322803228132282322833228432285322863228732288322893229032291322923229332294322953229632297322983229932300323013230232303323043230532306323073230832309323103231132312323133231432315323163231732318323193232032321323223232332324323253232632327323283232932330323313233232333323343233532336323373233832339323403234132342323433234432345323463234732348323493235032351323523235332354323553235632357323583235932360323613236232363323643236532366323673236832369323703237132372323733237432375323763237732378323793238032381323823238332384323853238632387323883238932390323913239232393323943239532396323973239832399324003240132402324033240432405324063240732408324093241032411324123241332414324153241632417324183241932420324213242232423324243242532426324273242832429324303243132432324333243432435324363243732438324393244032441324423244332444324453244632447324483244932450324513245232453324543245532456324573245832459324603246132462324633246432465324663246732468324693247032471324723247332474324753247632477324783247932480324813248232483324843248532486324873248832489324903249132492324933249432495324963249732498324993250032501325023250332504325053250632507325083250932510325113251232513325143251532516325173251832519325203252132522325233252432525325263252732528325293253032531325323253332534325353253632537325383253932540325413254232543325443254532546325473254832549325503255132552325533255432555325563255732558325593256032561325623256332564325653256632567325683256932570325713257232573325743257532576325773257832579325803258132582325833258432585325863258732588325893259032591325923259332594325953259632597325983259932600326013260232603326043260532606326073260832609326103261132612326133261432615326163261732618326193262032621326223262332624326253262632627326283262932630326313263232633326343263532636326373263832639326403264132642326433264432645326463264732648326493265032651326523265332654326553265632657326583265932660326613266232663326643266532666326673266832669326703267132672326733267432675326763267732678326793268032681326823268332684326853268632687326883268932690326913269232693326943269532696326973269832699327003270132702327033270432705327063270732708327093271032711327123271332714327153271632717327183271932720327213272232723327243272532726327273272832729327303273132732327333273432735327363273732738327393274032741327423274332744327453274632747327483274932750327513275232753327543275532756327573275832759327603276132762327633276432765327663276732768327693277032771327723277332774327753277632777327783277932780327813278232783327843278532786327873278832789327903279132792327933279432795327963279732798327993280032801328023280332804328053280632807328083280932810328113281232813328143281532816328173281832819328203282132822328233282432825328263282732828328293283032831328323283332834328353283632837328383283932840328413284232843328443284532846328473284832849328503285132852328533285432855328563285732858328593286032861328623286332864328653286632867328683286932870328713287232873328743287532876328773287832879328803288132882328833288432885328863288732888328893289032891328923289332894328953289632897328983289932900329013290232903329043290532906329073290832909329103291132912329133291432915329163291732918329193292032921329223292332924329253292632927329283292932930329313293232933329343293532936329373293832939329403294132942329433294432945329463294732948329493295032951329523295332954329553295632957329583295932960329613296232963329643296532966329673296832969329703297132972329733297432975329763297732978329793298032981329823298332984329853298632987329883298932990329913299232993329943299532996329973299832999330003300133002330033300433005330063300733008330093301033011330123301333014330153301633017330183301933020330213302233023330243302533026330273302833029330303303133032330333303433035330363303733038330393304033041330423304333044330453304633047330483304933050330513305233053330543305533056330573305833059330603306133062330633306433065330663306733068330693307033071330723307333074330753307633077330783307933080330813308233083330843308533086330873308833089330903309133092330933309433095330963309733098330993310033101331023310333104331053310633107331083310933110331113311233113331143311533116331173311833119331203312133122331233312433125331263312733128331293313033131331323313333134331353313633137331383313933140331413314233143331443314533146331473314833149331503315133152331533315433155331563315733158331593316033161331623316333164331653316633167331683316933170331713317233173331743317533176331773317833179331803318133182331833318433185331863318733188331893319033191331923319333194331953319633197331983319933200332013320233203332043320533206332073320833209332103321133212332133321433215332163321733218332193322033221332223322333224332253322633227332283322933230332313323233233332343323533236332373323833239332403324133242332433324433245332463324733248332493325033251332523325333254332553325633257332583325933260332613326233263332643326533266332673326833269332703327133272332733327433275332763327733278332793328033281332823328333284332853328633287332883328933290332913329233293332943329533296332973329833299333003330133302333033330433305333063330733308333093331033311333123331333314333153331633317333183331933320333213332233323333243332533326333273332833329333303333133332333333333433335333363333733338333393334033341333423334333344333453334633347333483334933350333513335233353333543335533356333573335833359333603336133362333633336433365333663336733368333693337033371333723337333374333753337633377333783337933380333813338233383333843338533386333873338833389333903339133392333933339433395333963339733398333993340033401334023340333404334053340633407334083340933410334113341233413334143341533416334173341833419334203342133422334233342433425334263342733428334293343033431334323343333434334353343633437334383343933440334413344233443334443344533446334473344833449334503345133452334533345433455334563345733458334593346033461334623346333464334653346633467334683346933470334713347233473334743347533476334773347833479334803348133482334833348433485334863348733488334893349033491334923349333494334953349633497334983349933500335013350233503335043350533506335073350833509335103351133512335133351433515335163351733518335193352033521335223352333524335253352633527335283352933530335313353233533335343353533536335373353833539335403354133542335433354433545335463354733548335493355033551335523355333554335553355633557335583355933560335613356233563335643356533566335673356833569335703357133572335733357433575335763357733578335793358033581335823358333584335853358633587335883358933590335913359233593335943359533596335973359833599336003360133602336033360433605336063360733608336093361033611336123361333614336153361633617336183361933620336213362233623336243362533626336273362833629336303363133632336333363433635336363363733638336393364033641336423364333644336453364633647336483364933650336513365233653336543365533656336573365833659336603366133662336633366433665336663366733668336693367033671336723367333674336753367633677336783367933680336813368233683336843368533686336873368833689336903369133692336933369433695336963369733698336993370033701337023370333704337053370633707337083370933710337113371233713337143371533716337173371833719337203372133722337233372433725337263372733728337293373033731337323373333734337353373633737337383373933740337413374233743337443374533746337473374833749337503375133752337533375433755337563375733758337593376033761337623376333764337653376633767337683376933770337713377233773337743377533776337773377833779337803378133782337833378433785337863378733788337893379033791337923379333794337953379633797337983379933800338013380233803338043380533806338073380833809338103381133812338133381433815338163381733818338193382033821338223382333824338253382633827338283382933830338313383233833338343383533836338373383833839338403384133842338433384433845338463384733848338493385033851338523385333854338553385633857338583385933860338613386233863338643386533866338673386833869338703387133872338733387433875338763387733878338793388033881338823388333884338853388633887338883388933890338913389233893338943389533896338973389833899339003390133902339033390433905339063390733908339093391033911339123391333914339153391633917339183391933920339213392233923339243392533926339273392833929339303393133932339333393433935339363393733938339393394033941339423394333944339453394633947339483394933950339513395233953339543395533956339573395833959339603396133962339633396433965339663396733968339693397033971339723397333974339753397633977339783397933980339813398233983339843398533986339873398833989339903399133992339933399433995339963399733998339993400034001340023400334004340053400634007340083400934010340113401234013340143401534016340173401834019340203402134022340233402434025340263402734028340293403034031340323403334034340353403634037340383403934040340413404234043340443404534046340473404834049340503405134052340533405434055340563405734058340593406034061340623406334064340653406634067340683406934070340713407234073340743407534076340773407834079340803408134082340833408434085340863408734088340893409034091340923409334094340953409634097340983409934100341013410234103341043410534106341073410834109341103411134112341133411434115341163411734118341193412034121341223412334124341253412634127341283412934130341313413234133341343413534136341373413834139341403414134142341433414434145341463414734148341493415034151341523415334154341553415634157341583415934160341613416234163341643416534166341673416834169341703417134172341733417434175341763417734178341793418034181341823418334184341853418634187341883418934190341913419234193341943419534196341973419834199342003420134202342033420434205342063420734208342093421034211342123421334214342153421634217342183421934220342213422234223342243422534226342273422834229342303423134232342333423434235342363423734238342393424034241342423424334244342453424634247342483424934250342513425234253342543425534256342573425834259342603426134262342633426434265342663426734268342693427034271342723427334274342753427634277342783427934280342813428234283342843428534286342873428834289342903429134292342933429434295342963429734298342993430034301343023430334304343053430634307343083430934310343113431234313343143431534316343173431834319343203432134322343233432434325343263432734328343293433034331343323433334334343353433634337343383433934340343413434234343343443434534346343473434834349343503435134352343533435434355343563435734358343593436034361343623436334364343653436634367343683436934370343713437234373343743437534376343773437834379343803438134382343833438434385343863438734388343893439034391343923439334394343953439634397343983439934400344013440234403344043440534406344073440834409344103441134412344133441434415344163441734418344193442034421344223442334424344253442634427344283442934430344313443234433344343443534436344373443834439344403444134442344433444434445344463444734448344493445034451344523445334454344553445634457344583445934460344613446234463344643446534466344673446834469344703447134472344733447434475344763447734478344793448034481344823448334484344853448634487344883448934490344913449234493344943449534496344973449834499345003450134502345033450434505345063450734508345093451034511345123451334514345153451634517345183451934520345213452234523345243452534526345273452834529345303453134532345333453434535345363453734538345393454034541345423454334544345453454634547345483454934550345513455234553345543455534556345573455834559345603456134562345633456434565345663456734568345693457034571345723457334574345753457634577345783457934580345813458234583345843458534586345873458834589345903459134592345933459434595345963459734598345993460034601346023460334604346053460634607346083460934610346113461234613346143461534616346173461834619346203462134622346233462434625346263462734628346293463034631346323463334634346353463634637346383463934640346413464234643346443464534646346473464834649346503465134652346533465434655346563465734658346593466034661346623466334664346653466634667346683466934670346713467234673346743467534676346773467834679346803468134682346833468434685346863468734688346893469034691346923469334694346953469634697346983469934700347013470234703347043470534706347073470834709347103471134712347133471434715347163471734718347193472034721347223472334724347253472634727347283472934730347313473234733347343473534736347373473834739347403474134742347433474434745347463474734748347493475034751347523475334754347553475634757347583475934760347613476234763347643476534766347673476834769347703477134772347733477434775347763477734778347793478034781347823478334784347853478634787347883478934790347913479234793347943479534796347973479834799348003480134802348033480434805348063480734808348093481034811348123481334814348153481634817348183481934820348213482234823348243482534826348273482834829348303483134832348333483434835348363483734838348393484034841348423484334844348453484634847348483484934850348513485234853348543485534856348573485834859348603486134862348633486434865348663486734868348693487034871348723487334874348753487634877348783487934880348813488234883348843488534886348873488834889348903489134892348933489434895348963489734898348993490034901349023490334904349053490634907349083490934910349113491234913349143491534916349173491834919349203492134922349233492434925349263492734928349293493034931349323493334934349353493634937349383493934940349413494234943349443494534946349473494834949349503495134952349533495434955349563495734958349593496034961349623496334964349653496634967349683496934970349713497234973349743497534976349773497834979349803498134982349833498434985349863498734988349893499034991349923499334994349953499634997349983499935000350013500235003350043500535006350073500835009350103501135012350133501435015350163501735018350193502035021350223502335024350253502635027350283502935030350313503235033350343503535036350373503835039350403504135042350433504435045350463504735048350493505035051350523505335054350553505635057350583505935060350613506235063350643506535066350673506835069350703507135072350733507435075350763507735078350793508035081350823508335084350853508635087350883508935090350913509235093350943509535096350973509835099351003510135102351033510435105351063510735108351093511035111351123511335114351153511635117351183511935120351213512235123351243512535126351273512835129351303513135132351333513435135351363513735138351393514035141351423514335144351453514635147351483514935150351513515235153351543515535156351573515835159351603516135162351633516435165351663516735168351693517035171351723517335174351753517635177351783517935180351813518235183351843518535186351873518835189351903519135192351933519435195351963519735198351993520035201352023520335204352053520635207352083520935210352113521235213352143521535216352173521835219352203522135222352233522435225352263522735228352293523035231352323523335234352353523635237352383523935240352413524235243352443524535246352473524835249352503525135252352533525435255352563525735258352593526035261352623526335264352653526635267352683526935270352713527235273352743527535276352773527835279352803528135282352833528435285352863528735288352893529035291352923529335294352953529635297352983529935300353013530235303353043530535306353073530835309353103531135312353133531435315353163531735318353193532035321353223532335324353253532635327353283532935330353313533235333353343533535336353373533835339353403534135342353433534435345353463534735348353493535035351353523535335354353553535635357353583535935360353613536235363353643536535366353673536835369353703537135372353733537435375353763537735378353793538035381353823538335384353853538635387353883538935390353913539235393353943539535396353973539835399354003540135402354033540435405354063540735408354093541035411354123541335414354153541635417354183541935420354213542235423354243542535426354273542835429354303543135432354333543435435354363543735438354393544035441354423544335444354453544635447354483544935450354513545235453354543545535456354573545835459354603546135462354633546435465354663546735468354693547035471354723547335474354753547635477354783547935480354813548235483354843548535486354873548835489354903549135492354933549435495354963549735498354993550035501355023550335504355053550635507355083550935510355113551235513355143551535516355173551835519355203552135522355233552435525355263552735528355293553035531355323553335534355353553635537355383553935540355413554235543355443554535546355473554835549355503555135552355533555435555355563555735558355593556035561355623556335564355653556635567355683556935570355713557235573355743557535576355773557835579355803558135582355833558435585355863558735588355893559035591355923559335594355953559635597355983559935600356013560235603356043560535606356073560835609356103561135612356133561435615356163561735618356193562035621356223562335624356253562635627356283562935630356313563235633356343563535636356373563835639356403564135642356433564435645356463564735648356493565035651356523565335654356553565635657356583565935660356613566235663356643566535666356673566835669356703567135672356733567435675356763567735678356793568035681356823568335684356853568635687356883568935690356913569235693356943569535696356973569835699357003570135702357033570435705357063570735708357093571035711357123571335714357153571635717357183571935720357213572235723357243572535726357273572835729357303573135732357333573435735357363573735738357393574035741357423574335744357453574635747357483574935750357513575235753357543575535756357573575835759357603576135762357633576435765357663576735768357693577035771357723577335774357753577635777357783577935780357813578235783357843578535786357873578835789357903579135792357933579435795357963579735798357993580035801358023580335804358053580635807358083580935810358113581235813358143581535816358173581835819358203582135822358233582435825358263582735828358293583035831358323583335834358353583635837358383583935840358413584235843358443584535846358473584835849358503585135852358533585435855358563585735858358593586035861358623586335864358653586635867358683586935870358713587235873358743587535876358773587835879358803588135882358833588435885358863588735888358893589035891358923589335894358953589635897358983589935900359013590235903359043590535906359073590835909359103591135912359133591435915359163591735918359193592035921359223592335924359253592635927359283592935930359313593235933359343593535936359373593835939359403594135942359433594435945359463594735948359493595035951359523595335954359553595635957359583595935960359613596235963359643596535966359673596835969359703597135972359733597435975359763597735978359793598035981359823598335984359853598635987359883598935990359913599235993359943599535996359973599835999360003600136002360033600436005360063600736008360093601036011360123601336014360153601636017360183601936020360213602236023360243602536026360273602836029360303603136032360333603436035360363603736038360393604036041360423604336044360453604636047360483604936050360513605236053360543605536056360573605836059360603606136062360633606436065360663606736068360693607036071360723607336074360753607636077360783607936080360813608236083360843608536086360873608836089360903609136092360933609436095360963609736098360993610036101361023610336104361053610636107361083610936110361113611236113361143611536116361173611836119361203612136122361233612436125361263612736128361293613036131361323613336134361353613636137361383613936140361413614236143361443614536146361473614836149361503615136152361533615436155361563615736158361593616036161361623616336164361653616636167361683616936170361713617236173361743617536176361773617836179361803618136182361833618436185361863618736188361893619036191361923619336194361953619636197361983619936200362013620236203362043620536206362073620836209362103621136212362133621436215362163621736218362193622036221362223622336224362253622636227362283622936230362313623236233362343623536236362373623836239362403624136242362433624436245362463624736248362493625036251362523625336254362553625636257362583625936260362613626236263362643626536266362673626836269362703627136272362733627436275362763627736278362793628036281362823628336284362853628636287362883628936290362913629236293362943629536296362973629836299363003630136302363033630436305363063630736308363093631036311363123631336314363153631636317363183631936320363213632236323363243632536326363273632836329363303633136332363333633436335363363633736338363393634036341363423634336344363453634636347363483634936350363513635236353363543635536356363573635836359363603636136362363633636436365363663636736368363693637036371363723637336374363753637636377363783637936380363813638236383363843638536386363873638836389363903639136392363933639436395363963639736398363993640036401364023640336404364053640636407364083640936410364113641236413364143641536416364173641836419364203642136422364233642436425364263642736428364293643036431364323643336434364353643636437364383643936440364413644236443364443644536446364473644836449364503645136452364533645436455364563645736458364593646036461364623646336464364653646636467364683646936470364713647236473364743647536476364773647836479364803648136482364833648436485364863648736488364893649036491364923649336494364953649636497364983649936500365013650236503365043650536506365073650836509365103651136512365133651436515365163651736518365193652036521365223652336524365253652636527365283652936530365313653236533365343653536536365373653836539365403654136542365433654436545365463654736548365493655036551365523655336554365553655636557365583655936560365613656236563365643656536566365673656836569365703657136572365733657436575365763657736578365793658036581365823658336584365853658636587365883658936590365913659236593365943659536596365973659836599366003660136602366033660436605366063660736608366093661036611366123661336614366153661636617366183661936620366213662236623366243662536626366273662836629366303663136632366333663436635366363663736638366393664036641366423664336644366453664636647366483664936650366513665236653366543665536656366573665836659366603666136662366633666436665366663666736668366693667036671366723667336674366753667636677366783667936680366813668236683366843668536686366873668836689366903669136692366933669436695366963669736698366993670036701367023670336704367053670636707367083670936710367113671236713367143671536716367173671836719367203672136722367233672436725367263672736728367293673036731367323673336734367353673636737367383673936740367413674236743367443674536746367473674836749367503675136752367533675436755367563675736758367593676036761367623676336764367653676636767367683676936770367713677236773367743677536776367773677836779367803678136782367833678436785367863678736788367893679036791367923679336794367953679636797367983679936800368013680236803368043680536806368073680836809368103681136812368133681436815368163681736818368193682036821368223682336824368253682636827368283682936830368313683236833368343683536836368373683836839368403684136842368433684436845368463684736848368493685036851368523685336854368553685636857368583685936860368613686236863368643686536866368673686836869368703687136872368733687436875368763687736878368793688036881368823688336884368853688636887368883688936890368913689236893368943689536896368973689836899369003690136902369033690436905369063690736908369093691036911369123691336914369153691636917369183691936920369213692236923369243692536926369273692836929369303693136932369333693436935369363693736938369393694036941369423694336944369453694636947369483694936950369513695236953369543695536956369573695836959369603696136962369633696436965369663696736968369693697036971369723697336974369753697636977369783697936980369813698236983369843698536986369873698836989369903699136992369933699436995369963699736998369993700037001370023700337004370053700637007370083700937010370113701237013370143701537016370173701837019370203702137022370233702437025370263702737028370293703037031370323703337034370353703637037370383703937040370413704237043370443704537046370473704837049370503705137052370533705437055370563705737058370593706037061370623706337064370653706637067370683706937070370713707237073370743707537076370773707837079370803708137082370833708437085370863708737088370893709037091370923709337094370953709637097370983709937100371013710237103371043710537106371073710837109371103711137112371133711437115371163711737118371193712037121371223712337124371253712637127371283712937130371313713237133371343713537136371373713837139371403714137142371433714437145371463714737148371493715037151371523715337154371553715637157371583715937160371613716237163371643716537166371673716837169371703717137172371733717437175371763717737178371793718037181371823718337184371853718637187371883718937190371913719237193371943719537196371973719837199372003720137202372033720437205372063720737208372093721037211372123721337214372153721637217372183721937220372213722237223372243722537226372273722837229372303723137232372333723437235372363723737238372393724037241372423724337244372453724637247372483724937250372513725237253372543725537256372573725837259372603726137262372633726437265372663726737268372693727037271372723727337274372753727637277372783727937280372813728237283372843728537286372873728837289372903729137292372933729437295372963729737298372993730037301373023730337304373053730637307373083730937310373113731237313373143731537316373173731837319373203732137322373233732437325373263732737328373293733037331373323733337334373353733637337373383733937340373413734237343373443734537346373473734837349373503735137352373533735437355373563735737358373593736037361373623736337364373653736637367373683736937370373713737237373373743737537376373773737837379373803738137382373833738437385373863738737388373893739037391373923739337394373953739637397373983739937400374013740237403374043740537406374073740837409374103741137412374133741437415374163741737418374193742037421374223742337424374253742637427374283742937430374313743237433374343743537436374373743837439374403744137442374433744437445374463744737448374493745037451374523745337454374553745637457374583745937460374613746237463374643746537466374673746837469374703747137472374733747437475374763747737478374793748037481374823748337484374853748637487374883748937490374913749237493374943749537496374973749837499375003750137502375033750437505375063750737508375093751037511375123751337514375153751637517375183751937520375213752237523375243752537526375273752837529375303753137532375333753437535375363753737538375393754037541375423754337544375453754637547375483754937550375513755237553375543755537556375573755837559375603756137562375633756437565375663756737568375693757037571375723757337574375753757637577375783757937580375813758237583375843758537586375873758837589375903759137592375933759437595375963759737598375993760037601376023760337604376053760637607376083760937610376113761237613376143761537616376173761837619376203762137622376233762437625376263762737628376293763037631376323763337634376353763637637376383763937640376413764237643376443764537646376473764837649376503765137652376533765437655376563765737658376593766037661376623766337664376653766637667376683766937670376713767237673376743767537676376773767837679376803768137682376833768437685376863768737688376893769037691376923769337694376953769637697376983769937700377013770237703377043770537706377073770837709377103771137712377133771437715377163771737718377193772037721377223772337724377253772637727377283772937730377313773237733377343773537736377373773837739377403774137742377433774437745377463774737748377493775037751377523775337754377553775637757377583775937760377613776237763377643776537766377673776837769377703777137772377733777437775377763777737778377793778037781377823778337784377853778637787377883778937790377913779237793377943779537796377973779837799378003780137802378033780437805378063780737808378093781037811378123781337814378153781637817378183781937820378213782237823378243782537826378273782837829378303783137832378333783437835378363783737838378393784037841378423784337844378453784637847378483784937850378513785237853378543785537856378573785837859378603786137862378633786437865378663786737868378693787037871378723787337874378753787637877378783787937880378813788237883378843788537886378873788837889378903789137892378933789437895378963789737898378993790037901379023790337904379053790637907379083790937910379113791237913379143791537916379173791837919379203792137922379233792437925379263792737928379293793037931379323793337934379353793637937379383793937940379413794237943379443794537946379473794837949379503795137952379533795437955379563795737958379593796037961379623796337964379653796637967379683796937970379713797237973379743797537976379773797837979379803798137982379833798437985379863798737988379893799037991379923799337994379953799637997379983799938000380013800238003380043800538006380073800838009380103801138012380133801438015380163801738018380193802038021380223802338024380253802638027380283802938030380313803238033380343803538036380373803838039380403804138042380433804438045380463804738048380493805038051380523805338054380553805638057380583805938060380613806238063380643806538066380673806838069380703807138072380733807438075380763807738078380793808038081380823808338084380853808638087380883808938090380913809238093380943809538096380973809838099381003810138102381033810438105381063810738108381093811038111381123811338114381153811638117381183811938120381213812238123381243812538126381273812838129381303813138132381333813438135381363813738138381393814038141381423814338144381453814638147381483814938150381513815238153381543815538156381573815838159381603816138162381633816438165381663816738168381693817038171381723817338174381753817638177381783817938180381813818238183381843818538186381873818838189381903819138192381933819438195381963819738198381993820038201382023820338204382053820638207382083820938210382113821238213382143821538216382173821838219382203822138222382233822438225382263822738228382293823038231382323823338234382353823638237382383823938240382413824238243382443824538246382473824838249382503825138252382533825438255382563825738258382593826038261382623826338264382653826638267382683826938270382713827238273382743827538276382773827838279382803828138282382833828438285382863828738288382893829038291382923829338294382953829638297382983829938300383013830238303383043830538306383073830838309383103831138312383133831438315383163831738318383193832038321383223832338324383253832638327383283832938330383313833238333383343833538336383373833838339383403834138342383433834438345383463834738348383493835038351383523835338354383553835638357383583835938360383613836238363383643836538366383673836838369383703837138372383733837438375383763837738378383793838038381383823838338384383853838638387383883838938390383913839238393383943839538396383973839838399384003840138402384033840438405384063840738408384093841038411384123841338414384153841638417384183841938420384213842238423384243842538426384273842838429384303843138432384333843438435384363843738438384393844038441384423844338444384453844638447384483844938450384513845238453384543845538456384573845838459384603846138462384633846438465384663846738468384693847038471384723847338474384753847638477384783847938480384813848238483384843848538486384873848838489384903849138492384933849438495384963849738498384993850038501385023850338504385053850638507385083850938510385113851238513385143851538516385173851838519385203852138522385233852438525385263852738528385293853038531385323853338534385353853638537385383853938540385413854238543385443854538546385473854838549385503855138552385533855438555385563855738558385593856038561385623856338564385653856638567385683856938570385713857238573385743857538576385773857838579385803858138582385833858438585385863858738588385893859038591385923859338594385953859638597385983859938600386013860238603386043860538606386073860838609386103861138612386133861438615386163861738618386193862038621386223862338624386253862638627386283862938630386313863238633386343863538636386373863838639386403864138642386433864438645386463864738648386493865038651386523865338654386553865638657386583865938660386613866238663386643866538666386673866838669386703867138672386733867438675386763867738678386793868038681386823868338684386853868638687386883868938690386913869238693386943869538696386973869838699387003870138702387033870438705387063870738708387093871038711387123871338714387153871638717387183871938720387213872238723387243872538726387273872838729387303873138732387333873438735387363873738738387393874038741387423874338744387453874638747387483874938750387513875238753387543875538756387573875838759387603876138762387633876438765387663876738768387693877038771387723877338774387753877638777387783877938780387813878238783387843878538786387873878838789387903879138792387933879438795387963879738798387993880038801388023880338804388053880638807388083880938810388113881238813388143881538816388173881838819388203882138822388233882438825388263882738828388293883038831388323883338834388353883638837388383883938840388413884238843388443884538846388473884838849388503885138852388533885438855388563885738858388593886038861388623886338864388653886638867388683886938870388713887238873388743887538876388773887838879388803888138882388833888438885388863888738888388893889038891388923889338894388953889638897388983889938900389013890238903389043890538906389073890838909389103891138912389133891438915389163891738918389193892038921389223892338924389253892638927389283892938930389313893238933389343893538936389373893838939389403894138942389433894438945389463894738948389493895038951389523895338954389553895638957389583895938960389613896238963389643896538966389673896838969389703897138972389733897438975389763897738978389793898038981389823898338984389853898638987389883898938990389913899238993389943899538996389973899838999390003900139002390033900439005390063900739008390093901039011390123901339014390153901639017390183901939020390213902239023390243902539026390273902839029390303903139032390333903439035390363903739038390393904039041390423904339044390453904639047390483904939050390513905239053390543905539056390573905839059390603906139062390633906439065390663906739068390693907039071390723907339074390753907639077390783907939080390813908239083390843908539086390873908839089390903909139092390933909439095390963909739098390993910039101391023910339104391053910639107391083910939110391113911239113391143911539116391173911839119391203912139122391233912439125391263912739128391293913039131391323913339134391353913639137391383913939140391413914239143391443914539146391473914839149391503915139152391533915439155391563915739158391593916039161391623916339164391653916639167391683916939170391713917239173391743917539176391773917839179391803918139182391833918439185391863918739188391893919039191391923919339194391953919639197391983919939200392013920239203392043920539206392073920839209392103921139212392133921439215392163921739218392193922039221392223922339224392253922639227392283922939230392313923239233392343923539236392373923839239392403924139242392433924439245392463924739248392493925039251392523925339254392553925639257392583925939260392613926239263392643926539266392673926839269392703927139272392733927439275392763927739278392793928039281392823928339284392853928639287392883928939290392913929239293392943929539296392973929839299393003930139302393033930439305393063930739308393093931039311393123931339314393153931639317393183931939320393213932239323393243932539326393273932839329393303933139332393333933439335393363933739338393393934039341393423934339344393453934639347393483934939350393513935239353393543935539356393573935839359393603936139362393633936439365393663936739368393693937039371393723937339374393753937639377393783937939380393813938239383393843938539386393873938839389393903939139392393933939439395393963939739398393993940039401394023940339404394053940639407394083940939410394113941239413394143941539416394173941839419394203942139422394233942439425394263942739428394293943039431394323943339434394353943639437394383943939440394413944239443394443944539446394473944839449394503945139452394533945439455394563945739458394593946039461394623946339464394653946639467394683946939470394713947239473394743947539476394773947839479394803948139482394833948439485394863948739488394893949039491394923949339494394953949639497394983949939500395013950239503395043950539506395073950839509395103951139512395133951439515395163951739518395193952039521395223952339524395253952639527395283952939530395313953239533395343953539536395373953839539395403954139542395433954439545395463954739548395493955039551395523955339554395553955639557395583955939560395613956239563395643956539566395673956839569395703957139572395733957439575395763957739578395793958039581395823958339584395853958639587395883958939590395913959239593395943959539596395973959839599396003960139602396033960439605396063960739608396093961039611396123961339614396153961639617396183961939620396213962239623396243962539626396273962839629396303963139632396333963439635396363963739638396393964039641396423964339644396453964639647396483964939650396513965239653396543965539656396573965839659396603966139662396633966439665396663966739668396693967039671396723967339674396753967639677396783967939680396813968239683396843968539686396873968839689396903969139692396933969439695396963969739698396993970039701397023970339704397053970639707397083970939710397113971239713397143971539716397173971839719397203972139722397233972439725397263972739728397293973039731397323973339734397353973639737397383973939740397413974239743397443974539746397473974839749397503975139752397533975439755397563975739758397593976039761397623976339764397653976639767397683976939770397713977239773397743977539776397773977839779397803978139782397833978439785397863978739788397893979039791397923979339794397953979639797397983979939800398013980239803398043980539806398073980839809398103981139812398133981439815398163981739818398193982039821398223982339824398253982639827398283982939830398313983239833398343983539836398373983839839398403984139842398433984439845398463984739848398493985039851398523985339854398553985639857398583985939860398613986239863398643986539866398673986839869398703987139872398733987439875398763987739878398793988039881398823988339884398853988639887398883988939890398913989239893398943989539896398973989839899399003990139902399033990439905399063990739908399093991039911399123991339914399153991639917399183991939920399213992239923399243992539926399273992839929399303993139932399333993439935399363993739938399393994039941399423994339944399453994639947399483994939950399513995239953399543995539956399573995839959399603996139962399633996439965399663996739968399693997039971399723997339974399753997639977399783997939980399813998239983399843998539986399873998839989399903999139992399933999439995399963999739998399994000040001400024000340004400054000640007400084000940010400114001240013400144001540016400174001840019400204002140022400234002440025400264002740028400294003040031400324003340034400354003640037400384003940040400414004240043400444004540046400474004840049400504005140052400534005440055400564005740058400594006040061400624006340064400654006640067400684006940070400714007240073400744007540076400774007840079400804008140082400834008440085400864008740088400894009040091400924009340094400954009640097400984009940100401014010240103401044010540106401074010840109401104011140112401134011440115401164011740118401194012040121401224012340124401254012640127401284012940130401314013240133401344013540136401374013840139401404014140142401434014440145401464014740148401494015040151401524015340154401554015640157401584015940160401614016240163401644016540166401674016840169401704017140172401734017440175401764017740178401794018040181401824018340184401854018640187401884018940190401914019240193401944019540196401974019840199402004020140202402034020440205402064020740208402094021040211402124021340214402154021640217402184021940220402214022240223402244022540226402274022840229402304023140232402334023440235402364023740238402394024040241402424024340244402454024640247402484024940250402514025240253402544025540256402574025840259402604026140262402634026440265402664026740268402694027040271402724027340274402754027640277402784027940280402814028240283402844028540286402874028840289402904029140292402934029440295402964029740298402994030040301403024030340304403054030640307403084030940310403114031240313403144031540316403174031840319403204032140322403234032440325403264032740328403294033040331403324033340334403354033640337403384033940340403414034240343403444034540346403474034840349403504035140352403534035440355403564035740358403594036040361403624036340364403654036640367403684036940370403714037240373403744037540376403774037840379403804038140382403834038440385403864038740388403894039040391403924039340394403954039640397403984039940400404014040240403404044040540406404074040840409404104041140412404134041440415404164041740418404194042040421404224042340424404254042640427404284042940430404314043240433404344043540436404374043840439404404044140442404434044440445404464044740448404494045040451404524045340454404554045640457404584045940460404614046240463404644046540466404674046840469404704047140472404734047440475404764047740478404794048040481404824048340484404854048640487404884048940490404914049240493404944049540496404974049840499405004050140502405034050440505405064050740508405094051040511405124051340514405154051640517405184051940520405214052240523405244052540526405274052840529405304053140532405334053440535405364053740538405394054040541405424054340544405454054640547405484054940550405514055240553405544055540556405574055840559405604056140562405634056440565405664056740568405694057040571405724057340574405754057640577405784057940580405814058240583405844058540586405874058840589405904059140592405934059440595405964059740598405994060040601406024060340604406054060640607406084060940610406114061240613406144061540616406174061840619406204062140622406234062440625406264062740628406294063040631406324063340634406354063640637406384063940640406414064240643406444064540646406474064840649406504065140652406534065440655406564065740658406594066040661406624066340664406654066640667406684066940670406714067240673406744067540676406774067840679406804068140682406834068440685406864068740688406894069040691406924069340694406954069640697406984069940700407014070240703407044070540706407074070840709407104071140712407134071440715407164071740718407194072040721407224072340724407254072640727407284072940730407314073240733407344073540736407374073840739407404074140742407434074440745407464074740748407494075040751407524075340754407554075640757407584075940760407614076240763407644076540766407674076840769407704077140772407734077440775407764077740778407794078040781407824078340784407854078640787407884078940790407914079240793407944079540796407974079840799408004080140802408034080440805408064080740808408094081040811408124081340814408154081640817408184081940820408214082240823408244082540826408274082840829408304083140832408334083440835408364083740838408394084040841408424084340844408454084640847408484084940850408514085240853408544085540856408574085840859408604086140862408634086440865408664086740868408694087040871408724087340874408754087640877408784087940880408814088240883408844088540886408874088840889408904089140892408934089440895408964089740898408994090040901409024090340904409054090640907409084090940910409114091240913409144091540916409174091840919409204092140922409234092440925409264092740928409294093040931409324093340934409354093640937409384093940940409414094240943409444094540946409474094840949409504095140952409534095440955409564095740958409594096040961409624096340964409654096640967409684096940970409714097240973409744097540976409774097840979409804098140982409834098440985409864098740988409894099040991409924099340994409954099640997409984099941000410014100241003410044100541006410074100841009410104101141012410134101441015410164101741018410194102041021410224102341024410254102641027410284102941030410314103241033410344103541036410374103841039410404104141042410434104441045410464104741048410494105041051410524105341054410554105641057410584105941060410614106241063410644106541066410674106841069410704107141072410734107441075410764107741078410794108041081410824108341084410854108641087410884108941090410914109241093410944109541096410974109841099411004110141102411034110441105411064110741108411094111041111411124111341114411154111641117411184111941120411214112241123411244112541126411274112841129411304113141132411334113441135411364113741138411394114041141411424114341144411454114641147411484114941150411514115241153411544115541156411574115841159411604116141162411634116441165411664116741168411694117041171411724117341174411754117641177411784117941180411814118241183411844118541186411874118841189411904119141192411934119441195411964119741198411994120041201412024120341204412054120641207412084120941210412114121241213412144121541216412174121841219412204122141222412234122441225412264122741228412294123041231412324123341234412354123641237412384123941240412414124241243412444124541246412474124841249412504125141252412534125441255412564125741258412594126041261412624126341264412654126641267412684126941270412714127241273412744127541276412774127841279412804128141282412834128441285412864128741288412894129041291412924129341294412954129641297412984129941300413014130241303413044130541306413074130841309413104131141312413134131441315413164131741318413194132041321413224132341324413254132641327413284132941330413314133241333413344133541336413374133841339413404134141342413434134441345413464134741348413494135041351413524135341354413554135641357413584135941360413614136241363413644136541366413674136841369413704137141372413734137441375413764137741378413794138041381413824138341384413854138641387413884138941390413914139241393413944139541396413974139841399414004140141402414034140441405414064140741408414094141041411414124141341414414154141641417414184141941420414214142241423414244142541426414274142841429414304143141432414334143441435414364143741438414394144041441414424144341444414454144641447414484144941450414514145241453414544145541456414574145841459414604146141462414634146441465414664146741468414694147041471414724147341474414754147641477414784147941480414814148241483414844148541486414874148841489414904149141492414934149441495414964149741498414994150041501415024150341504415054150641507415084150941510415114151241513415144151541516415174151841519415204152141522415234152441525415264152741528415294153041531415324153341534415354153641537415384153941540415414154241543415444154541546415474154841549415504155141552415534155441555415564155741558415594156041561415624156341564415654156641567415684156941570415714157241573415744157541576415774157841579415804158141582415834158441585415864158741588415894159041591415924159341594415954159641597415984159941600416014160241603416044160541606416074160841609416104161141612416134161441615416164161741618416194162041621416224162341624416254162641627416284162941630416314163241633416344163541636416374163841639416404164141642416434164441645416464164741648416494165041651416524165341654416554165641657416584165941660416614166241663416644166541666416674166841669416704167141672416734167441675416764167741678416794168041681416824168341684416854168641687416884168941690416914169241693416944169541696416974169841699417004170141702417034170441705417064170741708417094171041711417124171341714417154171641717417184171941720417214172241723417244172541726417274172841729417304173141732417334173441735417364173741738417394174041741417424174341744417454174641747417484174941750417514175241753417544175541756417574175841759417604176141762417634176441765417664176741768417694177041771417724177341774417754177641777417784177941780417814178241783417844178541786417874178841789417904179141792417934179441795417964179741798417994180041801418024180341804418054180641807418084180941810418114181241813418144181541816418174181841819418204182141822418234182441825418264182741828418294183041831418324183341834418354183641837418384183941840418414184241843418444184541846418474184841849418504185141852418534185441855418564185741858418594186041861418624186341864418654186641867418684186941870418714187241873418744187541876418774187841879418804188141882418834188441885418864188741888418894189041891418924189341894418954189641897418984189941900419014190241903419044190541906419074190841909419104191141912419134191441915419164191741918419194192041921419224192341924419254192641927419284192941930419314193241933419344193541936419374193841939419404194141942419434194441945419464194741948419494195041951419524195341954419554195641957419584195941960419614196241963419644196541966419674196841969419704197141972419734197441975419764197741978419794198041981419824198341984419854198641987419884198941990419914199241993419944199541996419974199841999420004200142002420034200442005420064200742008420094201042011420124201342014420154201642017420184201942020420214202242023420244202542026420274202842029420304203142032420334203442035420364203742038420394204042041420424204342044420454204642047420484204942050420514205242053420544205542056420574205842059420604206142062420634206442065420664206742068420694207042071420724207342074420754207642077420784207942080420814208242083420844208542086420874208842089420904209142092420934209442095420964209742098420994210042101421024210342104421054210642107421084210942110421114211242113421144211542116421174211842119421204212142122421234212442125421264212742128421294213042131421324213342134421354213642137421384213942140421414214242143421444214542146421474214842149421504215142152421534215442155421564215742158421594216042161421624216342164421654216642167421684216942170421714217242173421744217542176421774217842179421804218142182421834218442185421864218742188421894219042191421924219342194421954219642197421984219942200422014220242203422044220542206422074220842209422104221142212422134221442215422164221742218422194222042221422224222342224422254222642227422284222942230422314223242233422344223542236422374223842239422404224142242422434224442245422464224742248422494225042251422524225342254422554225642257422584225942260422614226242263422644226542266422674226842269422704227142272422734227442275422764227742278422794228042281422824228342284422854228642287422884228942290422914229242293422944229542296422974229842299423004230142302423034230442305423064230742308423094231042311423124231342314423154231642317423184231942320423214232242323423244232542326423274232842329423304233142332423334233442335423364233742338423394234042341423424234342344423454234642347423484234942350423514235242353423544235542356423574235842359423604236142362423634236442365423664236742368423694237042371423724237342374423754237642377423784237942380423814238242383423844238542386423874238842389423904239142392423934239442395423964239742398423994240042401424024240342404424054240642407424084240942410424114241242413424144241542416424174241842419424204242142422424234242442425424264242742428424294243042431424324243342434424354243642437424384243942440424414244242443424444244542446424474244842449424504245142452424534245442455424564245742458424594246042461424624246342464424654246642467424684246942470424714247242473424744247542476424774247842479424804248142482424834248442485424864248742488424894249042491424924249342494424954249642497424984249942500425014250242503425044250542506425074250842509425104251142512425134251442515425164251742518425194252042521425224252342524425254252642527425284252942530425314253242533425344253542536425374253842539425404254142542425434254442545425464254742548425494255042551425524255342554425554255642557425584255942560425614256242563425644256542566425674256842569425704257142572425734257442575425764257742578425794258042581425824258342584425854258642587425884258942590425914259242593425944259542596425974259842599426004260142602426034260442605426064260742608426094261042611426124261342614426154261642617426184261942620426214262242623426244262542626426274262842629426304263142632426334263442635426364263742638426394264042641426424264342644426454264642647426484264942650426514265242653426544265542656426574265842659426604266142662426634266442665426664266742668426694267042671426724267342674426754267642677426784267942680426814268242683426844268542686426874268842689426904269142692426934269442695426964269742698426994270042701427024270342704427054270642707427084270942710427114271242713427144271542716427174271842719427204272142722427234272442725427264272742728427294273042731427324273342734427354273642737427384273942740427414274242743427444274542746427474274842749427504275142752427534275442755427564275742758427594276042761427624276342764427654276642767427684276942770427714277242773427744277542776427774277842779427804278142782427834278442785427864278742788427894279042791427924279342794427954279642797427984279942800428014280242803428044280542806428074280842809428104281142812428134281442815428164281742818428194282042821428224282342824428254282642827428284282942830428314283242833428344283542836428374283842839428404284142842428434284442845428464284742848428494285042851428524285342854428554285642857428584285942860428614286242863428644286542866428674286842869428704287142872428734287442875428764287742878428794288042881428824288342884428854288642887428884288942890428914289242893428944289542896428974289842899429004290142902429034290442905429064290742908429094291042911429124291342914429154291642917429184291942920429214292242923429244292542926429274292842929429304293142932429334293442935429364293742938429394294042941429424294342944429454294642947429484294942950429514295242953429544295542956429574295842959429604296142962429634296442965429664296742968429694297042971429724297342974429754297642977429784297942980429814298242983429844298542986429874298842989429904299142992429934299442995429964299742998429994300043001430024300343004430054300643007430084300943010430114301243013430144301543016430174301843019430204302143022430234302443025430264302743028430294303043031430324303343034430354303643037430384303943040430414304243043430444304543046430474304843049430504305143052430534305443055430564305743058430594306043061430624306343064430654306643067430684306943070430714307243073430744307543076430774307843079430804308143082430834308443085430864308743088430894309043091430924309343094430954309643097430984309943100431014310243103431044310543106431074310843109431104311143112431134311443115431164311743118431194312043121431224312343124431254312643127431284312943130431314313243133431344313543136431374313843139431404314143142431434314443145431464314743148431494315043151431524315343154431554315643157431584315943160431614316243163431644316543166431674316843169431704317143172431734317443175431764317743178431794318043181431824318343184431854318643187431884318943190431914319243193431944319543196431974319843199432004320143202432034320443205432064320743208432094321043211432124321343214432154321643217432184321943220432214322243223432244322543226432274322843229432304323143232432334323443235432364323743238432394324043241432424324343244432454324643247432484324943250432514325243253432544325543256432574325843259432604326143262432634326443265432664326743268432694327043271432724327343274432754327643277432784327943280432814328243283432844328543286432874328843289432904329143292432934329443295432964329743298432994330043301433024330343304433054330643307433084330943310433114331243313433144331543316433174331843319433204332143322433234332443325433264332743328433294333043331433324333343334433354333643337433384333943340433414334243343433444334543346433474334843349433504335143352433534335443355433564335743358433594336043361433624336343364433654336643367433684336943370433714337243373433744337543376433774337843379433804338143382433834338443385433864338743388433894339043391433924339343394433954339643397433984339943400434014340243403434044340543406434074340843409434104341143412434134341443415434164341743418434194342043421434224342343424434254342643427434284342943430434314343243433434344343543436434374343843439434404344143442434434344443445434464344743448434494345043451434524345343454434554345643457434584345943460434614346243463434644346543466434674346843469434704347143472434734347443475434764347743478434794348043481434824348343484434854348643487434884348943490434914349243493434944349543496434974349843499435004350143502435034350443505435064350743508435094351043511435124351343514435154351643517435184351943520435214352243523435244352543526435274352843529435304353143532435334353443535435364353743538435394354043541435424354343544435454354643547435484354943550435514355243553435544355543556435574355843559435604356143562435634356443565435664356743568435694357043571435724357343574435754357643577435784357943580435814358243583435844358543586435874358843589435904359143592435934359443595435964359743598435994360043601436024360343604436054360643607436084360943610436114361243613436144361543616436174361843619436204362143622436234362443625436264362743628436294363043631436324363343634436354363643637436384363943640436414364243643436444364543646436474364843649436504365143652436534365443655436564365743658436594366043661436624366343664436654366643667436684366943670436714367243673436744367543676436774367843679436804368143682436834368443685436864368743688436894369043691436924369343694436954369643697436984369943700437014370243703437044370543706437074370843709437104371143712437134371443715437164371743718437194372043721437224372343724437254372643727437284372943730437314373243733437344373543736437374373843739437404374143742437434374443745437464374743748437494375043751437524375343754437554375643757437584375943760437614376243763437644376543766437674376843769437704377143772437734377443775437764377743778437794378043781437824378343784437854378643787437884378943790437914379243793437944379543796437974379843799438004380143802438034380443805438064380743808438094381043811438124381343814438154381643817438184381943820438214382243823438244382543826438274382843829438304383143832438334383443835438364383743838438394384043841438424384343844438454384643847438484384943850438514385243853438544385543856438574385843859438604386143862438634386443865438664386743868438694387043871438724387343874438754387643877438784387943880438814388243883438844388543886438874388843889438904389143892438934389443895438964389743898438994390043901439024390343904439054390643907439084390943910439114391243913439144391543916439174391843919439204392143922439234392443925439264392743928439294393043931439324393343934439354393643937439384393943940439414394243943439444394543946439474394843949439504395143952439534395443955439564395743958439594396043961439624396343964439654396643967439684396943970439714397243973439744397543976439774397843979439804398143982439834398443985439864398743988439894399043991439924399343994439954399643997439984399944000440014400244003440044400544006440074400844009440104401144012440134401444015440164401744018440194402044021440224402344024440254402644027440284402944030440314403244033440344403544036440374403844039440404404144042440434404444045440464404744048440494405044051440524405344054440554405644057440584405944060440614406244063440644406544066440674406844069440704407144072440734407444075440764407744078440794408044081440824408344084440854408644087440884408944090440914409244093440944409544096440974409844099441004410144102441034410444105441064410744108441094411044111441124411344114441154411644117441184411944120441214412244123441244412544126441274412844129441304413144132441334413444135441364413744138441394414044141441424414344144441454414644147441484414944150441514415244153441544415544156441574415844159441604416144162441634416444165441664416744168441694417044171441724417344174441754417644177441784417944180441814418244183441844418544186441874418844189441904419144192441934419444195441964419744198441994420044201442024420344204442054420644207442084420944210442114421244213442144421544216442174421844219442204422144222442234422444225442264422744228442294423044231442324423344234442354423644237442384423944240442414424244243442444424544246442474424844249442504425144252442534425444255442564425744258442594426044261442624426344264442654426644267442684426944270442714427244273442744427544276442774427844279442804428144282442834428444285442864428744288442894429044291442924429344294442954429644297442984429944300443014430244303443044430544306443074430844309443104431144312443134431444315443164431744318443194432044321443224432344324443254432644327443284432944330443314433244333443344433544336443374433844339443404434144342443434434444345443464434744348443494435044351443524435344354443554435644357443584435944360443614436244363443644436544366443674436844369443704437144372443734437444375443764437744378443794438044381443824438344384443854438644387443884438944390443914439244393443944439544396443974439844399444004440144402444034440444405444064440744408444094441044411444124441344414444154441644417444184441944420444214442244423444244442544426444274442844429444304443144432444334443444435444364443744438444394444044441444424444344444444454444644447444484444944450444514445244453444544445544456444574445844459444604446144462444634446444465444664446744468444694447044471444724447344474444754447644477444784447944480444814448244483444844448544486444874448844489444904449144492444934449444495444964449744498444994450044501445024450344504445054450644507445084450944510445114451244513445144451544516445174451844519445204452144522445234452444525445264452744528445294453044531445324453344534445354453644537445384453944540445414454244543445444454544546445474454844549445504455144552445534455444555445564455744558445594456044561445624456344564445654456644567445684456944570445714457244573445744457544576445774457844579445804458144582445834458444585445864458744588445894459044591445924459344594445954459644597445984459944600446014460244603446044460544606446074460844609446104461144612446134461444615446164461744618446194462044621446224462344624446254462644627446284462944630446314463244633446344463544636446374463844639446404464144642446434464444645446464464744648446494465044651446524465344654446554465644657446584465944660446614466244663446644466544666446674466844669446704467144672446734467444675446764467744678446794468044681446824468344684446854468644687446884468944690446914469244693446944469544696446974469844699447004470144702447034470444705447064470744708447094471044711447124471344714447154471644717447184471944720447214472244723447244472544726447274472844729447304473144732447334473444735447364473744738447394474044741447424474344744447454474644747447484474944750447514475244753447544475544756447574475844759447604476144762447634476444765447664476744768447694477044771447724477344774447754477644777447784477944780447814478244783447844478544786447874478844789447904479144792447934479444795447964479744798447994480044801448024480344804448054480644807448084480944810448114481244813448144481544816448174481844819448204482144822448234482444825448264482744828448294483044831448324483344834448354483644837448384483944840448414484244843448444484544846448474484844849448504485144852448534485444855448564485744858448594486044861448624486344864448654486644867448684486944870448714487244873448744487544876448774487844879448804488144882448834488444885448864488744888448894489044891448924489344894448954489644897448984489944900449014490244903449044490544906449074490844909449104491144912449134491444915449164491744918449194492044921449224492344924449254492644927449284492944930449314493244933449344493544936449374493844939449404494144942449434494444945449464494744948449494495044951449524495344954449554495644957449584495944960449614496244963449644496544966449674496844969449704497144972449734497444975449764497744978449794498044981449824498344984449854498644987449884498944990449914499244993449944499544996449974499844999450004500145002450034500445005450064500745008450094501045011450124501345014450154501645017450184501945020450214502245023450244502545026450274502845029450304503145032450334503445035450364503745038450394504045041450424504345044450454504645047450484504945050450514505245053450544505545056450574505845059450604506145062450634506445065450664506745068450694507045071450724507345074450754507645077450784507945080450814508245083450844508545086450874508845089450904509145092450934509445095450964509745098450994510045101451024510345104451054510645107451084510945110451114511245113451144511545116451174511845119451204512145122451234512445125451264512745128451294513045131451324513345134451354513645137451384513945140451414514245143451444514545146451474514845149451504515145152451534515445155451564515745158451594516045161451624516345164451654516645167451684516945170451714517245173451744517545176451774517845179451804518145182451834518445185451864518745188451894519045191451924519345194451954519645197451984519945200452014520245203452044520545206452074520845209452104521145212452134521445215452164521745218452194522045221452224522345224452254522645227452284522945230452314523245233452344523545236452374523845239452404524145242452434524445245452464524745248452494525045251452524525345254452554525645257452584525945260452614526245263452644526545266452674526845269452704527145272452734527445275452764527745278452794528045281452824528345284452854528645287452884528945290452914529245293452944529545296452974529845299453004530145302453034530445305453064530745308453094531045311453124531345314453154531645317453184531945320453214532245323453244532545326453274532845329453304533145332453334533445335453364533745338453394534045341453424534345344453454534645347453484534945350453514535245353453544535545356453574535845359453604536145362453634536445365453664536745368453694537045371453724537345374453754537645377453784537945380453814538245383453844538545386453874538845389453904539145392453934539445395453964539745398453994540045401454024540345404454054540645407454084540945410454114541245413454144541545416454174541845419454204542145422454234542445425454264542745428454294543045431454324543345434454354543645437454384543945440454414544245443454444544545446454474544845449454504545145452454534545445455454564545745458454594546045461454624546345464454654546645467454684546945470454714547245473454744547545476454774547845479454804548145482454834548445485454864548745488454894549045491454924549345494454954549645497454984549945500455014550245503455044550545506455074550845509455104551145512455134551445515455164551745518455194552045521455224552345524455254552645527455284552945530455314553245533455344553545536455374553845539455404554145542455434554445545455464554745548455494555045551455524555345554455554555645557455584555945560455614556245563455644556545566455674556845569455704557145572455734557445575455764557745578455794558045581455824558345584455854558645587455884558945590455914559245593455944559545596455974559845599456004560145602456034560445605456064560745608456094561045611456124561345614456154561645617456184561945620456214562245623456244562545626456274562845629456304563145632456334563445635456364563745638456394564045641456424564345644456454564645647456484564945650456514565245653456544565545656456574565845659456604566145662456634566445665456664566745668456694567045671456724567345674456754567645677456784567945680456814568245683456844568545686456874568845689456904569145692456934569445695456964569745698456994570045701457024570345704457054570645707457084570945710457114571245713457144571545716457174571845719457204572145722457234572445725457264572745728457294573045731457324573345734457354573645737457384573945740457414574245743457444574545746457474574845749457504575145752457534575445755457564575745758457594576045761457624576345764457654576645767457684576945770457714577245773457744577545776457774577845779457804578145782457834578445785457864578745788457894579045791457924579345794457954579645797457984579945800458014580245803458044580545806458074580845809458104581145812458134581445815458164581745818458194582045821458224582345824458254582645827458284582945830458314583245833458344583545836458374583845839458404584145842458434584445845458464584745848458494585045851458524585345854458554585645857458584585945860458614586245863458644586545866458674586845869458704587145872458734587445875458764587745878458794588045881458824588345884458854588645887458884588945890458914589245893458944589545896458974589845899459004590145902459034590445905459064590745908459094591045911459124591345914459154591645917459184591945920459214592245923459244592545926459274592845929459304593145932459334593445935459364593745938459394594045941459424594345944459454594645947459484594945950459514595245953459544595545956459574595845959459604596145962459634596445965459664596745968459694597045971459724597345974459754597645977459784597945980459814598245983459844598545986459874598845989459904599145992459934599445995459964599745998459994600046001460024600346004460054600646007460084600946010460114601246013460144601546016460174601846019460204602146022460234602446025460264602746028460294603046031460324603346034460354603646037460384603946040460414604246043460444604546046460474604846049460504605146052460534605446055460564605746058460594606046061460624606346064460654606646067460684606946070460714607246073460744607546076460774607846079460804608146082460834608446085460864608746088460894609046091460924609346094460954609646097460984609946100461014610246103461044610546106461074610846109461104611146112461134611446115461164611746118461194612046121461224612346124461254612646127461284612946130461314613246133461344613546136461374613846139461404614146142461434614446145461464614746148461494615046151461524615346154461554615646157461584615946160461614616246163461644616546166461674616846169461704617146172461734617446175461764617746178461794618046181461824618346184461854618646187461884618946190461914619246193461944619546196461974619846199462004620146202462034620446205462064620746208462094621046211462124621346214462154621646217462184621946220462214622246223462244622546226462274622846229462304623146232462334623446235462364623746238462394624046241462424624346244462454624646247462484624946250462514625246253462544625546256462574625846259462604626146262462634626446265462664626746268462694627046271462724627346274462754627646277462784627946280462814628246283462844628546286462874628846289462904629146292462934629446295462964629746298462994630046301463024630346304463054630646307463084630946310463114631246313463144631546316463174631846319463204632146322463234632446325463264632746328463294633046331463324633346334463354633646337463384633946340463414634246343463444634546346463474634846349463504635146352463534635446355463564635746358463594636046361463624636346364463654636646367463684636946370463714637246373463744637546376463774637846379463804638146382463834638446385463864638746388463894639046391463924639346394463954639646397463984639946400464014640246403464044640546406464074640846409464104641146412464134641446415464164641746418464194642046421464224642346424464254642646427464284642946430464314643246433464344643546436464374643846439464404644146442464434644446445464464644746448464494645046451464524645346454464554645646457464584645946460464614646246463464644646546466464674646846469464704647146472464734647446475464764647746478464794648046481464824648346484464854648646487464884648946490464914649246493464944649546496464974649846499465004650146502465034650446505465064650746508465094651046511465124651346514465154651646517465184651946520465214652246523465244652546526465274652846529465304653146532465334653446535465364653746538465394654046541465424654346544465454654646547465484654946550465514655246553465544655546556465574655846559465604656146562465634656446565465664656746568465694657046571465724657346574465754657646577465784657946580465814658246583465844658546586465874658846589465904659146592465934659446595465964659746598465994660046601466024660346604466054660646607466084660946610466114661246613466144661546616466174661846619466204662146622466234662446625466264662746628466294663046631466324663346634466354663646637466384663946640466414664246643466444664546646466474664846649466504665146652466534665446655466564665746658466594666046661466624666346664466654666646667466684666946670466714667246673466744667546676466774667846679466804668146682466834668446685466864668746688466894669046691466924669346694466954669646697466984669946700467014670246703467044670546706467074670846709467104671146712467134671446715467164671746718467194672046721467224672346724467254672646727467284672946730467314673246733467344673546736467374673846739467404674146742467434674446745467464674746748467494675046751467524675346754467554675646757467584675946760467614676246763467644676546766467674676846769467704677146772467734677446775467764677746778467794678046781467824678346784467854678646787467884678946790467914679246793467944679546796467974679846799468004680146802468034680446805468064680746808468094681046811468124681346814468154681646817468184681946820468214682246823468244682546826468274682846829468304683146832468334683446835468364683746838468394684046841468424684346844468454684646847468484684946850468514685246853468544685546856468574685846859468604686146862468634686446865468664686746868468694687046871468724687346874468754687646877468784687946880468814688246883468844688546886468874688846889468904689146892468934689446895468964689746898468994690046901469024690346904469054690646907469084690946910469114691246913469144691546916469174691846919469204692146922469234692446925469264692746928469294693046931469324693346934469354693646937469384693946940469414694246943469444694546946469474694846949469504695146952469534695446955469564695746958469594696046961469624696346964469654696646967469684696946970469714697246973469744697546976469774697846979469804698146982469834698446985469864698746988469894699046991469924699346994469954699646997469984699947000470014700247003470044700547006470074700847009470104701147012470134701447015470164701747018470194702047021470224702347024470254702647027470284702947030470314703247033470344703547036470374703847039470404704147042470434704447045470464704747048470494705047051470524705347054470554705647057470584705947060470614706247063470644706547066470674706847069470704707147072470734707447075470764707747078470794708047081470824708347084470854708647087470884708947090470914709247093470944709547096470974709847099471004710147102471034710447105471064710747108471094711047111471124711347114471154711647117471184711947120471214712247123471244712547126471274712847129471304713147132471334713447135471364713747138471394714047141471424714347144471454714647147471484714947150471514715247153471544715547156471574715847159471604716147162471634716447165471664716747168471694717047171471724717347174471754717647177471784717947180471814718247183471844718547186471874718847189471904719147192471934719447195471964719747198471994720047201472024720347204472054720647207472084720947210472114721247213472144721547216472174721847219472204722147222472234722447225472264722747228472294723047231472324723347234472354723647237472384723947240472414724247243472444724547246472474724847249472504725147252472534725447255472564725747258472594726047261472624726347264472654726647267472684726947270472714727247273472744727547276472774727847279472804728147282472834728447285472864728747288472894729047291472924729347294472954729647297472984729947300473014730247303473044730547306473074730847309473104731147312473134731447315473164731747318473194732047321473224732347324473254732647327473284732947330473314733247333473344733547336473374733847339473404734147342473434734447345473464734747348473494735047351473524735347354473554735647357473584735947360473614736247363473644736547366473674736847369473704737147372473734737447375473764737747378473794738047381473824738347384473854738647387473884738947390473914739247393473944739547396473974739847399474004740147402474034740447405474064740747408474094741047411474124741347414474154741647417474184741947420474214742247423474244742547426474274742847429474304743147432474334743447435474364743747438474394744047441474424744347444474454744647447474484744947450474514745247453474544745547456474574745847459474604746147462474634746447465474664746747468474694747047471474724747347474474754747647477474784747947480474814748247483474844748547486474874748847489474904749147492474934749447495474964749747498474994750047501475024750347504475054750647507475084750947510475114751247513475144751547516475174751847519475204752147522475234752447525475264752747528475294753047531475324753347534475354753647537475384753947540475414754247543475444754547546475474754847549475504755147552475534755447555475564755747558475594756047561475624756347564475654756647567475684756947570475714757247573475744757547576475774757847579475804758147582475834758447585475864758747588475894759047591475924759347594475954759647597475984759947600476014760247603476044760547606476074760847609476104761147612476134761447615476164761747618476194762047621476224762347624476254762647627476284762947630476314763247633476344763547636476374763847639476404764147642476434764447645476464764747648476494765047651476524765347654476554765647657476584765947660476614766247663476644766547666476674766847669476704767147672476734767447675476764767747678476794768047681476824768347684476854768647687476884768947690476914769247693476944769547696476974769847699477004770147702477034770447705477064770747708477094771047711477124771347714477154771647717477184771947720477214772247723477244772547726477274772847729477304773147732477334773447735477364773747738477394774047741477424774347744477454774647747477484774947750477514775247753477544775547756477574775847759477604776147762477634776447765477664776747768477694777047771477724777347774477754777647777477784777947780477814778247783477844778547786477874778847789477904779147792477934779447795477964779747798477994780047801478024780347804478054780647807478084780947810478114781247813478144781547816478174781847819478204782147822478234782447825478264782747828478294783047831478324783347834478354783647837478384783947840478414784247843478444784547846478474784847849478504785147852478534785447855478564785747858478594786047861478624786347864478654786647867478684786947870478714787247873478744787547876478774787847879478804788147882478834788447885478864788747888478894789047891478924789347894478954789647897478984789947900479014790247903479044790547906479074790847909479104791147912479134791447915479164791747918479194792047921479224792347924479254792647927479284792947930479314793247933479344793547936479374793847939479404794147942479434794447945479464794747948479494795047951479524795347954479554795647957479584795947960479614796247963479644796547966479674796847969479704797147972479734797447975479764797747978479794798047981479824798347984479854798647987479884798947990479914799247993479944799547996479974799847999480004800148002480034800448005480064800748008480094801048011480124801348014480154801648017480184801948020480214802248023480244802548026480274802848029480304803148032480334803448035480364803748038480394804048041480424804348044480454804648047480484804948050480514805248053480544805548056480574805848059480604806148062480634806448065480664806748068480694807048071480724807348074480754807648077480784807948080480814808248083480844808548086480874808848089480904809148092480934809448095480964809748098480994810048101481024810348104481054810648107481084810948110481114811248113481144811548116481174811848119481204812148122481234812448125481264812748128481294813048131481324813348134481354813648137481384813948140481414814248143481444814548146481474814848149481504815148152481534815448155481564815748158481594816048161481624816348164481654816648167481684816948170481714817248173481744817548176481774817848179481804818148182481834818448185481864818748188481894819048191481924819348194481954819648197481984819948200482014820248203482044820548206482074820848209482104821148212482134821448215482164821748218482194822048221482224822348224482254822648227482284822948230482314823248233482344823548236482374823848239482404824148242482434824448245482464824748248482494825048251482524825348254482554825648257482584825948260482614826248263482644826548266482674826848269482704827148272482734827448275482764827748278482794828048281482824828348284482854828648287482884828948290482914829248293482944829548296482974829848299483004830148302483034830448305483064830748308483094831048311483124831348314483154831648317483184831948320483214832248323483244832548326483274832848329483304833148332483334833448335483364833748338483394834048341483424834348344483454834648347483484834948350483514835248353483544835548356483574835848359483604836148362483634836448365483664836748368483694837048371483724837348374483754837648377483784837948380483814838248383483844838548386483874838848389483904839148392483934839448395483964839748398483994840048401484024840348404484054840648407484084840948410484114841248413484144841548416484174841848419484204842148422484234842448425484264842748428484294843048431484324843348434484354843648437484384843948440484414844248443484444844548446484474844848449484504845148452484534845448455484564845748458484594846048461484624846348464484654846648467484684846948470484714847248473484744847548476484774847848479484804848148482484834848448485484864848748488484894849048491484924849348494484954849648497484984849948500485014850248503485044850548506485074850848509485104851148512485134851448515485164851748518485194852048521485224852348524485254852648527485284852948530485314853248533485344853548536485374853848539485404854148542485434854448545485464854748548485494855048551485524855348554485554855648557485584855948560485614856248563485644856548566485674856848569485704857148572485734857448575485764857748578485794858048581485824858348584485854858648587485884858948590485914859248593485944859548596485974859848599486004860148602486034860448605486064860748608486094861048611486124861348614486154861648617486184861948620486214862248623486244862548626486274862848629486304863148632486334863448635486364863748638486394864048641486424864348644486454864648647486484864948650486514865248653486544865548656486574865848659486604866148662486634866448665486664866748668486694867048671486724867348674486754867648677486784867948680486814868248683486844868548686486874868848689486904869148692486934869448695486964869748698486994870048701487024870348704487054870648707487084870948710487114871248713487144871548716487174871848719487204872148722487234872448725487264872748728487294873048731487324873348734487354873648737487384873948740487414874248743487444874548746487474874848749487504875148752487534875448755487564875748758487594876048761487624876348764487654876648767487684876948770487714877248773487744877548776487774877848779487804878148782487834878448785487864878748788487894879048791487924879348794487954879648797487984879948800488014880248803488044880548806488074880848809488104881148812488134881448815488164881748818488194882048821488224882348824488254882648827488284882948830488314883248833488344883548836488374883848839488404884148842488434884448845488464884748848488494885048851488524885348854488554885648857488584885948860488614886248863488644886548866488674886848869488704887148872488734887448875488764887748878488794888048881488824888348884488854888648887488884888948890488914889248893488944889548896488974889848899489004890148902489034890448905489064890748908489094891048911489124891348914489154891648917489184891948920489214892248923489244892548926489274892848929489304893148932489334893448935489364893748938489394894048941489424894348944489454894648947489484894948950489514895248953489544895548956489574895848959489604896148962489634896448965489664896748968489694897048971489724897348974489754897648977489784897948980489814898248983489844898548986489874898848989489904899148992489934899448995489964899748998489994900049001490024900349004490054900649007490084900949010490114901249013490144901549016490174901849019490204902149022490234902449025490264902749028490294903049031490324903349034490354903649037490384903949040490414904249043490444904549046490474904849049490504905149052490534905449055490564905749058490594906049061490624906349064490654906649067490684906949070490714907249073490744907549076490774907849079490804908149082490834908449085490864908749088490894909049091490924909349094490954909649097490984909949100491014910249103491044910549106491074910849109491104911149112491134911449115491164911749118491194912049121491224912349124491254912649127491284912949130491314913249133491344913549136491374913849139491404914149142491434914449145491464914749148491494915049151491524915349154491554915649157491584915949160491614916249163491644916549166491674916849169491704917149172491734917449175491764917749178491794918049181491824918349184491854918649187491884918949190491914919249193491944919549196491974919849199492004920149202492034920449205492064920749208492094921049211492124921349214492154921649217492184921949220492214922249223492244922549226492274922849229492304923149232492334923449235492364923749238492394924049241492424924349244492454924649247492484924949250492514925249253492544925549256492574925849259492604926149262492634926449265492664926749268492694927049271492724927349274492754927649277492784927949280492814928249283492844928549286492874928849289492904929149292492934929449295492964929749298492994930049301493024930349304493054930649307493084930949310493114931249313493144931549316493174931849319493204932149322493234932449325493264932749328493294933049331493324933349334493354933649337493384933949340493414934249343493444934549346493474934849349493504935149352493534935449355493564935749358493594936049361493624936349364493654936649367493684936949370493714937249373493744937549376493774937849379493804938149382493834938449385493864938749388493894939049391493924939349394493954939649397493984939949400494014940249403494044940549406494074940849409494104941149412494134941449415494164941749418494194942049421494224942349424494254942649427494284942949430494314943249433494344943549436494374943849439494404944149442494434944449445494464944749448494494945049451494524945349454494554945649457494584945949460494614946249463494644946549466494674946849469494704947149472494734947449475494764947749478494794948049481494824948349484494854948649487494884948949490494914949249493494944949549496494974949849499495004950149502495034950449505495064950749508495094951049511495124951349514495154951649517495184951949520495214952249523495244952549526495274952849529495304953149532495334953449535495364953749538495394954049541495424954349544495454954649547495484954949550495514955249553495544955549556495574955849559495604956149562495634956449565495664956749568495694957049571495724957349574495754957649577495784957949580495814958249583495844958549586495874958849589495904959149592495934959449595495964959749598495994960049601496024960349604496054960649607496084960949610496114961249613496144961549616496174961849619496204962149622496234962449625496264962749628496294963049631496324963349634496354963649637496384963949640496414964249643496444964549646496474964849649496504965149652496534965449655496564965749658496594966049661496624966349664496654966649667496684966949670496714967249673496744967549676496774967849679496804968149682496834968449685496864968749688496894969049691496924969349694496954969649697496984969949700497014970249703497044970549706497074970849709497104971149712497134971449715497164971749718497194972049721497224972349724497254972649727497284972949730497314973249733497344973549736497374973849739497404974149742497434974449745497464974749748497494975049751497524975349754497554975649757497584975949760497614976249763497644976549766497674976849769497704977149772497734977449775497764977749778497794978049781497824978349784497854978649787497884978949790497914979249793497944979549796497974979849799498004980149802498034980449805498064980749808498094981049811498124981349814498154981649817498184981949820498214982249823498244982549826498274982849829498304983149832498334983449835498364983749838498394984049841498424984349844498454984649847498484984949850498514985249853498544985549856498574985849859498604986149862498634986449865498664986749868498694987049871498724987349874498754987649877498784987949880498814988249883498844988549886498874988849889498904989149892498934989449895498964989749898498994990049901499024990349904499054990649907499084990949910499114991249913499144991549916499174991849919499204992149922499234992449925499264992749928499294993049931499324993349934499354993649937499384993949940499414994249943499444994549946499474994849949499504995149952499534995449955499564995749958499594996049961499624996349964499654996649967499684996949970499714997249973499744997549976499774997849979499804998149982499834998449985499864998749988499894999049991499924999349994499954999649997499984999950000500015000250003500045000550006500075000850009500105001150012500135001450015500165001750018500195002050021500225002350024500255002650027500285002950030500315003250033500345003550036500375003850039500405004150042500435004450045500465004750048500495005050051500525005350054500555005650057500585005950060500615006250063500645006550066500675006850069500705007150072500735007450075500765007750078500795008050081500825008350084500855008650087500885008950090500915009250093500945009550096500975009850099501005010150102501035010450105501065010750108501095011050111501125011350114501155011650117501185011950120501215012250123501245012550126501275012850129501305013150132501335013450135501365013750138501395014050141501425014350144501455014650147501485014950150501515015250153501545015550156501575015850159501605016150162501635016450165501665016750168501695017050171501725017350174501755017650177501785017950180501815018250183501845018550186501875018850189501905019150192501935019450195501965019750198501995020050201502025020350204502055020650207502085020950210502115021250213502145021550216502175021850219502205022150222502235022450225502265022750228502295023050231502325023350234502355023650237502385023950240502415024250243502445024550246502475024850249502505025150252502535025450255502565025750258502595026050261502625026350264502655026650267502685026950270502715027250273502745027550276502775027850279502805028150282502835028450285502865028750288502895029050291502925029350294502955029650297502985029950300503015030250303503045030550306503075030850309503105031150312503135031450315503165031750318503195032050321503225032350324503255032650327503285032950330503315033250333503345033550336503375033850339503405034150342503435034450345503465034750348503495035050351503525035350354503555035650357503585035950360503615036250363503645036550366503675036850369503705037150372503735037450375503765037750378503795038050381503825038350384503855038650387503885038950390503915039250393503945039550396503975039850399504005040150402504035040450405504065040750408504095041050411504125041350414504155041650417504185041950420504215042250423504245042550426504275042850429504305043150432504335043450435504365043750438504395044050441504425044350444504455044650447504485044950450504515045250453504545045550456504575045850459504605046150462504635046450465504665046750468504695047050471504725047350474504755047650477504785047950480504815048250483504845048550486504875048850489504905049150492504935049450495504965049750498504995050050501505025050350504505055050650507505085050950510505115051250513505145051550516505175051850519505205052150522505235052450525505265052750528505295053050531505325053350534505355053650537505385053950540505415054250543505445054550546505475054850549505505055150552505535055450555505565055750558505595056050561505625056350564505655056650567505685056950570505715057250573505745057550576505775057850579505805058150582505835058450585505865058750588505895059050591505925059350594505955059650597505985059950600506015060250603506045060550606506075060850609506105061150612506135061450615506165061750618506195062050621506225062350624506255062650627506285062950630506315063250633506345063550636506375063850639506405064150642506435064450645506465064750648506495065050651506525065350654506555065650657506585065950660506615066250663506645066550666506675066850669506705067150672506735067450675506765067750678506795068050681506825068350684506855068650687506885068950690506915069250693506945069550696506975069850699507005070150702507035070450705507065070750708507095071050711507125071350714507155071650717507185071950720507215072250723507245072550726507275072850729507305073150732507335073450735507365073750738507395074050741507425074350744507455074650747507485074950750507515075250753507545075550756507575075850759507605076150762507635076450765507665076750768507695077050771507725077350774507755077650777507785077950780507815078250783507845078550786507875078850789507905079150792507935079450795507965079750798507995080050801508025080350804508055080650807508085080950810508115081250813508145081550816508175081850819508205082150822508235082450825508265082750828508295083050831508325083350834508355083650837508385083950840508415084250843508445084550846508475084850849508505085150852508535085450855508565085750858508595086050861508625086350864508655086650867508685086950870508715087250873508745087550876508775087850879508805088150882508835088450885508865088750888508895089050891508925089350894508955089650897508985089950900509015090250903509045090550906509075090850909509105091150912509135091450915509165091750918509195092050921509225092350924509255092650927509285092950930509315093250933509345093550936509375093850939509405094150942509435094450945509465094750948509495095050951509525095350954509555095650957509585095950960509615096250963509645096550966509675096850969509705097150972509735097450975509765097750978509795098050981509825098350984509855098650987509885098950990509915099250993509945099550996509975099850999510005100151002510035100451005510065100751008510095101051011510125101351014510155101651017510185101951020510215102251023510245102551026510275102851029510305103151032510335103451035510365103751038510395104051041510425104351044510455104651047510485104951050510515105251053510545105551056510575105851059510605106151062510635106451065510665106751068510695107051071510725107351074510755107651077510785107951080510815108251083510845108551086510875108851089510905109151092510935109451095510965109751098510995110051101511025110351104511055110651107511085110951110511115111251113511145111551116511175111851119511205112151122511235112451125511265112751128511295113051131511325113351134511355113651137511385113951140511415114251143511445114551146511475114851149511505115151152511535115451155511565115751158511595116051161511625116351164511655116651167511685116951170511715117251173511745117551176511775117851179511805118151182511835118451185511865118751188511895119051191511925119351194511955119651197511985119951200512015120251203512045120551206512075120851209512105121151212512135121451215512165121751218512195122051221512225122351224512255122651227512285122951230512315123251233512345123551236512375123851239512405124151242512435124451245512465124751248512495125051251512525125351254512555125651257512585125951260512615126251263512645126551266512675126851269512705127151272512735127451275512765127751278512795128051281512825128351284512855128651287512885128951290512915129251293512945129551296512975129851299513005130151302513035130451305513065130751308513095131051311513125131351314513155131651317513185131951320513215132251323513245132551326513275132851329513305133151332513335133451335513365133751338513395134051341513425134351344513455134651347513485134951350513515135251353513545135551356513575135851359513605136151362513635136451365513665136751368513695137051371513725137351374513755137651377513785137951380513815138251383513845138551386513875138851389513905139151392513935139451395513965139751398513995140051401514025140351404514055140651407514085140951410514115141251413514145141551416514175141851419514205142151422514235142451425514265142751428514295143051431514325143351434514355143651437514385143951440514415144251443514445144551446514475144851449514505145151452514535145451455514565145751458514595146051461514625146351464514655146651467514685146951470514715147251473514745147551476514775147851479514805148151482514835148451485514865148751488514895149051491514925149351494514955149651497514985149951500515015150251503515045150551506515075150851509515105151151512515135151451515515165151751518515195152051521515225152351524515255152651527515285152951530515315153251533515345153551536515375153851539515405154151542515435154451545515465154751548515495155051551515525155351554515555155651557515585155951560515615156251563515645156551566515675156851569515705157151572515735157451575515765157751578515795158051581515825158351584515855158651587515885158951590515915159251593515945159551596515975159851599516005160151602516035160451605516065160751608516095161051611516125161351614516155161651617516185161951620516215162251623516245162551626516275162851629516305163151632516335163451635516365163751638516395164051641516425164351644516455164651647516485164951650516515165251653516545165551656516575165851659516605166151662516635166451665516665166751668516695167051671516725167351674516755167651677516785167951680516815168251683516845168551686516875168851689516905169151692516935169451695516965169751698516995170051701517025170351704517055170651707517085170951710517115171251713517145171551716517175171851719517205172151722517235172451725517265172751728517295173051731517325173351734517355173651737517385173951740517415174251743517445174551746517475174851749517505175151752517535175451755517565175751758517595176051761517625176351764517655176651767517685176951770517715177251773517745177551776517775177851779517805178151782517835178451785517865178751788517895179051791517925179351794517955179651797517985179951800518015180251803518045180551806518075180851809518105181151812518135181451815518165181751818518195182051821518225182351824518255182651827518285182951830518315183251833518345183551836518375183851839518405184151842518435184451845518465184751848518495185051851518525185351854518555185651857518585185951860518615186251863518645186551866518675186851869518705187151872518735187451875518765187751878518795188051881518825188351884518855188651887518885188951890518915189251893518945189551896518975189851899519005190151902519035190451905519065190751908519095191051911519125191351914519155191651917519185191951920519215192251923519245192551926519275192851929519305193151932519335193451935519365193751938519395194051941519425194351944519455194651947519485194951950519515195251953519545195551956519575195851959519605196151962519635196451965519665196751968519695197051971519725197351974519755197651977519785197951980519815198251983519845198551986519875198851989519905199151992519935199451995519965199751998519995200052001520025200352004520055200652007520085200952010520115201252013520145201552016520175201852019520205202152022520235202452025520265202752028520295203052031520325203352034520355203652037520385203952040520415204252043520445204552046520475204852049520505205152052520535205452055520565205752058520595206052061520625206352064520655206652067520685206952070520715207252073520745207552076520775207852079520805208152082520835208452085520865208752088520895209052091520925209352094520955209652097520985209952100521015210252103521045210552106521075210852109521105211152112521135211452115521165211752118521195212052121521225212352124521255212652127521285212952130521315213252133521345213552136521375213852139521405214152142521435214452145521465214752148521495215052151521525215352154521555215652157521585215952160521615216252163521645216552166521675216852169521705217152172521735217452175521765217752178521795218052181521825218352184521855218652187521885218952190521915219252193521945219552196521975219852199522005220152202522035220452205522065220752208522095221052211522125221352214522155221652217522185221952220522215222252223522245222552226522275222852229522305223152232522335223452235522365223752238522395224052241522425224352244522455224652247522485224952250522515225252253522545225552256522575225852259522605226152262522635226452265522665226752268522695227052271522725227352274522755227652277522785227952280522815228252283522845228552286522875228852289522905229152292522935229452295522965229752298522995230052301523025230352304523055230652307523085230952310523115231252313523145231552316523175231852319523205232152322523235232452325523265232752328523295233052331523325233352334523355233652337523385233952340523415234252343523445234552346523475234852349523505235152352523535235452355523565235752358523595236052361523625236352364523655236652367523685236952370523715237252373523745237552376523775237852379523805238152382523835238452385523865238752388523895239052391523925239352394523955239652397523985239952400524015240252403524045240552406524075240852409524105241152412524135241452415524165241752418524195242052421524225242352424524255242652427524285242952430524315243252433524345243552436524375243852439524405244152442524435244452445524465244752448524495245052451524525245352454524555245652457524585245952460524615246252463524645246552466524675246852469524705247152472524735247452475524765247752478524795248052481524825248352484524855248652487524885248952490524915249252493524945249552496524975249852499525005250152502525035250452505525065250752508525095251052511525125251352514525155251652517525185251952520525215252252523525245252552526525275252852529525305253152532525335253452535525365253752538525395254052541525425254352544525455254652547525485254952550525515255252553525545255552556525575255852559525605256152562525635256452565525665256752568525695257052571525725257352574525755257652577525785257952580525815258252583525845258552586525875258852589525905259152592525935259452595525965259752598525995260052601526025260352604526055260652607526085260952610526115261252613526145261552616526175261852619526205262152622526235262452625526265262752628526295263052631526325263352634526355263652637526385263952640526415264252643526445264552646526475264852649526505265152652526535265452655526565265752658526595266052661526625266352664526655266652667526685266952670526715267252673526745267552676526775267852679526805268152682526835268452685526865268752688526895269052691526925269352694526955269652697526985269952700527015270252703527045270552706527075270852709527105271152712527135271452715527165271752718527195272052721527225272352724527255272652727527285272952730527315273252733527345273552736527375273852739527405274152742527435274452745527465274752748527495275052751527525275352754527555275652757527585275952760527615276252763527645276552766527675276852769527705277152772527735277452775527765277752778527795278052781527825278352784527855278652787527885278952790527915279252793527945279552796527975279852799528005280152802528035280452805528065280752808528095281052811528125281352814528155281652817528185281952820528215282252823528245282552826528275282852829528305283152832528335283452835528365283752838528395284052841528425284352844528455284652847528485284952850528515285252853528545285552856528575285852859528605286152862528635286452865528665286752868528695287052871528725287352874528755287652877528785287952880528815288252883528845288552886528875288852889528905289152892528935289452895528965289752898528995290052901529025290352904529055290652907529085290952910529115291252913529145291552916529175291852919529205292152922529235292452925529265292752928529295293052931529325293352934529355293652937529385293952940529415294252943529445294552946529475294852949529505295152952529535295452955529565295752958529595296052961529625296352964529655296652967529685296952970529715297252973529745297552976529775297852979529805298152982529835298452985529865298752988529895299052991529925299352994529955299652997529985299953000530015300253003530045300553006530075300853009530105301153012530135301453015530165301753018530195302053021530225302353024530255302653027530285302953030530315303253033530345303553036530375303853039530405304153042530435304453045530465304753048530495305053051530525305353054530555305653057530585305953060530615306253063530645306553066530675306853069530705307153072530735307453075530765307753078530795308053081530825308353084530855308653087530885308953090530915309253093530945309553096530975309853099531005310153102531035310453105531065310753108531095311053111531125311353114531155311653117531185311953120531215312253123531245312553126531275312853129531305313153132531335313453135531365313753138531395314053141531425314353144531455314653147531485314953150531515315253153531545315553156531575315853159531605316153162531635316453165531665316753168531695317053171531725317353174531755317653177531785317953180531815318253183531845318553186531875318853189531905319153192531935319453195531965319753198531995320053201532025320353204532055320653207532085320953210532115321253213532145321553216532175321853219532205322153222532235322453225532265322753228532295323053231532325323353234532355323653237532385323953240532415324253243532445324553246532475324853249532505325153252532535325453255532565325753258532595326053261532625326353264532655326653267532685326953270532715327253273532745327553276532775327853279532805328153282532835328453285532865328753288532895329053291532925329353294532955329653297532985329953300533015330253303533045330553306533075330853309533105331153312533135331453315533165331753318533195332053321533225332353324533255332653327533285332953330533315333253333533345333553336533375333853339533405334153342533435334453345533465334753348533495335053351533525335353354533555335653357533585335953360533615336253363533645336553366533675336853369533705337153372533735337453375533765337753378533795338053381533825338353384533855338653387533885338953390533915339253393533945339553396533975339853399534005340153402534035340453405534065340753408534095341053411534125341353414534155341653417534185341953420534215342253423534245342553426534275342853429534305343153432534335343453435534365343753438534395344053441534425344353444534455344653447534485344953450534515345253453534545345553456534575345853459534605346153462534635346453465534665346753468534695347053471534725347353474534755347653477534785347953480534815348253483534845348553486534875348853489534905349153492534935349453495534965349753498534995350053501535025350353504535055350653507535085350953510535115351253513535145351553516535175351853519535205352153522535235352453525535265352753528535295353053531535325353353534535355353653537535385353953540535415354253543535445354553546535475354853549535505355153552535535355453555535565355753558535595356053561535625356353564535655356653567535685356953570535715357253573535745357553576535775357853579535805358153582535835358453585535865358753588535895359053591535925359353594535955359653597535985359953600536015360253603536045360553606536075360853609536105361153612536135361453615536165361753618536195362053621536225362353624536255362653627536285362953630536315363253633536345363553636536375363853639536405364153642536435364453645536465364753648536495365053651536525365353654536555365653657536585365953660536615366253663536645366553666536675366853669536705367153672536735367453675536765367753678536795368053681536825368353684536855368653687536885368953690536915369253693536945369553696536975369853699537005370153702537035370453705537065370753708537095371053711537125371353714537155371653717537185371953720537215372253723537245372553726537275372853729537305373153732537335373453735537365373753738537395374053741537425374353744537455374653747537485374953750537515375253753537545375553756537575375853759537605376153762537635376453765537665376753768537695377053771537725377353774537755377653777537785377953780537815378253783537845378553786537875378853789537905379153792537935379453795537965379753798537995380053801538025380353804538055380653807538085380953810538115381253813538145381553816538175381853819538205382153822538235382453825538265382753828538295383053831538325383353834538355383653837538385383953840538415384253843538445384553846538475384853849538505385153852538535385453855538565385753858538595386053861538625386353864538655386653867538685386953870538715387253873538745387553876538775387853879538805388153882538835388453885538865388753888538895389053891538925389353894538955389653897538985389953900539015390253903539045390553906539075390853909539105391153912539135391453915539165391753918539195392053921539225392353924539255392653927539285392953930539315393253933539345393553936539375393853939539405394153942539435394453945539465394753948539495395053951539525395353954539555395653957539585395953960539615396253963539645396553966539675396853969539705397153972539735397453975539765397753978539795398053981539825398353984539855398653987539885398953990539915399253993539945399553996539975399853999540005400154002540035400454005540065400754008540095401054011540125401354014540155401654017540185401954020540215402254023540245402554026540275402854029540305403154032540335403454035540365403754038540395404054041540425404354044540455404654047540485404954050540515405254053540545405554056540575405854059540605406154062540635406454065540665406754068540695407054071540725407354074540755407654077540785407954080540815408254083540845408554086540875408854089540905409154092540935409454095540965409754098540995410054101541025410354104541055410654107541085410954110541115411254113541145411554116541175411854119541205412154122541235412454125541265412754128541295413054131541325413354134541355413654137541385413954140541415414254143541445414554146541475414854149541505415154152541535415454155541565415754158541595416054161541625416354164541655416654167541685416954170541715417254173541745417554176541775417854179541805418154182541835418454185541865418754188541895419054191541925419354194541955419654197541985419954200542015420254203542045420554206542075420854209542105421154212542135421454215542165421754218542195422054221542225422354224542255422654227542285422954230542315423254233542345423554236542375423854239542405424154242542435424454245542465424754248542495425054251542525425354254542555425654257542585425954260542615426254263542645426554266542675426854269542705427154272542735427454275542765427754278542795428054281542825428354284542855428654287542885428954290542915429254293542945429554296542975429854299543005430154302543035430454305543065430754308543095431054311543125431354314543155431654317543185431954320543215432254323543245432554326543275432854329543305433154332543335433454335543365433754338543395434054341543425434354344543455434654347543485434954350543515435254353543545435554356543575435854359543605436154362543635436454365543665436754368543695437054371543725437354374543755437654377543785437954380543815438254383543845438554386543875438854389543905439154392543935439454395543965439754398543995440054401544025440354404544055440654407544085440954410544115441254413544145441554416544175441854419544205442154422544235442454425544265442754428544295443054431544325443354434544355443654437544385443954440544415444254443544445444554446544475444854449544505445154452544535445454455544565445754458544595446054461544625446354464544655446654467544685446954470544715447254473544745447554476544775447854479544805448154482544835448454485544865448754488544895449054491544925449354494544955449654497544985449954500545015450254503545045450554506545075450854509545105451154512545135451454515545165451754518545195452054521545225452354524545255452654527545285452954530545315453254533545345453554536545375453854539545405454154542545435454454545545465454754548545495455054551545525455354554545555455654557545585455954560545615456254563545645456554566545675456854569545705457154572545735457454575545765457754578545795458054581545825458354584545855458654587545885458954590545915459254593545945459554596545975459854599546005460154602546035460454605546065460754608546095461054611546125461354614546155461654617546185461954620546215462254623546245462554626546275462854629546305463154632546335463454635546365463754638546395464054641546425464354644546455464654647546485464954650546515465254653546545465554656546575465854659546605466154662546635466454665546665466754668546695467054671546725467354674546755467654677546785467954680546815468254683546845468554686546875468854689546905469154692546935469454695546965469754698546995470054701547025470354704547055470654707547085470954710547115471254713547145471554716547175471854719547205472154722547235472454725547265472754728547295473054731547325473354734547355473654737547385473954740547415474254743547445474554746547475474854749547505475154752547535475454755547565475754758547595476054761547625476354764547655476654767547685476954770547715477254773547745477554776547775477854779547805478154782547835478454785547865478754788547895479054791547925479354794547955479654797547985479954800548015480254803548045480554806548075480854809548105481154812548135481454815548165481754818548195482054821548225482354824548255482654827548285482954830548315483254833548345483554836548375483854839548405484154842548435484454845548465484754848548495485054851548525485354854548555485654857548585485954860548615486254863548645486554866548675486854869548705487154872548735487454875548765487754878548795488054881548825488354884548855488654887548885488954890548915489254893548945489554896548975489854899549005490154902549035490454905549065490754908549095491054911549125491354914549155491654917549185491954920549215492254923549245492554926549275492854929549305493154932549335493454935549365493754938549395494054941549425494354944549455494654947549485494954950549515495254953549545495554956549575495854959549605496154962549635496454965549665496754968549695497054971549725497354974549755497654977549785497954980549815498254983549845498554986549875498854989549905499154992549935499454995549965499754998549995500055001550025500355004550055500655007550085500955010550115501255013550145501555016550175501855019550205502155022550235502455025550265502755028550295503055031550325503355034550355503655037550385503955040550415504255043550445504555046550475504855049550505505155052550535505455055550565505755058550595506055061550625506355064550655506655067550685506955070550715507255073550745507555076550775507855079550805508155082550835508455085550865508755088550895509055091550925509355094550955509655097550985509955100551015510255103551045510555106551075510855109551105511155112551135511455115551165511755118551195512055121551225512355124551255512655127551285512955130551315513255133551345513555136551375513855139551405514155142551435514455145551465514755148551495515055151551525515355154551555515655157551585515955160551615516255163551645516555166551675516855169551705517155172551735517455175551765517755178551795518055181551825518355184551855518655187551885518955190551915519255193551945519555196551975519855199552005520155202552035520455205552065520755208552095521055211552125521355214552155521655217552185521955220552215522255223552245522555226552275522855229552305523155232552335523455235552365523755238552395524055241552425524355244552455524655247552485524955250552515525255253552545525555256552575525855259552605526155262552635526455265552665526755268552695527055271552725527355274552755527655277552785527955280552815528255283552845528555286552875528855289552905529155292552935529455295552965529755298552995530055301553025530355304553055530655307553085530955310553115531255313553145531555316553175531855319553205532155322553235532455325553265532755328553295533055331553325533355334553355533655337553385533955340553415534255343553445534555346553475534855349553505535155352553535535455355553565535755358553595536055361553625536355364553655536655367553685536955370553715537255373553745537555376553775537855379553805538155382553835538455385553865538755388553895539055391553925539355394553955539655397553985539955400554015540255403554045540555406554075540855409554105541155412554135541455415554165541755418554195542055421554225542355424554255542655427554285542955430554315543255433554345543555436554375543855439554405544155442554435544455445554465544755448554495545055451554525545355454554555545655457554585545955460554615546255463554645546555466554675546855469554705547155472554735547455475554765547755478554795548055481554825548355484554855548655487554885548955490554915549255493554945549555496554975549855499555005550155502555035550455505555065550755508555095551055511555125551355514555155551655517555185551955520555215552255523555245552555526555275552855529555305553155532555335553455535555365553755538555395554055541555425554355544555455554655547555485554955550555515555255553555545555555556555575555855559555605556155562555635556455565555665556755568555695557055571555725557355574555755557655577555785557955580555815558255583555845558555586555875558855589555905559155592555935559455595555965559755598555995560055601556025560355604556055560655607556085560955610556115561255613556145561555616556175561855619556205562155622556235562455625556265562755628556295563055631556325563355634556355563655637556385563955640556415564255643556445564555646556475564855649556505565155652556535565455655556565565755658556595566055661556625566355664556655566655667556685566955670556715567255673556745567555676556775567855679556805568155682556835568455685556865568755688556895569055691556925569355694556955569655697556985569955700557015570255703557045570555706557075570855709557105571155712557135571455715557165571755718557195572055721557225572355724557255572655727557285572955730557315573255733557345573555736557375573855739557405574155742557435574455745557465574755748557495575055751557525575355754557555575655757557585575955760557615576255763557645576555766557675576855769557705577155772557735577455775557765577755778557795578055781557825578355784557855578655787557885578955790557915579255793557945579555796557975579855799558005580155802558035580455805558065580755808558095581055811558125581355814558155581655817558185581955820558215582255823558245582555826558275582855829558305583155832558335583455835558365583755838558395584055841558425584355844558455584655847558485584955850558515585255853558545585555856558575585855859558605586155862558635586455865558665586755868558695587055871558725587355874558755587655877558785587955880558815588255883558845588555886558875588855889558905589155892558935589455895558965589755898558995590055901559025590355904559055590655907559085590955910559115591255913559145591555916559175591855919559205592155922559235592455925559265592755928559295593055931559325593355934559355593655937559385593955940559415594255943559445594555946559475594855949559505595155952559535595455955559565595755958559595596055961559625596355964559655596655967559685596955970559715597255973559745597555976559775597855979559805598155982559835598455985559865598755988559895599055991559925599355994559955599655997559985599956000560015600256003560045600556006560075600856009560105601156012560135601456015560165601756018560195602056021560225602356024560255602656027560285602956030560315603256033560345603556036560375603856039560405604156042560435604456045560465604756048560495605056051560525605356054560555605656057560585605956060560615606256063560645606556066560675606856069560705607156072560735607456075560765607756078560795608056081560825608356084560855608656087560885608956090560915609256093560945609556096560975609856099561005610156102561035610456105561065610756108561095611056111561125611356114561155611656117561185611956120561215612256123561245612556126561275612856129561305613156132561335613456135561365613756138561395614056141561425614356144561455614656147561485614956150561515615256153561545615556156561575615856159561605616156162561635616456165561665616756168561695617056171561725617356174561755617656177561785617956180561815618256183561845618556186561875618856189561905619156192561935619456195561965619756198561995620056201562025620356204562055620656207562085620956210562115621256213562145621556216562175621856219562205622156222562235622456225562265622756228562295623056231562325623356234562355623656237562385623956240562415624256243562445624556246562475624856249562505625156252562535625456255562565625756258562595626056261562625626356264562655626656267562685626956270562715627256273562745627556276562775627856279562805628156282562835628456285562865628756288562895629056291562925629356294562955629656297562985629956300563015630256303563045630556306563075630856309563105631156312563135631456315563165631756318563195632056321563225632356324563255632656327563285632956330563315633256333563345633556336563375633856339563405634156342563435634456345563465634756348563495635056351563525635356354563555635656357563585635956360563615636256363563645636556366563675636856369563705637156372563735637456375563765637756378563795638056381563825638356384563855638656387563885638956390563915639256393563945639556396563975639856399564005640156402564035640456405564065640756408564095641056411564125641356414564155641656417564185641956420564215642256423564245642556426564275642856429564305643156432564335643456435564365643756438564395644056441564425644356444564455644656447564485644956450564515645256453564545645556456564575645856459564605646156462564635646456465564665646756468564695647056471564725647356474564755647656477564785647956480564815648256483564845648556486564875648856489564905649156492564935649456495564965649756498564995650056501565025650356504565055650656507565085650956510565115651256513565145651556516565175651856519565205652156522565235652456525565265652756528565295653056531565325653356534565355653656537565385653956540565415654256543565445654556546565475654856549565505655156552565535655456555565565655756558565595656056561565625656356564565655656656567565685656956570565715657256573565745657556576565775657856579565805658156582565835658456585565865658756588565895659056591565925659356594565955659656597565985659956600566015660256603566045660556606566075660856609566105661156612566135661456615566165661756618566195662056621566225662356624566255662656627566285662956630566315663256633566345663556636566375663856639566405664156642566435664456645566465664756648566495665056651566525665356654566555665656657566585665956660566615666256663566645666556666566675666856669566705667156672566735667456675566765667756678566795668056681566825668356684566855668656687566885668956690566915669256693566945669556696566975669856699567005670156702567035670456705567065670756708567095671056711567125671356714567155671656717567185671956720567215672256723567245672556726567275672856729567305673156732567335673456735567365673756738567395674056741567425674356744567455674656747567485674956750567515675256753567545675556756567575675856759567605676156762567635676456765567665676756768567695677056771567725677356774567755677656777567785677956780567815678256783567845678556786567875678856789567905679156792567935679456795567965679756798567995680056801568025680356804568055680656807568085680956810568115681256813568145681556816568175681856819568205682156822568235682456825568265682756828568295683056831568325683356834568355683656837568385683956840568415684256843568445684556846568475684856849568505685156852568535685456855568565685756858568595686056861568625686356864568655686656867568685686956870568715687256873568745687556876568775687856879568805688156882568835688456885568865688756888568895689056891568925689356894568955689656897568985689956900569015690256903569045690556906569075690856909569105691156912569135691456915569165691756918569195692056921569225692356924569255692656927569285692956930569315693256933569345693556936569375693856939569405694156942569435694456945569465694756948569495695056951569525695356954569555695656957569585695956960569615696256963569645696556966569675696856969569705697156972569735697456975569765697756978569795698056981569825698356984569855698656987569885698956990569915699256993569945699556996569975699856999570005700157002570035700457005570065700757008570095701057011570125701357014570155701657017570185701957020570215702257023570245702557026570275702857029570305703157032570335703457035570365703757038570395704057041570425704357044570455704657047570485704957050570515705257053570545705557056570575705857059570605706157062570635706457065570665706757068570695707057071570725707357074570755707657077570785707957080570815708257083570845708557086570875708857089570905709157092570935709457095570965709757098570995710057101571025710357104571055710657107571085710957110571115711257113571145711557116571175711857119571205712157122571235712457125571265712757128571295713057131571325713357134571355713657137571385713957140571415714257143571445714557146571475714857149571505715157152571535715457155571565715757158571595716057161571625716357164571655716657167571685716957170571715717257173571745717557176571775717857179571805718157182571835718457185571865718757188571895719057191571925719357194571955719657197571985719957200572015720257203572045720557206572075720857209572105721157212572135721457215572165721757218572195722057221572225722357224572255722657227572285722957230572315723257233572345723557236572375723857239572405724157242572435724457245572465724757248572495725057251572525725357254572555725657257572585725957260572615726257263572645726557266572675726857269572705727157272572735727457275572765727757278572795728057281572825728357284572855728657287572885728957290572915729257293572945729557296572975729857299573005730157302573035730457305573065730757308573095731057311573125731357314573155731657317573185731957320573215732257323573245732557326573275732857329573305733157332573335733457335573365733757338573395734057341573425734357344573455734657347573485734957350573515735257353573545735557356573575735857359573605736157362573635736457365573665736757368573695737057371573725737357374573755737657377573785737957380573815738257383573845738557386573875738857389573905739157392573935739457395573965739757398573995740057401574025740357404574055740657407574085740957410574115741257413574145741557416574175741857419574205742157422574235742457425574265742757428574295743057431574325743357434574355743657437574385743957440574415744257443574445744557446574475744857449574505745157452574535745457455574565745757458574595746057461574625746357464574655746657467574685746957470574715747257473574745747557476574775747857479574805748157482574835748457485574865748757488574895749057491574925749357494574955749657497574985749957500575015750257503575045750557506575075750857509575105751157512575135751457515575165751757518575195752057521575225752357524575255752657527575285752957530575315753257533575345753557536575375753857539575405754157542575435754457545575465754757548575495755057551575525755357554575555755657557575585755957560575615756257563575645756557566575675756857569575705757157572575735757457575575765757757578575795758057581575825758357584575855758657587575885758957590575915759257593575945759557596575975759857599576005760157602576035760457605576065760757608576095761057611576125761357614576155761657617576185761957620576215762257623576245762557626576275762857629576305763157632576335763457635576365763757638576395764057641576425764357644576455764657647576485764957650576515765257653576545765557656576575765857659576605766157662576635766457665576665766757668576695767057671576725767357674576755767657677576785767957680576815768257683576845768557686576875768857689576905769157692576935769457695576965769757698576995770057701577025770357704577055770657707577085770957710577115771257713577145771557716577175771857719577205772157722577235772457725577265772757728577295773057731577325773357734577355773657737577385773957740577415774257743577445774557746577475774857749577505775157752577535775457755577565775757758577595776057761577625776357764577655776657767577685776957770577715777257773577745777557776577775777857779577805778157782577835778457785577865778757788577895779057791577925779357794577955779657797577985779957800578015780257803578045780557806578075780857809578105781157812578135781457815578165781757818578195782057821578225782357824578255782657827578285782957830578315783257833578345783557836578375783857839578405784157842578435784457845578465784757848578495785057851578525785357854578555785657857578585785957860578615786257863578645786557866578675786857869578705787157872578735787457875578765787757878578795788057881578825788357884578855788657887578885788957890578915789257893578945789557896578975789857899579005790157902579035790457905579065790757908579095791057911579125791357914579155791657917579185791957920579215792257923579245792557926579275792857929579305793157932579335793457935579365793757938579395794057941579425794357944579455794657947579485794957950579515795257953579545795557956579575795857959579605796157962579635796457965579665796757968579695797057971579725797357974579755797657977579785797957980579815798257983579845798557986579875798857989579905799157992579935799457995579965799757998579995800058001580025800358004580055800658007580085800958010580115801258013580145801558016580175801858019580205802158022580235802458025580265802758028580295803058031580325803358034580355803658037580385803958040580415804258043580445804558046580475804858049580505805158052580535805458055580565805758058580595806058061580625806358064580655806658067580685806958070580715807258073580745807558076580775807858079580805808158082580835808458085580865808758088580895809058091580925809358094580955809658097580985809958100581015810258103581045810558106581075810858109581105811158112581135811458115581165811758118581195812058121581225812358124581255812658127581285812958130581315813258133581345813558136581375813858139581405814158142581435814458145581465814758148581495815058151581525815358154581555815658157581585815958160581615816258163581645816558166581675816858169581705817158172581735817458175581765817758178581795818058181581825818358184581855818658187581885818958190581915819258193581945819558196581975819858199582005820158202582035820458205582065820758208582095821058211582125821358214582155821658217582185821958220582215822258223582245822558226582275822858229582305823158232582335823458235582365823758238582395824058241582425824358244582455824658247582485824958250582515825258253582545825558256582575825858259582605826158262582635826458265582665826758268582695827058271582725827358274582755827658277582785827958280582815828258283582845828558286582875828858289582905829158292582935829458295582965829758298582995830058301583025830358304583055830658307583085830958310583115831258313583145831558316583175831858319583205832158322583235832458325583265832758328583295833058331583325833358334583355833658337583385833958340583415834258343583445834558346583475834858349583505835158352583535835458355583565835758358583595836058361583625836358364583655836658367583685836958370583715837258373583745837558376583775837858379583805838158382583835838458385583865838758388583895839058391583925839358394583955839658397583985839958400584015840258403584045840558406584075840858409584105841158412584135841458415584165841758418584195842058421584225842358424584255842658427584285842958430584315843258433584345843558436584375843858439584405844158442584435844458445584465844758448584495845058451584525845358454584555845658457584585845958460584615846258463584645846558466584675846858469584705847158472584735847458475584765847758478584795848058481584825848358484584855848658487584885848958490584915849258493584945849558496584975849858499585005850158502585035850458505585065850758508585095851058511585125851358514585155851658517585185851958520585215852258523585245852558526585275852858529585305853158532585335853458535585365853758538585395854058541585425854358544585455854658547585485854958550585515855258553585545855558556585575855858559585605856158562585635856458565585665856758568585695857058571585725857358574585755857658577585785857958580585815858258583585845858558586585875858858589585905859158592585935859458595585965859758598585995860058601586025860358604586055860658607586085860958610586115861258613586145861558616586175861858619586205862158622586235862458625586265862758628586295863058631586325863358634586355863658637586385863958640586415864258643586445864558646586475864858649586505865158652586535865458655586565865758658586595866058661586625866358664586655866658667586685866958670586715867258673586745867558676586775867858679586805868158682586835868458685586865868758688586895869058691586925869358694586955869658697586985869958700587015870258703587045870558706587075870858709587105871158712587135871458715587165871758718587195872058721587225872358724587255872658727587285872958730587315873258733587345873558736587375873858739587405874158742587435874458745587465874758748587495875058751587525875358754587555875658757587585875958760587615876258763587645876558766587675876858769587705877158772587735877458775587765877758778587795878058781587825878358784587855878658787587885878958790587915879258793587945879558796587975879858799588005880158802588035880458805588065880758808588095881058811588125881358814588155881658817588185881958820588215882258823588245882558826588275882858829588305883158832588335883458835588365883758838588395884058841588425884358844588455884658847588485884958850588515885258853588545885558856588575885858859588605886158862588635886458865588665886758868588695887058871588725887358874588755887658877588785887958880588815888258883588845888558886588875888858889588905889158892588935889458895588965889758898588995890058901589025890358904589055890658907589085890958910589115891258913589145891558916589175891858919589205892158922589235892458925589265892758928589295893058931589325893358934589355893658937589385893958940589415894258943589445894558946589475894858949589505895158952589535895458955589565895758958589595896058961589625896358964589655896658967589685896958970589715897258973589745897558976589775897858979589805898158982589835898458985589865898758988589895899058991589925899358994589955899658997589985899959000590015900259003590045900559006590075900859009590105901159012590135901459015590165901759018590195902059021590225902359024590255902659027590285902959030590315903259033590345903559036590375903859039590405904159042590435904459045590465904759048590495905059051590525905359054590555905659057590585905959060590615906259063590645906559066590675906859069590705907159072590735907459075590765907759078590795908059081590825908359084590855908659087590885908959090590915909259093590945909559096590975909859099591005910159102591035910459105591065910759108591095911059111591125911359114591155911659117591185911959120591215912259123591245912559126591275912859129591305913159132591335913459135591365913759138591395914059141591425914359144591455914659147591485914959150591515915259153591545915559156591575915859159591605916159162591635916459165591665916759168591695917059171591725917359174591755917659177591785917959180591815918259183591845918559186591875918859189591905919159192591935919459195591965919759198591995920059201592025920359204592055920659207592085920959210592115921259213592145921559216592175921859219592205922159222592235922459225592265922759228592295923059231592325923359234592355923659237592385923959240592415924259243592445924559246592475924859249592505925159252592535925459255592565925759258592595926059261592625926359264592655926659267592685926959270592715927259273592745927559276592775927859279592805928159282592835928459285592865928759288592895929059291592925929359294592955929659297592985929959300593015930259303593045930559306593075930859309593105931159312593135931459315593165931759318593195932059321593225932359324593255932659327593285932959330593315933259333593345933559336593375933859339593405934159342593435934459345593465934759348593495935059351593525935359354593555935659357593585935959360593615936259363593645936559366593675936859369593705937159372593735937459375593765937759378593795938059381593825938359384593855938659387593885938959390593915939259393593945939559396593975939859399594005940159402594035940459405594065940759408594095941059411594125941359414594155941659417594185941959420594215942259423594245942559426594275942859429594305943159432594335943459435594365943759438594395944059441594425944359444594455944659447594485944959450594515945259453594545945559456594575945859459594605946159462594635946459465594665946759468594695947059471594725947359474594755947659477594785947959480594815948259483594845948559486594875948859489594905949159492594935949459495594965949759498594995950059501595025950359504595055950659507595085950959510595115951259513595145951559516595175951859519595205952159522595235952459525595265952759528595295953059531595325953359534595355953659537595385953959540595415954259543595445954559546595475954859549595505955159552595535955459555595565955759558595595956059561595625956359564595655956659567595685956959570595715957259573595745957559576595775957859579595805958159582595835958459585595865958759588595895959059591595925959359594595955959659597595985959959600596015960259603596045960559606596075960859609596105961159612596135961459615596165961759618596195962059621596225962359624596255962659627596285962959630596315963259633596345963559636596375963859639596405964159642596435964459645596465964759648596495965059651596525965359654596555965659657596585965959660596615966259663596645966559666596675966859669596705967159672596735967459675596765967759678596795968059681596825968359684596855968659687596885968959690596915969259693596945969559696596975969859699597005970159702597035970459705597065970759708597095971059711597125971359714597155971659717597185971959720597215972259723597245972559726597275972859729597305973159732597335973459735597365973759738597395974059741597425974359744597455974659747597485974959750597515975259753597545975559756597575975859759597605976159762597635976459765597665976759768597695977059771597725977359774597755977659777597785977959780597815978259783597845978559786597875978859789597905979159792597935979459795597965979759798597995980059801598025980359804598055980659807598085980959810598115981259813598145981559816598175981859819598205982159822598235982459825598265982759828598295983059831598325983359834598355983659837598385983959840598415984259843598445984559846598475984859849598505985159852598535985459855598565985759858598595986059861598625986359864598655986659867598685986959870598715987259873598745987559876598775987859879598805988159882598835988459885598865988759888598895989059891598925989359894598955989659897598985989959900599015990259903599045990559906599075990859909599105991159912599135991459915599165991759918599195992059921599225992359924599255992659927599285992959930599315993259933599345993559936599375993859939599405994159942599435994459945599465994759948599495995059951599525995359954599555995659957599585995959960599615996259963599645996559966599675996859969599705997159972599735997459975599765997759978599795998059981599825998359984599855998659987599885998959990599915999259993599945999559996599975999859999600006000160002600036000460005600066000760008600096001060011600126001360014600156001660017600186001960020600216002260023600246002560026600276002860029600306003160032600336003460035600366003760038600396004060041600426004360044600456004660047600486004960050600516005260053600546005560056600576005860059600606006160062600636006460065600666006760068600696007060071600726007360074600756007660077600786007960080600816008260083600846008560086600876008860089600906009160092600936009460095600966009760098600996010060101601026010360104601056010660107601086010960110601116011260113601146011560116601176011860119601206012160122601236012460125601266012760128601296013060131601326013360134601356013660137601386013960140601416014260143601446014560146601476014860149601506015160152601536015460155601566015760158601596016060161601626016360164601656016660167601686016960170601716017260173601746017560176601776017860179601806018160182601836018460185601866018760188601896019060191601926019360194601956019660197601986019960200602016020260203602046020560206602076020860209602106021160212602136021460215602166021760218602196022060221602226022360224602256022660227602286022960230602316023260233602346023560236602376023860239602406024160242602436024460245602466024760248602496025060251602526025360254602556025660257602586025960260602616026260263602646026560266602676026860269602706027160272602736027460275602766027760278602796028060281602826028360284602856028660287602886028960290602916029260293602946029560296602976029860299603006030160302603036030460305603066030760308603096031060311603126031360314603156031660317603186031960320603216032260323603246032560326603276032860329603306033160332603336033460335603366033760338603396034060341603426034360344603456034660347603486034960350603516035260353603546035560356603576035860359603606036160362603636036460365603666036760368603696037060371603726037360374603756037660377603786037960380603816038260383603846038560386603876038860389603906039160392603936039460395603966039760398603996040060401604026040360404604056040660407604086040960410604116041260413604146041560416604176041860419604206042160422604236042460425604266042760428604296043060431604326043360434604356043660437604386043960440604416044260443604446044560446604476044860449604506045160452604536045460455604566045760458604596046060461604626046360464604656046660467604686046960470604716047260473604746047560476604776047860479604806048160482604836048460485604866048760488604896049060491604926049360494604956049660497604986049960500605016050260503605046050560506605076050860509605106051160512605136051460515605166051760518605196052060521605226052360524605256052660527605286052960530605316053260533605346053560536605376053860539605406054160542605436054460545605466054760548605496055060551605526055360554605556055660557605586055960560605616056260563605646056560566605676056860569605706057160572605736057460575605766057760578605796058060581605826058360584605856058660587605886058960590605916059260593605946059560596605976059860599606006060160602606036060460605606066060760608606096061060611606126061360614606156061660617606186061960620606216062260623606246062560626606276062860629606306063160632606336063460635606366063760638606396064060641606426064360644606456064660647606486064960650606516065260653606546065560656606576065860659606606066160662606636066460665606666066760668606696067060671606726067360674606756067660677606786067960680606816068260683606846068560686606876068860689606906069160692606936069460695606966069760698606996070060701607026070360704607056070660707607086070960710607116071260713607146071560716607176071860719607206072160722607236072460725607266072760728607296073060731607326073360734607356073660737607386073960740607416074260743607446074560746607476074860749607506075160752607536075460755607566075760758607596076060761607626076360764607656076660767607686076960770607716077260773607746077560776607776077860779607806078160782607836078460785607866078760788607896079060791607926079360794607956079660797607986079960800608016080260803608046080560806608076080860809608106081160812608136081460815608166081760818608196082060821608226082360824608256082660827608286082960830608316083260833608346083560836608376083860839608406084160842608436084460845608466084760848608496085060851608526085360854608556085660857608586085960860608616086260863608646086560866608676086860869608706087160872608736087460875608766087760878608796088060881608826088360884608856088660887608886088960890608916089260893608946089560896608976089860899609006090160902609036090460905609066090760908609096091060911609126091360914609156091660917609186091960920609216092260923609246092560926609276092860929609306093160932609336093460935609366093760938609396094060941609426094360944609456094660947609486094960950609516095260953609546095560956609576095860959609606096160962609636096460965609666096760968609696097060971609726097360974609756097660977609786097960980609816098260983609846098560986609876098860989609906099160992609936099460995609966099760998609996100061001610026100361004610056100661007610086100961010610116101261013610146101561016610176101861019610206102161022610236102461025610266102761028610296103061031610326103361034610356103661037610386103961040610416104261043610446104561046610476104861049610506105161052610536105461055610566105761058610596106061061610626106361064610656106661067610686106961070610716107261073610746107561076610776107861079610806108161082610836108461085610866108761088610896109061091610926109361094610956109661097610986109961100611016110261103611046110561106611076110861109611106111161112611136111461115611166111761118611196112061121611226112361124611256112661127611286112961130611316113261133611346113561136611376113861139611406114161142611436114461145611466114761148611496115061151611526115361154611556115661157611586115961160611616116261163611646116561166611676116861169611706117161172611736117461175611766117761178611796118061181611826118361184611856118661187611886118961190611916119261193611946119561196611976119861199612006120161202612036120461205612066120761208612096121061211612126121361214612156121661217612186121961220612216122261223612246122561226612276122861229612306123161232612336123461235612366123761238612396124061241612426124361244612456124661247612486124961250612516125261253612546125561256612576125861259612606126161262612636126461265612666126761268612696127061271612726127361274612756127661277612786127961280612816128261283612846128561286612876128861289612906129161292612936129461295612966129761298612996130061301613026130361304613056130661307613086130961310613116131261313613146131561316613176131861319613206132161322613236132461325613266132761328613296133061331613326133361334613356133661337613386133961340613416134261343613446134561346613476134861349613506135161352613536135461355613566135761358613596136061361613626136361364613656136661367613686136961370613716137261373613746137561376613776137861379613806138161382613836138461385613866138761388613896139061391613926139361394613956139661397613986139961400614016140261403614046140561406614076140861409614106141161412614136141461415614166141761418614196142061421614226142361424614256142661427614286142961430614316143261433614346143561436614376143861439614406144161442614436144461445614466144761448614496145061451614526145361454614556145661457614586145961460614616146261463614646146561466614676146861469614706147161472614736147461475614766147761478614796148061481614826148361484614856148661487614886148961490614916149261493614946149561496614976149861499615006150161502615036150461505615066150761508615096151061511615126151361514615156151661517615186151961520615216152261523615246152561526615276152861529615306153161532615336153461535615366153761538615396154061541615426154361544615456154661547615486154961550615516155261553615546155561556615576155861559615606156161562615636156461565615666156761568615696157061571615726157361574615756157661577615786157961580615816158261583615846158561586615876158861589615906159161592615936159461595615966159761598615996160061601616026160361604616056160661607616086160961610616116161261613616146161561616616176161861619616206162161622616236162461625616266162761628616296163061631616326163361634616356163661637616386163961640616416164261643616446164561646616476164861649616506165161652616536165461655616566165761658616596166061661616626166361664616656166661667616686166961670616716167261673616746167561676616776167861679616806168161682616836168461685616866168761688616896169061691616926169361694616956169661697616986169961700617016170261703617046170561706617076170861709617106171161712617136171461715617166171761718617196172061721617226172361724617256172661727617286172961730617316173261733617346173561736617376173861739617406174161742617436174461745617466174761748617496175061751617526175361754617556175661757617586175961760617616176261763617646176561766617676176861769617706177161772617736177461775617766177761778617796178061781617826178361784617856178661787617886178961790617916179261793617946179561796617976179861799618006180161802618036180461805618066180761808618096181061811618126181361814618156181661817618186181961820618216182261823618246182561826618276182861829618306183161832618336183461835618366183761838618396184061841618426184361844618456184661847618486184961850618516185261853618546185561856618576185861859618606186161862618636186461865618666186761868618696187061871618726187361874618756187661877618786187961880618816188261883618846188561886618876188861889618906189161892618936189461895618966189761898618996190061901619026190361904619056190661907619086190961910619116191261913619146191561916619176191861919619206192161922619236192461925619266192761928619296193061931619326193361934619356193661937619386193961940619416194261943619446194561946619476194861949619506195161952619536195461955619566195761958619596196061961619626196361964619656196661967619686196961970619716197261973619746197561976619776197861979619806198161982619836198461985619866198761988619896199061991619926199361994619956199661997619986199962000620016200262003620046200562006620076200862009620106201162012620136201462015620166201762018620196202062021620226202362024620256202662027620286202962030620316203262033620346203562036620376203862039620406204162042620436204462045620466204762048620496205062051620526205362054620556205662057620586205962060620616206262063620646206562066620676206862069620706207162072620736207462075620766207762078620796208062081620826208362084620856208662087620886208962090620916209262093620946209562096620976209862099621006210162102621036210462105621066210762108621096211062111621126211362114621156211662117621186211962120621216212262123621246212562126621276212862129621306213162132621336213462135621366213762138621396214062141621426214362144621456214662147621486214962150621516215262153621546215562156621576215862159621606216162162621636216462165621666216762168621696217062171621726217362174621756217662177621786217962180621816218262183621846218562186621876218862189621906219162192621936219462195621966219762198621996220062201622026220362204622056220662207622086220962210622116221262213622146221562216622176221862219622206222162222622236222462225622266222762228622296223062231622326223362234622356223662237622386223962240622416224262243622446224562246622476224862249622506225162252622536225462255622566225762258622596226062261622626226362264622656226662267622686226962270622716227262273622746227562276622776227862279622806228162282622836228462285622866228762288622896229062291622926229362294622956229662297622986229962300623016230262303623046230562306623076230862309623106231162312623136231462315623166231762318623196232062321623226232362324623256232662327623286232962330623316233262333623346233562336623376233862339623406234162342623436234462345623466234762348623496235062351623526235362354623556235662357623586235962360623616236262363623646236562366623676236862369623706237162372623736237462375623766237762378623796238062381623826238362384623856238662387623886238962390623916239262393623946239562396623976239862399624006240162402624036240462405624066240762408624096241062411624126241362414624156241662417624186241962420624216242262423624246242562426624276242862429624306243162432624336243462435624366243762438624396244062441624426244362444624456244662447624486244962450624516245262453624546245562456624576245862459624606246162462624636246462465624666246762468624696247062471624726247362474624756247662477624786247962480624816248262483624846248562486624876248862489624906249162492624936249462495624966249762498624996250062501625026250362504625056250662507625086250962510625116251262513625146251562516625176251862519625206252162522625236252462525625266252762528625296253062531625326253362534625356253662537625386253962540625416254262543625446254562546625476254862549625506255162552625536255462555625566255762558625596256062561625626256362564625656256662567625686256962570625716257262573625746257562576625776257862579625806258162582625836258462585625866258762588625896259062591625926259362594625956259662597625986259962600626016260262603626046260562606626076260862609626106261162612626136261462615626166261762618626196262062621626226262362624626256262662627626286262962630626316263262633626346263562636626376263862639626406264162642626436264462645626466264762648626496265062651626526265362654626556265662657626586265962660626616266262663626646266562666626676266862669626706267162672626736267462675626766267762678626796268062681626826268362684626856268662687626886268962690626916269262693626946269562696626976269862699627006270162702627036270462705627066270762708627096271062711627126271362714627156271662717627186271962720627216272262723627246272562726627276272862729627306273162732627336273462735627366273762738627396274062741627426274362744627456274662747627486274962750627516275262753627546275562756627576275862759627606276162762627636276462765627666276762768627696277062771627726277362774627756277662777627786277962780627816278262783627846278562786627876278862789627906279162792627936279462795627966279762798627996280062801628026280362804628056280662807628086280962810628116281262813628146281562816628176281862819628206282162822628236282462825628266282762828628296283062831628326283362834628356283662837628386283962840628416284262843628446284562846628476284862849628506285162852628536285462855628566285762858628596286062861628626286362864628656286662867628686286962870628716287262873628746287562876628776287862879628806288162882628836288462885628866288762888628896289062891628926289362894628956289662897628986289962900629016290262903629046290562906629076290862909629106291162912629136291462915629166291762918629196292062921629226292362924629256292662927629286292962930629316293262933629346293562936629376293862939629406294162942629436294462945629466294762948629496295062951629526295362954629556295662957629586295962960629616296262963629646296562966629676296862969629706297162972629736297462975629766297762978629796298062981629826298362984629856298662987629886298962990629916299262993629946299562996629976299862999630006300163002630036300463005630066300763008630096301063011630126301363014630156301663017630186301963020630216302263023630246302563026630276302863029630306303163032630336303463035630366303763038630396304063041630426304363044630456304663047630486304963050630516305263053630546305563056630576305863059630606306163062630636306463065630666306763068630696307063071630726307363074630756307663077630786307963080630816308263083630846308563086630876308863089630906309163092630936309463095630966309763098630996310063101631026310363104631056310663107631086310963110631116311263113631146311563116631176311863119631206312163122631236312463125631266312763128631296313063131631326313363134631356313663137631386313963140631416314263143631446314563146631476314863149631506315163152631536315463155631566315763158631596316063161631626316363164631656316663167631686316963170631716317263173631746317563176631776317863179631806318163182631836318463185631866318763188631896319063191631926319363194631956319663197631986319963200632016320263203632046320563206632076320863209632106321163212632136321463215632166321763218632196322063221632226322363224632256322663227632286322963230632316323263233632346323563236632376323863239632406324163242632436324463245632466324763248632496325063251632526325363254632556325663257632586325963260632616326263263632646326563266632676326863269632706327163272632736327463275632766327763278632796328063281632826328363284632856328663287632886328963290632916329263293632946329563296632976329863299633006330163302633036330463305633066330763308633096331063311633126331363314633156331663317633186331963320633216332263323633246332563326633276332863329633306333163332633336333463335633366333763338633396334063341633426334363344633456334663347633486334963350633516335263353633546335563356633576335863359633606336163362633636336463365633666336763368633696337063371633726337363374633756337663377633786337963380633816338263383633846338563386633876338863389633906339163392633936339463395633966339763398633996340063401634026340363404634056340663407634086340963410634116341263413634146341563416634176341863419634206342163422634236342463425634266342763428634296343063431634326343363434634356343663437634386343963440634416344263443634446344563446634476344863449634506345163452634536345463455634566345763458634596346063461634626346363464634656346663467634686346963470634716347263473634746347563476634776347863479634806348163482634836348463485634866348763488634896349063491634926349363494634956349663497634986349963500635016350263503635046350563506635076350863509635106351163512635136351463515635166351763518635196352063521635226352363524635256352663527635286352963530635316353263533635346353563536635376353863539635406354163542635436354463545635466354763548635496355063551635526355363554635556355663557635586355963560635616356263563635646356563566635676356863569635706357163572635736357463575635766357763578635796358063581635826358363584635856358663587635886358963590635916359263593635946359563596635976359863599636006360163602636036360463605636066360763608636096361063611636126361363614636156361663617636186361963620636216362263623636246362563626636276362863629636306363163632636336363463635636366363763638636396364063641636426364363644636456364663647636486364963650636516365263653636546365563656636576365863659636606366163662636636366463665636666366763668636696367063671636726367363674636756367663677636786367963680636816368263683636846368563686636876368863689636906369163692636936369463695636966369763698636996370063701637026370363704637056370663707637086370963710637116371263713637146371563716637176371863719637206372163722637236372463725637266372763728637296373063731637326373363734637356373663737637386373963740637416374263743637446374563746637476374863749637506375163752637536375463755637566375763758637596376063761637626376363764637656376663767637686376963770637716377263773637746377563776637776377863779637806378163782637836378463785637866378763788637896379063791637926379363794637956379663797637986379963800638016380263803638046380563806638076380863809638106381163812638136381463815638166381763818638196382063821638226382363824638256382663827638286382963830638316383263833638346383563836638376383863839638406384163842638436384463845638466384763848638496385063851638526385363854638556385663857638586385963860638616386263863638646386563866638676386863869638706387163872638736387463875638766387763878638796388063881638826388363884638856388663887638886388963890638916389263893638946389563896638976389863899639006390163902639036390463905639066390763908639096391063911639126391363914639156391663917639186391963920639216392263923639246392563926639276392863929639306393163932639336393463935639366393763938639396394063941639426394363944639456394663947639486394963950639516395263953639546395563956639576395863959639606396163962639636396463965639666396763968639696397063971639726397363974639756397663977639786397963980639816398263983639846398563986639876398863989639906399163992639936399463995639966399763998639996400064001640026400364004640056400664007640086400964010640116401264013640146401564016640176401864019640206402164022640236402464025640266402764028640296403064031640326403364034640356403664037640386403964040640416404264043640446404564046640476404864049640506405164052640536405464055640566405764058640596406064061640626406364064640656406664067640686406964070640716407264073640746407564076640776407864079640806408164082640836408464085640866408764088640896409064091640926409364094640956409664097640986409964100641016410264103641046410564106641076410864109641106411164112641136411464115641166411764118641196412064121641226412364124641256412664127641286412964130641316413264133641346413564136641376413864139641406414164142641436414464145641466414764148641496415064151641526415364154641556415664157641586415964160641616416264163641646416564166641676416864169641706417164172641736417464175641766417764178641796418064181641826418364184641856418664187641886418964190641916419264193641946419564196641976419864199642006420164202642036420464205642066420764208642096421064211642126421364214642156421664217642186421964220642216422264223642246422564226642276422864229642306423164232642336423464235642366423764238642396424064241642426424364244642456424664247642486424964250642516425264253642546425564256642576425864259642606426164262642636426464265642666426764268642696427064271642726427364274642756427664277642786427964280642816428264283642846428564286642876428864289642906429164292642936429464295642966429764298642996430064301643026430364304643056430664307643086430964310643116431264313643146431564316643176431864319643206432164322643236432464325643266432764328643296433064331643326433364334643356433664337643386433964340643416434264343643446434564346643476434864349643506435164352643536435464355643566435764358643596436064361643626436364364643656436664367643686436964370643716437264373643746437564376643776437864379643806438164382643836438464385643866438764388643896439064391643926439364394643956439664397643986439964400644016440264403644046440564406644076440864409644106441164412644136441464415644166441764418644196442064421644226442364424644256442664427644286442964430644316443264433644346443564436644376443864439644406444164442644436444464445644466444764448644496445064451644526445364454644556445664457644586445964460644616446264463644646446564466644676446864469644706447164472644736447464475644766447764478644796448064481644826448364484644856448664487644886448964490644916449264493644946449564496644976449864499645006450164502645036450464505645066450764508645096451064511645126451364514645156451664517645186451964520645216452264523645246452564526645276452864529645306453164532645336453464535645366453764538645396454064541645426454364544645456454664547645486454964550645516455264553645546455564556645576455864559645606456164562645636456464565645666456764568645696457064571645726457364574645756457664577645786457964580645816458264583645846458564586645876458864589645906459164592645936459464595645966459764598645996460064601646026460364604646056460664607646086460964610646116461264613646146461564616646176461864619646206462164622646236462464625646266462764628646296463064631646326463364634646356463664637646386463964640646416464264643646446464564646646476464864649646506465164652646536465464655646566465764658646596466064661646626466364664646656466664667646686466964670646716467264673646746467564676646776467864679646806468164682646836468464685646866468764688646896469064691646926469364694646956469664697646986469964700647016470264703647046470564706647076470864709647106471164712647136471464715647166471764718647196472064721647226472364724647256472664727647286472964730647316473264733647346473564736647376473864739647406474164742647436474464745647466474764748647496475064751647526475364754647556475664757647586475964760647616476264763647646476564766647676476864769647706477164772647736477464775647766477764778647796478064781647826478364784647856478664787647886478964790647916479264793647946479564796647976479864799648006480164802648036480464805648066480764808648096481064811648126481364814648156481664817648186481964820648216482264823648246482564826648276482864829648306483164832648336483464835648366483764838648396484064841648426484364844648456484664847648486484964850648516485264853648546485564856648576485864859648606486164862648636486464865648666486764868648696487064871648726487364874648756487664877648786487964880648816488264883648846488564886648876488864889648906489164892648936489464895648966489764898648996490064901649026490364904649056490664907649086490964910649116491264913649146491564916649176491864919649206492164922649236492464925649266492764928649296493064931649326493364934649356493664937649386493964940649416494264943649446494564946649476494864949649506495164952649536495464955649566495764958649596496064961649626496364964649656496664967649686496964970649716497264973649746497564976649776497864979649806498164982649836498464985649866498764988649896499064991649926499364994649956499664997649986499965000650016500265003650046500565006650076500865009650106501165012650136501465015650166501765018650196502065021650226502365024650256502665027650286502965030650316503265033650346503565036650376503865039650406504165042650436504465045650466504765048650496505065051650526505365054650556505665057650586505965060650616506265063650646506565066650676506865069650706507165072650736507465075650766507765078650796508065081650826508365084650856508665087650886508965090650916509265093650946509565096650976509865099651006510165102651036510465105651066510765108651096511065111651126511365114651156511665117651186511965120651216512265123651246512565126651276512865129651306513165132651336513465135651366513765138651396514065141651426514365144651456514665147651486514965150651516515265153651546515565156651576515865159651606516165162651636516465165651666516765168651696517065171651726517365174651756517665177651786517965180651816518265183651846518565186651876518865189651906519165192651936519465195651966519765198651996520065201652026520365204652056520665207652086520965210652116521265213652146521565216652176521865219652206522165222652236522465225652266522765228652296523065231652326523365234652356523665237652386523965240652416524265243652446524565246652476524865249652506525165252652536525465255652566525765258652596526065261652626526365264652656526665267652686526965270652716527265273652746527565276652776527865279652806528165282652836528465285652866528765288652896529065291652926529365294652956529665297652986529965300653016530265303653046530565306653076530865309653106531165312653136531465315653166531765318653196532065321653226532365324653256532665327653286532965330653316533265333653346533565336653376533865339653406534165342653436534465345653466534765348653496535065351653526535365354653556535665357653586535965360653616536265363653646536565366653676536865369653706537165372653736537465375653766537765378653796538065381653826538365384653856538665387653886538965390653916539265393653946539565396653976539865399654006540165402654036540465405654066540765408654096541065411654126541365414654156541665417654186541965420654216542265423654246542565426654276542865429654306543165432654336543465435654366543765438654396544065441654426544365444654456544665447654486544965450654516545265453654546545565456654576545865459654606546165462654636546465465654666546765468654696547065471654726547365474654756547665477654786547965480654816548265483654846548565486654876548865489654906549165492654936549465495654966549765498654996550065501655026550365504655056550665507655086550965510655116551265513655146551565516655176551865519655206552165522655236552465525655266552765528655296553065531655326553365534655356553665537655386553965540655416554265543655446554565546655476554865549655506555165552655536555465555655566555765558655596556065561655626556365564655656556665567655686556965570655716557265573655746557565576655776557865579655806558165582655836558465585655866558765588655896559065591655926559365594655956559665597655986559965600656016560265603656046560565606656076560865609656106561165612656136561465615656166561765618656196562065621656226562365624656256562665627656286562965630656316563265633656346563565636656376563865639656406564165642656436564465645656466564765648656496565065651656526565365654656556565665657656586565965660656616566265663656646566565666656676566865669656706567165672656736567465675656766567765678656796568065681656826568365684656856568665687656886568965690656916569265693656946569565696656976569865699657006570165702657036570465705657066570765708657096571065711657126571365714657156571665717657186571965720657216572265723657246572565726657276572865729657306573165732657336573465735657366573765738657396574065741657426574365744657456574665747657486574965750657516575265753657546575565756657576575865759657606576165762657636576465765657666576765768657696577065771657726577365774657756577665777657786577965780657816578265783657846578565786657876578865789657906579165792657936579465795657966579765798657996580065801658026580365804658056580665807658086580965810658116581265813658146581565816658176581865819658206582165822658236582465825658266582765828658296583065831658326583365834658356583665837658386583965840658416584265843658446584565846658476584865849658506585165852658536585465855658566585765858658596586065861658626586365864658656586665867658686586965870658716587265873658746587565876658776587865879658806588165882658836588465885658866588765888658896589065891658926589365894658956589665897658986589965900659016590265903659046590565906659076590865909659106591165912659136591465915659166591765918659196592065921659226592365924659256592665927659286592965930659316593265933659346593565936659376593865939659406594165942659436594465945659466594765948659496595065951659526595365954659556595665957659586595965960659616596265963659646596565966659676596865969659706597165972659736597465975659766597765978659796598065981659826598365984659856598665987659886598965990659916599265993659946599565996659976599865999660006600166002660036600466005660066600766008660096601066011660126601366014660156601666017660186601966020660216602266023660246602566026660276602866029660306603166032660336603466035660366603766038660396604066041660426604366044660456604666047660486604966050660516605266053660546605566056660576605866059660606606166062660636606466065660666606766068660696607066071660726607366074660756607666077660786607966080660816608266083660846608566086660876608866089660906609166092660936609466095660966609766098660996610066101661026610366104661056610666107661086610966110661116611266113661146611566116661176611866119661206612166122661236612466125661266612766128661296613066131661326613366134661356613666137661386613966140661416614266143661446614566146661476614866149661506615166152661536615466155661566615766158661596616066161661626616366164661656616666167661686616966170661716617266173661746617566176661776617866179661806618166182661836618466185661866618766188661896619066191661926619366194661956619666197661986619966200662016620266203662046620566206662076620866209662106621166212662136621466215662166621766218662196622066221662226622366224662256622666227662286622966230662316623266233662346623566236662376623866239662406624166242662436624466245662466624766248662496625066251662526625366254662556625666257662586625966260662616626266263662646626566266662676626866269662706627166272662736627466275662766627766278662796628066281662826628366284662856628666287662886628966290662916629266293662946629566296662976629866299663006630166302663036630466305663066630766308663096631066311663126631366314663156631666317663186631966320663216632266323663246632566326663276632866329663306633166332663336633466335663366633766338663396634066341663426634366344663456634666347663486634966350663516635266353663546635566356663576635866359663606636166362663636636466365663666636766368663696637066371663726637366374663756637666377663786637966380663816638266383663846638566386663876638866389663906639166392663936639466395663966639766398663996640066401664026640366404664056640666407664086640966410664116641266413664146641566416664176641866419664206642166422664236642466425664266642766428664296643066431664326643366434664356643666437664386643966440664416644266443664446644566446664476644866449664506645166452664536645466455664566645766458664596646066461664626646366464664656646666467664686646966470664716647266473664746647566476664776647866479664806648166482664836648466485664866648766488664896649066491664926649366494664956649666497664986649966500665016650266503665046650566506665076650866509665106651166512665136651466515665166651766518665196652066521665226652366524665256652666527665286652966530665316653266533665346653566536665376653866539665406654166542665436654466545665466654766548665496655066551665526655366554665556655666557665586655966560665616656266563665646656566566665676656866569665706657166572665736657466575665766657766578665796658066581665826658366584665856658666587665886658966590665916659266593665946659566596665976659866599666006660166602666036660466605666066660766608666096661066611666126661366614666156661666617666186661966620666216662266623666246662566626666276662866629666306663166632666336663466635666366663766638666396664066641666426664366644666456664666647666486664966650666516665266653666546665566656666576665866659666606666166662666636666466665666666666766668666696667066671666726667366674666756667666677666786667966680666816668266683666846668566686666876668866689666906669166692666936669466695666966669766698666996670066701667026670366704667056670666707667086670966710667116671266713667146671566716667176671866719667206672166722667236672466725667266672766728667296673066731667326673366734667356673666737667386673966740667416674266743667446674566746667476674866749667506675166752667536675466755667566675766758667596676066761667626676366764667656676666767667686676966770667716677266773667746677566776667776677866779667806678166782667836678466785667866678766788667896679066791667926679366794667956679666797667986679966800668016680266803668046680566806668076680866809668106681166812668136681466815668166681766818668196682066821668226682366824668256682666827668286682966830668316683266833668346683566836668376683866839668406684166842668436684466845668466684766848668496685066851668526685366854668556685666857668586685966860668616686266863668646686566866668676686866869668706687166872668736687466875668766687766878668796688066881668826688366884668856688666887668886688966890668916689266893668946689566896668976689866899669006690166902669036690466905669066690766908669096691066911669126691366914669156691666917669186691966920669216692266923669246692566926669276692866929669306693166932669336693466935669366693766938669396694066941669426694366944669456694666947669486694966950669516695266953669546695566956669576695866959669606696166962669636696466965669666696766968669696697066971669726697366974669756697666977669786697966980669816698266983669846698566986669876698866989669906699166992669936699466995669966699766998669996700067001670026700367004670056700667007670086700967010670116701267013670146701567016670176701867019670206702167022670236702467025670266702767028670296703067031670326703367034670356703667037670386703967040670416704267043670446704567046670476704867049670506705167052670536705467055670566705767058670596706067061670626706367064670656706667067670686706967070670716707267073670746707567076670776707867079670806708167082670836708467085670866708767088670896709067091670926709367094670956709667097670986709967100671016710267103671046710567106671076710867109671106711167112671136711467115671166711767118671196712067121671226712367124671256712667127671286712967130671316713267133671346713567136671376713867139671406714167142671436714467145671466714767148671496715067151671526715367154671556715667157671586715967160671616716267163671646716567166671676716867169671706717167172671736717467175671766717767178671796718067181671826718367184671856718667187671886718967190671916719267193671946719567196671976719867199672006720167202672036720467205672066720767208672096721067211672126721367214672156721667217672186721967220672216722267223672246722567226672276722867229672306723167232672336723467235672366723767238672396724067241672426724367244672456724667247672486724967250672516725267253672546725567256672576725867259672606726167262672636726467265672666726767268672696727067271672726727367274672756727667277672786727967280672816728267283672846728567286672876728867289672906729167292672936729467295672966729767298672996730067301673026730367304673056730667307673086730967310673116731267313673146731567316673176731867319673206732167322673236732467325673266732767328673296733067331673326733367334673356733667337673386733967340673416734267343673446734567346673476734867349673506735167352673536735467355673566735767358673596736067361673626736367364673656736667367673686736967370673716737267373673746737567376673776737867379673806738167382673836738467385673866738767388673896739067391673926739367394673956739667397673986739967400674016740267403674046740567406674076740867409674106741167412674136741467415674166741767418674196742067421674226742367424674256742667427674286742967430674316743267433674346743567436674376743867439674406744167442674436744467445674466744767448674496745067451674526745367454674556745667457674586745967460674616746267463674646746567466674676746867469674706747167472674736747467475674766747767478674796748067481674826748367484674856748667487674886748967490674916749267493674946749567496674976749867499675006750167502675036750467505675066750767508675096751067511675126751367514675156751667517675186751967520675216752267523675246752567526675276752867529675306753167532675336753467535675366753767538675396754067541675426754367544675456754667547675486754967550675516755267553675546755567556675576755867559675606756167562675636756467565675666756767568675696757067571675726757367574675756757667577675786757967580675816758267583675846758567586675876758867589675906759167592675936759467595675966759767598675996760067601676026760367604676056760667607676086760967610676116761267613676146761567616676176761867619676206762167622676236762467625676266762767628676296763067631676326763367634676356763667637676386763967640676416764267643676446764567646676476764867649676506765167652676536765467655676566765767658676596766067661676626766367664676656766667667676686766967670676716767267673676746767567676676776767867679676806768167682676836768467685676866768767688676896769067691676926769367694676956769667697676986769967700677016770267703677046770567706677076770867709677106771167712677136771467715677166771767718677196772067721677226772367724677256772667727677286772967730677316773267733677346773567736677376773867739677406774167742677436774467745677466774767748677496775067751677526775367754677556775667757677586775967760677616776267763677646776567766
  1. /*
  2. #
  3. # File : CImg.h
  4. # ( C++ header file )
  5. #
  6. # Description : C++ Template Image Processing Toolkit.
  7. # This file is the main component of the CImg Library project.
  8. # ( http://cimg.eu )
  9. #
  10. # Project manager : David Tschumperlé
  11. # ( http://tschumperle.users.greyc.fr/ )
  12. #
  13. # A complete list of contributors is available in file 'README.txt'
  14. # distributed within the CImg package.
  15. #
  16. # Licenses : This file is 'dual-licensed', you have to choose one
  17. # of the two licenses below to apply.
  18. #
  19. # CeCILL-C
  20. # The CeCILL-C license is close to the GNU LGPL.
  21. # ( http://cecill.info/licences/Licence_CeCILL-C_V1-en.html )
  22. #
  23. # or CeCILL v2.1
  24. # The CeCILL license is compatible with the GNU GPL.
  25. # ( http://cecill.info/licences/Licence_CeCILL_V2.1-en.html )
  26. #
  27. # This software is governed either by the CeCILL or the CeCILL-C license
  28. # under French law and abiding by the rules of distribution of free software.
  29. # You can use, modify and or redistribute the software under the terms of
  30. # the CeCILL or CeCILL-C licenses as circulated by CEA, CNRS and INRIA
  31. # at the following URL: "http://cecill.info".
  32. #
  33. # As a counterpart to the access to the source code and rights to copy,
  34. # modify and redistribute granted by the license, users are provided only
  35. # with a limited warranty and the software's author, the holder of the
  36. # economic rights, and the successive licensors have only limited
  37. # liability.
  38. #
  39. # In this respect, the user's attention is drawn to the risks associated
  40. # with loading, using, modifying and/or developing or reproducing the
  41. # software by the user in light of its specific status of free software,
  42. # that may mean that it is complicated to manipulate, and that also
  43. # therefore means that it is reserved for developers and experienced
  44. # professionals having in-depth computer knowledge. Users are therefore
  45. # encouraged to load and test the software's suitability as regards their
  46. # requirements in conditions enabling the security of their systems and/or
  47. # data to be ensured and, more generally, to use and operate it in the
  48. # same conditions as regards security.
  49. #
  50. # The fact that you are presently reading this means that you have had
  51. # knowledge of the CeCILL and CeCILL-C licenses and that you accept its terms.
  52. #
  53. */
  54. // Set version number of the library.
  55. #ifndef cimg_version
  56. #define cimg_version 333
  57. /*-----------------------------------------------------------
  58. #
  59. # Test and possibly auto-set CImg configuration variables
  60. # and include required headers.
  61. #
  62. # If you find that the default configuration variables are
  63. # not adapted to your system, you can override their values
  64. # before including the header file "CImg.h"
  65. # (use the #define directive).
  66. #
  67. ------------------------------------------------------------*/
  68. // Include standard C++ headers.
  69. // This is the minimal set of required headers to make CImg-based codes compile.
  70. #include <cstdio>
  71. #include <cstdlib>
  72. #include <cstdarg>
  73. #include <cstring>
  74. #include <cmath>
  75. #include <cfloat>
  76. #include <climits>
  77. #include <ctime>
  78. #include <exception>
  79. #include <algorithm>
  80. #define cimg_str(x) #x
  81. #define cimg_str2(x) cimg_str(x)
  82. // Detect/configure OS variables.
  83. //
  84. // Define 'cimg_OS' to: '0' for an unknown OS (will try to minize library dependencies).
  85. // '1' for a Unix-like OS (Linux, Solaris, BSD, MacOSX, Irix, ...).
  86. // '2' for Microsoft Windows.
  87. // (auto-detection is performed if 'cimg_OS' is not set by the user).
  88. #ifndef cimg_OS
  89. #if defined(unix) || defined(__unix) || defined(__unix__) \
  90. || defined(linux) || defined(__linux) || defined(__linux__) \
  91. || defined(sun) || defined(__sun) \
  92. || defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) \
  93. || defined(__FreeBSD__) || defined (__DragonFly__) \
  94. || defined(sgi) || defined(__sgi) \
  95. || defined(__OSX__) || defined(__MACOSX__) || defined(__APPLE__) \
  96. || defined(__CYGWIN__)
  97. #define cimg_OS 1
  98. #elif defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) \
  99. || defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
  100. #define cimg_OS 2
  101. #else
  102. #define cimg_OS 0
  103. #endif
  104. #elif !(cimg_OS==0 || cimg_OS==1 || cimg_OS==2)
  105. #error CImg Library: Invalid configuration variable 'cimg_OS'.
  106. #error (correct values are '0 = unknown OS', '1 = Unix-like OS', '2 = Microsoft Windows').
  107. #endif
  108. #ifndef cimg_date
  109. #define cimg_date __DATE__
  110. #endif
  111. #ifndef cimg_time
  112. #define cimg_time __TIME__
  113. #endif
  114. // Disable silly warnings on some Microsoft VC++ compilers.
  115. #ifdef _MSC_VER
  116. #pragma warning(push)
  117. #pragma warning(disable:4127)
  118. #pragma warning(disable:4244)
  119. #pragma warning(disable:4307)
  120. #pragma warning(disable:4311)
  121. #pragma warning(disable:4312)
  122. #pragma warning(disable:4319)
  123. #pragma warning(disable:4512)
  124. #pragma warning(disable:4571)
  125. #pragma warning(disable:4640)
  126. #pragma warning(disable:4706)
  127. #pragma warning(disable:4710)
  128. #pragma warning(disable:4800)
  129. #pragma warning(disable:4804)
  130. #pragma warning(disable:4820)
  131. #pragma warning(disable:4995)
  132. #pragma warning(disable:4996)
  133. #ifndef _CRT_SECURE_NO_DEPRECATE
  134. #define _CRT_SECURE_NO_DEPRECATE 1
  135. #endif
  136. #ifndef _CRT_SECURE_NO_WARNINGS
  137. #define _CRT_SECURE_NO_WARNINGS 1
  138. #endif
  139. #ifndef _CRT_NONSTDC_NO_DEPRECATE
  140. #define _CRT_NONSTDC_NO_DEPRECATE 1
  141. #endif
  142. #endif
  143. // Define correct string functions for each compiler and OS.
  144. #if cimg_OS==2 && defined(_MSC_VER)
  145. #define cimg_sscanf std::sscanf
  146. #define cimg_snprintf cimg::_snprintf
  147. #define cimg_vsnprintf cimg::_vsnprintf
  148. #else
  149. #include <stdio.h>
  150. #if defined(__MACOSX__) || defined(__APPLE__)
  151. #define cimg_sscanf cimg::_sscanf
  152. #define cimg_snprintf cimg::_snprintf
  153. #define cimg_vsnprintf cimg::_vsnprintf
  154. #else
  155. #define cimg_sscanf std::sscanf
  156. #define cimg_snprintf snprintf
  157. #define cimg_vsnprintf vsnprintf
  158. #endif
  159. #endif
  160. // Include OS-specific headers.
  161. #if cimg_OS==1
  162. #include <sys/types.h>
  163. #include <sys/time.h>
  164. #include <sys/stat.h>
  165. #include <unistd.h>
  166. #include <dirent.h>
  167. #include <fnmatch.h>
  168. #elif cimg_OS==2
  169. #ifndef NOMINMAX
  170. #define NOMINMAX
  171. #endif
  172. #ifndef WIN32_LEAN_AND_MEAN
  173. #define WIN32_LEAN_AND_MEAN
  174. #endif
  175. #include <windows.h>
  176. #ifndef _WIN32_IE
  177. #define _WIN32_IE 0x0400
  178. #endif
  179. #include <shlobj.h>
  180. #include <process.h>
  181. #include <io.h>
  182. enum {FALSE_WIN = 0};
  183. #endif
  184. // Look for C++11 features.
  185. #ifndef cimg_use_cpp11
  186. #if __cplusplus>201100
  187. #define cimg_use_cpp11 1
  188. #else
  189. #define cimg_use_cpp11 0
  190. #endif
  191. #endif
  192. #if cimg_use_cpp11==1
  193. #include <initializer_list>
  194. #include <utility>
  195. #endif
  196. // Convenient macro to define pragma
  197. #ifdef _MSC_VER
  198. #define cimg_pragma(x) __pragma(x)
  199. #else
  200. #define cimg_pragma(x) _Pragma(#x)
  201. #endif
  202. // Define own datatypes to ensure portability.
  203. // ( 'sizeof(cimg_ulong/cimg_long) = sizeof(void*)' ).
  204. #define cimg_uint8 unsigned char
  205. #if defined(CHAR_MAX) && CHAR_MAX==255
  206. #define cimg_int8 signed char
  207. #else
  208. #define cimg_int8 char
  209. #endif
  210. #define cimg_uint16 unsigned short
  211. #define cimg_int16 short
  212. #define cimg_uint32 unsigned int
  213. #define cimg_int32 int
  214. #define cimg_float32 float
  215. #define cimg_float64 double
  216. #if cimg_OS==2
  217. #define cimg_uint64 unsigned __int64
  218. #define cimg_int64 __int64
  219. #define cimg_ulong UINT_PTR
  220. #define cimg_long INT_PTR
  221. #ifdef _MSC_VER
  222. #define cimg_fuint64 "%I64u"
  223. #define cimg_fint64 "%I64d"
  224. #else
  225. #define cimg_fuint64 "%llu"
  226. #define cimg_fint64 "%lld"
  227. #endif
  228. #else
  229. #if UINTPTR_MAX==0xffffffff || defined(__arm__) || defined(_M_ARM) || ((ULONG_MAX)==(UINT_MAX))
  230. #define cimg_uint64 unsigned long long
  231. #define cimg_int64 long long
  232. #define cimg_fuint64 "%llu"
  233. #define cimg_fint64 "%lld"
  234. #else
  235. #define cimg_uint64 unsigned long
  236. #define cimg_int64 long
  237. #define cimg_fuint64 "%lu"
  238. #define cimg_fint64 "%ld"
  239. #endif
  240. #if defined(__arm__) || defined(_M_ARM)
  241. #define cimg_ulong unsigned long long
  242. #define cimg_long long long
  243. #else
  244. #define cimg_ulong unsigned long
  245. #define cimg_long long
  246. #endif
  247. #endif
  248. #ifndef cimg_max_buf_size
  249. #if UINTPTR_MAX==0xffffffff
  250. #define cimg_max_buf_size ((cimg_ulong)3*1024*1024*1024)
  251. #else
  252. #define cimg_max_buf_size ((cimg_ulong)16*1024*1024*1024)
  253. #endif
  254. #endif
  255. // Configure filename separator.
  256. //
  257. // Filename separator is set by default to '/', except for Windows where it is '\'.
  258. #ifndef cimg_file_separator
  259. #if cimg_OS==2
  260. #define cimg_file_separator '\\'
  261. #else
  262. #define cimg_file_separator '/'
  263. #endif
  264. #endif
  265. // Configure verbosity of output messages.
  266. //
  267. // Define 'cimg_verbosity' to: '0' to hide library messages (quiet mode).
  268. // '1' to output library messages on the console.
  269. // '2' to output library messages on a basic dialog window (default behavior).
  270. // '3' to do as '1' + add extra warnings (may slow down the code!).
  271. // '4' to do as '2' + add extra warnings (may slow down the code!).
  272. //
  273. // Define 'cimg_strict_warnings' to replace warning messages by exception throwns.
  274. //
  275. // Define 'cimg_use_vt100' to allow output of color messages on VT100-compatible terminals.
  276. #ifndef cimg_verbosity
  277. #if cimg_OS==2
  278. #define cimg_verbosity 2
  279. #else
  280. #define cimg_verbosity 1
  281. #endif
  282. #elif !(cimg_verbosity==0 || cimg_verbosity==1 || cimg_verbosity==2 || cimg_verbosity==3 || cimg_verbosity==4)
  283. #error CImg Library: Configuration variable 'cimg_verbosity' is badly defined.
  284. #error (should be { 0=quiet | 1=console | 2=dialog | 3=console+warnings | 4=dialog+warnings }).
  285. #endif
  286. // Configure OpenMP support.
  287. // (http://www.openmp.org)
  288. //
  289. // Define 'cimg_use_openmp' to enable OpenMP support (requires OpenMP 3.0+).
  290. //
  291. // OpenMP directives are used in many CImg functions to get
  292. // advantages of multi-core CPUs.
  293. #if !defined(cimg_use_openmp)
  294. #ifdef _OPENMP
  295. #define cimg_use_openmp 1
  296. #else
  297. #define cimg_use_openmp 0
  298. #endif
  299. #else
  300. #undef cimg_use_openmp
  301. #define cimg_use_openmp 1
  302. #endif
  303. #if cimg_use_openmp!=0
  304. #include <omp.h>
  305. #define cimg_pragma_openmp(p) cimg_pragma(omp p)
  306. #else
  307. #define cimg_pragma_openmp(p)
  308. #endif
  309. // Configure the 'abort' signal handler (does nothing by default).
  310. // A typical signal handler can be defined in your own source like this:
  311. // #define cimg_abort_test if (is_abort) throw CImgAbortException("")
  312. //
  313. // where 'is_abort' is a boolean variable defined somewhere in your code and reachable in the method.
  314. // 'cimg_abort_test2' does the same but is called more often (in inner loops).
  315. #if defined(cimg_abort_test) && cimg_use_openmp!=0
  316. // Define abort macros to be used with OpenMP.
  317. #ifndef _cimg_abort_init_openmp
  318. #define _cimg_abort_init_openmp unsigned int _cimg_abort_go_openmp = 1; cimg::unused(_cimg_abort_go_openmp)
  319. #endif
  320. #ifndef _cimg_abort_try_openmp
  321. #define _cimg_abort_try_openmp if (_cimg_abort_go_openmp) try
  322. #endif
  323. #ifndef _cimg_abort_catch_openmp
  324. #define _cimg_abort_catch_openmp catch (CImgAbortException&) { \
  325. cimg_pragma_openmp(atomic) _cimg_abort_go_openmp&=0; \
  326. }
  327. #endif
  328. #ifndef _cimg_abort_catch_fill_openmp
  329. #define _cimg_abort_catch_fill_openmp \
  330. catch (CImgException& e) { cimg_pragma(omp critical(abort)) CImg<charT>::string(e._message).move_to(is_error_expr); \
  331. cimg_pragma_openmp(atomic) _cimg_abort_go_openmp&=0; }
  332. #endif
  333. #ifdef cimg_abort_test2
  334. #ifndef _cimg_abort_try_openmp2
  335. #define _cimg_abort_try_openmp2 _cimg_abort_try_openmp
  336. #endif
  337. #ifndef _cimg_abort_catch_openmp2
  338. #define _cimg_abort_catch_openmp2 _cimg_abort_catch_openmp
  339. #endif
  340. #endif
  341. #endif
  342. #ifndef _cimg_abort_init_openmp
  343. #define _cimg_abort_init_openmp
  344. #endif
  345. #ifndef _cimg_abort_try_openmp
  346. #define _cimg_abort_try_openmp
  347. #endif
  348. #ifndef _cimg_abort_catch_openmp
  349. #define _cimg_abort_catch_openmp
  350. #endif
  351. #ifndef _cimg_abort_try_openmp2
  352. #define _cimg_abort_try_openmp2
  353. #endif
  354. #ifndef _cimg_abort_catch_openmp2
  355. #define _cimg_abort_catch_openmp2
  356. #endif
  357. #ifndef _cimg_abort_catch_fill_openmp
  358. #define _cimg_abort_catch_fill_openmp
  359. #endif
  360. #ifndef cimg_abort_init
  361. #define cimg_abort_init
  362. #endif
  363. #ifndef cimg_abort_test
  364. #define cimg_abort_test
  365. #endif
  366. #ifndef cimg_abort_test2
  367. #define cimg_abort_test2
  368. #endif
  369. // Configure display framework.
  370. //
  371. // Define 'cimg_display' to: '0' to disable display capabilities.
  372. // '1' to use the X-Window framework (X11).
  373. // '2' to use the Microsoft GDI32 framework.
  374. #ifndef cimg_display
  375. #if cimg_OS==0
  376. #define cimg_display 0
  377. #elif cimg_OS==1
  378. #define cimg_display 1
  379. #elif cimg_OS==2
  380. #define cimg_display 2
  381. #endif
  382. #elif !(cimg_display==0 || cimg_display==1 || cimg_display==2)
  383. #error CImg Library: Configuration variable 'cimg_display' is badly defined.
  384. #error (should be { 0=none | 1=X-Window (X11) | 2=Microsoft GDI32 }).
  385. #endif
  386. // Include display-specific headers.
  387. #if cimg_display==1
  388. #include <X11/Xlib.h>
  389. #include <X11/Xutil.h>
  390. #include <X11/keysym.h>
  391. #include <pthread.h>
  392. #ifdef cimg_use_xshm
  393. #include <sys/ipc.h>
  394. #include <sys/shm.h>
  395. #include <X11/extensions/XShm.h>
  396. #endif
  397. #ifdef cimg_use_xrandr
  398. #include <X11/extensions/Xrandr.h>
  399. #endif
  400. #endif
  401. #ifndef cimg_appname
  402. #define cimg_appname "CImg"
  403. #endif
  404. // Configure OpenCV support.
  405. // (http://opencv.willowgarage.com/wiki/)
  406. //
  407. // Define 'cimg_use_opencv' to enable OpenCV support.
  408. //
  409. // OpenCV library may be used to access images from cameras
  410. // (see method 'CImg<T>::load_camera()').
  411. #ifdef cimg_use_opencv
  412. #ifdef True
  413. #undef True
  414. #define _cimg_redefine_True
  415. #endif
  416. #ifdef False
  417. #undef False
  418. #define _cimg_redefine_False
  419. #endif
  420. #ifdef Status
  421. #undef Status
  422. #define _cimg_redefine_Status
  423. #endif
  424. #include <cstddef>
  425. #include <opencv2/opencv.hpp>
  426. #if CV_MAJOR_VERSION>=3
  427. #define _cimg_fourcc cv::VideoWriter::fourcc
  428. #define _cimg_cap_prop_frame_width cv::VideoCaptureProperties::CAP_PROP_FRAME_WIDTH
  429. #define _cimg_cap_prop_frame_height cv::VideoCaptureProperties::CAP_PROP_FRAME_HEIGHT
  430. #define _cimg_cap_prop_frame_count cv::VideoCaptureProperties::CAP_PROP_FRAME_COUNT
  431. #else
  432. #define _cimg_fourcc CV_FOURCC
  433. #define _cimg_cap_prop_frame_width CV_CAP_PROP_FRAME_WIDTH
  434. #define _cimg_cap_prop_frame_height CV_CAP_PROP_FRAME_HEIGHT
  435. #define _cimg_cap_prop_frame_count CV_CAP_PROP_FRAME_COUNT
  436. #endif
  437. #endif
  438. // Configure LibPNG support.
  439. // (http://www.libpng.org)
  440. //
  441. // Define 'cimg_use_png' to enable LibPNG support.
  442. //
  443. // PNG library may be used to get a native support of '.png' files.
  444. // (see methods 'CImg<T>::{load,save}_png()'.
  445. #ifdef cimg_use_png
  446. extern "C" {
  447. #include "png.h"
  448. }
  449. #endif
  450. // Configure LibJPEG support.
  451. // (http://en.wikipedia.org/wiki/Libjpeg)
  452. //
  453. // Define 'cimg_use_jpeg' to enable LibJPEG support.
  454. //
  455. // JPEG library may be used to get a native support of '.jpg' files.
  456. // (see methods 'CImg<T>::{load,save}_jpeg()').
  457. #ifdef cimg_use_jpeg
  458. extern "C" {
  459. #include "jpeglib.h"
  460. #include "setjmp.h"
  461. }
  462. #endif
  463. // Configure LibTIFF support.
  464. // (http://www.libtiff.org)
  465. //
  466. // Define 'cimg_use_tiff' to enable LibTIFF support.
  467. //
  468. // TIFF library may be used to get a native support of '.tif' files.
  469. // (see methods 'CImg[List]<T>::{load,save}_tiff()').
  470. #ifdef cimg_use_tiff
  471. extern "C" {
  472. #define uint64 uint64_hack_
  473. #define int64 int64_hack_
  474. #include "tiffio.h"
  475. #undef uint64
  476. #undef int64
  477. }
  478. #endif
  479. // Configure HEIF support
  480. // (https://github.com/strukturag/libheif)
  481. //
  482. // Define 'cimg_use_heif' to enable HEIF support.
  483. //
  484. // HEIF library may be used to get a native support of '.heic' and '.avif' files.
  485. // (see method 'CImg<T>::load_heif()').
  486. #ifdef cimg_use_heif
  487. #include <libheif/heif_cxx.h>
  488. #endif
  489. // Configure LibMINC2 support.
  490. // (http://en.wikibooks.org/wiki/MINC/Reference/MINC2.0_File_Format_Reference)
  491. //
  492. // Define 'cimg_use_minc2' to enable LibMINC2 support.
  493. //
  494. // MINC2 library may be used to get a native support of '.mnc' files.
  495. // (see methods 'CImg<T>::{load,save}_minc2()').
  496. #ifdef cimg_use_minc2
  497. #include "minc_io_simple_volume.h"
  498. #include "minc_1_simple.h"
  499. #include "minc_1_simple_rw.h"
  500. #endif
  501. // Configure Zlib support.
  502. // (http://www.zlib.net)
  503. //
  504. // Define 'cimg_use_zlib' to enable Zlib support.
  505. //
  506. // Zlib library may be used to allow compressed data in '.cimgz' files
  507. // (see methods 'CImg[List]<T>::{load,save}_cimg()').
  508. #ifdef cimg_use_zlib
  509. extern "C" {
  510. #include "zlib.h"
  511. }
  512. #endif
  513. // Configure libcurl support.
  514. // (http://curl.haxx.se/libcurl/)
  515. //
  516. // Define 'cimg_use_curl' to enable libcurl support.
  517. //
  518. // Libcurl may be used to get a native support of file downloading from the network.
  519. // (see method 'cimg::load_network()'.)
  520. #ifdef cimg_use_curl
  521. #include "curl/curl.h"
  522. #endif
  523. // Configure Magick++ support.
  524. // (http://www.imagemagick.org/Magick++)
  525. //
  526. // Define 'cimg_use_magick' to enable Magick++ support.
  527. //
  528. // Magick++ library may be used to get a native support of various image file formats.
  529. // (see methods 'CImg<T>::{load,save}()').
  530. #ifdef cimg_use_magick
  531. #include "Magick++.h"
  532. #endif
  533. // Configure FFTW3 support.
  534. // (http://www.fftw.org)
  535. //
  536. // Define 'cimg_use_fftw3' to enable libFFTW3 support.
  537. //
  538. // FFTW3 library may be used to efficiently compute the Fast Fourier Transform
  539. // of image data, without restriction on the image size.
  540. // (see method 'CImg[List]<T>::FFT()').
  541. #ifdef cimg_use_fftw3
  542. extern "C" {
  543. #include "fftw3.h"
  544. }
  545. #endif
  546. // Configure LibBoard support.
  547. // (http://libboard.sourceforge.net/)
  548. //
  549. // Define 'cimg_use_board' to enable Board support.
  550. //
  551. // Board library may be used to draw 3D objects in vector-graphics canvas
  552. // that can be saved as '.ps' or '.svg' files afterwards.
  553. // (see method 'CImg<T>::draw_object3d()').
  554. #ifdef cimg_use_board
  555. #include "Board.h"
  556. #endif
  557. // Configure OpenEXR support.
  558. // (http://www.openexr.com/)
  559. //
  560. // Define 'cimg_use_openexr' to enable OpenEXR support.
  561. //
  562. // OpenEXR library may be used to get a native support of '.exr' files.
  563. // (see methods 'CImg<T>::{load,save}_exr()').
  564. #ifdef cimg_use_openexr
  565. #if __GNUC__>=5
  566. #pragma GCC diagnostic push
  567. #pragma GCC diagnostic ignored "-Wdeprecated"
  568. #pragma GCC diagnostic ignored "-Wdeprecated-copy"
  569. #pragma GCC diagnostic ignored "-Wshadow"
  570. #endif
  571. #include "ImfRgbaFile.h"
  572. #include "ImfInputFile.h"
  573. #include "ImfChannelList.h"
  574. #include "ImfMatrixAttribute.h"
  575. #include "ImfArray.h"
  576. #if __GNUC__>=5
  577. #pragma GCC diagnostic pop
  578. #endif
  579. #endif
  580. // Configure TinyEXR support.
  581. // (https://github.com/syoyo/tinyexr)
  582. //
  583. // Define 'cimg_use_tinyexr' to enable TinyEXR support.
  584. //
  585. // TinyEXR is a small, single header-only library to load and save OpenEXR(.exr) images.
  586. #ifdef cimg_use_tinyexr
  587. #ifndef TINYEXR_IMPLEMENTATION
  588. #define TINYEXR_IMPLEMENTATION
  589. #endif
  590. #include "tinyexr.h"
  591. #endif
  592. // Lapack configuration.
  593. // (http://www.netlib.org/lapack)
  594. //
  595. // Define 'cimg_use_lapack' to enable LAPACK support.
  596. //
  597. // Lapack library may be used in several CImg methods to speed up
  598. // matrix computations (eigenvalues, inverse, ...).
  599. #ifdef cimg_use_lapack
  600. extern "C" {
  601. extern void sgetrf_(int*, int*, float*, int*, int*, int*);
  602. extern void sgetri_(int*, float*, int*, int*, float*, int*, int*);
  603. extern void sgetrs_(char*, int*, int*, float*, int*, int*, float*, int*, int*);
  604. extern void sgesvd_(char*, char*, int*, int*, float*, int*, float*, float*, int*, float*, int*, float*, int*, int*);
  605. extern void ssyev_(char*, char*, int*, float*, int*, float*, float*, int*, int*);
  606. extern void dgetrf_(int*, int*, double*, int*, int*, int*);
  607. extern void dgetri_(int*, double*, int*, int*, double*, int*, int*);
  608. extern void dgetrs_(char*, int*, int*, double*, int*, int*, double*, int*, int*);
  609. extern void dgesvd_(char*, char*, int*, int*, double*, int*, double*, double*,
  610. int*, double*, int*, double*, int*, int*);
  611. extern void dsyev_(char*, char*, int*, double*, int*, double*, double*, int*, int*);
  612. extern void dgels_(char*, int*,int*,int*,double*,int*,double*,int*,double*,int*,int*);
  613. extern void sgels_(char*, int*,int*,int*,float*,int*,float*,int*,float*,int*,int*);
  614. }
  615. #endif
  616. // Check if min/max/PI macros are defined.
  617. //
  618. // CImg does not compile if macros 'min', 'max' or 'PI' are defined,
  619. // because it redefines functions min(), max() and const variable PI in the cimg:: namespace.
  620. // so it '#undef' these macros if necessary, and restore them to reasonable
  621. // values at the end of this file.
  622. #ifdef min
  623. #undef min
  624. #define _cimg_redefine_min
  625. #endif
  626. #ifdef max
  627. #undef max
  628. #define _cimg_redefine_max
  629. #endif
  630. #ifdef PI
  631. #undef PI
  632. #define _cimg_redefine_PI
  633. #endif
  634. /*------------------------------------------------------------------------------
  635. #
  636. # Define user-friendly macros.
  637. #
  638. # These CImg macros are prefixed by 'cimg_' and can be used safely in your own
  639. # code. They are useful to parse command line options, or to write image loops.
  640. #
  641. ------------------------------------------------------------------------------*/
  642. // Macros to define program usage, and retrieve command line arguments.
  643. #define cimg_usage(usage) cimg_library::cimg::option((char*)0,argc,argv,(char*)0,usage,false)
  644. #define cimg_help(str) cimg_library::cimg::option((char*)0,argc,argv,str,(char*)0)
  645. #define cimg_option(name,_default,usage) cimg_library::cimg::option(name,argc,argv,_default,usage)
  646. // Macros to define and manipulate local neighborhoods.
  647. #define CImg_2x2(I,T) T I[4]; \
  648. T& I##cc = I[0]; T& I##nc = I[1]; \
  649. T& I##cn = I[2]; T& I##nn = I[3]; \
  650. I##cc = I##nc = \
  651. I##cn = I##nn = 0
  652. #define CImg_3x3(I,T) T I[9]; \
  653. T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; \
  654. T& I##pc = I[3]; T& I##cc = I[4]; T& I##nc = I[5]; \
  655. T& I##pn = I[6]; T& I##cn = I[7]; T& I##nn = I[8]; \
  656. I##pp = I##cp = I##np = \
  657. I##pc = I##cc = I##nc = \
  658. I##pn = I##cn = I##nn = 0
  659. #define CImg_4x4(I,T) T I[16]; \
  660. T& I##pp = I[0]; T& I##cp = I[1]; T& I##np = I[2]; T& I##ap = I[3]; \
  661. T& I##pc = I[4]; T& I##cc = I[5]; T& I##nc = I[6]; T& I##ac = I[7]; \
  662. T& I##pn = I[8]; T& I##cn = I[9]; T& I##nn = I[10]; T& I##an = I[11]; \
  663. T& I##pa = I[12]; T& I##ca = I[13]; T& I##na = I[14]; T& I##aa = I[15]; \
  664. I##pp = I##cp = I##np = I##ap = \
  665. I##pc = I##cc = I##nc = I##ac = \
  666. I##pn = I##cn = I##nn = I##an = \
  667. I##pa = I##ca = I##na = I##aa = 0
  668. #define CImg_5x5(I,T) T I[25]; \
  669. 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]; \
  670. 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]; \
  671. 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]; \
  672. 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]; \
  673. 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]; \
  674. I##bb = I##pb = I##cb = I##nb = I##ab = \
  675. I##bp = I##pp = I##cp = I##np = I##ap = \
  676. I##bc = I##pc = I##cc = I##nc = I##ac = \
  677. I##bn = I##pn = I##cn = I##nn = I##an = \
  678. I##ba = I##pa = I##ca = I##na = I##aa = 0
  679. #define CImg_2x2x2(I,T) T I[8]; \
  680. T& I##ccc = I[0]; T& I##ncc = I[1]; \
  681. T& I##cnc = I[2]; T& I##nnc = I[3]; \
  682. T& I##ccn = I[4]; T& I##ncn = I[5]; \
  683. T& I##cnn = I[6]; T& I##nnn = I[7]; \
  684. I##ccc = I##ncc = \
  685. I##cnc = I##nnc = \
  686. I##ccn = I##ncn = \
  687. I##cnn = I##nnn = 0
  688. #define CImg_3x3x3(I,T) T I[27]; \
  689. T& I##ppp = I[0]; T& I##cpp = I[1]; T& I##npp = I[2]; \
  690. T& I##pcp = I[3]; T& I##ccp = I[4]; T& I##ncp = I[5]; \
  691. T& I##pnp = I[6]; T& I##cnp = I[7]; T& I##nnp = I[8]; \
  692. T& I##ppc = I[9]; T& I##cpc = I[10]; T& I##npc = I[11]; \
  693. T& I##pcc = I[12]; T& I##ccc = I[13]; T& I##ncc = I[14]; \
  694. T& I##pnc = I[15]; T& I##cnc = I[16]; T& I##nnc = I[17]; \
  695. T& I##ppn = I[18]; T& I##cpn = I[19]; T& I##npn = I[20]; \
  696. T& I##pcn = I[21]; T& I##ccn = I[22]; T& I##ncn = I[23]; \
  697. T& I##pnn = I[24]; T& I##cnn = I[25]; T& I##nnn = I[26]; \
  698. I##ppp = I##cpp = I##npp = \
  699. I##pcp = I##ccp = I##ncp = \
  700. I##pnp = I##cnp = I##nnp = \
  701. I##ppc = I##cpc = I##npc = \
  702. I##pcc = I##ccc = I##ncc = \
  703. I##pnc = I##cnc = I##nnc = \
  704. I##ppn = I##cpn = I##npn = \
  705. I##pcn = I##ccn = I##ncn = \
  706. I##pnn = I##cnn = I##nnn = 0
  707. #define cimg_def2x2(img,x,y) \
  708. int _n1##x = x<(img).width() - 1?x + 1:(img).width() - 1, \
  709. _n1##y = y<(img).height() - 1?y + 1:(img).height() - 1
  710. #define cimg_def3x3(img,x,y) \
  711. cimg_def2x2(img,x,y); \
  712. int _p1##x = x>1?x - 1:0, \
  713. _p1##y = y>1?y - 1:0
  714. #define cimg_def4x4(img,x,y) \
  715. cimg_def3x3(img,x,y); \
  716. int _n2##x = x<(img).width() - 2?x + 2:(img).width() - 1, \
  717. _n2##y = y<(img).height() - 2?y + 2:(img).height() - 1
  718. #define cimg_def5x5(img,x,y) \
  719. cimg_def4x4(img,x,y); \
  720. int _p2##x = x>2?x - 2:0, \
  721. _p2##y = y>2?y - 2:0
  722. #define cimg_def6x6(img,x,y) \
  723. cimg_def5x5(img,x,y); \
  724. int _n3##x = x<(img).width() - 3?x + 3:(img).width() - 1, \
  725. _n3##y = y<(img).height() - 3?y + 3:(img).height() - 1
  726. #define cimg_def7x7(img,x,y) \
  727. cimg_def6x6(img,x,y); \
  728. int _p3##x = x>3?x - 3:0, \
  729. _p3##y = y>3?y - 3:0
  730. #define cimg_def8x8(img,x,y) \
  731. cimg_def7x7(img,x,y); \
  732. int _n4##x = x<(img).width() - 4?x + 4:(img).width() - 1, \
  733. _n4##y = y<(img).height() - 4?y + 4:(img).height() - 1
  734. #define cimg_def9x9(img,x,y) \
  735. cimg_def8x8(img,x,y); \
  736. int _p4##x = x>4?x - 4:0, \
  737. _p4##y = y>4?y - 4:0
  738. #define cimg_def2x2x2(img,x,y,z) \
  739. cimg_def2x2(img,x,y); \
  740. int _n1##z = z<(img).depth() - 1?z + 1:(img).depth() - 1
  741. #define cimg_def3x3x3(img,x,y,z) \
  742. cimg_def2x2x2(img,x,y,z); \
  743. int _p1##x = x>1?x - 1:0, \
  744. _p1##y = y>1?y - 1:0, \
  745. _p1##z = z>1?z - 1:0
  746. #define cimg_get2x2(img,x,y,z,c,I,T) \
  747. 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), \
  748. I[3] = (T)(img)(_n1##x,_n1##y,z,c)
  749. #define cimg_get3x3(img,x,y,z,c,I,T) \
  750. 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), \
  751. 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), \
  752. 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)
  753. #define cimg_get4x4(img,x,y,z,c,I,T) \
  754. 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), \
  755. 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), \
  756. 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), \
  757. 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), \
  758. 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), \
  759. I[15] = (T)(img)(_n2##x,_n2##y,z,c)
  760. #define cimg_get5x5(img,x,y,z,c,I,T) \
  761. 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), \
  762. 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), \
  763. 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), \
  764. 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), \
  765. 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), \
  766. 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), \
  767. 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), \
  768. 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), \
  769. I[24] = (T)(img)(_n2##x,_n2##y,z,c)
  770. #define cimg_get6x6(img,x,y,z,c,I,T) \
  771. 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), \
  772. 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), \
  773. 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), \
  774. 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), \
  775. 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), \
  776. 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), \
  777. 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), \
  778. 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), \
  779. 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), \
  780. 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), \
  781. 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), \
  782. 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)
  783. #define cimg_get7x7(img,x,y,z,c,I,T) \
  784. 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), \
  785. 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), \
  786. 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), \
  787. 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), \
  788. 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), \
  789. 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), \
  790. 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), \
  791. 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), \
  792. 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), \
  793. 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), \
  794. 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), \
  795. 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), \
  796. 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), \
  797. 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), \
  798. 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), \
  799. 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), \
  800. I[48] = (T)(img)(_n3##x,_n3##y,z,c)
  801. #define cimg_get8x8(img,x,y,z,c,I,T) \
  802. 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), \
  803. 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), \
  804. 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), \
  805. 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), \
  806. 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), \
  807. 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), \
  808. 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), \
  809. 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), \
  810. 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), \
  811. 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), \
  812. 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), \
  813. 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), \
  814. 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), \
  815. 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), \
  816. 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), \
  817. 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), \
  818. 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), \
  819. 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), \
  820. 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), \
  821. 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), \
  822. 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), \
  823. I[63] = (T)(img)(_n4##x,_n4##y,z,c);
  824. #define cimg_get9x9(img,x,y,z,c,I,T) \
  825. 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), \
  826. 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), \
  827. 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), \
  828. 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), \
  829. 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), \
  830. 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), \
  831. 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), \
  832. 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), \
  833. 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), \
  834. 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), \
  835. 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), \
  836. 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), \
  837. 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), \
  838. 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), \
  839. 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), \
  840. 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), \
  841. 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), \
  842. 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), \
  843. 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), \
  844. 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), \
  845. 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), \
  846. 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), \
  847. 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), \
  848. 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), \
  849. 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), \
  850. 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), \
  851. 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)
  852. #define cimg_get2x2x2(img,x,y,z,c,I,T) \
  853. 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), \
  854. 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), \
  855. I[6] = (T)(img)(x,_n1##y,_n1##z,c), I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)
  856. #define cimg_get3x3x3(img,x,y,z,c,I,T) \
  857. I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c), I[1] = (T)(img)(x,_p1##y,_p1##z,c), \
  858. 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), \
  859. 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), \
  860. 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), \
  861. 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), \
  862. 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), \
  863. 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), \
  864. 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), \
  865. 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), \
  866. I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)
  867. // Macros to perform various image loops.
  868. //
  869. // These macros are simpler to use than loops with C++ iterators.
  870. #define cimg_for(img,ptrs,T_ptrs) \
  871. for (T_ptrs *ptrs = (img)._data, *_max##ptrs = (img)._data + (img).size(); ptrs<_max##ptrs; ++ptrs)
  872. #define cimg_rof(img,ptrs,T_ptrs) for (T_ptrs *ptrs = (img)._data + (img).size() - 1; ptrs>=(img)._data; --ptrs)
  873. #define cimg_foroff(img,off) for (cimg_ulong off = 0, _max##off = (img).size(); off<_max##off; ++off)
  874. #define cimg_rofoff(img,off) for (cimg_long off = (cimg_long)((img).size() - 1); off>=0; --off)
  875. #define cimg_for1(bound,i) for (int i = 0; i<(int)(bound); ++i)
  876. #define cimg_forX(img,x) cimg_for1((img)._width,x)
  877. #define cimg_forY(img,y) cimg_for1((img)._height,y)
  878. #define cimg_forZ(img,z) cimg_for1((img)._depth,z)
  879. #define cimg_forC(img,c) cimg_for1((img)._spectrum,c)
  880. #define cimg_forXY(img,x,y) cimg_forY(img,y) cimg_forX(img,x)
  881. #define cimg_forXZ(img,x,z) cimg_forZ(img,z) cimg_forX(img,x)
  882. #define cimg_forYZ(img,y,z) cimg_forZ(img,z) cimg_forY(img,y)
  883. #define cimg_forXC(img,x,c) cimg_forC(img,c) cimg_forX(img,x)
  884. #define cimg_forYC(img,y,c) cimg_forC(img,c) cimg_forY(img,y)
  885. #define cimg_forZC(img,z,c) cimg_forC(img,c) cimg_forZ(img,z)
  886. #define cimg_forXYZ(img,x,y,z) cimg_forZ(img,z) cimg_forXY(img,x,y)
  887. #define cimg_forXYC(img,x,y,c) cimg_forC(img,c) cimg_forXY(img,x,y)
  888. #define cimg_forXZC(img,x,z,c) cimg_forC(img,c) cimg_forXZ(img,x,z)
  889. #define cimg_forYZC(img,y,z,c) cimg_forC(img,c) cimg_forYZ(img,y,z)
  890. #define cimg_forXYZC(img,x,y,z,c) cimg_forC(img,c) cimg_forXYZ(img,x,y,z)
  891. #define cimg_rof1(bound,i) for (int i = (int)(bound) - 1; i>=0; --i)
  892. #define cimg_rofX(img,x) cimg_rof1((img)._width,x)
  893. #define cimg_rofY(img,y) cimg_rof1((img)._height,y)
  894. #define cimg_rofZ(img,z) cimg_rof1((img)._depth,z)
  895. #define cimg_rofC(img,c) cimg_rof1((img)._spectrum,c)
  896. #define cimg_rofXY(img,x,y) cimg_rofY(img,y) cimg_rofX(img,x)
  897. #define cimg_rofXZ(img,x,z) cimg_rofZ(img,z) cimg_rofX(img,x)
  898. #define cimg_rofYZ(img,y,z) cimg_rofZ(img,z) cimg_rofY(img,y)
  899. #define cimg_rofXC(img,x,c) cimg_rofC(img,c) cimg_rofX(img,x)
  900. #define cimg_rofYC(img,y,c) cimg_rofC(img,c) cimg_rofY(img,y)
  901. #define cimg_rofZC(img,z,c) cimg_rofC(img,c) cimg_rofZ(img,z)
  902. #define cimg_rofXYZ(img,x,y,z) cimg_rofZ(img,z) cimg_rofXY(img,x,y)
  903. #define cimg_rofXYC(img,x,y,c) cimg_rofC(img,c) cimg_rofXY(img,x,y)
  904. #define cimg_rofXZC(img,x,z,c) cimg_rofC(img,c) cimg_rofXZ(img,x,z)
  905. #define cimg_rofYZC(img,y,z,c) cimg_rofC(img,c) cimg_rofYZ(img,y,z)
  906. #define cimg_rofXYZC(img,x,y,z,c) cimg_rofC(img,c) cimg_rofXYZ(img,x,y,z)
  907. #define cimg_for_in1(bound,i0,i1,i) \
  908. for (int i = (int)(i0)<0?0:(int)(i0), _max##i = (int)(i1)<(int)(bound)?(int)(i1):(int)(bound) - 1; i<=_max##i; ++i)
  909. #define cimg_for_inX(img,x0,x1,x) cimg_for_in1((img)._width,x0,x1,x)
  910. #define cimg_for_inY(img,y0,y1,y) cimg_for_in1((img)._height,y0,y1,y)
  911. #define cimg_for_inZ(img,z0,z1,z) cimg_for_in1((img)._depth,z0,z1,z)
  912. #define cimg_for_inC(img,c0,c1,c) cimg_for_in1((img)._spectrum,c0,c1,c)
  913. #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)
  914. #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)
  915. #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)
  916. #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)
  917. #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)
  918. #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)
  919. #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)
  920. #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)
  921. #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)
  922. #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)
  923. #define cimg_for_inXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
  924. cimg_for_inC(img,c0,c1,c) cimg_for_inXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
  925. #define cimg_for_insideX(img,x,n) cimg_for_inX(img,n,(img)._width - 1 - (n),x)
  926. #define cimg_for_insideY(img,y,n) cimg_for_inY(img,n,(img)._height - 1 - (n),y)
  927. #define cimg_for_insideZ(img,z,n) cimg_for_inZ(img,n,(img)._depth - 1 - (n),z)
  928. #define cimg_for_insideC(img,c,n) cimg_for_inC(img,n,(img)._spectrum - 1 - (n),c)
  929. #define cimg_for_insideXY(img,x,y,n) cimg_for_inXY(img,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),x,y)
  930. #define cimg_for_insideXYZ(img,x,y,z,n) \
  931. cimg_for_inXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z)
  932. #define cimg_for_insideXYZC(img,x,y,z,c,n) \
  933. cimg_for_inXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z)
  934. #define cimg_for_out1(boundi,i0,i1,i) \
  935. for (int i = (int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); ++i, i = i==(int)(i0)?(int)(i1) + 1:i)
  936. #define cimg_for_out2(boundi,boundj,i0,j0,i1,j1,i,j) \
  937. for (int j = 0; j<(int)(boundj); ++j) \
  938. for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j?0:(int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); \
  939. ++i, i = _n1j?i:(i==(int)(i0)?(int)(i1) + 1:i))
  940. #define cimg_for_out3(boundi,boundj,boundk,i0,j0,k0,i1,j1,k1,i,j,k) \
  941. for (int k = 0; k<(int)(boundk); ++k) \
  942. for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \
  943. for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k?0:(int)(i0)>0?0:(int)(i1) + 1; i<(int)(boundi); \
  944. ++i, i = _n1j || _n1k?i:(i==(int)(i0)?(int)(i1) + 1:i))
  945. #define cimg_for_out4(boundi,boundj,boundk,boundl,i0,j0,k0,l0,i1,j1,k1,l1,i,j,k,l) \
  946. for (int l = 0; l<(int)(boundl); ++l) \
  947. for (int _n1l = (int)(l<(int)(l0) || l>(int)(l1)), k = 0; k<(int)(boundk); ++k) \
  948. for (int _n1k = (int)(k<(int)(k0) || k>(int)(k1)), j = 0; j<(int)(boundj); ++j) \
  949. for (int _n1j = (int)(j<(int)(j0) || j>(int)(j1)), i = _n1j || _n1k || _n1l?0:(int)(i0)>0?0:(int)(i1) + 1; \
  950. i<(int)(boundi); ++i, i = _n1j || _n1k || _n1l?i:(i==(int)(i0)?(int)(i1) + 1:i))
  951. #define cimg_for_outX(img,x0,x1,x) cimg_for_out1((img)._width,x0,x1,x)
  952. #define cimg_for_outY(img,y0,y1,y) cimg_for_out1((img)._height,y0,y1,y)
  953. #define cimg_for_outZ(img,z0,z1,z) cimg_for_out1((img)._depth,z0,z1,z)
  954. #define cimg_for_outC(img,c0,c1,c) cimg_for_out1((img)._spectrum,c0,c1,c)
  955. #define cimg_for_outXY(img,x0,y0,x1,y1,x,y) cimg_for_out2((img)._width,(img)._height,x0,y0,x1,y1,x,y)
  956. #define cimg_for_outXZ(img,x0,z0,x1,z1,x,z) cimg_for_out2((img)._width,(img)._depth,x0,z0,x1,z1,x,z)
  957. #define cimg_for_outXC(img,x0,c0,x1,c1,x,c) cimg_for_out2((img)._width,(img)._spectrum,x0,c0,x1,c1,x,c)
  958. #define cimg_for_outYZ(img,y0,z0,y1,z1,y,z) cimg_for_out2((img)._height,(img)._depth,y0,z0,y1,z1,y,z)
  959. #define cimg_for_outYC(img,y0,c0,y1,c1,y,c) cimg_for_out2((img)._height,(img)._spectrum,y0,c0,y1,c1,y,c)
  960. #define cimg_for_outZC(img,z0,c0,z1,c1,z,c) cimg_for_out2((img)._depth,(img)._spectrum,z0,c0,z1,c1,z,c)
  961. #define cimg_for_outXYZ(img,x0,y0,z0,x1,y1,z1,x,y,z) \
  962. cimg_for_out3((img)._width,(img)._height,(img)._depth,x0,y0,z0,x1,y1,z1,x,y,z)
  963. #define cimg_for_outXYC(img,x0,y0,c0,x1,y1,c1,x,y,c) \
  964. cimg_for_out3((img)._width,(img)._height,(img)._spectrum,x0,y0,c0,x1,y1,c1,x,y,c)
  965. #define cimg_for_outXZC(img,x0,z0,c0,x1,z1,c1,x,z,c) \
  966. cimg_for_out3((img)._width,(img)._depth,(img)._spectrum,x0,z0,c0,x1,z1,c1,x,z,c)
  967. #define cimg_for_outYZC(img,y0,z0,c0,y1,z1,c1,y,z,c) \
  968. cimg_for_out3((img)._height,(img)._depth,(img)._spectrum,y0,z0,c0,y1,z1,c1,y,z,c)
  969. #define cimg_for_outXYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
  970. cimg_for_out4((img)._width,(img)._height,(img)._depth,(img)._spectrum,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c)
  971. #define cimg_for_borderX(img,x,n) cimg_for_outX(img,n,(img)._width - 1 - (n),x)
  972. #define cimg_for_borderY(img,y,n) cimg_for_outY(img,n,(img)._height - 1 - (n),y)
  973. #define cimg_for_borderZ(img,z,n) cimg_for_outZ(img,n,(img)._depth - 1 - (n),z)
  974. #define cimg_for_borderC(img,c,n) cimg_for_outC(img,n,(img)._spectrum - 1 - (n),c)
  975. #define cimg_for_borderXY(img,x,y,n) cimg_for_outXY(img,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),x,y)
  976. #define cimg_for_borderXYZ(img,x,y,z,n) \
  977. cimg_for_outXYZ(img,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n),(img)._depth - 1 - (n),x,y,z)
  978. #define cimg_for_borderXYZC(img,x,y,z,c,n) \
  979. cimg_for_outXYZC(img,n,n,n,n,(img)._width - 1 - (n),(img)._height - 1 - (n), \
  980. (img)._depth - 1 - (n),(img)._spectrum - 1 - (n),x,y,z,c)
  981. #define cimg_for_spiralXY(img,x,y) \
  982. for (int x = 0, y = 0, _n1##x = 1, _n1##y = (img).width()*(img).height(); _n1##y; \
  983. --_n1##y, _n1##x+=(_n1##x>>2) - ((!(_n1##x&3)?--y:((_n1##x&3)==1?(img)._width - 1 - ++x:\
  984. ((_n1##x&3)==2?(img)._height - 1 - ++y:--x))))?0:1)
  985. #define cimg_for_lineXY(x,y,x0,y0,x1,y1) \
  986. for (int x = (int)(x0), y = (int)(y0), _sx = 1, _sy = 1, _steep = 0, \
  987. _dx=(x1)>(x0)?(int)(x1) - (int)(x0):(_sx=-1,(int)(x0) - (int)(x1)), \
  988. _dy=(y1)>(y0)?(int)(y1) - (int)(y0):(_sy=-1,(int)(y0) - (int)(y1)), \
  989. _counter = _dx, \
  990. _err = _dx>_dy?(_dy>>1):((_steep=1),(_counter=_dy),(_dx>>1)); \
  991. _counter>=0; \
  992. --_counter, x+=_steep? \
  993. (y+=_sy,(_err-=_dx)<0?_err+=_dy,_sx:0): \
  994. (y+=(_err-=_dy)<0?_err+=_dx,_sy:0,_sx))
  995. #define cimg_for2(bound,i) \
  996. for (int i = 0, _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
  997. _n1##i<(int)(bound) || i==--_n1##i; \
  998. ++i, ++_n1##i)
  999. #define cimg_for2X(img,x) cimg_for2((img)._width,x)
  1000. #define cimg_for2Y(img,y) cimg_for2((img)._height,y)
  1001. #define cimg_for2Z(img,z) cimg_for2((img)._depth,z)
  1002. #define cimg_for2C(img,c) cimg_for2((img)._spectrum,c)
  1003. #define cimg_for2XY(img,x,y) cimg_for2Y(img,y) cimg_for2X(img,x)
  1004. #define cimg_for2XZ(img,x,z) cimg_for2Z(img,z) cimg_for2X(img,x)
  1005. #define cimg_for2XC(img,x,c) cimg_for2C(img,c) cimg_for2X(img,x)
  1006. #define cimg_for2YZ(img,y,z) cimg_for2Z(img,z) cimg_for2Y(img,y)
  1007. #define cimg_for2YC(img,y,c) cimg_for2C(img,c) cimg_for2Y(img,y)
  1008. #define cimg_for2ZC(img,z,c) cimg_for2C(img,c) cimg_for2Z(img,z)
  1009. #define cimg_for2XYZ(img,x,y,z) cimg_for2Z(img,z) cimg_for2XY(img,x,y)
  1010. #define cimg_for2XZC(img,x,z,c) cimg_for2C(img,c) cimg_for2XZ(img,x,z)
  1011. #define cimg_for2YZC(img,y,z,c) cimg_for2C(img,c) cimg_for2YZ(img,y,z)
  1012. #define cimg_for2XYZC(img,x,y,z,c) cimg_for2C(img,c) cimg_for2XYZ(img,x,y,z)
  1013. #define cimg_for_in2(bound,i0,i1,i) \
  1014. for (int i = (int)(i0)<0?0:(int)(i0), \
  1015. _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1; \
  1016. i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \
  1017. ++i, ++_n1##i)
  1018. #define cimg_for_in2X(img,x0,x1,x) cimg_for_in2((img)._width,x0,x1,x)
  1019. #define cimg_for_in2Y(img,y0,y1,y) cimg_for_in2((img)._height,y0,y1,y)
  1020. #define cimg_for_in2Z(img,z0,z1,z) cimg_for_in2((img)._depth,z0,z1,z)
  1021. #define cimg_for_in2C(img,c0,c1,c) cimg_for_in2((img)._spectrum,c0,c1,c)
  1022. #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)
  1023. #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)
  1024. #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)
  1025. #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)
  1026. #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)
  1027. #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)
  1028. #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)
  1029. #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)
  1030. #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)
  1031. #define cimg_for_in2XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
  1032. cimg_for_in2C(img,c0,c1,c) cimg_for_in2XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
  1033. #define cimg_for3(bound,i) \
  1034. for (int i = 0, _p1##i = 0, \
  1035. _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
  1036. _n1##i<(int)(bound) || i==--_n1##i; \
  1037. _p1##i = i++, ++_n1##i)
  1038. #define cimg_for3X(img,x) cimg_for3((img)._width,x)
  1039. #define cimg_for3Y(img,y) cimg_for3((img)._height,y)
  1040. #define cimg_for3Z(img,z) cimg_for3((img)._depth,z)
  1041. #define cimg_for3C(img,c) cimg_for3((img)._spectrum,c)
  1042. #define cimg_for3XY(img,x,y) cimg_for3Y(img,y) cimg_for3X(img,x)
  1043. #define cimg_for3XZ(img,x,z) cimg_for3Z(img,z) cimg_for3X(img,x)
  1044. #define cimg_for3XC(img,x,c) cimg_for3C(img,c) cimg_for3X(img,x)
  1045. #define cimg_for3YZ(img,y,z) cimg_for3Z(img,z) cimg_for3Y(img,y)
  1046. #define cimg_for3YC(img,y,c) cimg_for3C(img,c) cimg_for3Y(img,y)
  1047. #define cimg_for3ZC(img,z,c) cimg_for3C(img,c) cimg_for3Z(img,z)
  1048. #define cimg_for3XYZ(img,x,y,z) cimg_for3Z(img,z) cimg_for3XY(img,x,y)
  1049. #define cimg_for3XZC(img,x,z,c) cimg_for3C(img,c) cimg_for3XZ(img,x,z)
  1050. #define cimg_for3YZC(img,y,z,c) cimg_for3C(img,c) cimg_for3YZ(img,y,z)
  1051. #define cimg_for3XYZC(img,x,y,z,c) cimg_for3C(img,c) cimg_for3XYZ(img,x,y,z)
  1052. #define cimg_for_in3(bound,i0,i1,i) \
  1053. for (int i = (int)(i0)<0?0:(int)(i0), \
  1054. _p1##i = i - 1<0?0:i - 1, \
  1055. _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1; \
  1056. i<=(int)(i1) && (_n1##i<(int)(bound) || i==--_n1##i); \
  1057. _p1##i = i++, ++_n1##i)
  1058. #define cimg_for_in3X(img,x0,x1,x) cimg_for_in3((img)._width,x0,x1,x)
  1059. #define cimg_for_in3Y(img,y0,y1,y) cimg_for_in3((img)._height,y0,y1,y)
  1060. #define cimg_for_in3Z(img,z0,z1,z) cimg_for_in3((img)._depth,z0,z1,z)
  1061. #define cimg_for_in3C(img,c0,c1,c) cimg_for_in3((img)._spectrum,c0,c1,c)
  1062. #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)
  1063. #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)
  1064. #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)
  1065. #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)
  1066. #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)
  1067. #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)
  1068. #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)
  1069. #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)
  1070. #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)
  1071. #define cimg_for_in3XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
  1072. cimg_for_in3C(img,c0,c1,c) cimg_for_in3XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
  1073. #define cimg_for4(bound,i) \
  1074. for (int i = 0, _p1##i = 0, _n1##i = 1>=(bound)?(int)(bound) - 1:1, \
  1075. _n2##i = 2>=(bound)?(int)(bound) - 1:2; \
  1076. _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \
  1077. _p1##i = i++, ++_n1##i, ++_n2##i)
  1078. #define cimg_for4X(img,x) cimg_for4((img)._width,x)
  1079. #define cimg_for4Y(img,y) cimg_for4((img)._height,y)
  1080. #define cimg_for4Z(img,z) cimg_for4((img)._depth,z)
  1081. #define cimg_for4C(img,c) cimg_for4((img)._spectrum,c)
  1082. #define cimg_for4XY(img,x,y) cimg_for4Y(img,y) cimg_for4X(img,x)
  1083. #define cimg_for4XZ(img,x,z) cimg_for4Z(img,z) cimg_for4X(img,x)
  1084. #define cimg_for4XC(img,x,c) cimg_for4C(img,c) cimg_for4X(img,x)
  1085. #define cimg_for4YZ(img,y,z) cimg_for4Z(img,z) cimg_for4Y(img,y)
  1086. #define cimg_for4YC(img,y,c) cimg_for4C(img,c) cimg_for4Y(img,y)
  1087. #define cimg_for4ZC(img,z,c) cimg_for4C(img,c) cimg_for4Z(img,z)
  1088. #define cimg_for4XYZ(img,x,y,z) cimg_for4Z(img,z) cimg_for4XY(img,x,y)
  1089. #define cimg_for4XZC(img,x,z,c) cimg_for4C(img,c) cimg_for4XZ(img,x,z)
  1090. #define cimg_for4YZC(img,y,z,c) cimg_for4C(img,c) cimg_for4YZ(img,y,z)
  1091. #define cimg_for4XYZC(img,x,y,z,c) cimg_for4C(img,c) cimg_for4XYZ(img,x,y,z)
  1092. #define cimg_for_in4(bound,i0,i1,i) \
  1093. for (int i = (int)(i0)<0?0:(int)(i0), \
  1094. _p1##i = i - 1<0?0:i - 1, \
  1095. _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
  1096. _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2; \
  1097. i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \
  1098. _p1##i = i++, ++_n1##i, ++_n2##i)
  1099. #define cimg_for_in4X(img,x0,x1,x) cimg_for_in4((img)._width,x0,x1,x)
  1100. #define cimg_for_in4Y(img,y0,y1,y) cimg_for_in4((img)._height,y0,y1,y)
  1101. #define cimg_for_in4Z(img,z0,z1,z) cimg_for_in4((img)._depth,z0,z1,z)
  1102. #define cimg_for_in4C(img,c0,c1,c) cimg_for_in4((img)._spectrum,c0,c1,c)
  1103. #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)
  1104. #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)
  1105. #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)
  1106. #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)
  1107. #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)
  1108. #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)
  1109. #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)
  1110. #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)
  1111. #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)
  1112. #define cimg_for_in4XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
  1113. cimg_for_in4C(img,c0,c1,c) cimg_for_in4XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
  1114. #define cimg_for5(bound,i) \
  1115. for (int i = 0, _p2##i = 0, _p1##i = 0, \
  1116. _n1##i = 1>=(bound)?(int)(bound) - 1:1, \
  1117. _n2##i = 2>=(bound)?(int)(bound) - 1:2; \
  1118. _n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i); \
  1119. _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i)
  1120. #define cimg_for5X(img,x) cimg_for5((img)._width,x)
  1121. #define cimg_for5Y(img,y) cimg_for5((img)._height,y)
  1122. #define cimg_for5Z(img,z) cimg_for5((img)._depth,z)
  1123. #define cimg_for5C(img,c) cimg_for5((img)._spectrum,c)
  1124. #define cimg_for5XY(img,x,y) cimg_for5Y(img,y) cimg_for5X(img,x)
  1125. #define cimg_for5XZ(img,x,z) cimg_for5Z(img,z) cimg_for5X(img,x)
  1126. #define cimg_for5XC(img,x,c) cimg_for5C(img,c) cimg_for5X(img,x)
  1127. #define cimg_for5YZ(img,y,z) cimg_for5Z(img,z) cimg_for5Y(img,y)
  1128. #define cimg_for5YC(img,y,c) cimg_for5C(img,c) cimg_for5Y(img,y)
  1129. #define cimg_for5ZC(img,z,c) cimg_for5C(img,c) cimg_for5Z(img,z)
  1130. #define cimg_for5XYZ(img,x,y,z) cimg_for5Z(img,z) cimg_for5XY(img,x,y)
  1131. #define cimg_for5XZC(img,x,z,c) cimg_for5C(img,c) cimg_for5XZ(img,x,z)
  1132. #define cimg_for5YZC(img,y,z,c) cimg_for5C(img,c) cimg_for5YZ(img,y,z)
  1133. #define cimg_for5XYZC(img,x,y,z,c) cimg_for5C(img,c) cimg_for5XYZ(img,x,y,z)
  1134. #define cimg_for_in5(bound,i0,i1,i) \
  1135. for (int i = (int)(i0)<0?0:(int)(i0), \
  1136. _p2##i = i - 2<0?0:i - 2, \
  1137. _p1##i = i - 1<0?0:i - 1, \
  1138. _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
  1139. _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2; \
  1140. i<=(int)(i1) && (_n2##i<(int)(bound) || _n1##i==--_n2##i || i==(_n2##i = --_n1##i)); \
  1141. _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i)
  1142. #define cimg_for_in5X(img,x0,x1,x) cimg_for_in5((img)._width,x0,x1,x)
  1143. #define cimg_for_in5Y(img,y0,y1,y) cimg_for_in5((img)._height,y0,y1,y)
  1144. #define cimg_for_in5Z(img,z0,z1,z) cimg_for_in5((img)._depth,z0,z1,z)
  1145. #define cimg_for_in5C(img,c0,c1,c) cimg_for_in5((img)._spectrum,c0,c1,c)
  1146. #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)
  1147. #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)
  1148. #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)
  1149. #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)
  1150. #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)
  1151. #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)
  1152. #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)
  1153. #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)
  1154. #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)
  1155. #define cimg_for_in5XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
  1156. cimg_for_in5C(img,c0,c1,c) cimg_for_in5XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
  1157. #define cimg_for6(bound,i) \
  1158. for (int i = 0, _p2##i = 0, _p1##i = 0, \
  1159. _n1##i = 1>=(bound)?(int)(bound) - 1:1, \
  1160. _n2##i = 2>=(bound)?(int)(bound) - 1:2, \
  1161. _n3##i = 3>=(bound)?(int)(bound) - 1:3; \
  1162. _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \
  1163. _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
  1164. #define cimg_for6X(img,x) cimg_for6((img)._width,x)
  1165. #define cimg_for6Y(img,y) cimg_for6((img)._height,y)
  1166. #define cimg_for6Z(img,z) cimg_for6((img)._depth,z)
  1167. #define cimg_for6C(img,c) cimg_for6((img)._spectrum,c)
  1168. #define cimg_for6XY(img,x,y) cimg_for6Y(img,y) cimg_for6X(img,x)
  1169. #define cimg_for6XZ(img,x,z) cimg_for6Z(img,z) cimg_for6X(img,x)
  1170. #define cimg_for6XC(img,x,c) cimg_for6C(img,c) cimg_for6X(img,x)
  1171. #define cimg_for6YZ(img,y,z) cimg_for6Z(img,z) cimg_for6Y(img,y)
  1172. #define cimg_for6YC(img,y,c) cimg_for6C(img,c) cimg_for6Y(img,y)
  1173. #define cimg_for6ZC(img,z,c) cimg_for6C(img,c) cimg_for6Z(img,z)
  1174. #define cimg_for6XYZ(img,x,y,z) cimg_for6Z(img,z) cimg_for6XY(img,x,y)
  1175. #define cimg_for6XZC(img,x,z,c) cimg_for6C(img,c) cimg_for6XZ(img,x,z)
  1176. #define cimg_for6YZC(img,y,z,c) cimg_for6C(img,c) cimg_for6YZ(img,y,z)
  1177. #define cimg_for6XYZC(img,x,y,z,c) cimg_for6C(img,c) cimg_for6XYZ(img,x,y,z)
  1178. #define cimg_for_in6(bound,i0,i1,i) \
  1179. for (int i = (int)(i0)<0?0:(int)(i0), \
  1180. _p2##i = i - 2<0?0:i - 2, \
  1181. _p1##i = i - 1<0?0:i - 1, \
  1182. _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
  1183. _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \
  1184. _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3; \
  1185. i<=(int)(i1) && \
  1186. (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \
  1187. _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
  1188. #define cimg_for_in6X(img,x0,x1,x) cimg_for_in6((img)._width,x0,x1,x)
  1189. #define cimg_for_in6Y(img,y0,y1,y) cimg_for_in6((img)._height,y0,y1,y)
  1190. #define cimg_for_in6Z(img,z0,z1,z) cimg_for_in6((img)._depth,z0,z1,z)
  1191. #define cimg_for_in6C(img,c0,c1,c) cimg_for_in6((img)._spectrum,c0,c1,c)
  1192. #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)
  1193. #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)
  1194. #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)
  1195. #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)
  1196. #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)
  1197. #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)
  1198. #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)
  1199. #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)
  1200. #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)
  1201. #define cimg_for_in6XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
  1202. cimg_for_in6C(img,c0,c1,c) cimg_for_in6XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
  1203. #define cimg_for7(bound,i) \
  1204. for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
  1205. _n1##i = 1>=(bound)?(int)(bound) - 1:1, \
  1206. _n2##i = 2>=(bound)?(int)(bound) - 1:2, \
  1207. _n3##i = 3>=(bound)?(int)(bound) - 1:3; \
  1208. _n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i); \
  1209. _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
  1210. #define cimg_for7X(img,x) cimg_for7((img)._width,x)
  1211. #define cimg_for7Y(img,y) cimg_for7((img)._height,y)
  1212. #define cimg_for7Z(img,z) cimg_for7((img)._depth,z)
  1213. #define cimg_for7C(img,c) cimg_for7((img)._spectrum,c)
  1214. #define cimg_for7XY(img,x,y) cimg_for7Y(img,y) cimg_for7X(img,x)
  1215. #define cimg_for7XZ(img,x,z) cimg_for7Z(img,z) cimg_for7X(img,x)
  1216. #define cimg_for7XC(img,x,c) cimg_for7C(img,c) cimg_for7X(img,x)
  1217. #define cimg_for7YZ(img,y,z) cimg_for7Z(img,z) cimg_for7Y(img,y)
  1218. #define cimg_for7YC(img,y,c) cimg_for7C(img,c) cimg_for7Y(img,y)
  1219. #define cimg_for7ZC(img,z,c) cimg_for7C(img,c) cimg_for7Z(img,z)
  1220. #define cimg_for7XYZ(img,x,y,z) cimg_for7Z(img,z) cimg_for7XY(img,x,y)
  1221. #define cimg_for7XZC(img,x,z,c) cimg_for7C(img,c) cimg_for7XZ(img,x,z)
  1222. #define cimg_for7YZC(img,y,z,c) cimg_for7C(img,c) cimg_for7YZ(img,y,z)
  1223. #define cimg_for7XYZC(img,x,y,z,c) cimg_for7C(img,c) cimg_for7XYZ(img,x,y,z)
  1224. #define cimg_for_in7(bound,i0,i1,i) \
  1225. for (int i = (int)(i0)<0?0:(int)(i0), \
  1226. _p3##i = i - 3<0?0:i - 3, \
  1227. _p2##i = i - 2<0?0:i - 2, \
  1228. _p1##i = i - 1<0?0:i - 1, \
  1229. _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
  1230. _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \
  1231. _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3; \
  1232. i<=(int)(i1) && \
  1233. (_n3##i<(int)(bound) || _n2##i==--_n3##i || _n1##i==--_n2##i || i==(_n3##i = _n2##i = --_n1##i)); \
  1234. _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i)
  1235. #define cimg_for_in7X(img,x0,x1,x) cimg_for_in7((img)._width,x0,x1,x)
  1236. #define cimg_for_in7Y(img,y0,y1,y) cimg_for_in7((img)._height,y0,y1,y)
  1237. #define cimg_for_in7Z(img,z0,z1,z) cimg_for_in7((img)._depth,z0,z1,z)
  1238. #define cimg_for_in7C(img,c0,c1,c) cimg_for_in7((img)._spectrum,c0,c1,c)
  1239. #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)
  1240. #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)
  1241. #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)
  1242. #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)
  1243. #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)
  1244. #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)
  1245. #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)
  1246. #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)
  1247. #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)
  1248. #define cimg_for_in7XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
  1249. cimg_for_in7C(img,c0,c1,c) cimg_for_in7XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
  1250. #define cimg_for8(bound,i) \
  1251. for (int i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
  1252. _n1##i = 1>=(bound)?(int)(bound) - 1:1, \
  1253. _n2##i = 2>=(bound)?(int)(bound) - 1:2, \
  1254. _n3##i = 3>=(bound)?(int)(bound) - 1:3, \
  1255. _n4##i = 4>=(bound)?(int)(bound) - 1:4; \
  1256. _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
  1257. i==(_n4##i = _n3##i = _n2##i = --_n1##i); \
  1258. _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
  1259. #define cimg_for8X(img,x) cimg_for8((img)._width,x)
  1260. #define cimg_for8Y(img,y) cimg_for8((img)._height,y)
  1261. #define cimg_for8Z(img,z) cimg_for8((img)._depth,z)
  1262. #define cimg_for8C(img,c) cimg_for8((img)._spectrum,c)
  1263. #define cimg_for8XY(img,x,y) cimg_for8Y(img,y) cimg_for8X(img,x)
  1264. #define cimg_for8XZ(img,x,z) cimg_for8Z(img,z) cimg_for8X(img,x)
  1265. #define cimg_for8XC(img,x,c) cimg_for8C(img,c) cimg_for8X(img,x)
  1266. #define cimg_for8YZ(img,y,z) cimg_for8Z(img,z) cimg_for8Y(img,y)
  1267. #define cimg_for8YC(img,y,c) cimg_for8C(img,c) cimg_for8Y(img,y)
  1268. #define cimg_for8ZC(img,z,c) cimg_for8C(img,c) cimg_for8Z(img,z)
  1269. #define cimg_for8XYZ(img,x,y,z) cimg_for8Z(img,z) cimg_for8XY(img,x,y)
  1270. #define cimg_for8XZC(img,x,z,c) cimg_for8C(img,c) cimg_for8XZ(img,x,z)
  1271. #define cimg_for8YZC(img,y,z,c) cimg_for8C(img,c) cimg_for8YZ(img,y,z)
  1272. #define cimg_for8XYZC(img,x,y,z,c) cimg_for8C(img,c) cimg_for8XYZ(img,x,y,z)
  1273. #define cimg_for_in8(bound,i0,i1,i) \
  1274. for (int i = (int)(i0)<0?0:(int)(i0), \
  1275. _p3##i = i - 3<0?0:i - 3, \
  1276. _p2##i = i - 2<0?0:i - 2, \
  1277. _p1##i = i - 1<0?0:i - 1, \
  1278. _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
  1279. _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \
  1280. _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3, \
  1281. _n4##i = i + 4>=(int)(bound)?(int)(bound) - 1:i + 4; \
  1282. i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
  1283. i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \
  1284. _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
  1285. #define cimg_for_in8X(img,x0,x1,x) cimg_for_in8((img)._width,x0,x1,x)
  1286. #define cimg_for_in8Y(img,y0,y1,y) cimg_for_in8((img)._height,y0,y1,y)
  1287. #define cimg_for_in8Z(img,z0,z1,z) cimg_for_in8((img)._depth,z0,z1,z)
  1288. #define cimg_for_in8C(img,c0,c1,c) cimg_for_in8((img)._spectrum,c0,c1,c)
  1289. #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)
  1290. #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)
  1291. #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)
  1292. #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)
  1293. #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)
  1294. #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)
  1295. #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)
  1296. #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)
  1297. #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)
  1298. #define cimg_for_in8XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
  1299. cimg_for_in8C(img,c0,c1,c) cimg_for_in8XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
  1300. #define cimg_for9(bound,i) \
  1301. for (int i = 0, _p4##i = 0, _p3##i = 0, _p2##i = 0, _p1##i = 0, \
  1302. _n1##i = 1>=(int)(bound)?(int)(bound) - 1:1, \
  1303. _n2##i = 2>=(int)(bound)?(int)(bound) - 1:2, \
  1304. _n3##i = 3>=(int)(bound)?(int)(bound) - 1:3, \
  1305. _n4##i = 4>=(int)(bound)?(int)(bound) - 1:4; \
  1306. _n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
  1307. i==(_n4##i = _n3##i = _n2##i = --_n1##i); \
  1308. _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
  1309. #define cimg_for9X(img,x) cimg_for9((img)._width,x)
  1310. #define cimg_for9Y(img,y) cimg_for9((img)._height,y)
  1311. #define cimg_for9Z(img,z) cimg_for9((img)._depth,z)
  1312. #define cimg_for9C(img,c) cimg_for9((img)._spectrum,c)
  1313. #define cimg_for9XY(img,x,y) cimg_for9Y(img,y) cimg_for9X(img,x)
  1314. #define cimg_for9XZ(img,x,z) cimg_for9Z(img,z) cimg_for9X(img,x)
  1315. #define cimg_for9XC(img,x,c) cimg_for9C(img,c) cimg_for9X(img,x)
  1316. #define cimg_for9YZ(img,y,z) cimg_for9Z(img,z) cimg_for9Y(img,y)
  1317. #define cimg_for9YC(img,y,c) cimg_for9C(img,c) cimg_for9Y(img,y)
  1318. #define cimg_for9ZC(img,z,c) cimg_for9C(img,c) cimg_for9Z(img,z)
  1319. #define cimg_for9XYZ(img,x,y,z) cimg_for9Z(img,z) cimg_for9XY(img,x,y)
  1320. #define cimg_for9XZC(img,x,z,c) cimg_for9C(img,c) cimg_for9XZ(img,x,z)
  1321. #define cimg_for9YZC(img,y,z,c) cimg_for9C(img,c) cimg_for9YZ(img,y,z)
  1322. #define cimg_for9XYZC(img,x,y,z,c) cimg_for9C(img,c) cimg_for9XYZ(img,x,y,z)
  1323. #define cimg_for_in9(bound,i0,i1,i) \
  1324. for (int i = (int)(i0)<0?0:(int)(i0), \
  1325. _p4##i = i - 4<0?0:i - 4, \
  1326. _p3##i = i - 3<0?0:i - 3, \
  1327. _p2##i = i - 2<0?0:i - 2, \
  1328. _p1##i = i - 1<0?0:i - 1, \
  1329. _n1##i = i + 1>=(int)(bound)?(int)(bound) - 1:i + 1, \
  1330. _n2##i = i + 2>=(int)(bound)?(int)(bound) - 1:i + 2, \
  1331. _n3##i = i + 3>=(int)(bound)?(int)(bound) - 1:i + 3, \
  1332. _n4##i = i + 4>=(int)(bound)?(int)(bound) - 1:i + 4; \
  1333. i<=(int)(i1) && (_n4##i<(int)(bound) || _n3##i==--_n4##i || _n2##i==--_n3##i || _n1##i==--_n2##i || \
  1334. i==(_n4##i = _n3##i = _n2##i = --_n1##i)); \
  1335. _p4##i = _p3##i, _p3##i = _p2##i, _p2##i = _p1##i, _p1##i = i++, ++_n1##i, ++_n2##i, ++_n3##i, ++_n4##i)
  1336. #define cimg_for_in9X(img,x0,x1,x) cimg_for_in9((img)._width,x0,x1,x)
  1337. #define cimg_for_in9Y(img,y0,y1,y) cimg_for_in9((img)._height,y0,y1,y)
  1338. #define cimg_for_in9Z(img,z0,z1,z) cimg_for_in9((img)._depth,z0,z1,z)
  1339. #define cimg_for_in9C(img,c0,c1,c) cimg_for_in9((img)._spectrum,c0,c1,c)
  1340. #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)
  1341. #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)
  1342. #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)
  1343. #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)
  1344. #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)
  1345. #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)
  1346. #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)
  1347. #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)
  1348. #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)
  1349. #define cimg_for_in9XYZC(img,x0,y0,z0,c0,x1,y1,z1,c1,x,y,z,c) \
  1350. cimg_for_in9C(img,c0,c1,c) cimg_for_in9XYZ(img,x0,y0,z0,x1,y1,z1,x,y,z)
  1351. #define cimg_for2x2(img,x,y,z,c,I,T) \
  1352. cimg_for2((img)._height,y) for (int x = 0, \
  1353. _n1##x = (int)( \
  1354. (I[0] = (T)(img)(0,y,z,c)), \
  1355. (I[2] = (T)(img)(0,_n1##y,z,c)), \
  1356. 1>=(img)._width?(img).width() - 1:1); \
  1357. (_n1##x<(img).width() && ( \
  1358. (I[1] = (T)(img)(_n1##x,y,z,c)), \
  1359. (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
  1360. x==--_n1##x; \
  1361. I[0] = I[1], \
  1362. I[2] = I[3], \
  1363. ++x, ++_n1##x)
  1364. #define cimg_for_in2x2(img,x0,y0,x1,y1,x,y,z,c,I,T) \
  1365. cimg_for_in2((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
  1366. _n1##x = (int)( \
  1367. (I[0] = (T)(img)(x,y,z,c)), \
  1368. (I[2] = (T)(img)(x,_n1##y,z,c)), \
  1369. x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \
  1370. x<=(int)(x1) && ((_n1##x<(img).width() && ( \
  1371. (I[1] = (T)(img)(_n1##x,y,z,c)), \
  1372. (I[3] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
  1373. x==--_n1##x); \
  1374. I[0] = I[1], \
  1375. I[2] = I[3], \
  1376. ++x, ++_n1##x)
  1377. #define cimg_for3x3(img,x,y,z,c,I,T) \
  1378. cimg_for3((img)._height,y) for (int x = 0, \
  1379. _p1##x = 0, \
  1380. _n1##x = (int)( \
  1381. (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
  1382. (I[3] = I[4] = (T)(img)(0,y,z,c)), \
  1383. (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)), \
  1384. 1>=(img)._width?(img).width() - 1:1); \
  1385. (_n1##x<(img).width() && ( \
  1386. (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1387. (I[5] = (T)(img)(_n1##x,y,z,c)), \
  1388. (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
  1389. x==--_n1##x; \
  1390. I[0] = I[1], I[1] = I[2], \
  1391. I[3] = I[4], I[4] = I[5], \
  1392. I[6] = I[7], I[7] = I[8], \
  1393. _p1##x = x++, ++_n1##x)
  1394. #define cimg_for_in3x3(img,x0,y0,x1,y1,x,y,z,c,I,T) \
  1395. cimg_for_in3((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
  1396. _p1##x = x - 1<0?0:x - 1, \
  1397. _n1##x = (int)( \
  1398. (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \
  1399. (I[3] = (T)(img)(_p1##x,y,z,c)), \
  1400. (I[6] = (T)(img)(_p1##x,_n1##y,z,c)), \
  1401. (I[1] = (T)(img)(x,_p1##y,z,c)), \
  1402. (I[4] = (T)(img)(x,y,z,c)), \
  1403. (I[7] = (T)(img)(x,_n1##y,z,c)), \
  1404. x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \
  1405. x<=(int)(x1) && ((_n1##x<(img).width() && ( \
  1406. (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1407. (I[5] = (T)(img)(_n1##x,y,z,c)), \
  1408. (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
  1409. x==--_n1##x); \
  1410. I[0] = I[1], I[1] = I[2], \
  1411. I[3] = I[4], I[4] = I[5], \
  1412. I[6] = I[7], I[7] = I[8], \
  1413. _p1##x = x++, ++_n1##x)
  1414. #define cimg_for4x4(img,x,y,z,c,I,T) \
  1415. cimg_for4((img)._height,y) for (int x = 0, \
  1416. _p1##x = 0, \
  1417. _n1##x = 1>=(img)._width?(img).width() - 1:1, \
  1418. _n2##x = (int)( \
  1419. (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
  1420. (I[4] = I[5] = (T)(img)(0,y,z,c)), \
  1421. (I[8] = I[9] = (T)(img)(0,_n1##y,z,c)), \
  1422. (I[12] = I[13] = (T)(img)(0,_n2##y,z,c)), \
  1423. (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1424. (I[6] = (T)(img)(_n1##x,y,z,c)), \
  1425. (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1426. (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \
  1427. 2>=(img)._width?(img).width() - 1:2); \
  1428. (_n2##x<(img).width() && ( \
  1429. (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \
  1430. (I[7] = (T)(img)(_n2##x,y,z,c)), \
  1431. (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \
  1432. (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
  1433. _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \
  1434. I[0] = I[1], I[1] = I[2], I[2] = I[3], \
  1435. I[4] = I[5], I[5] = I[6], I[6] = I[7], \
  1436. I[8] = I[9], I[9] = I[10], I[10] = I[11], \
  1437. I[12] = I[13], I[13] = I[14], I[14] = I[15], \
  1438. _p1##x = x++, ++_n1##x, ++_n2##x)
  1439. #define cimg_for_in4x4(img,x0,y0,x1,y1,x,y,z,c,I,T) \
  1440. cimg_for_in4((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
  1441. _p1##x = x - 1<0?0:x - 1, \
  1442. _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \
  1443. _n2##x = (int)( \
  1444. (I[0] = (T)(img)(_p1##x,_p1##y,z,c)), \
  1445. (I[4] = (T)(img)(_p1##x,y,z,c)), \
  1446. (I[8] = (T)(img)(_p1##x,_n1##y,z,c)), \
  1447. (I[12] = (T)(img)(_p1##x,_n2##y,z,c)), \
  1448. (I[1] = (T)(img)(x,_p1##y,z,c)), \
  1449. (I[5] = (T)(img)(x,y,z,c)), \
  1450. (I[9] = (T)(img)(x,_n1##y,z,c)), \
  1451. (I[13] = (T)(img)(x,_n2##y,z,c)), \
  1452. (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1453. (I[6] = (T)(img)(_n1##x,y,z,c)), \
  1454. (I[10] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1455. (I[14] = (T)(img)(_n1##x,_n2##y,z,c)), \
  1456. x + 2>=(int)(img)._width?(img).width() - 1:x + 2); \
  1457. x<=(int)(x1) && ((_n2##x<(img).width() && ( \
  1458. (I[3] = (T)(img)(_n2##x,_p1##y,z,c)), \
  1459. (I[7] = (T)(img)(_n2##x,y,z,c)), \
  1460. (I[11] = (T)(img)(_n2##x,_n1##y,z,c)), \
  1461. (I[15] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
  1462. _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \
  1463. I[0] = I[1], I[1] = I[2], I[2] = I[3], \
  1464. I[4] = I[5], I[5] = I[6], I[6] = I[7], \
  1465. I[8] = I[9], I[9] = I[10], I[10] = I[11], \
  1466. I[12] = I[13], I[13] = I[14], I[14] = I[15], \
  1467. _p1##x = x++, ++_n1##x, ++_n2##x)
  1468. #define cimg_for5x5(img,x,y,z,c,I,T) \
  1469. cimg_for5((img)._height,y) for (int x = 0, \
  1470. _p2##x = 0, _p1##x = 0, \
  1471. _n1##x = 1>=(img)._width?(img).width() - 1:1, \
  1472. _n2##x = (int)( \
  1473. (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \
  1474. (I[5] = I[6] = I[7] = (T)(img)(0,_p1##y,z,c)), \
  1475. (I[10] = I[11] = I[12] = (T)(img)(0,y,z,c)), \
  1476. (I[15] = I[16] = I[17] = (T)(img)(0,_n1##y,z,c)), \
  1477. (I[20] = I[21] = I[22] = (T)(img)(0,_n2##y,z,c)), \
  1478. (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
  1479. (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1480. (I[13] = (T)(img)(_n1##x,y,z,c)), \
  1481. (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1482. (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \
  1483. 2>=(img)._width?(img).width() - 1:2); \
  1484. (_n2##x<(img).width() && ( \
  1485. (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
  1486. (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \
  1487. (I[14] = (T)(img)(_n2##x,y,z,c)), \
  1488. (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \
  1489. (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
  1490. _n1##x==--_n2##x || x==(_n2##x = --_n1##x); \
  1491. I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \
  1492. I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \
  1493. I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \
  1494. I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \
  1495. I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
  1496. _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x)
  1497. #define cimg_for_in5x5(img,x0,y0,x1,y1,x,y,z,c,I,T) \
  1498. cimg_for_in5((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
  1499. _p2##x = x - 2<0?0:x - 2, \
  1500. _p1##x = x - 1<0?0:x - 1, \
  1501. _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \
  1502. _n2##x = (int)( \
  1503. (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \
  1504. (I[5] = (T)(img)(_p2##x,_p1##y,z,c)), \
  1505. (I[10] = (T)(img)(_p2##x,y,z,c)), \
  1506. (I[15] = (T)(img)(_p2##x,_n1##y,z,c)), \
  1507. (I[20] = (T)(img)(_p2##x,_n2##y,z,c)), \
  1508. (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \
  1509. (I[6] = (T)(img)(_p1##x,_p1##y,z,c)), \
  1510. (I[11] = (T)(img)(_p1##x,y,z,c)), \
  1511. (I[16] = (T)(img)(_p1##x,_n1##y,z,c)), \
  1512. (I[21] = (T)(img)(_p1##x,_n2##y,z,c)), \
  1513. (I[2] = (T)(img)(x,_p2##y,z,c)), \
  1514. (I[7] = (T)(img)(x,_p1##y,z,c)), \
  1515. (I[12] = (T)(img)(x,y,z,c)), \
  1516. (I[17] = (T)(img)(x,_n1##y,z,c)), \
  1517. (I[22] = (T)(img)(x,_n2##y,z,c)), \
  1518. (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
  1519. (I[8] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1520. (I[13] = (T)(img)(_n1##x,y,z,c)), \
  1521. (I[18] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1522. (I[23] = (T)(img)(_n1##x,_n2##y,z,c)), \
  1523. x + 2>=(int)(img)._width?(img).width() - 1:x + 2); \
  1524. x<=(int)(x1) && ((_n2##x<(img).width() && ( \
  1525. (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
  1526. (I[9] = (T)(img)(_n2##x,_p1##y,z,c)), \
  1527. (I[14] = (T)(img)(_n2##x,y,z,c)), \
  1528. (I[19] = (T)(img)(_n2##x,_n1##y,z,c)), \
  1529. (I[24] = (T)(img)(_n2##x,_n2##y,z,c)),1)) || \
  1530. _n1##x==--_n2##x || x==(_n2##x = --_n1##x)); \
  1531. I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], \
  1532. I[5] = I[6], I[6] = I[7], I[7] = I[8], I[8] = I[9], \
  1533. I[10] = I[11], I[11] = I[12], I[12] = I[13], I[13] = I[14], \
  1534. I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], \
  1535. I[20] = I[21], I[21] = I[22], I[22] = I[23], I[23] = I[24], \
  1536. _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x)
  1537. #define cimg_for6x6(img,x,y,z,c,I,T) \
  1538. cimg_for6((img)._height,y) for (int x = 0, \
  1539. _p2##x = 0, _p1##x = 0, \
  1540. _n1##x = 1>=(img)._width?(img).width() - 1:1, \
  1541. _n2##x = 2>=(img)._width?(img).width() - 1:2, \
  1542. _n3##x = (int)( \
  1543. (I[0] = I[1] = I[2] = (T)(img)(_p2##x,_p2##y,z,c)), \
  1544. (I[6] = I[7] = I[8] = (T)(img)(0,_p1##y,z,c)), \
  1545. (I[12] = I[13] = I[14] = (T)(img)(0,y,z,c)), \
  1546. (I[18] = I[19] = I[20] = (T)(img)(0,_n1##y,z,c)), \
  1547. (I[24] = I[25] = I[26] = (T)(img)(0,_n2##y,z,c)), \
  1548. (I[30] = I[31] = I[32] = (T)(img)(0,_n3##y,z,c)), \
  1549. (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
  1550. (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1551. (I[15] = (T)(img)(_n1##x,y,z,c)), \
  1552. (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1553. (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \
  1554. (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \
  1555. (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
  1556. (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \
  1557. (I[16] = (T)(img)(_n2##x,y,z,c)), \
  1558. (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \
  1559. (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \
  1560. (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \
  1561. 3>=(img)._width?(img).width() - 1:3); \
  1562. (_n3##x<(img).width() && ( \
  1563. (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \
  1564. (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \
  1565. (I[17] = (T)(img)(_n3##x,y,z,c)), \
  1566. (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \
  1567. (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \
  1568. (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
  1569. _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x); \
  1570. I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \
  1571. I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \
  1572. I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \
  1573. I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
  1574. I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \
  1575. I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
  1576. _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
  1577. #define cimg_for_in6x6(img,x0,y0,x1,y1,x,y,z,c,I,T) \
  1578. cimg_for_in6((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)x0, \
  1579. _p2##x = x - 2<0?0:x - 2, \
  1580. _p1##x = x - 1<0?0:x - 1, \
  1581. _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \
  1582. _n2##x = x + 2>=(int)(img)._width?(img).width() - 1:x + 2, \
  1583. _n3##x = (int)( \
  1584. (I[0] = (T)(img)(_p2##x,_p2##y,z,c)), \
  1585. (I[6] = (T)(img)(_p2##x,_p1##y,z,c)), \
  1586. (I[12] = (T)(img)(_p2##x,y,z,c)), \
  1587. (I[18] = (T)(img)(_p2##x,_n1##y,z,c)), \
  1588. (I[24] = (T)(img)(_p2##x,_n2##y,z,c)), \
  1589. (I[30] = (T)(img)(_p2##x,_n3##y,z,c)), \
  1590. (I[1] = (T)(img)(_p1##x,_p2##y,z,c)), \
  1591. (I[7] = (T)(img)(_p1##x,_p1##y,z,c)), \
  1592. (I[13] = (T)(img)(_p1##x,y,z,c)), \
  1593. (I[19] = (T)(img)(_p1##x,_n1##y,z,c)), \
  1594. (I[25] = (T)(img)(_p1##x,_n2##y,z,c)), \
  1595. (I[31] = (T)(img)(_p1##x,_n3##y,z,c)), \
  1596. (I[2] = (T)(img)(x,_p2##y,z,c)), \
  1597. (I[8] = (T)(img)(x,_p1##y,z,c)), \
  1598. (I[14] = (T)(img)(x,y,z,c)), \
  1599. (I[20] = (T)(img)(x,_n1##y,z,c)), \
  1600. (I[26] = (T)(img)(x,_n2##y,z,c)), \
  1601. (I[32] = (T)(img)(x,_n3##y,z,c)), \
  1602. (I[3] = (T)(img)(_n1##x,_p2##y,z,c)), \
  1603. (I[9] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1604. (I[15] = (T)(img)(_n1##x,y,z,c)), \
  1605. (I[21] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1606. (I[27] = (T)(img)(_n1##x,_n2##y,z,c)), \
  1607. (I[33] = (T)(img)(_n1##x,_n3##y,z,c)), \
  1608. (I[4] = (T)(img)(_n2##x,_p2##y,z,c)), \
  1609. (I[10] = (T)(img)(_n2##x,_p1##y,z,c)), \
  1610. (I[16] = (T)(img)(_n2##x,y,z,c)), \
  1611. (I[22] = (T)(img)(_n2##x,_n1##y,z,c)), \
  1612. (I[28] = (T)(img)(_n2##x,_n2##y,z,c)), \
  1613. (I[34] = (T)(img)(_n2##x,_n3##y,z,c)), \
  1614. x + 3>=(int)(img)._width?(img).width() - 1:x + 3); \
  1615. x<=(int)(x1) && ((_n3##x<(img).width() && ( \
  1616. (I[5] = (T)(img)(_n3##x,_p2##y,z,c)), \
  1617. (I[11] = (T)(img)(_n3##x,_p1##y,z,c)), \
  1618. (I[17] = (T)(img)(_n3##x,y,z,c)), \
  1619. (I[23] = (T)(img)(_n3##x,_n1##y,z,c)), \
  1620. (I[29] = (T)(img)(_n3##x,_n2##y,z,c)), \
  1621. (I[35] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
  1622. _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3## x = _n2##x = --_n1##x)); \
  1623. I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], \
  1624. I[6] = I[7], I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], \
  1625. I[12] = I[13], I[13] = I[14], I[14] = I[15], I[15] = I[16], I[16] = I[17], \
  1626. I[18] = I[19], I[19] = I[20], I[20] = I[21], I[21] = I[22], I[22] = I[23], \
  1627. I[24] = I[25], I[25] = I[26], I[26] = I[27], I[27] = I[28], I[28] = I[29], \
  1628. I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], I[34] = I[35], \
  1629. _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
  1630. #define cimg_for7x7(img,x,y,z,c,I,T) \
  1631. cimg_for7((img)._height,y) for (int x = 0, \
  1632. _p3##x = 0, _p2##x = 0, _p1##x = 0, \
  1633. _n1##x = 1>=(img)._width?(img).width() - 1:1, \
  1634. _n2##x = 2>=(img)._width?(img).width() - 1:2, \
  1635. _n3##x = (int)( \
  1636. (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \
  1637. (I[7] = I[8] = I[9] = I[10] = (T)(img)(0,_p2##y,z,c)), \
  1638. (I[14] = I[15] = I[16] = I[17] = (T)(img)(0,_p1##y,z,c)), \
  1639. (I[21] = I[22] = I[23] = I[24] = (T)(img)(0,y,z,c)), \
  1640. (I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_n1##y,z,c)), \
  1641. (I[35] = I[36] = I[37] = I[38] = (T)(img)(0,_n2##y,z,c)), \
  1642. (I[42] = I[43] = I[44] = I[45] = (T)(img)(0,_n3##y,z,c)), \
  1643. (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
  1644. (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \
  1645. (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1646. (I[25] = (T)(img)(_n1##x,y,z,c)), \
  1647. (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1648. (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \
  1649. (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \
  1650. (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
  1651. (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \
  1652. (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \
  1653. (I[26] = (T)(img)(_n2##x,y,z,c)), \
  1654. (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \
  1655. (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \
  1656. (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \
  1657. 3>=(img)._width?(img).width() - 1:3); \
  1658. (_n3##x<(img).width() && ( \
  1659. (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
  1660. (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \
  1661. (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \
  1662. (I[27] = (T)(img)(_n3##x,y,z,c)), \
  1663. (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \
  1664. (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \
  1665. (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
  1666. _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x); \
  1667. I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \
  1668. I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \
  1669. I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \
  1670. I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \
  1671. I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \
  1672. I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \
  1673. I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
  1674. _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
  1675. #define cimg_for_in7x7(img,x0,y0,x1,y1,x,y,z,c,I,T) \
  1676. cimg_for_in7((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
  1677. _p3##x = x - 3<0?0:x - 3, \
  1678. _p2##x = x - 2<0?0:x - 2, \
  1679. _p1##x = x - 1<0?0:x - 1, \
  1680. _n1##x = x + 1>=(int)(img)._width?(img).width() - 1:x + 1, \
  1681. _n2##x = x + 2>=(int)(img)._width?(img).width() - 1:x + 2, \
  1682. _n3##x = (int)( \
  1683. (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \
  1684. (I[7] = (T)(img)(_p3##x,_p2##y,z,c)), \
  1685. (I[14] = (T)(img)(_p3##x,_p1##y,z,c)), \
  1686. (I[21] = (T)(img)(_p3##x,y,z,c)), \
  1687. (I[28] = (T)(img)(_p3##x,_n1##y,z,c)), \
  1688. (I[35] = (T)(img)(_p3##x,_n2##y,z,c)), \
  1689. (I[42] = (T)(img)(_p3##x,_n3##y,z,c)), \
  1690. (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \
  1691. (I[8] = (T)(img)(_p2##x,_p2##y,z,c)), \
  1692. (I[15] = (T)(img)(_p2##x,_p1##y,z,c)), \
  1693. (I[22] = (T)(img)(_p2##x,y,z,c)), \
  1694. (I[29] = (T)(img)(_p2##x,_n1##y,z,c)), \
  1695. (I[36] = (T)(img)(_p2##x,_n2##y,z,c)), \
  1696. (I[43] = (T)(img)(_p2##x,_n3##y,z,c)), \
  1697. (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \
  1698. (I[9] = (T)(img)(_p1##x,_p2##y,z,c)), \
  1699. (I[16] = (T)(img)(_p1##x,_p1##y,z,c)), \
  1700. (I[23] = (T)(img)(_p1##x,y,z,c)), \
  1701. (I[30] = (T)(img)(_p1##x,_n1##y,z,c)), \
  1702. (I[37] = (T)(img)(_p1##x,_n2##y,z,c)), \
  1703. (I[44] = (T)(img)(_p1##x,_n3##y,z,c)), \
  1704. (I[3] = (T)(img)(x,_p3##y,z,c)), \
  1705. (I[10] = (T)(img)(x,_p2##y,z,c)), \
  1706. (I[17] = (T)(img)(x,_p1##y,z,c)), \
  1707. (I[24] = (T)(img)(x,y,z,c)), \
  1708. (I[31] = (T)(img)(x,_n1##y,z,c)), \
  1709. (I[38] = (T)(img)(x,_n2##y,z,c)), \
  1710. (I[45] = (T)(img)(x,_n3##y,z,c)), \
  1711. (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
  1712. (I[11] = (T)(img)(_n1##x,_p2##y,z,c)), \
  1713. (I[18] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1714. (I[25] = (T)(img)(_n1##x,y,z,c)), \
  1715. (I[32] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1716. (I[39] = (T)(img)(_n1##x,_n2##y,z,c)), \
  1717. (I[46] = (T)(img)(_n1##x,_n3##y,z,c)), \
  1718. (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
  1719. (I[12] = (T)(img)(_n2##x,_p2##y,z,c)), \
  1720. (I[19] = (T)(img)(_n2##x,_p1##y,z,c)), \
  1721. (I[26] = (T)(img)(_n2##x,y,z,c)), \
  1722. (I[33] = (T)(img)(_n2##x,_n1##y,z,c)), \
  1723. (I[40] = (T)(img)(_n2##x,_n2##y,z,c)), \
  1724. (I[47] = (T)(img)(_n2##x,_n3##y,z,c)), \
  1725. x + 3>=(int)(img)._width?(img).width() - 1:x + 3); \
  1726. x<=(int)(x1) && ((_n3##x<(img).width() && ( \
  1727. (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
  1728. (I[13] = (T)(img)(_n3##x,_p2##y,z,c)), \
  1729. (I[20] = (T)(img)(_n3##x,_p1##y,z,c)), \
  1730. (I[27] = (T)(img)(_n3##x,y,z,c)), \
  1731. (I[34] = (T)(img)(_n3##x,_n1##y,z,c)), \
  1732. (I[41] = (T)(img)(_n3##x,_n2##y,z,c)), \
  1733. (I[48] = (T)(img)(_n3##x,_n3##y,z,c)),1)) || \
  1734. _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n3##x = _n2##x = --_n1##x)); \
  1735. I[0] = I[1], I[1] = I[2], I[2] = I[3], I[3] = I[4], I[4] = I[5], I[5] = I[6], \
  1736. I[7] = I[8], I[8] = I[9], I[9] = I[10], I[10] = I[11], I[11] = I[12], I[12] = I[13], \
  1737. I[14] = I[15], I[15] = I[16], I[16] = I[17], I[17] = I[18], I[18] = I[19], I[19] = I[20], \
  1738. I[21] = I[22], I[22] = I[23], I[23] = I[24], I[24] = I[25], I[25] = I[26], I[26] = I[27], \
  1739. I[28] = I[29], I[29] = I[30], I[30] = I[31], I[31] = I[32], I[32] = I[33], I[33] = I[34], \
  1740. I[35] = I[36], I[36] = I[37], I[37] = I[38], I[38] = I[39], I[39] = I[40], I[40] = I[41], \
  1741. I[42] = I[43], I[43] = I[44], I[44] = I[45], I[45] = I[46], I[46] = I[47], I[47] = I[48], \
  1742. _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x)
  1743. #define cimg_for8x8(img,x,y,z,c,I,T) \
  1744. cimg_for8((img)._height,y) for (int x = 0, \
  1745. _p3##x = 0, _p2##x = 0, _p1##x = 0, \
  1746. _n1##x = 1>=((img)._width)?(img).width() - 1:1, \
  1747. _n2##x = 2>=((img)._width)?(img).width() - 1:2, \
  1748. _n3##x = 3>=((img)._width)?(img).width() - 1:3, \
  1749. _n4##x = (int)( \
  1750. (I[0] = I[1] = I[2] = I[3] = (T)(img)(_p3##x,_p3##y,z,c)), \
  1751. (I[8] = I[9] = I[10] = I[11] = (T)(img)(0,_p2##y,z,c)), \
  1752. (I[16] = I[17] = I[18] = I[19] = (T)(img)(0,_p1##y,z,c)), \
  1753. (I[24] = I[25] = I[26] = I[27] = (T)(img)(0,y,z,c)), \
  1754. (I[32] = I[33] = I[34] = I[35] = (T)(img)(0,_n1##y,z,c)), \
  1755. (I[40] = I[41] = I[42] = I[43] = (T)(img)(0,_n2##y,z,c)), \
  1756. (I[48] = I[49] = I[50] = I[51] = (T)(img)(0,_n3##y,z,c)), \
  1757. (I[56] = I[57] = I[58] = I[59] = (T)(img)(0,_n4##y,z,c)), \
  1758. (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
  1759. (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \
  1760. (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1761. (I[28] = (T)(img)(_n1##x,y,z,c)), \
  1762. (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1763. (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \
  1764. (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \
  1765. (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \
  1766. (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
  1767. (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \
  1768. (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \
  1769. (I[29] = (T)(img)(_n2##x,y,z,c)), \
  1770. (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \
  1771. (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \
  1772. (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \
  1773. (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \
  1774. (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
  1775. (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \
  1776. (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \
  1777. (I[30] = (T)(img)(_n3##x,y,z,c)), \
  1778. (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \
  1779. (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \
  1780. (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \
  1781. (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \
  1782. 4>=((img)._width)?(img).width() - 1:4); \
  1783. (_n4##x<(img).width() && ( \
  1784. (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \
  1785. (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \
  1786. (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \
  1787. (I[31] = (T)(img)(_n4##x,y,z,c)), \
  1788. (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \
  1789. (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \
  1790. (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \
  1791. (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
  1792. _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \
  1793. 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], \
  1794. 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], \
  1795. 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], \
  1796. 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], \
  1797. 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], \
  1798. 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], \
  1799. 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], \
  1800. 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], \
  1801. _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
  1802. #define cimg_for_in8x8(img,x0,y0,x1,y1,x,y,z,c,I,T) \
  1803. cimg_for_in8((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
  1804. _p3##x = x - 3<0?0:x - 3, \
  1805. _p2##x = x - 2<0?0:x - 2, \
  1806. _p1##x = x - 1<0?0:x - 1, \
  1807. _n1##x = x + 1>=(img).width()?(img).width() - 1:x + 1, \
  1808. _n2##x = x + 2>=(img).width()?(img).width() - 1:x + 2, \
  1809. _n3##x = x + 3>=(img).width()?(img).width() - 1:x + 3, \
  1810. _n4##x = (int)( \
  1811. (I[0] = (T)(img)(_p3##x,_p3##y,z,c)), \
  1812. (I[8] = (T)(img)(_p3##x,_p2##y,z,c)), \
  1813. (I[16] = (T)(img)(_p3##x,_p1##y,z,c)), \
  1814. (I[24] = (T)(img)(_p3##x,y,z,c)), \
  1815. (I[32] = (T)(img)(_p3##x,_n1##y,z,c)), \
  1816. (I[40] = (T)(img)(_p3##x,_n2##y,z,c)), \
  1817. (I[48] = (T)(img)(_p3##x,_n3##y,z,c)), \
  1818. (I[56] = (T)(img)(_p3##x,_n4##y,z,c)), \
  1819. (I[1] = (T)(img)(_p2##x,_p3##y,z,c)), \
  1820. (I[9] = (T)(img)(_p2##x,_p2##y,z,c)), \
  1821. (I[17] = (T)(img)(_p2##x,_p1##y,z,c)), \
  1822. (I[25] = (T)(img)(_p2##x,y,z,c)), \
  1823. (I[33] = (T)(img)(_p2##x,_n1##y,z,c)), \
  1824. (I[41] = (T)(img)(_p2##x,_n2##y,z,c)), \
  1825. (I[49] = (T)(img)(_p2##x,_n3##y,z,c)), \
  1826. (I[57] = (T)(img)(_p2##x,_n4##y,z,c)), \
  1827. (I[2] = (T)(img)(_p1##x,_p3##y,z,c)), \
  1828. (I[10] = (T)(img)(_p1##x,_p2##y,z,c)), \
  1829. (I[18] = (T)(img)(_p1##x,_p1##y,z,c)), \
  1830. (I[26] = (T)(img)(_p1##x,y,z,c)), \
  1831. (I[34] = (T)(img)(_p1##x,_n1##y,z,c)), \
  1832. (I[42] = (T)(img)(_p1##x,_n2##y,z,c)), \
  1833. (I[50] = (T)(img)(_p1##x,_n3##y,z,c)), \
  1834. (I[58] = (T)(img)(_p1##x,_n4##y,z,c)), \
  1835. (I[3] = (T)(img)(x,_p3##y,z,c)), \
  1836. (I[11] = (T)(img)(x,_p2##y,z,c)), \
  1837. (I[19] = (T)(img)(x,_p1##y,z,c)), \
  1838. (I[27] = (T)(img)(x,y,z,c)), \
  1839. (I[35] = (T)(img)(x,_n1##y,z,c)), \
  1840. (I[43] = (T)(img)(x,_n2##y,z,c)), \
  1841. (I[51] = (T)(img)(x,_n3##y,z,c)), \
  1842. (I[59] = (T)(img)(x,_n4##y,z,c)), \
  1843. (I[4] = (T)(img)(_n1##x,_p3##y,z,c)), \
  1844. (I[12] = (T)(img)(_n1##x,_p2##y,z,c)), \
  1845. (I[20] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1846. (I[28] = (T)(img)(_n1##x,y,z,c)), \
  1847. (I[36] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1848. (I[44] = (T)(img)(_n1##x,_n2##y,z,c)), \
  1849. (I[52] = (T)(img)(_n1##x,_n3##y,z,c)), \
  1850. (I[60] = (T)(img)(_n1##x,_n4##y,z,c)), \
  1851. (I[5] = (T)(img)(_n2##x,_p3##y,z,c)), \
  1852. (I[13] = (T)(img)(_n2##x,_p2##y,z,c)), \
  1853. (I[21] = (T)(img)(_n2##x,_p1##y,z,c)), \
  1854. (I[29] = (T)(img)(_n2##x,y,z,c)), \
  1855. (I[37] = (T)(img)(_n2##x,_n1##y,z,c)), \
  1856. (I[45] = (T)(img)(_n2##x,_n2##y,z,c)), \
  1857. (I[53] = (T)(img)(_n2##x,_n3##y,z,c)), \
  1858. (I[61] = (T)(img)(_n2##x,_n4##y,z,c)), \
  1859. (I[6] = (T)(img)(_n3##x,_p3##y,z,c)), \
  1860. (I[14] = (T)(img)(_n3##x,_p2##y,z,c)), \
  1861. (I[22] = (T)(img)(_n3##x,_p1##y,z,c)), \
  1862. (I[30] = (T)(img)(_n3##x,y,z,c)), \
  1863. (I[38] = (T)(img)(_n3##x,_n1##y,z,c)), \
  1864. (I[46] = (T)(img)(_n3##x,_n2##y,z,c)), \
  1865. (I[54] = (T)(img)(_n3##x,_n3##y,z,c)), \
  1866. (I[62] = (T)(img)(_n3##x,_n4##y,z,c)), \
  1867. x + 4>=(img).width()?(img).width() - 1:x + 4); \
  1868. x<=(int)(x1) && ((_n4##x<(img).width() && ( \
  1869. (I[7] = (T)(img)(_n4##x,_p3##y,z,c)), \
  1870. (I[15] = (T)(img)(_n4##x,_p2##y,z,c)), \
  1871. (I[23] = (T)(img)(_n4##x,_p1##y,z,c)), \
  1872. (I[31] = (T)(img)(_n4##x,y,z,c)), \
  1873. (I[39] = (T)(img)(_n4##x,_n1##y,z,c)), \
  1874. (I[47] = (T)(img)(_n4##x,_n2##y,z,c)), \
  1875. (I[55] = (T)(img)(_n4##x,_n3##y,z,c)), \
  1876. (I[63] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
  1877. _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \
  1878. 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], \
  1879. 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], \
  1880. 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], \
  1881. 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], \
  1882. 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], \
  1883. 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], \
  1884. 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], \
  1885. 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], \
  1886. _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
  1887. #define cimg_for9x9(img,x,y,z,c,I,T) \
  1888. cimg_for9((img)._height,y) for (int x = 0, \
  1889. _p4##x = 0, _p3##x = 0, _p2##x = 0, _p1##x = 0, \
  1890. _n1##x = 1>=((img)._width)?(img).width() - 1:1, \
  1891. _n2##x = 2>=((img)._width)?(img).width() - 1:2, \
  1892. _n3##x = 3>=((img)._width)?(img).width() - 1:3, \
  1893. _n4##x = (int)( \
  1894. (I[0] = I[1] = I[2] = I[3] = I[4] = (T)(img)(_p4##x,_p4##y,z,c)), \
  1895. (I[9] = I[10] = I[11] = I[12] = I[13] = (T)(img)(0,_p3##y,z,c)), \
  1896. (I[18] = I[19] = I[20] = I[21] = I[22] = (T)(img)(0,_p2##y,z,c)), \
  1897. (I[27] = I[28] = I[29] = I[30] = I[31] = (T)(img)(0,_p1##y,z,c)), \
  1898. (I[36] = I[37] = I[38] = I[39] = I[40] = (T)(img)(0,y,z,c)), \
  1899. (I[45] = I[46] = I[47] = I[48] = I[49] = (T)(img)(0,_n1##y,z,c)), \
  1900. (I[54] = I[55] = I[56] = I[57] = I[58] = (T)(img)(0,_n2##y,z,c)), \
  1901. (I[63] = I[64] = I[65] = I[66] = I[67] = (T)(img)(0,_n3##y,z,c)), \
  1902. (I[72] = I[73] = I[74] = I[75] = I[76] = (T)(img)(0,_n4##y,z,c)), \
  1903. (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \
  1904. (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \
  1905. (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \
  1906. (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \
  1907. (I[41] = (T)(img)(_n1##x,y,z,c)), \
  1908. (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \
  1909. (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \
  1910. (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \
  1911. (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \
  1912. (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \
  1913. (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \
  1914. (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \
  1915. (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \
  1916. (I[42] = (T)(img)(_n2##x,y,z,c)), \
  1917. (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \
  1918. (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \
  1919. (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \
  1920. (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \
  1921. (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \
  1922. (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \
  1923. (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \
  1924. (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \
  1925. (I[43] = (T)(img)(_n3##x,y,z,c)), \
  1926. (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \
  1927. (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \
  1928. (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \
  1929. (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \
  1930. 4>=((img)._width)?(img).width() - 1:4); \
  1931. (_n4##x<(img).width() && ( \
  1932. (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \
  1933. (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \
  1934. (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \
  1935. (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \
  1936. (I[44] = (T)(img)(_n4##x,y,z,c)), \
  1937. (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \
  1938. (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \
  1939. (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \
  1940. (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
  1941. _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x); \
  1942. 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], \
  1943. 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], \
  1944. 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], \
  1945. 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], \
  1946. 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], \
  1947. 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], \
  1948. 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], \
  1949. 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], \
  1950. 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], \
  1951. 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], \
  1952. I[79] = I[80], \
  1953. _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
  1954. #define cimg_for_in9x9(img,x0,y0,x1,y1,x,y,z,c,I,T) \
  1955. cimg_for_in9((img)._height,y0,y1,y) for (int x = (int)(x0)<0?0:(int)(x0), \
  1956. _p4##x = x - 4<0?0:x - 4, \
  1957. _p3##x = x - 3<0?0:x - 3, \
  1958. _p2##x = x - 2<0?0:x - 2, \
  1959. _p1##x = x - 1<0?0:x - 1, \
  1960. _n1##x = x + 1>=(img).width()?(img).width() - 1:x + 1, \
  1961. _n2##x = x + 2>=(img).width()?(img).width() - 1:x + 2, \
  1962. _n3##x = x + 3>=(img).width()?(img).width() - 1:x + 3, \
  1963. _n4##x = (int)( \
  1964. (I[0] = (T)(img)(_p4##x,_p4##y,z,c)), \
  1965. (I[9] = (T)(img)(_p4##x,_p3##y,z,c)), \
  1966. (I[18] = (T)(img)(_p4##x,_p2##y,z,c)), \
  1967. (I[27] = (T)(img)(_p4##x,_p1##y,z,c)), \
  1968. (I[36] = (T)(img)(_p4##x,y,z,c)), \
  1969. (I[45] = (T)(img)(_p4##x,_n1##y,z,c)), \
  1970. (I[54] = (T)(img)(_p4##x,_n2##y,z,c)), \
  1971. (I[63] = (T)(img)(_p4##x,_n3##y,z,c)), \
  1972. (I[72] = (T)(img)(_p4##x,_n4##y,z,c)), \
  1973. (I[1] = (T)(img)(_p3##x,_p4##y,z,c)), \
  1974. (I[10] = (T)(img)(_p3##x,_p3##y,z,c)), \
  1975. (I[19] = (T)(img)(_p3##x,_p2##y,z,c)), \
  1976. (I[28] = (T)(img)(_p3##x,_p1##y,z,c)), \
  1977. (I[37] = (T)(img)(_p3##x,y,z,c)), \
  1978. (I[46] = (T)(img)(_p3##x,_n1##y,z,c)), \
  1979. (I[55] = (T)(img)(_p3##x,_n2##y,z,c)), \
  1980. (I[64] = (T)(img)(_p3##x,_n3##y,z,c)), \
  1981. (I[73] = (T)(img)(_p3##x,_n4##y,z,c)), \
  1982. (I[2] = (T)(img)(_p2##x,_p4##y,z,c)), \
  1983. (I[11] = (T)(img)(_p2##x,_p3##y,z,c)), \
  1984. (I[20] = (T)(img)(_p2##x,_p2##y,z,c)), \
  1985. (I[29] = (T)(img)(_p2##x,_p1##y,z,c)), \
  1986. (I[38] = (T)(img)(_p2##x,y,z,c)), \
  1987. (I[47] = (T)(img)(_p2##x,_n1##y,z,c)), \
  1988. (I[56] = (T)(img)(_p2##x,_n2##y,z,c)), \
  1989. (I[65] = (T)(img)(_p2##x,_n3##y,z,c)), \
  1990. (I[74] = (T)(img)(_p2##x,_n4##y,z,c)), \
  1991. (I[3] = (T)(img)(_p1##x,_p4##y,z,c)), \
  1992. (I[12] = (T)(img)(_p1##x,_p3##y,z,c)), \
  1993. (I[21] = (T)(img)(_p1##x,_p2##y,z,c)), \
  1994. (I[30] = (T)(img)(_p1##x,_p1##y,z,c)), \
  1995. (I[39] = (T)(img)(_p1##x,y,z,c)), \
  1996. (I[48] = (T)(img)(_p1##x,_n1##y,z,c)), \
  1997. (I[57] = (T)(img)(_p1##x,_n2##y,z,c)), \
  1998. (I[66] = (T)(img)(_p1##x,_n3##y,z,c)), \
  1999. (I[75] = (T)(img)(_p1##x,_n4##y,z,c)), \
  2000. (I[4] = (T)(img)(x,_p4##y,z,c)), \
  2001. (I[13] = (T)(img)(x,_p3##y,z,c)), \
  2002. (I[22] = (T)(img)(x,_p2##y,z,c)), \
  2003. (I[31] = (T)(img)(x,_p1##y,z,c)), \
  2004. (I[40] = (T)(img)(x,y,z,c)), \
  2005. (I[49] = (T)(img)(x,_n1##y,z,c)), \
  2006. (I[58] = (T)(img)(x,_n2##y,z,c)), \
  2007. (I[67] = (T)(img)(x,_n3##y,z,c)), \
  2008. (I[76] = (T)(img)(x,_n4##y,z,c)), \
  2009. (I[5] = (T)(img)(_n1##x,_p4##y,z,c)), \
  2010. (I[14] = (T)(img)(_n1##x,_p3##y,z,c)), \
  2011. (I[23] = (T)(img)(_n1##x,_p2##y,z,c)), \
  2012. (I[32] = (T)(img)(_n1##x,_p1##y,z,c)), \
  2013. (I[41] = (T)(img)(_n1##x,y,z,c)), \
  2014. (I[50] = (T)(img)(_n1##x,_n1##y,z,c)), \
  2015. (I[59] = (T)(img)(_n1##x,_n2##y,z,c)), \
  2016. (I[68] = (T)(img)(_n1##x,_n3##y,z,c)), \
  2017. (I[77] = (T)(img)(_n1##x,_n4##y,z,c)), \
  2018. (I[6] = (T)(img)(_n2##x,_p4##y,z,c)), \
  2019. (I[15] = (T)(img)(_n2##x,_p3##y,z,c)), \
  2020. (I[24] = (T)(img)(_n2##x,_p2##y,z,c)), \
  2021. (I[33] = (T)(img)(_n2##x,_p1##y,z,c)), \
  2022. (I[42] = (T)(img)(_n2##x,y,z,c)), \
  2023. (I[51] = (T)(img)(_n2##x,_n1##y,z,c)), \
  2024. (I[60] = (T)(img)(_n2##x,_n2##y,z,c)), \
  2025. (I[69] = (T)(img)(_n2##x,_n3##y,z,c)), \
  2026. (I[78] = (T)(img)(_n2##x,_n4##y,z,c)), \
  2027. (I[7] = (T)(img)(_n3##x,_p4##y,z,c)), \
  2028. (I[16] = (T)(img)(_n3##x,_p3##y,z,c)), \
  2029. (I[25] = (T)(img)(_n3##x,_p2##y,z,c)), \
  2030. (I[34] = (T)(img)(_n3##x,_p1##y,z,c)), \
  2031. (I[43] = (T)(img)(_n3##x,y,z,c)), \
  2032. (I[52] = (T)(img)(_n3##x,_n1##y,z,c)), \
  2033. (I[61] = (T)(img)(_n3##x,_n2##y,z,c)), \
  2034. (I[70] = (T)(img)(_n3##x,_n3##y,z,c)), \
  2035. (I[79] = (T)(img)(_n3##x,_n4##y,z,c)), \
  2036. x + 4>=(img).width()?(img).width() - 1:x + 4); \
  2037. x<=(int)(x1) && ((_n4##x<(img).width() && ( \
  2038. (I[8] = (T)(img)(_n4##x,_p4##y,z,c)), \
  2039. (I[17] = (T)(img)(_n4##x,_p3##y,z,c)), \
  2040. (I[26] = (T)(img)(_n4##x,_p2##y,z,c)), \
  2041. (I[35] = (T)(img)(_n4##x,_p1##y,z,c)), \
  2042. (I[44] = (T)(img)(_n4##x,y,z,c)), \
  2043. (I[53] = (T)(img)(_n4##x,_n1##y,z,c)), \
  2044. (I[62] = (T)(img)(_n4##x,_n2##y,z,c)), \
  2045. (I[71] = (T)(img)(_n4##x,_n3##y,z,c)), \
  2046. (I[80] = (T)(img)(_n4##x,_n4##y,z,c)),1)) || \
  2047. _n3##x==--_n4##x || _n2##x==--_n3##x || _n1##x==--_n2##x || x==(_n4##x = _n3##x = _n2##x = --_n1##x)); \
  2048. 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], \
  2049. 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], \
  2050. 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], \
  2051. 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], \
  2052. 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], \
  2053. 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], \
  2054. 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], \
  2055. 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], \
  2056. 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], \
  2057. 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], \
  2058. I[79] = I[80], \
  2059. _p4##x = _p3##x, _p3##x = _p2##x, _p2##x = _p1##x, _p1##x = x++, ++_n1##x, ++_n2##x, ++_n3##x, ++_n4##x)
  2060. #define cimg_for2x2x2(img,x,y,z,c,I,T) \
  2061. cimg_for2((img)._depth,z) cimg_for2((img)._height,y) for (int x = 0, \
  2062. _n1##x = (int)( \
  2063. (I[0] = (T)(img)(0,y,z,c)), \
  2064. (I[2] = (T)(img)(0,_n1##y,z,c)), \
  2065. (I[4] = (T)(img)(0,y,_n1##z,c)), \
  2066. (I[6] = (T)(img)(0,_n1##y,_n1##z,c)), \
  2067. 1>=(img)._width?(img).width() - 1:1); \
  2068. (_n1##x<(img).width() && ( \
  2069. (I[1] = (T)(img)(_n1##x,y,z,c)), \
  2070. (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \
  2071. (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \
  2072. (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
  2073. x==--_n1##x; \
  2074. I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \
  2075. ++x, ++_n1##x)
  2076. #define cimg_for_in2x2x2(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \
  2077. 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), \
  2078. _n1##x = (int)( \
  2079. (I[0] = (T)(img)(x,y,z,c)), \
  2080. (I[2] = (T)(img)(x,_n1##y,z,c)), \
  2081. (I[4] = (T)(img)(x,y,_n1##z,c)), \
  2082. (I[6] = (T)(img)(x,_n1##y,_n1##z,c)), \
  2083. x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \
  2084. x<=(int)(x1) && ((_n1##x<(img).width() && ( \
  2085. (I[1] = (T)(img)(_n1##x,y,z,c)), \
  2086. (I[3] = (T)(img)(_n1##x,_n1##y,z,c)), \
  2087. (I[5] = (T)(img)(_n1##x,y,_n1##z,c)), \
  2088. (I[7] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
  2089. x==--_n1##x); \
  2090. I[0] = I[1], I[2] = I[3], I[4] = I[5], I[6] = I[7], \
  2091. ++x, ++_n1##x)
  2092. #define cimg_for3x3x3(img,x,y,z,c,I,T) \
  2093. cimg_for3((img)._depth,z) cimg_for3((img)._height,y) for (int x = 0, \
  2094. _p1##x = 0, \
  2095. _n1##x = (int)( \
  2096. (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \
  2097. (I[3] = I[4] = (T)(img)(0,y,_p1##z,c)), \
  2098. (I[6] = I[7] = (T)(img)(0,_n1##y,_p1##z,c)), \
  2099. (I[9] = I[10] = (T)(img)(0,_p1##y,z,c)), \
  2100. (I[12] = I[13] = (T)(img)(0,y,z,c)), \
  2101. (I[15] = I[16] = (T)(img)(0,_n1##y,z,c)), \
  2102. (I[18] = I[19] = (T)(img)(0,_p1##y,_n1##z,c)), \
  2103. (I[21] = I[22] = (T)(img)(0,y,_n1##z,c)), \
  2104. (I[24] = I[25] = (T)(img)(0,_n1##y,_n1##z,c)), \
  2105. 1>=(img)._width?(img).width() - 1:1); \
  2106. (_n1##x<(img).width() && ( \
  2107. (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \
  2108. (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \
  2109. (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \
  2110. (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \
  2111. (I[14] = (T)(img)(_n1##x,y,z,c)), \
  2112. (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \
  2113. (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \
  2114. (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \
  2115. (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
  2116. x==--_n1##x; \
  2117. I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \
  2118. I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \
  2119. I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \
  2120. _p1##x = x++, ++_n1##x)
  2121. #define cimg_for_in3x3x3(img,x0,y0,z0,x1,y1,z1,x,y,z,c,I,T) \
  2122. 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), \
  2123. _p1##x = x - 1<0?0:x - 1, \
  2124. _n1##x = (int)( \
  2125. (I[0] = (T)(img)(_p1##x,_p1##y,_p1##z,c)), \
  2126. (I[3] = (T)(img)(_p1##x,y,_p1##z,c)), \
  2127. (I[6] = (T)(img)(_p1##x,_n1##y,_p1##z,c)), \
  2128. (I[9] = (T)(img)(_p1##x,_p1##y,z,c)), \
  2129. (I[12] = (T)(img)(_p1##x,y,z,c)), \
  2130. (I[15] = (T)(img)(_p1##x,_n1##y,z,c)), \
  2131. (I[18] = (T)(img)(_p1##x,_p1##y,_n1##z,c)), \
  2132. (I[21] = (T)(img)(_p1##x,y,_n1##z,c)), \
  2133. (I[24] = (T)(img)(_p1##x,_n1##y,_n1##z,c)), \
  2134. (I[1] = (T)(img)(x,_p1##y,_p1##z,c)), \
  2135. (I[4] = (T)(img)(x,y,_p1##z,c)), \
  2136. (I[7] = (T)(img)(x,_n1##y,_p1##z,c)), \
  2137. (I[10] = (T)(img)(x,_p1##y,z,c)), \
  2138. (I[13] = (T)(img)(x,y,z,c)), \
  2139. (I[16] = (T)(img)(x,_n1##y,z,c)), \
  2140. (I[19] = (T)(img)(x,_p1##y,_n1##z,c)), \
  2141. (I[22] = (T)(img)(x,y,_n1##z,c)), \
  2142. (I[25] = (T)(img)(x,_n1##y,_n1##z,c)), \
  2143. x + 1>=(int)(img)._width?(img).width() - 1:x + 1); \
  2144. x<=(int)(x1) && ((_n1##x<(img).width() && ( \
  2145. (I[2] = (T)(img)(_n1##x,_p1##y,_p1##z,c)), \
  2146. (I[5] = (T)(img)(_n1##x,y,_p1##z,c)), \
  2147. (I[8] = (T)(img)(_n1##x,_n1##y,_p1##z,c)), \
  2148. (I[11] = (T)(img)(_n1##x,_p1##y,z,c)), \
  2149. (I[14] = (T)(img)(_n1##x,y,z,c)), \
  2150. (I[17] = (T)(img)(_n1##x,_n1##y,z,c)), \
  2151. (I[20] = (T)(img)(_n1##x,_p1##y,_n1##z,c)), \
  2152. (I[23] = (T)(img)(_n1##x,y,_n1##z,c)), \
  2153. (I[26] = (T)(img)(_n1##x,_n1##y,_n1##z,c)),1)) || \
  2154. x==--_n1##x); \
  2155. I[0] = I[1], I[1] = I[2], I[3] = I[4], I[4] = I[5], I[6] = I[7], I[7] = I[8], \
  2156. I[9] = I[10], I[10] = I[11], I[12] = I[13], I[13] = I[14], I[15] = I[16], I[16] = I[17], \
  2157. I[18] = I[19], I[19] = I[20], I[21] = I[22], I[22] = I[23], I[24] = I[25], I[25] = I[26], \
  2158. _p1##x = x++, ++_n1##x)
  2159. #define cimglist_for(list,l) for (int l = 0; l<(int)(list)._width; ++l)
  2160. #define cimglist_rof(list,l) for (int l = (int)(list)._width - 1; l>=0; --l)
  2161. #define cimglist_for_in(list,l0,l1,l) \
  2162. for (int l = (int)(l0)<0?0:(int)(l0), _max##l = (unsigned int)l1<(list)._width?(int)(l1):(int)(list)._width - 1; \
  2163. l<=_max##l; ++l)
  2164. #define cimglist_apply(list,fn) cimglist_for(list,__##fn) (list)[__##fn].fn
  2165. // Macros used to display error messages when exceptions are thrown.
  2166. // You should not use these macros is your own code.
  2167. #define _cimgdisplay_instance "[instance(%u,%u,%u,%c%s%c)] CImgDisplay::"
  2168. #define cimgdisplay_instance _width,_height,_normalization,_title?'\"':'[',_title?_title:"untitled",_title?'\"':']'
  2169. #define _cimg_instance "[instance(%u,%u,%u,%u,%p,%sshared)] CImg<%s>::"
  2170. #define cimg_instance _width,_height,_depth,_spectrum,_data,_is_shared?"":"non-",pixel_type()
  2171. #define _cimglist_instance "[instance(%u,%u,%p)] CImgList<%s>::"
  2172. #define cimglist_instance _width,_allocated_width,_data,pixel_type()
  2173. /*------------------------------------------------
  2174. #
  2175. #
  2176. # Define cimg_library:: namespace
  2177. #
  2178. #
  2179. -------------------------------------------------*/
  2180. //! Contains <i>all classes and functions</i> of the \CImg library.
  2181. /**
  2182. This namespace is defined to avoid functions and class names collisions
  2183. that could happen with the inclusion of other C++ header files.
  2184. Anyway, it should not happen often and you should reasonably start most of your
  2185. \CImg-based programs with
  2186. \code
  2187. #include "CImg.h"
  2188. using namespace cimg_library;
  2189. \endcode
  2190. to simplify the declaration of \CImg Library objects afterwards.
  2191. **/
  2192. namespace cimg_library {
  2193. // Declare the four classes of the CImg Library.
  2194. template<typename T=float> struct CImg;
  2195. template<typename T=float> struct CImgList;
  2196. struct CImgDisplay;
  2197. struct CImgException;
  2198. // Declare cimg:: namespace.
  2199. // This is an incomplete namespace definition here. It only contains some
  2200. // necessary stuff to ensure a correct declaration order of the classes and functions
  2201. // defined afterwards.
  2202. namespace cimg {
  2203. // Define character sequences for colored terminal output.
  2204. #ifdef cimg_use_vt100
  2205. static const char t_normal[] = { 0x1b, '[', '0', ';', '0', ';', '0', 'm', 0 };
  2206. static const char t_black[] = { 0x1b, '[', '0', ';', '3', '0', ';', '5', '9', 'm', 0 };
  2207. static const char t_red[] = { 0x1b, '[', '0', ';', '3', '1', ';', '5', '9', 'm', 0 };
  2208. static const char t_green[] = { 0x1b, '[', '0', ';', '3', '2', ';', '5', '9', 'm', 0 };
  2209. static const char t_yellow[] = { 0x1b, '[', '0', ';', '3', '3', ';', '5', '9', 'm', 0 };
  2210. static const char t_blue[] = { 0x1b, '[', '0', ';', '3', '4', ';', '5', '9', 'm', 0 };
  2211. static const char t_magenta[] = { 0x1b, '[', '0', ';', '3', '5', ';', '5', '9', 'm', 0 };
  2212. static const char t_cyan[] = { 0x1b, '[', '0', ';', '3', '6', ';', '5', '9', 'm', 0 };
  2213. static const char t_white[] = { 0x1b, '[', '0', ';', '3', '7', ';', '5', '9', 'm', 0 };
  2214. static const char t_bold[] = { 0x1b, '[', '1', 'm', 0 };
  2215. static const char t_underscore[] = { 0x1b, '[', '4', 'm', 0 };
  2216. #else
  2217. static const char t_normal[] = { 0 };
  2218. static const char *const t_black = cimg::t_normal,
  2219. *const t_red = cimg::t_normal,
  2220. *const t_green = cimg::t_normal,
  2221. *const t_yellow = cimg::t_normal,
  2222. *const t_blue = cimg::t_normal,
  2223. *const t_magenta = cimg::t_normal,
  2224. *const t_cyan = cimg::t_normal,
  2225. *const t_white = cimg::t_normal,
  2226. *const t_bold = cimg::t_normal,
  2227. *const t_underscore = cimg::t_normal;
  2228. #endif
  2229. inline std::FILE* output(std::FILE *file=0);
  2230. inline void info();
  2231. //! Avoid warning messages due to unused parameters. Do nothing actually.
  2232. template<typename T>
  2233. inline void unused(const T&, ...) {}
  2234. // [internal] Lock/unlock a mutex for managing concurrent threads.
  2235. // 'lock_mode' can be { 0=unlock | 1=lock | 2=trylock }.
  2236. // 'n' can be in [0,31] but mutex range [0,15] is reserved by CImg.
  2237. inline int mutex(const unsigned int n, const int lock_mode=1);
  2238. inline unsigned int& exception_mode(const unsigned int value, const bool is_set) {
  2239. static unsigned int mode = cimg_verbosity;
  2240. if (is_set) { cimg::mutex(0); mode = value<4?value:4; cimg::mutex(0,0); }
  2241. return mode;
  2242. }
  2243. // Functions to return standard streams 'stdin', 'stdout' and 'stderr'.
  2244. inline FILE* _stdin(const bool throw_exception=true);
  2245. inline FILE* _stdout(const bool throw_exception=true);
  2246. inline FILE* _stderr(const bool throw_exception=true);
  2247. // Mandatory because Microsoft's _snprintf() and _vsnprintf() do not add the '\0' character
  2248. // at the end of the string.
  2249. #if cimg_OS==2 && defined(_MSC_VER)
  2250. inline int _snprintf(char *const s, const size_t size, const char *const format, ...) {
  2251. va_list ap;
  2252. va_start(ap,format);
  2253. const int result = _vsnprintf(s,size,format,ap);
  2254. va_end(ap);
  2255. return result;
  2256. }
  2257. inline int _vsnprintf(char *const s, const size_t size, const char *const format, va_list ap) {
  2258. int result = -1;
  2259. cimg::mutex(6);
  2260. if (size) result = _vsnprintf_s(s,size,_TRUNCATE,format,ap);
  2261. if (result==-1) result = _vscprintf(format,ap);
  2262. cimg::mutex(6,0);
  2263. return result;
  2264. }
  2265. // Mutex-protected version of sscanf, snprintf and vnsprintf.
  2266. // Used only MacOSX, as it seems those functions are not re-entrant on MacOSX.
  2267. #elif defined(__MACOSX__) || defined(__APPLE__)
  2268. inline int _sscanf(const char *const s, const char *const format, ...) {
  2269. cimg::mutex(6);
  2270. va_list args;
  2271. va_start(args,format);
  2272. const int result = std::vsscanf(s,format,args);
  2273. va_end(args);
  2274. cimg::mutex(6,0);
  2275. return result;
  2276. }
  2277. inline int _snprintf(char *const s, const size_t n, const char *const format, ...) {
  2278. cimg::mutex(6);
  2279. va_list args;
  2280. va_start(args,format);
  2281. const int result = std::vsnprintf(s,n,format,args);
  2282. va_end(args);
  2283. cimg::mutex(6,0);
  2284. return result;
  2285. }
  2286. inline int _vsnprintf(char *const s, const size_t size, const char* format, va_list ap) {
  2287. cimg::mutex(6);
  2288. const int result = std::vsnprintf(s,size,format,ap);
  2289. cimg::mutex(6,0);
  2290. return result;
  2291. }
  2292. #endif
  2293. //! Set current \CImg exception mode.
  2294. /**
  2295. The way error messages are handled by \CImg can be changed dynamically, using this function.
  2296. \param mode Desired exception mode. Possible values are:
  2297. - \c 0: Hide library messages (quiet mode).
  2298. - \c 1: Print library messages on the console.
  2299. - \c 2: Display library messages on a dialog window.
  2300. - \c 3: Do as \c 1 + add extra debug warnings (slow down the code!).
  2301. - \c 4: Do as \c 2 + add extra debug warnings (slow down the code!).
  2302. **/
  2303. inline unsigned int& exception_mode(const unsigned int mode) {
  2304. return exception_mode(mode,true);
  2305. }
  2306. //! Return current \CImg exception mode.
  2307. /**
  2308. \note By default, return the value of configuration macro \c cimg_verbosity
  2309. **/
  2310. inline unsigned int& exception_mode() {
  2311. return exception_mode(0,false);
  2312. }
  2313. inline unsigned int openmp_mode(const unsigned int value, const bool is_set) {
  2314. #if cimg_use_openmp!=0
  2315. static unsigned int mode = 2;
  2316. if (is_set) { cimg::mutex(0); mode = value<2?value:2; cimg::mutex(0,0); }
  2317. return mode;
  2318. #else
  2319. cimg::unused(value,is_set);
  2320. return 0;
  2321. #endif
  2322. }
  2323. //! Set current \CImg openmp mode.
  2324. /**
  2325. The way openmp-based methods are handled by \CImg can be changed dynamically, using this function.
  2326. \param mode Desired openmp mode. Possible values are:
  2327. - \c 0: Never parallelize.
  2328. - \c 1: Always parallelize.
  2329. - \c 2: Adaptive parallelization mode (default behavior).
  2330. **/
  2331. inline unsigned int openmp_mode(const unsigned int mode) {
  2332. return openmp_mode(mode,true);
  2333. }
  2334. //! Return current \CImg openmp mode.
  2335. inline unsigned int openmp_mode() {
  2336. return openmp_mode(0,false);
  2337. }
  2338. #ifndef cimg_openmp_sizefactor
  2339. #define cimg_openmp_sizefactor 1
  2340. #endif
  2341. #define cimg_openmp_if(cond) if ((cimg::openmp_mode()==1 || (cimg::openmp_mode()>1 && (cond))))
  2342. #define cimg_openmp_if_size(size,min_size) cimg_openmp_if((size)>=(cimg_openmp_sizefactor)*(min_size))
  2343. #ifdef _MSC_VER
  2344. // Disable 'collapse()' directive for MSVC (supports only OpenMP 2.0).
  2345. #define cimg_openmp_collapse(k)
  2346. #else
  2347. #define cimg_openmp_collapse(k) collapse(k)
  2348. #endif
  2349. #if cimg_OS==2
  2350. // Disable parallelization of simple loops on Windows, due to noticed performance drop.
  2351. #define cimg_openmp_for(instance,expr,min_size) cimg_rof((instance),ptr,T) *ptr = (T)(expr);
  2352. #else
  2353. #define cimg_openmp_for(instance,expr,min_size) \
  2354. cimg_pragma_openmp(parallel for cimg_openmp_if_size((instance).size(),min_size)) \
  2355. cimg_rof((instance),ptr,T) *ptr = (T)(expr);
  2356. #endif
  2357. // Display a simple dialog box, and wait for the user's response.
  2358. inline int dialog(const char *const title, const char *const msg,
  2359. const char *const button1_label="OK", const char *const button2_label=0,
  2360. const char *const button3_label=0, const char *const button4_label=0,
  2361. const char *const button5_label=0, const char *const button6_label=0,
  2362. const bool centering=false);
  2363. // Evaluate math expression.
  2364. inline double eval(const char *const expression,
  2365. const double x=0, const double y=0, const double z=0, const double c=0);
  2366. } // namespace cimg { ...
  2367. /*---------------------------------------
  2368. #
  2369. # Define the CImgException structures
  2370. #
  2371. --------------------------------------*/
  2372. //! Instances of \c CImgException are thrown when errors are encountered in a \CImg function call.
  2373. /**
  2374. \par Overview
  2375. CImgException is the base class of all exceptions thrown by \CImg (except \b CImgAbortException).
  2376. CImgException is never thrown itself. Derived classes that specify the type of errord are thrown instead.
  2377. These classes can be:
  2378. - \b CImgAbortException: Thrown when a computationally-intensive function is aborted by an external signal.
  2379. This is the only \c non-derived exception class.
  2380. - \b CImgArgumentException: Thrown when one argument of a called \CImg function is invalid.
  2381. This is probably one of the most thrown exception by \CImg.
  2382. For instance, the following example throws a \c CImgArgumentException:
  2383. \code
  2384. CImg<float> img(100,100,1,3); // Define a 100x100 color image with float-valued pixels
  2385. img.mirror('e'); // Try to mirror image along the (non-existing) 'e'-axis
  2386. \endcode
  2387. - \b CImgDisplayException: Thrown when something went wrong during the display of images in CImgDisplay instances.
  2388. - \b CImgInstanceException: Thrown when an instance associated to a called \CImg method does not fit
  2389. the function requirements. For instance, the following example throws a \c CImgInstanceException:
  2390. \code
  2391. const CImg<float> img; // Define an empty image
  2392. const float value = img.at(0); // Try to read first pixel value (does not exist)
  2393. \endcode
  2394. - \b CImgIOException: Thrown when an error occurred when trying to load or save image files.
  2395. This happens when trying to read files that do not exist or with invalid formats.
  2396. For instance, the following example throws a \c CImgIOException:
  2397. \code
  2398. const CImg<float> img("missing_file.jpg"); // Try to load a file that does not exist
  2399. \endcode
  2400. - \b CImgWarningException: Thrown only if configuration macro \c cimg_strict_warnings is set, and
  2401. when a \CImg function has to display a warning message (see cimg::warn()).
  2402. It is not recommended to throw CImgException instances by yourself,
  2403. since they are expected to be thrown only by \CImg.
  2404. When an error occurs in a library function call, \CImg may display error messages on the screen or on the
  2405. standard output, depending on the current \CImg exception mode.
  2406. The \CImg exception mode can be get and set by functions cimg::exception_mode() and
  2407. cimg::exception_mode(unsigned int).
  2408. \par Exceptions handling
  2409. In all cases, when an error occurs in \CImg, an instance of the corresponding exception class is thrown.
  2410. This may lead the program to break (this is the default behavior), but you can bypass this behavior by
  2411. handling the exceptions by yourself,
  2412. using a usual <tt>try { ... } catch () { ... }</tt> block, as in the following example:
  2413. \code
  2414. #define "CImg.h"
  2415. using namespace cimg_library;
  2416. int main() {
  2417. cimg::exception_mode(0); // Enable quiet exception mode
  2418. try {
  2419. ... // Here, do what you want to stress CImg
  2420. } catch (CImgException& e) { // You succeeded: something went wrong!
  2421. std::fprintf(stderr,"CImg Library Error: %s",e.what()); // Display your custom error message
  2422. ... // Do what you want now to save the ship!
  2423. }
  2424. }
  2425. \endcode
  2426. **/
  2427. struct CImgException : public std::exception {
  2428. #define _cimg_exception_err(etype,disp_flag) \
  2429. std::va_list ap, ap2; \
  2430. va_start(ap,format); va_start(ap2,format); \
  2431. int size = cimg_vsnprintf(0,0,format,ap2); \
  2432. if (size++>=0) { \
  2433. delete[] _message; \
  2434. _message = new char[(size_t)size]; \
  2435. cimg_vsnprintf(_message,(size_t)size,format,ap); \
  2436. if (cimg::exception_mode()) { \
  2437. std::fprintf(cimg::output(),"\n%s[CImg] *** %s ***%s %s\n",cimg::t_red,etype,cimg::t_normal,_message); \
  2438. if (cimg_display && disp_flag && !(cimg::exception_mode()%2)) try { cimg::dialog(etype,_message,"Abort"); } \
  2439. catch (CImgException&) {} \
  2440. if (cimg::exception_mode()>=3) cimg_library::cimg::info(); \
  2441. } \
  2442. } \
  2443. va_end(ap); va_end(ap2);
  2444. char *_message;
  2445. CImgException() { _message = new char[1]; *_message = 0; }
  2446. CImgException(const char *const format, ...):_message(0) { _cimg_exception_err("CImgException",true); }
  2447. CImgException(const CImgException& e):std::exception(e) {
  2448. const size_t size = std::strlen(e._message);
  2449. _message = new char[size + 1];
  2450. std::strncpy(_message,e._message,size);
  2451. _message[size] = 0;
  2452. }
  2453. ~CImgException() throw() { delete[] _message; }
  2454. CImgException& operator=(const CImgException& e) {
  2455. const size_t size = std::strlen(e._message);
  2456. _message = new char[size + 1];
  2457. std::strncpy(_message,e._message,size);
  2458. _message[size] = 0;
  2459. return *this;
  2460. }
  2461. //! Return a C-string containing the error message associated to the thrown exception.
  2462. const char *what() const throw() { return _message; }
  2463. }; // struct CImgException { ...
  2464. // The CImgAbortException class is used to throw an exception when
  2465. // a computationally-intensive function has been aborted by an external signal.
  2466. struct CImgAbortException : public std::exception {
  2467. char *_message;
  2468. CImgAbortException() { _message = new char[1]; *_message = 0; }
  2469. CImgAbortException(const char *const format, ...):_message(0) { _cimg_exception_err("CImgAbortException",true); }
  2470. CImgAbortException(const CImgAbortException& e):std::exception(e) {
  2471. const size_t size = std::strlen(e._message);
  2472. _message = new char[size + 1];
  2473. std::strncpy(_message,e._message,size);
  2474. _message[size] = 0;
  2475. }
  2476. ~CImgAbortException() throw() { delete[] _message; }
  2477. CImgAbortException& operator=(const CImgAbortException& e) {
  2478. const size_t size = std::strlen(e._message);
  2479. _message = new char[size + 1];
  2480. std::strncpy(_message,e._message,size);
  2481. _message[size] = 0;
  2482. return *this;
  2483. }
  2484. //! Return a C-string containing the error message associated to the thrown exception.
  2485. const char *what() const throw() { return _message; }
  2486. }; // struct CImgAbortException { ...
  2487. // The CImgArgumentException class is used to throw an exception related
  2488. // to invalid arguments encountered in a library function call.
  2489. struct CImgArgumentException : public CImgException {
  2490. CImgArgumentException(const char *const format, ...) { _cimg_exception_err("CImgArgumentException",true); }
  2491. }; // struct CImgArgumentException { ...
  2492. // The CImgDisplayException class is used to throw an exception related
  2493. // to display problems encountered in a library function call.
  2494. struct CImgDisplayException : public CImgException {
  2495. CImgDisplayException(const char *const format, ...) { _cimg_exception_err("CImgDisplayException",false); }
  2496. }; // struct CImgDisplayException { ...
  2497. // The CImgInstanceException class is used to throw an exception related
  2498. // to an invalid instance encountered in a library function call.
  2499. struct CImgInstanceException : public CImgException {
  2500. CImgInstanceException(const char *const format, ...) { _cimg_exception_err("CImgInstanceException",true); }
  2501. }; // struct CImgInstanceException { ...
  2502. // The CImgIOException class is used to throw an exception related
  2503. // to input/output file problems encountered in a library function call.
  2504. struct CImgIOException : public CImgException {
  2505. CImgIOException(const char *const format, ...) { _cimg_exception_err("CImgIOException",true); }
  2506. }; // struct CImgIOException { ...
  2507. // The CImgWarningException class is used to throw an exception for warnings
  2508. // encountered in a library function call.
  2509. struct CImgWarningException : public CImgException {
  2510. CImgWarningException(const char *const format, ...) { _cimg_exception_err("CImgWarningException",false); }
  2511. }; // struct CImgWarningException { ...
  2512. /*-------------------------------------
  2513. #
  2514. # Define cimg:: namespace
  2515. #
  2516. -----------------------------------*/
  2517. //! Contains \a low-level functions and variables of the \CImg Library.
  2518. /**
  2519. Most of the functions and variables within this namespace are used by the \CImg library for low-level operations.
  2520. You may use them to access specific const values or environment variables internally used by \CImg.
  2521. \warning Never write <tt>using namespace cimg_library::cimg;</tt> in your source code. Lot of functions in the
  2522. <tt>cimg:: namespace</tt> have the same names as standard C functions that may be defined in the global
  2523. namespace <tt>::</tt>.
  2524. **/
  2525. namespace cimg {
  2526. // Define traits that will be used to determine the best data type to work in CImg functions.
  2527. //
  2528. template<typename T> struct type {
  2529. static const char* string() {
  2530. static const char* s[] = { "unknown", "unknown8", "unknown16", "unknown24",
  2531. "unknown32", "unknown40", "unknown48", "unknown56",
  2532. "unknown64", "unknown72", "unknown80", "unknown88",
  2533. "unknown96", "unknown104", "unknown112", "unknown120",
  2534. "unknown128" };
  2535. return s[(sizeof(T)<17)?sizeof(T):0];
  2536. }
  2537. static bool is_float() { return false; }
  2538. static bool is_inf(const T) { return false; }
  2539. static bool is_nan(const T) { return false; }
  2540. static bool is_finite(const T) { return true; }
  2541. static T min() { return ~max(); }
  2542. static T max() { return (T)1<<(8*sizeof(T) - 1); }
  2543. static T inf() { return max(); }
  2544. static T nan() { return inf(); }
  2545. static T cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(T)val; }
  2546. static const char* format() { return "%s"; }
  2547. static const char* format_s() { return "%s"; }
  2548. static const char* format(const T& val) { static const char *const s = "unknown"; cimg::unused(val); return s; }
  2549. };
  2550. template<> struct type<bool> {
  2551. static const char* string() {
  2552. static const char *const s = "bool";
  2553. return s;
  2554. }
  2555. static bool is_float() { return false; }
  2556. static bool is_inf(const bool) { return false; }
  2557. static bool is_nan(const bool) { return false; }
  2558. static bool is_finite(const bool) { return true; }
  2559. static bool min() { return false; }
  2560. static bool max() { return true; }
  2561. static bool inf() { return max(); }
  2562. static bool nan() { return inf(); }
  2563. static bool is_inf() { return false; }
  2564. static bool cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(bool)val; }
  2565. static const char* format() { return "%s"; }
  2566. static const char* format_s() { return "%s"; }
  2567. static const char* format(const bool val) { static const char* s[] = { "false", "true" }; return s[val?1:0]; }
  2568. };
  2569. template<> struct type<unsigned char> {
  2570. static const char* string() { static const char *const s = "uint8"; return s; }
  2571. static bool is_float() { return false; }
  2572. static bool is_inf(const unsigned char) { return false; }
  2573. static bool is_nan(const unsigned char) { return false; }
  2574. static bool is_finite(const unsigned char) { return true; }
  2575. static unsigned char min() { return 0; }
  2576. static unsigned char max() { return (unsigned char)-1; }
  2577. static unsigned char inf() { return max(); }
  2578. static unsigned char nan() { return inf(); }
  2579. static unsigned char cut(const double val) {
  2580. return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; }
  2581. static const char* format() { return "%u"; }
  2582. static const char* format_s() { return "%u"; }
  2583. static unsigned int format(const unsigned char val) { return (unsigned int)val; }
  2584. };
  2585. #if defined(CHAR_MAX) && CHAR_MAX==255
  2586. template<> struct type<char> {
  2587. static const char* string() { static const char *const s = "uint8"; return s; }
  2588. static bool is_float() { return false; }
  2589. static bool is_inf(const char) { return false; }
  2590. static bool is_nan(const char) { return false; }
  2591. static bool is_finite(const char) { return true; }
  2592. static char min() { return 0; }
  2593. static char max() { return (char)-1; }
  2594. static char inf() { return max(); }
  2595. static char nan() { return inf(); }
  2596. static char cut(const double val) {
  2597. return val<(double)min()?min():val>(double)max()?max():(unsigned char)val; }
  2598. static const char* format() { return "%u"; }
  2599. static const char* format_s() { return "%u"; }
  2600. static unsigned int format(const char val) { return (unsigned int)val; }
  2601. };
  2602. #else
  2603. template<> struct type<char> {
  2604. static const char* string() { static const char *const s = "int8"; return s; }
  2605. static bool is_float() { return false; }
  2606. static bool is_inf(const char) { return false; }
  2607. static bool is_nan(const char) { return false; }
  2608. static bool is_finite(const char) { return true; }
  2609. static char min() { return ~max(); }
  2610. static char max() { return (char)((unsigned char)-1>>1); }
  2611. static char inf() { return max(); }
  2612. static char nan() { return inf(); }
  2613. static char cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(char)val; }
  2614. static const char* format() { return "%d"; }
  2615. static const char* format_s() { return "%d"; }
  2616. static int format(const char val) { return (int)val; }
  2617. };
  2618. #endif
  2619. template<> struct type<signed char> {
  2620. static const char* string() { static const char *const s = "int8"; return s; }
  2621. static bool is_float() { return false; }
  2622. static bool is_inf(const signed char) { return false; }
  2623. static bool is_nan(const signed char) { return false; }
  2624. static bool is_finite(const signed char) { return true; }
  2625. static signed char min() { return ~max(); }
  2626. static signed char max() { return (signed char)((unsigned char)-1>>1); }
  2627. static signed char inf() { return max(); }
  2628. static signed char nan() { return inf(); }
  2629. static signed char cut(const double val) {
  2630. return val<(double)min()?min():val>(double)max()?max():(signed char)val; }
  2631. static const char* format() { return "%d"; }
  2632. static const char* format_s() { return "%d"; }
  2633. static int format(const signed char val) { return (int)val; }
  2634. };
  2635. template<> struct type<unsigned short> {
  2636. static const char* string() { static const char *const s = "uint16"; return s; }
  2637. static bool is_float() { return false; }
  2638. static bool is_inf(const unsigned short) { return false; }
  2639. static bool is_nan(const unsigned short) { return false; }
  2640. static bool is_finite(const unsigned short) { return true; }
  2641. static unsigned short min() { return 0; }
  2642. static unsigned short max() { return (unsigned short)-1; }
  2643. static unsigned short inf() { return max(); }
  2644. static unsigned short nan() { return inf(); }
  2645. static unsigned short cut(const double val) {
  2646. return val<(double)min()?min():val>(double)max()?max():(unsigned short)val; }
  2647. static const char* format() { return "%u"; }
  2648. static const char* format_s() { return "%u"; }
  2649. static unsigned int format(const unsigned short val) { return (unsigned int)val; }
  2650. };
  2651. template<> struct type<short> {
  2652. static const char* string() { static const char *const s = "int16"; return s; }
  2653. static bool is_float() { return false; }
  2654. static bool is_inf(const short) { return false; }
  2655. static bool is_nan(const short) { return false; }
  2656. static bool is_finite(const short) { return true; }
  2657. static short min() { return ~max(); }
  2658. static short max() { return (short)((unsigned short)-1>>1); }
  2659. static short inf() { return max(); }
  2660. static short nan() { return inf(); }
  2661. static short cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(short)val; }
  2662. static const char* format() { return "%d"; }
  2663. static const char* format_s() { return "%d"; }
  2664. static int format(const short val) { return (int)val; }
  2665. };
  2666. template<> struct type<unsigned int> {
  2667. static const char* string() { static const char *const s = "uint32"; return s; }
  2668. static bool is_float() { return false; }
  2669. static bool is_inf(const unsigned int) { return false; }
  2670. static bool is_nan(const unsigned int) { return false; }
  2671. static bool is_finite(const unsigned int) { return true; }
  2672. static unsigned int min() { return 0; }
  2673. static unsigned int max() { return (unsigned int)-1; }
  2674. static unsigned int inf() { return max(); }
  2675. static unsigned int nan() { return inf(); }
  2676. static unsigned int cut(const double val) {
  2677. return val<(double)min()?min():val>(double)max()?max():(unsigned int)val; }
  2678. static const char* format() { return "%u"; }
  2679. static const char* format_s() { return "%u"; }
  2680. static unsigned int format(const unsigned int val) { return val; }
  2681. };
  2682. template<> struct type<int> {
  2683. static const char* string() { static const char *const s = "int32"; return s; }
  2684. static bool is_float() { return false; }
  2685. static bool is_inf(const int) { return false; }
  2686. static bool is_nan(const int) { return false; }
  2687. static bool is_finite(const int) { return true; }
  2688. static int min() { return ~max(); }
  2689. static int max() { return (int)(~0U>>1); }
  2690. static int inf() { return max(); }
  2691. static int nan() { return inf(); }
  2692. static int cut(const double val) { return val<(double)min()?min():val>(double)max()?max():(int)val; }
  2693. static const char* format() { return "%d"; }
  2694. static const char* format_s() { return "%d"; }
  2695. static int format(const int val) { return val; }
  2696. };
  2697. template<> struct type<cimg_uint64> {
  2698. static const char* string() { static const char *const s = "uint64"; return s; }
  2699. static bool is_float() { return false; }
  2700. static bool is_inf(const cimg_uint64) { return false; }
  2701. static bool is_nan(const cimg_uint64) { return false; }
  2702. static bool is_finite(const cimg_uint64) { return true; }
  2703. static cimg_uint64 min() { return 0; }
  2704. static cimg_uint64 max() { return (cimg_uint64)-1; }
  2705. static cimg_uint64 inf() { return max(); }
  2706. static cimg_uint64 nan() { return inf(); }
  2707. static cimg_uint64 cut(const double val) {
  2708. return val<(double)min()?min():val>(double)max()?max():(cimg_uint64)val; }
  2709. static const char* format() { return cimg_fuint64; }
  2710. static const char* format_s() { return cimg_fuint64; }
  2711. static cimg_uint64 format(const cimg_uint64 val) { return val; }
  2712. };
  2713. template<> struct type<cimg_int64> {
  2714. static const char* string() { static const char *const s = "int64"; return s; }
  2715. static bool is_float() { return false; }
  2716. static bool is_inf(const cimg_int64) { return false; }
  2717. static bool is_nan(const cimg_int64) { return false; }
  2718. static bool is_finite(const cimg_int64) { return true; }
  2719. static cimg_int64 min() { return ~max(); }
  2720. static cimg_int64 max() { return (cimg_int64)((cimg_uint64)-1>>1); }
  2721. static cimg_int64 inf() { return max(); }
  2722. static cimg_int64 nan() { return inf(); }
  2723. static cimg_int64 cut(const double val) {
  2724. return val<(double)min()?min():val>(double)max()?max():(cimg_int64)val;
  2725. }
  2726. static const char* format() { return cimg_fint64; }
  2727. static const char* format_s() { return cimg_fint64; }
  2728. static long format(const long val) { return (long)val; }
  2729. };
  2730. #if cimg_use_cpp11==1 && \
  2731. (!(UINTPTR_MAX==0xffffffff || defined(__arm__) || defined(_M_ARM) || ((ULONG_MAX)==(UINT_MAX))))
  2732. template<> struct type<unsigned long long> {
  2733. static const char* string() { static const char *const s = "uint64"; return s; }
  2734. static bool is_float() { return false; }
  2735. static bool is_inf(const cimg_uint64) { return false; }
  2736. static bool is_nan(const cimg_uint64) { return false; }
  2737. static bool is_finite(const cimg_uint64) { return true; }
  2738. static cimg_uint64 min() { return 0; }
  2739. static cimg_uint64 max() { return (cimg_uint64)-1; }
  2740. static cimg_uint64 inf() { return max(); }
  2741. static cimg_uint64 nan() { return inf(); }
  2742. static cimg_uint64 cut(const double val) {
  2743. return val<(double)min()?min():val>(double)max()?max():(cimg_uint64)val; }
  2744. static const char* format() { return cimg_fuint64; }
  2745. static const char* format_s() { return cimg_fuint64; }
  2746. static cimg_uint64 format(const cimg_uint64 val) { return val; }
  2747. };
  2748. template<> struct type<long long> {
  2749. static const char* string() { static const char *const s = "int64"; return s; }
  2750. static bool is_float() { return false; }
  2751. static bool is_inf(const cimg_int64) { return false; }
  2752. static bool is_nan(const cimg_int64) { return false; }
  2753. static bool is_finite(const cimg_int64) { return true; }
  2754. static long long min() { return ~max(); }
  2755. static long long max() { return (cimg_int64)((cimg_uint64)-1>>1); }
  2756. static long long inf() { return max(); }
  2757. static long long nan() { return max(); }
  2758. static long long cut(const double val) {
  2759. return val<(double)min()?min():val>(double)max()?max():(cimg_int64)val;
  2760. }
  2761. static const char* format() { return cimg_fint64; }
  2762. static const char* format_s() { return cimg_fint64; }
  2763. static long format(const long val) { return (long)val; }
  2764. };
  2765. #endif
  2766. template<> struct type<double> {
  2767. static const char* string() { static const char *const s = "float64"; return s; }
  2768. static bool is_float() { return true; }
  2769. static bool is_inf(const double val) {
  2770. #ifdef isinf
  2771. return (bool)isinf(val);
  2772. #else
  2773. return !is_nan(val) && (val<cimg::type<double>::min() || val>cimg::type<double>::max());
  2774. #endif
  2775. }
  2776. static bool is_nan(const double val) { // Custom version that works with '-ffast-math'
  2777. if (sizeof(double)==8) {
  2778. cimg_uint64 u;
  2779. std::memcpy(&u,&val,sizeof(double));
  2780. return ((unsigned int)(u>>32)&0x7fffffff) + ((unsigned int)u!=0)>0x7ff00000;
  2781. }
  2782. #ifdef isnan
  2783. return (bool)isnan(val);
  2784. #else
  2785. return !(val==val);
  2786. #endif
  2787. }
  2788. static bool is_finite(const double val) {
  2789. #ifdef isfinite
  2790. return (bool)isfinite(val);
  2791. #else
  2792. return !is_nan(val) && !is_inf(val);
  2793. #endif
  2794. }
  2795. static double min() { return -DBL_MAX; }
  2796. static double max() { return DBL_MAX; }
  2797. static double inf() {
  2798. #ifdef INFINITY
  2799. return (double)INFINITY;
  2800. #else
  2801. return max()*max();
  2802. #endif
  2803. }
  2804. static double nan() {
  2805. #ifdef NAN
  2806. return (double)NAN;
  2807. #else
  2808. const double val_nan = -std::sqrt(-1.); return val_nan;
  2809. #endif
  2810. }
  2811. static double cut(const double val) { return val; }
  2812. static const char* format() { return "%.17g"; }
  2813. static const char* format_s() { return "%g"; }
  2814. static double format(const double val) { return val; }
  2815. };
  2816. template<> struct type<float> {
  2817. static const char* string() { static const char *const s = "float32"; return s; }
  2818. static bool is_float() { return true; }
  2819. static bool is_inf(const float val) {
  2820. #ifdef isinf
  2821. return (bool)isinf(val);
  2822. #else
  2823. return !is_nan(val) && (val<cimg::type<float>::min() || val>cimg::type<float>::max());
  2824. #endif
  2825. }
  2826. static bool is_nan(const float val) { // Custom version that works with '-ffast-math'
  2827. if (sizeof(float)==4) {
  2828. unsigned int u;
  2829. std::memcpy(&u,&val,sizeof(float));
  2830. return (u&0x7fffffff)>0x7f800000;
  2831. }
  2832. #ifdef isnan
  2833. return (bool)isnan(val);
  2834. #else
  2835. return !(val==val);
  2836. #endif
  2837. }
  2838. static bool is_finite(const float val) {
  2839. #ifdef isfinite
  2840. return (bool)isfinite(val);
  2841. #else
  2842. return !is_nan(val) && !is_inf(val);
  2843. #endif
  2844. }
  2845. static float min() { return -FLT_MAX; }
  2846. static float max() { return FLT_MAX; }
  2847. static float inf() { return (float)cimg::type<double>::inf(); }
  2848. static float nan() { return (float)cimg::type<double>::nan(); }
  2849. static float cut(const double val) { return (float)val; }
  2850. static float cut(const float val) { return (float)val; }
  2851. static const char* format() { return "%.9g"; }
  2852. static const char* format_s() { return "%g"; }
  2853. static double format(const float val) { return (double)val; }
  2854. };
  2855. template<> struct type<long double> {
  2856. static const char* string() { static const char *const s = "float128"; return s; }
  2857. static bool is_float() { return true; }
  2858. static bool is_inf(const long double val) {
  2859. #ifdef isinf
  2860. return (bool)isinf(val);
  2861. #else
  2862. return !is_nan(val) && (val<cimg::type<long double>::min() || val>cimg::type<long double>::max());
  2863. #endif
  2864. }
  2865. static bool is_nan(const long double val) {
  2866. #ifdef isnan
  2867. return (bool)isnan(val);
  2868. #else
  2869. return !(val==val);
  2870. #endif
  2871. }
  2872. static bool is_finite(const long double val) {
  2873. #ifdef isfinite
  2874. return (bool)isfinite(val);
  2875. #else
  2876. return !is_nan(val) && !is_inf(val);
  2877. #endif
  2878. }
  2879. static long double min() { return -LDBL_MAX; }
  2880. static long double max() { return LDBL_MAX; }
  2881. static long double inf() { return max()*max(); }
  2882. static long double nan() { const long double val_nan = -std::sqrt(-1.L); return val_nan; }
  2883. static long double cut(const long double val) { return val; }
  2884. static const char* format() { return "%.17g"; }
  2885. static const char* format_s() { return "%g"; }
  2886. static double format(const long double val) { return (double)val; }
  2887. };
  2888. #ifdef cimg_use_half
  2889. template<> struct type<half> {
  2890. static const char* string() { static const char *const s = "float16"; return s; }
  2891. static bool is_float() { return true; }
  2892. static bool is_inf(const long double val) {
  2893. #ifdef isinf
  2894. return (bool)isinf(val);
  2895. #else
  2896. return !is_nan(val) && (val<cimg::type<half>::min() || val>cimg::type<half>::max());
  2897. #endif
  2898. }
  2899. static bool is_nan(const half val) { // Custom version that works with '-ffast-math'
  2900. if (sizeof(half)==2) {
  2901. short u;
  2902. std::memcpy(&u,&val,sizeof(short));
  2903. return (bool)((u&0x7fff)>0x7c00);
  2904. }
  2905. return cimg::type<float>::is_nan((float)val);
  2906. }
  2907. static bool is_finite(const half val) {
  2908. #ifdef isfinite
  2909. return (bool)isfinite(val);
  2910. #else
  2911. return !is_nan(val) && !is_inf(val);
  2912. #endif
  2913. }
  2914. static half min() { return (half)-65504; }
  2915. static half max() { return (half)65504; }
  2916. static half inf() { return max()*max(); }
  2917. static half nan() { const half val_nan = (half)-std::sqrt(-1.); return val_nan; }
  2918. static half cut(const double val) { return (half)val; }
  2919. static const char* format() { return "%.9g"; }
  2920. static const char* format_s() { return "%g"; }
  2921. static double format(const half val) { return (double)val; }
  2922. };
  2923. #endif
  2924. template<typename T, typename t> struct superset { typedef T type; };
  2925. template<> struct superset<bool,unsigned char> { typedef unsigned char type; };
  2926. template<> struct superset<bool,char> { typedef char type; };
  2927. template<> struct superset<bool,signed char> { typedef signed char type; };
  2928. template<> struct superset<bool,unsigned short> { typedef unsigned short type; };
  2929. template<> struct superset<bool,short> { typedef short type; };
  2930. template<> struct superset<bool,unsigned int> { typedef unsigned int type; };
  2931. template<> struct superset<bool,int> { typedef int type; };
  2932. template<> struct superset<bool,cimg_uint64> { typedef cimg_uint64 type; };
  2933. template<> struct superset<bool,cimg_int64> { typedef cimg_int64 type; };
  2934. template<> struct superset<bool,float> { typedef float type; };
  2935. template<> struct superset<bool,double> { typedef double type; };
  2936. template<> struct superset<unsigned char,char> { typedef short type; };
  2937. template<> struct superset<unsigned char,signed char> { typedef short type; };
  2938. template<> struct superset<unsigned char,unsigned short> { typedef unsigned short type; };
  2939. template<> struct superset<unsigned char,short> { typedef short type; };
  2940. template<> struct superset<unsigned char,unsigned int> { typedef unsigned int type; };
  2941. template<> struct superset<unsigned char,int> { typedef int type; };
  2942. template<> struct superset<unsigned char,cimg_uint64> { typedef cimg_uint64 type; };
  2943. template<> struct superset<unsigned char,cimg_int64> { typedef cimg_int64 type; };
  2944. template<> struct superset<unsigned char,float> { typedef float type; };
  2945. template<> struct superset<unsigned char,double> { typedef double type; };
  2946. template<> struct superset<signed char,unsigned char> { typedef short type; };
  2947. template<> struct superset<signed char,char> { typedef short type; };
  2948. template<> struct superset<signed char,unsigned short> { typedef int type; };
  2949. template<> struct superset<signed char,short> { typedef short type; };
  2950. template<> struct superset<signed char,unsigned int> { typedef cimg_int64 type; };
  2951. template<> struct superset<signed char,int> { typedef int type; };
  2952. template<> struct superset<signed char,cimg_uint64> { typedef cimg_int64 type; };
  2953. template<> struct superset<signed char,cimg_int64> { typedef cimg_int64 type; };
  2954. template<> struct superset<signed char,float> { typedef float type; };
  2955. template<> struct superset<signed char,double> { typedef double type; };
  2956. template<> struct superset<char,unsigned char> { typedef short type; };
  2957. template<> struct superset<char,signed char> { typedef short type; };
  2958. template<> struct superset<char,unsigned short> { typedef int type; };
  2959. template<> struct superset<char,short> { typedef short type; };
  2960. template<> struct superset<char,unsigned int> { typedef cimg_int64 type; };
  2961. template<> struct superset<char,int> { typedef int type; };
  2962. template<> struct superset<char,cimg_uint64> { typedef cimg_int64 type; };
  2963. template<> struct superset<char,cimg_int64> { typedef cimg_int64 type; };
  2964. template<> struct superset<char,float> { typedef float type; };
  2965. template<> struct superset<char,double> { typedef double type; };
  2966. template<> struct superset<unsigned short,char> { typedef int type; };
  2967. template<> struct superset<unsigned short,signed char> { typedef int type; };
  2968. template<> struct superset<unsigned short,short> { typedef int type; };
  2969. template<> struct superset<unsigned short,unsigned int> { typedef unsigned int type; };
  2970. template<> struct superset<unsigned short,int> { typedef int type; };
  2971. template<> struct superset<unsigned short,cimg_uint64> { typedef cimg_uint64 type; };
  2972. template<> struct superset<unsigned short,cimg_int64> { typedef cimg_int64 type; };
  2973. template<> struct superset<unsigned short,float> { typedef float type; };
  2974. template<> struct superset<unsigned short,double> { typedef double type; };
  2975. template<> struct superset<short,unsigned short> { typedef int type; };
  2976. template<> struct superset<short,unsigned int> { typedef cimg_int64 type; };
  2977. template<> struct superset<short,int> { typedef int type; };
  2978. template<> struct superset<short,cimg_uint64> { typedef cimg_int64 type; };
  2979. template<> struct superset<short,cimg_int64> { typedef cimg_int64 type; };
  2980. template<> struct superset<short,float> { typedef float type; };
  2981. template<> struct superset<short,double> { typedef double type; };
  2982. template<> struct superset<unsigned int,char> { typedef cimg_int64 type; };
  2983. template<> struct superset<unsigned int,signed char> { typedef cimg_int64 type; };
  2984. template<> struct superset<unsigned int,short> { typedef cimg_int64 type; };
  2985. template<> struct superset<unsigned int,int> { typedef cimg_int64 type; };
  2986. template<> struct superset<unsigned int,cimg_uint64> { typedef cimg_uint64 type; };
  2987. template<> struct superset<unsigned int,cimg_int64> { typedef cimg_int64 type; };
  2988. template<> struct superset<unsigned int,float> { typedef float type; };
  2989. template<> struct superset<unsigned int,double> { typedef double type; };
  2990. template<> struct superset<int,unsigned int> { typedef cimg_int64 type; };
  2991. template<> struct superset<int,cimg_uint64> { typedef cimg_int64 type; };
  2992. template<> struct superset<int,cimg_int64> { typedef cimg_int64 type; };
  2993. template<> struct superset<int,float> { typedef float type; };
  2994. template<> struct superset<int,double> { typedef double type; };
  2995. template<> struct superset<cimg_uint64,char> { typedef cimg_int64 type; };
  2996. template<> struct superset<cimg_uint64,signed char> { typedef cimg_int64 type; };
  2997. template<> struct superset<cimg_uint64,short> { typedef cimg_int64 type; };
  2998. template<> struct superset<cimg_uint64,int> { typedef cimg_int64 type; };
  2999. template<> struct superset<cimg_uint64,cimg_int64> { typedef cimg_int64 type; };
  3000. template<> struct superset<cimg_uint64,float> { typedef double type; };
  3001. template<> struct superset<cimg_uint64,double> { typedef double type; };
  3002. template<> struct superset<cimg_int64,float> { typedef double type; };
  3003. template<> struct superset<cimg_int64,double> { typedef double type; };
  3004. #if cimg_use_cpp11==1 && \
  3005. (!(UINTPTR_MAX==0xffffffff || defined(__arm__) || defined(_M_ARM) || ((ULONG_MAX)==(UINT_MAX))))
  3006. template<> struct superset<unsigned long long,char> { typedef cimg_int64 type; };
  3007. template<> struct superset<unsigned long long,signed char> { typedef cimg_int64 type; };
  3008. template<> struct superset<unsigned long long,short> { typedef cimg_int64 type; };
  3009. template<> struct superset<unsigned long long,int> { typedef cimg_int64 type; };
  3010. template<> struct superset<unsigned long long,cimg_int64> { typedef cimg_int64 type; };
  3011. template<> struct superset<unsigned long long,float> { typedef double type; };
  3012. template<> struct superset<unsigned long long,double> { typedef double type; };
  3013. template<> struct superset<long long,float> { typedef double type; };
  3014. template<> struct superset<long long,double> { typedef double type; };
  3015. #endif
  3016. template<> struct superset<float,cimg_uint64> { typedef double type; };
  3017. template<> struct superset<float,cimg_int64> { typedef double type; };
  3018. template<> struct superset<float,double> { typedef double type; };
  3019. #ifdef cimg_use_half
  3020. template<> struct superset<half,unsigned short> { typedef float type; };
  3021. template<> struct superset<half,short> { typedef float type; };
  3022. template<> struct superset<half,unsigned int> { typedef float type; };
  3023. template<> struct superset<half,int> { typedef float type; };
  3024. template<> struct superset<half,cimg_uint64> { typedef float type; };
  3025. template<> struct superset<half,cimg_int64> { typedef float type; };
  3026. template<> struct superset<half,float> { typedef float type; };
  3027. template<> struct superset<half,double> { typedef double type; };
  3028. #endif
  3029. template<typename t1, typename t2, typename t3> struct superset2 {
  3030. typedef typename superset<t1, typename superset<t2,t3>::type>::type type;
  3031. };
  3032. template<typename t1, typename t2, typename t3, typename t4> struct superset3 {
  3033. typedef typename superset<t1, typename superset2<t2,t3,t4>::type>::type type;
  3034. };
  3035. template<typename t1, typename t2> struct last { typedef t2 type; };
  3036. #define _cimg_Tt typename cimg::superset<T,t>::type
  3037. #define _cimg_Tfloat typename cimg::superset<T,float>::type
  3038. #define _cimg_tfloat typename cimg::superset<t,float>::type
  3039. #define _cimg_Ttfloat typename cimg::superset2<T,t,float>::type
  3040. #define _cimg_Ttdouble typename cimg::superset2<T,t,double>::type
  3041. // Define variables used internally by CImg.
  3042. #if cimg_display==1
  3043. struct X11_static {
  3044. unsigned int nb_wins;
  3045. pthread_t *events_thread;
  3046. pthread_cond_t wait_event;
  3047. pthread_mutex_t wait_event_mutex;
  3048. CImgDisplay **wins;
  3049. Display *display;
  3050. unsigned int nb_bits;
  3051. bool is_blue_first;
  3052. bool is_shm_enabled;
  3053. bool byte_order;
  3054. #ifdef cimg_use_xrandr
  3055. XRRScreenSize *resolutions;
  3056. Rotation curr_rotation;
  3057. unsigned int curr_resolution;
  3058. unsigned int nb_resolutions;
  3059. #endif
  3060. X11_static():nb_wins(0),events_thread(0),display(0),
  3061. nb_bits(0),is_blue_first(false),is_shm_enabled(false),byte_order(false) {
  3062. #ifdef __FreeBSD__
  3063. XInitThreads();
  3064. #endif
  3065. wins = new CImgDisplay*[1024];
  3066. pthread_mutex_init(&wait_event_mutex,0);
  3067. pthread_cond_init(&wait_event,0);
  3068. #ifdef cimg_use_xrandr
  3069. resolutions = 0;
  3070. curr_rotation = 0;
  3071. curr_resolution = nb_resolutions = 0;
  3072. #endif
  3073. }
  3074. ~X11_static() {
  3075. delete[] wins;
  3076. /*
  3077. if (events_thread) {
  3078. pthread_cancel(*events_thread);
  3079. delete events_thread;
  3080. }
  3081. if (display) { } // XCloseDisplay(display); }
  3082. pthread_cond_destroy(&wait_event);
  3083. pthread_mutex_unlock(&wait_event_mutex);
  3084. pthread_mutex_destroy(&wait_event_mutex);
  3085. */
  3086. }
  3087. }; // struct X11_static { ...
  3088. #if defined(cimg_module)
  3089. X11_static& X11_attr();
  3090. #elif defined(cimg_main)
  3091. X11_static& X11_attr() { static X11_static val; return val; }
  3092. #else
  3093. inline X11_static& X11_attr() { static X11_static val; return val; }
  3094. #endif
  3095. #elif cimg_display==2
  3096. struct Win32_static {
  3097. HANDLE wait_event;
  3098. Win32_static() { wait_event = CreateEvent(0,FALSE_WIN,FALSE_WIN,0); }
  3099. }; // struct Win32_static { ...
  3100. #if defined(cimg_module)
  3101. Win32_static& Win32_attr();
  3102. #elif defined(cimg_main)
  3103. Win32_static& Win32_attr() { static Win32_static val; return val; }
  3104. #else
  3105. inline Win32_static& Win32_attr() { static Win32_static val; return val; }
  3106. #endif
  3107. #endif
  3108. #define cimg_lock_display() cimg::mutex(15)
  3109. #define cimg_unlock_display() cimg::mutex(15,0)
  3110. struct Mutex_static {
  3111. #if cimg_OS==1 && (defined(cimg_use_pthread) || cimg_display==1)
  3112. pthread_mutex_t mutex[32];
  3113. Mutex_static() { for (unsigned int i = 0; i<32; ++i) pthread_mutex_init(&mutex[i],0); }
  3114. void lock(const unsigned int n) { pthread_mutex_lock(&mutex[n]); }
  3115. void unlock(const unsigned int n) { pthread_mutex_unlock(&mutex[n]); }
  3116. int trylock(const unsigned int n) { return pthread_mutex_trylock(&mutex[n]); }
  3117. #elif cimg_OS==2
  3118. HANDLE mutex[32];
  3119. Mutex_static() { for (unsigned int i = 0; i<32; ++i) mutex[i] = CreateMutex(0,FALSE_WIN,0); }
  3120. void lock(const unsigned int n) { WaitForSingleObject(mutex[n],INFINITE); }
  3121. void unlock(const unsigned int n) { ReleaseMutex(mutex[n]); }
  3122. int trylock(const unsigned int) { return 0; }
  3123. #else
  3124. Mutex_static() {}
  3125. void lock(const unsigned int) {}
  3126. void unlock(const unsigned int) {}
  3127. int trylock(const unsigned int) { return 0; }
  3128. #endif
  3129. }; // struct Mutex_static { ...
  3130. #if defined(cimg_module)
  3131. Mutex_static& Mutex_attr();
  3132. #elif defined(cimg_main)
  3133. Mutex_static& Mutex_attr() { static Mutex_static val; return val; }
  3134. #else
  3135. inline Mutex_static& Mutex_attr() { static Mutex_static val; return val; }
  3136. #endif
  3137. #if defined(cimg_use_magick)
  3138. struct Magick_static {
  3139. Magick_static() {
  3140. Magick::InitializeMagick("");
  3141. }
  3142. }; // struct Magick_static { ...
  3143. static Magick_static _Magick_static;
  3144. #endif
  3145. #if defined(cimg_use_fftw3) && !defined(cimg_use_fftw3_singlethread)
  3146. struct FFTW3_static {
  3147. FFTW3_static() {
  3148. fftw_init_threads();
  3149. }
  3150. }; // struct FFTW3_static { ...
  3151. static FFTW3_static _FFTW3_static;
  3152. #endif
  3153. #if cimg_display==1
  3154. // Define keycodes for X11-based graphical systems.
  3155. const unsigned int keyESC = XK_Escape;
  3156. const unsigned int keyF1 = XK_F1;
  3157. const unsigned int keyF2 = XK_F2;
  3158. const unsigned int keyF3 = XK_F3;
  3159. const unsigned int keyF4 = XK_F4;
  3160. const unsigned int keyF5 = XK_F5;
  3161. const unsigned int keyF6 = XK_F6;
  3162. const unsigned int keyF7 = XK_F7;
  3163. const unsigned int keyF8 = XK_F8;
  3164. const unsigned int keyF9 = XK_F9;
  3165. const unsigned int keyF10 = XK_F10;
  3166. const unsigned int keyF11 = XK_F11;
  3167. const unsigned int keyF12 = XK_F12;
  3168. const unsigned int keyPAUSE = XK_Pause;
  3169. const unsigned int key1 = XK_1;
  3170. const unsigned int key2 = XK_2;
  3171. const unsigned int key3 = XK_3;
  3172. const unsigned int key4 = XK_4;
  3173. const unsigned int key5 = XK_5;
  3174. const unsigned int key6 = XK_6;
  3175. const unsigned int key7 = XK_7;
  3176. const unsigned int key8 = XK_8;
  3177. const unsigned int key9 = XK_9;
  3178. const unsigned int key0 = XK_0;
  3179. const unsigned int keyBACKSPACE = XK_BackSpace;
  3180. const unsigned int keyINSERT = XK_Insert;
  3181. const unsigned int keyHOME = XK_Home;
  3182. const unsigned int keyPAGEUP = XK_Page_Up;
  3183. const unsigned int keyTAB = XK_Tab;
  3184. const unsigned int keyQ = XK_q;
  3185. const unsigned int keyW = XK_w;
  3186. const unsigned int keyE = XK_e;
  3187. const unsigned int keyR = XK_r;
  3188. const unsigned int keyT = XK_t;
  3189. const unsigned int keyY = XK_y;
  3190. const unsigned int keyU = XK_u;
  3191. const unsigned int keyI = XK_i;
  3192. const unsigned int keyO = XK_o;
  3193. const unsigned int keyP = XK_p;
  3194. const unsigned int keyDELETE = XK_Delete;
  3195. const unsigned int keyEND = XK_End;
  3196. const unsigned int keyPAGEDOWN = XK_Page_Down;
  3197. const unsigned int keyCAPSLOCK = XK_Caps_Lock;
  3198. const unsigned int keyA = XK_a;
  3199. const unsigned int keyS = XK_s;
  3200. const unsigned int keyD = XK_d;
  3201. const unsigned int keyF = XK_f;
  3202. const unsigned int keyG = XK_g;
  3203. const unsigned int keyH = XK_h;
  3204. const unsigned int keyJ = XK_j;
  3205. const unsigned int keyK = XK_k;
  3206. const unsigned int keyL = XK_l;
  3207. const unsigned int keyENTER = XK_Return;
  3208. const unsigned int keySHIFTLEFT = XK_Shift_L;
  3209. const unsigned int keyZ = XK_z;
  3210. const unsigned int keyX = XK_x;
  3211. const unsigned int keyC = XK_c;
  3212. const unsigned int keyV = XK_v;
  3213. const unsigned int keyB = XK_b;
  3214. const unsigned int keyN = XK_n;
  3215. const unsigned int keyM = XK_m;
  3216. const unsigned int keySHIFTRIGHT = XK_Shift_R;
  3217. const unsigned int keyARROWUP = XK_Up;
  3218. const unsigned int keyCTRLLEFT = XK_Control_L;
  3219. const unsigned int keyAPPLEFT = XK_Super_L;
  3220. const unsigned int keyALT = XK_Alt_L;
  3221. const unsigned int keySPACE = XK_space;
  3222. const unsigned int keyALTGR = XK_Alt_R;
  3223. const unsigned int keyAPPRIGHT = XK_Super_R;
  3224. const unsigned int keyMENU = XK_Menu;
  3225. const unsigned int keyCTRLRIGHT = XK_Control_R;
  3226. const unsigned int keyARROWLEFT = XK_Left;
  3227. const unsigned int keyARROWDOWN = XK_Down;
  3228. const unsigned int keyARROWRIGHT = XK_Right;
  3229. const unsigned int keyPAD0 = XK_KP_0;
  3230. const unsigned int keyPAD1 = XK_KP_1;
  3231. const unsigned int keyPAD2 = XK_KP_2;
  3232. const unsigned int keyPAD3 = XK_KP_3;
  3233. const unsigned int keyPAD4 = XK_KP_4;
  3234. const unsigned int keyPAD5 = XK_KP_5;
  3235. const unsigned int keyPAD6 = XK_KP_6;
  3236. const unsigned int keyPAD7 = XK_KP_7;
  3237. const unsigned int keyPAD8 = XK_KP_8;
  3238. const unsigned int keyPAD9 = XK_KP_9;
  3239. const unsigned int keyPADADD = XK_KP_Add;
  3240. const unsigned int keyPADSUB = XK_KP_Subtract;
  3241. const unsigned int keyPADMUL = XK_KP_Multiply;
  3242. const unsigned int keyPADDIV = XK_KP_Divide;
  3243. #elif cimg_display==2
  3244. // Define keycodes for Windows.
  3245. const unsigned int keyESC = VK_ESCAPE;
  3246. const unsigned int keyF1 = VK_F1;
  3247. const unsigned int keyF2 = VK_F2;
  3248. const unsigned int keyF3 = VK_F3;
  3249. const unsigned int keyF4 = VK_F4;
  3250. const unsigned int keyF5 = VK_F5;
  3251. const unsigned int keyF6 = VK_F6;
  3252. const unsigned int keyF7 = VK_F7;
  3253. const unsigned int keyF8 = VK_F8;
  3254. const unsigned int keyF9 = VK_F9;
  3255. const unsigned int keyF10 = VK_F10;
  3256. const unsigned int keyF11 = VK_F11;
  3257. const unsigned int keyF12 = VK_F12;
  3258. const unsigned int keyPAUSE = VK_PAUSE;
  3259. const unsigned int key1 = '1';
  3260. const unsigned int key2 = '2';
  3261. const unsigned int key3 = '3';
  3262. const unsigned int key4 = '4';
  3263. const unsigned int key5 = '5';
  3264. const unsigned int key6 = '6';
  3265. const unsigned int key7 = '7';
  3266. const unsigned int key8 = '8';
  3267. const unsigned int key9 = '9';
  3268. const unsigned int key0 = '0';
  3269. const unsigned int keyBACKSPACE = VK_BACK;
  3270. const unsigned int keyINSERT = VK_INSERT;
  3271. const unsigned int keyHOME = VK_HOME;
  3272. const unsigned int keyPAGEUP = VK_PRIOR;
  3273. const unsigned int keyTAB = VK_TAB;
  3274. const unsigned int keyQ = 'Q';
  3275. const unsigned int keyW = 'W';
  3276. const unsigned int keyE = 'E';
  3277. const unsigned int keyR = 'R';
  3278. const unsigned int keyT = 'T';
  3279. const unsigned int keyY = 'Y';
  3280. const unsigned int keyU = 'U';
  3281. const unsigned int keyI = 'I';
  3282. const unsigned int keyO = 'O';
  3283. const unsigned int keyP = 'P';
  3284. const unsigned int keyDELETE = VK_DELETE;
  3285. const unsigned int keyEND = VK_END;
  3286. const unsigned int keyPAGEDOWN = VK_NEXT;
  3287. const unsigned int keyCAPSLOCK = VK_CAPITAL;
  3288. const unsigned int keyA = 'A';
  3289. const unsigned int keyS = 'S';
  3290. const unsigned int keyD = 'D';
  3291. const unsigned int keyF = 'F';
  3292. const unsigned int keyG = 'G';
  3293. const unsigned int keyH = 'H';
  3294. const unsigned int keyJ = 'J';
  3295. const unsigned int keyK = 'K';
  3296. const unsigned int keyL = 'L';
  3297. const unsigned int keyENTER = VK_RETURN;
  3298. const unsigned int keySHIFTLEFT = VK_SHIFT;
  3299. const unsigned int keyZ = 'Z';
  3300. const unsigned int keyX = 'X';
  3301. const unsigned int keyC = 'C';
  3302. const unsigned int keyV = 'V';
  3303. const unsigned int keyB = 'B';
  3304. const unsigned int keyN = 'N';
  3305. const unsigned int keyM = 'M';
  3306. const unsigned int keySHIFTRIGHT = VK_SHIFT;
  3307. const unsigned int keyARROWUP = VK_UP;
  3308. const unsigned int keyCTRLLEFT = VK_CONTROL;
  3309. const unsigned int keyAPPLEFT = VK_LWIN;
  3310. const unsigned int keyALT = VK_LMENU;
  3311. const unsigned int keySPACE = VK_SPACE;
  3312. const unsigned int keyALTGR = VK_CONTROL;
  3313. const unsigned int keyAPPRIGHT = VK_RWIN;
  3314. const unsigned int keyMENU = VK_APPS;
  3315. const unsigned int keyCTRLRIGHT = VK_CONTROL;
  3316. const unsigned int keyARROWLEFT = VK_LEFT;
  3317. const unsigned int keyARROWDOWN = VK_DOWN;
  3318. const unsigned int keyARROWRIGHT = VK_RIGHT;
  3319. const unsigned int keyPAD0 = 0x60;
  3320. const unsigned int keyPAD1 = 0x61;
  3321. const unsigned int keyPAD2 = 0x62;
  3322. const unsigned int keyPAD3 = 0x63;
  3323. const unsigned int keyPAD4 = 0x64;
  3324. const unsigned int keyPAD5 = 0x65;
  3325. const unsigned int keyPAD6 = 0x66;
  3326. const unsigned int keyPAD7 = 0x67;
  3327. const unsigned int keyPAD8 = 0x68;
  3328. const unsigned int keyPAD9 = 0x69;
  3329. const unsigned int keyPADADD = VK_ADD;
  3330. const unsigned int keyPADSUB = VK_SUBTRACT;
  3331. const unsigned int keyPADMUL = VK_MULTIPLY;
  3332. const unsigned int keyPADDIV = VK_DIVIDE;
  3333. #else
  3334. // Define random keycodes when no display is available.
  3335. // (should rarely be used then!).
  3336. const unsigned int keyESC = 1U; //!< Keycode for the \c ESC key (architecture-dependent)
  3337. const unsigned int keyF1 = 2U; //!< Keycode for the \c F1 key (architecture-dependent)
  3338. const unsigned int keyF2 = 3U; //!< Keycode for the \c F2 key (architecture-dependent)
  3339. const unsigned int keyF3 = 4U; //!< Keycode for the \c F3 key (architecture-dependent)
  3340. const unsigned int keyF4 = 5U; //!< Keycode for the \c F4 key (architecture-dependent)
  3341. const unsigned int keyF5 = 6U; //!< Keycode for the \c F5 key (architecture-dependent)
  3342. const unsigned int keyF6 = 7U; //!< Keycode for the \c F6 key (architecture-dependent)
  3343. const unsigned int keyF7 = 8U; //!< Keycode for the \c F7 key (architecture-dependent)
  3344. const unsigned int keyF8 = 9U; //!< Keycode for the \c F8 key (architecture-dependent)
  3345. const unsigned int keyF9 = 10U; //!< Keycode for the \c F9 key (architecture-dependent)
  3346. const unsigned int keyF10 = 11U; //!< Keycode for the \c F10 key (architecture-dependent)
  3347. const unsigned int keyF11 = 12U; //!< Keycode for the \c F11 key (architecture-dependent)
  3348. const unsigned int keyF12 = 13U; //!< Keycode for the \c F12 key (architecture-dependent)
  3349. const unsigned int keyPAUSE = 14U; //!< Keycode for the \c PAUSE key (architecture-dependent)
  3350. const unsigned int key1 = 15U; //!< Keycode for the \c 1 key (architecture-dependent)
  3351. const unsigned int key2 = 16U; //!< Keycode for the \c 2 key (architecture-dependent)
  3352. const unsigned int key3 = 17U; //!< Keycode for the \c 3 key (architecture-dependent)
  3353. const unsigned int key4 = 18U; //!< Keycode for the \c 4 key (architecture-dependent)
  3354. const unsigned int key5 = 19U; //!< Keycode for the \c 5 key (architecture-dependent)
  3355. const unsigned int key6 = 20U; //!< Keycode for the \c 6 key (architecture-dependent)
  3356. const unsigned int key7 = 21U; //!< Keycode for the \c 7 key (architecture-dependent)
  3357. const unsigned int key8 = 22U; //!< Keycode for the \c 8 key (architecture-dependent)
  3358. const unsigned int key9 = 23U; //!< Keycode for the \c 9 key (architecture-dependent)
  3359. const unsigned int key0 = 24U; //!< Keycode for the \c 0 key (architecture-dependent)
  3360. const unsigned int keyBACKSPACE = 25U; //!< Keycode for the \c BACKSPACE key (architecture-dependent)
  3361. const unsigned int keyINSERT = 26U; //!< Keycode for the \c INSERT key (architecture-dependent)
  3362. const unsigned int keyHOME = 27U; //!< Keycode for the \c HOME key (architecture-dependent)
  3363. const unsigned int keyPAGEUP = 28U; //!< Keycode for the \c PAGEUP key (architecture-dependent)
  3364. const unsigned int keyTAB = 29U; //!< Keycode for the \c TAB key (architecture-dependent)
  3365. const unsigned int keyQ = 30U; //!< Keycode for the \c Q key (architecture-dependent)
  3366. const unsigned int keyW = 31U; //!< Keycode for the \c W key (architecture-dependent)
  3367. const unsigned int keyE = 32U; //!< Keycode for the \c E key (architecture-dependent)
  3368. const unsigned int keyR = 33U; //!< Keycode for the \c R key (architecture-dependent)
  3369. const unsigned int keyT = 34U; //!< Keycode for the \c T key (architecture-dependent)
  3370. const unsigned int keyY = 35U; //!< Keycode for the \c Y key (architecture-dependent)
  3371. const unsigned int keyU = 36U; //!< Keycode for the \c U key (architecture-dependent)
  3372. const unsigned int keyI = 37U; //!< Keycode for the \c I key (architecture-dependent)
  3373. const unsigned int keyO = 38U; //!< Keycode for the \c O key (architecture-dependent)
  3374. const unsigned int keyP = 39U; //!< Keycode for the \c P key (architecture-dependent)
  3375. const unsigned int keyDELETE = 40U; //!< Keycode for the \c DELETE key (architecture-dependent)
  3376. const unsigned int keyEND = 41U; //!< Keycode for the \c END key (architecture-dependent)
  3377. const unsigned int keyPAGEDOWN = 42U; //!< Keycode for the \c PAGEDOWN key (architecture-dependent)
  3378. const unsigned int keyCAPSLOCK = 43U; //!< Keycode for the \c CAPSLOCK key (architecture-dependent)
  3379. const unsigned int keyA = 44U; //!< Keycode for the \c A key (architecture-dependent)
  3380. const unsigned int keyS = 45U; //!< Keycode for the \c S key (architecture-dependent)
  3381. const unsigned int keyD = 46U; //!< Keycode for the \c D key (architecture-dependent)
  3382. const unsigned int keyF = 47U; //!< Keycode for the \c F key (architecture-dependent)
  3383. const unsigned int keyG = 48U; //!< Keycode for the \c G key (architecture-dependent)
  3384. const unsigned int keyH = 49U; //!< Keycode for the \c H key (architecture-dependent)
  3385. const unsigned int keyJ = 50U; //!< Keycode for the \c J key (architecture-dependent)
  3386. const unsigned int keyK = 51U; //!< Keycode for the \c K key (architecture-dependent)
  3387. const unsigned int keyL = 52U; //!< Keycode for the \c L key (architecture-dependent)
  3388. const unsigned int keyENTER = 53U; //!< Keycode for the \c ENTER key (architecture-dependent)
  3389. const unsigned int keySHIFTLEFT = 54U; //!< Keycode for the \c SHIFTLEFT key (architecture-dependent)
  3390. const unsigned int keyZ = 55U; //!< Keycode for the \c Z key (architecture-dependent)
  3391. const unsigned int keyX = 56U; //!< Keycode for the \c X key (architecture-dependent)
  3392. const unsigned int keyC = 57U; //!< Keycode for the \c C key (architecture-dependent)
  3393. const unsigned int keyV = 58U; //!< Keycode for the \c V key (architecture-dependent)
  3394. const unsigned int keyB = 59U; //!< Keycode for the \c B key (architecture-dependent)
  3395. const unsigned int keyN = 60U; //!< Keycode for the \c N key (architecture-dependent)
  3396. const unsigned int keyM = 61U; //!< Keycode for the \c M key (architecture-dependent)
  3397. const unsigned int keySHIFTRIGHT = 62U; //!< Keycode for the \c SHIFTRIGHT key (architecture-dependent)
  3398. const unsigned int keyARROWUP = 63U; //!< Keycode for the \c ARROWUP key (architecture-dependent)
  3399. const unsigned int keyCTRLLEFT = 64U; //!< Keycode for the \c CTRLLEFT key (architecture-dependent)
  3400. const unsigned int keyAPPLEFT = 65U; //!< Keycode for the \c APPLEFT key (architecture-dependent)
  3401. const unsigned int keyALT = 66U; //!< Keycode for the \c ALT key (architecture-dependent)
  3402. const unsigned int keySPACE = 67U; //!< Keycode for the \c SPACE key (architecture-dependent)
  3403. const unsigned int keyALTGR = 68U; //!< Keycode for the \c ALTGR key (architecture-dependent)
  3404. const unsigned int keyAPPRIGHT = 69U; //!< Keycode for the \c APPRIGHT key (architecture-dependent)
  3405. const unsigned int keyMENU = 70U; //!< Keycode for the \c MENU key (architecture-dependent)
  3406. const unsigned int keyCTRLRIGHT = 71U; //!< Keycode for the \c CTRLRIGHT key (architecture-dependent)
  3407. const unsigned int keyARROWLEFT = 72U; //!< Keycode for the \c ARROWLEFT key (architecture-dependent)
  3408. const unsigned int keyARROWDOWN = 73U; //!< Keycode for the \c ARROWDOWN key (architecture-dependent)
  3409. const unsigned int keyARROWRIGHT = 74U; //!< Keycode for the \c ARROWRIGHT key (architecture-dependent)
  3410. const unsigned int keyPAD0 = 75U; //!< Keycode for the \c PAD0 key (architecture-dependent)
  3411. const unsigned int keyPAD1 = 76U; //!< Keycode for the \c PAD1 key (architecture-dependent)
  3412. const unsigned int keyPAD2 = 77U; //!< Keycode for the \c PAD2 key (architecture-dependent)
  3413. const unsigned int keyPAD3 = 78U; //!< Keycode for the \c PAD3 key (architecture-dependent)
  3414. const unsigned int keyPAD4 = 79U; //!< Keycode for the \c PAD4 key (architecture-dependent)
  3415. const unsigned int keyPAD5 = 80U; //!< Keycode for the \c PAD5 key (architecture-dependent)
  3416. const unsigned int keyPAD6 = 81U; //!< Keycode for the \c PAD6 key (architecture-dependent)
  3417. const unsigned int keyPAD7 = 82U; //!< Keycode for the \c PAD7 key (architecture-dependent)
  3418. const unsigned int keyPAD8 = 83U; //!< Keycode for the \c PAD8 key (architecture-dependent)
  3419. const unsigned int keyPAD9 = 84U; //!< Keycode for the \c PAD9 key (architecture-dependent)
  3420. const unsigned int keyPADADD = 85U; //!< Keycode for the \c PADADD key (architecture-dependent)
  3421. const unsigned int keyPADSUB = 86U; //!< Keycode for the \c PADSUB key (architecture-dependent)
  3422. const unsigned int keyPADMUL = 87U; //!< Keycode for the \c PADMUL key (architecture-dependent)
  3423. const unsigned int keyPADDIV = 88U; //!< Keycode for the \c PADDDIV key (architecture-dependent)
  3424. #endif
  3425. const double PI = 3.14159265358979323846; //!< Value of the mathematical constant PI
  3426. // Define a 10x13 binary font (small sans).
  3427. static const char *const data_font_small[] = {
  3428. " UwlwnwoyuwHwlwmwcwlwnw[xuwowlwmwoyuwRwlwnxcw Mw (wnwnwuwpwuypwuwoy"
  3429. "ZwnwmwuwowuwmwnwnwuwowuwfwuxnwnwmwuwpwuypwuwZwnwnwtwpwtwow'y Hw cwnw >{ jw %xdxZwdw_wexfwYwkw 7yowoyFx=w "
  3430. "ry qw %wuw !xnwkwnwoyuwfwuw[wkwnwcwowrwpwdwuwoxuwpwkwnwoyuwRwkwnwbwpwNyoyoyoyoy;wdwnxpxtxowG|!ydwnwuwowtwow"
  3431. "pxswqxlwnxnxmwDwoyoxnyoymwp{oyq{pyoy>ypwqwpwp{oyqzo{q{pzrwrwowlwqwswpwnwqwsxswpypzoyqzozq}swrwrwqwtwswswtxsxswq"
  3432. "ws}qwnwkwnydwew_wfwdwkwmwowkw(w0wmwmwGwtwdxQw swuwnwo{q{pynwp|rwtwtwqydwcwcwcwmwmxgwqwpwnzpwuwpzoyRzoyoyexnynwd"
  3433. "z\\xnxgxrwsxrwsyswowmwmwmwmwmwmwo}ryp{q{q{q{nwmwnwmwozqxswpyoyoyoyoyeyuwswrwrwrwrwrwrwrwrwqwrwmwtwnwmwnwuwpwuyp"
  3434. "wuwoyZwmwnwuwowuwmwqwkwuwowuwoxnwuxowmwnwuwpwuypwuwZwmwnwuwowuwnwowmwtw\\wuwuwqwswqwswqwswqwswEwqwtweypzr~qyIw "
  3435. "rwswewnwuwowuwozswtwuwqwtwmwnwlwowuwuwowOxpxuxqwuwowswqwswoxpwlwjwqwswqwsw<wrwowrwuwqwrwqwswrwswpwmwmwrwswrwowl"
  3436. "wqwtwownxsxsxswqwswqwswqwswrwswqwrwowpwrwrwqwtwswswswswqwswmwpwmwlwoxuxSw_wfwdwYwkw(w0wmwmwGwtwoxnwNw uwswpwuwp"
  3437. "wmwmwswq{rwrwrwtwtwrwswfydwdyZwnwtwrwqwrwswowowdwrwqxuwSwrwfwuwnwlwnw[yuw[wowtwgwswqwswqwswewuwowuwowuwowuwowuw"
  3438. "nwowuwowswqwmwmwmwjwmwnwmwowswrxswqwswqwswqwswqwswqwswrwrwqwswrwrwrwrwrwrwrwrwqwswqzpwtw #w DwPwtwtwswqwswuwuwu"
  3439. "wswswuwswqwGwqxtwf{qzr~r{qzqwrwpxowtwrw rzcwnwuwq}rwuwqwtwuwqwtwmwnwlwnynwOwowswowkwmwpwuwpwmwjwpwswqwswowmwjwi"
  3440. "wjxswsytwrwuwqwrwrwmwrwqwmwnwmwrwowlwqwuwnwnxsxswuwtwrwqwrwswrwqwswswqwjwpwrwqwswrwtwtwqwuwowuwmwowmwlwpxsx]ypz"
  3441. "oyozpypzozqznwmwowtwnwqzuyrzoypzozqwuxoypzpwswrwrwrwtwtwswrwrwrwq{owmwmwQyuwqwtwmwoxnypzqxswowowswqwswqwtxr|rwt"
  3442. "wtwqyp{q{qwswpwuwownwnwqwsxuwuxswrwrwtwtwswqwrwmwuwuwnwnwowtwpwuwuwewnzpwn{pwuwnwnxgwtxtwrwtwowtw_wuytwgynwmwlw"
  3443. "gwswpyuw[wowtwqwtwpwtwpwtwowuwmwnwuwowuwowuwowuwowuwowuwqxuwpwlwmwmwmwjwmwnwmwowrwswuwtwrwqwswqwswqwswqwswqwrwt"
  3444. "wqwswuwswrwrwrwrwrwrwrwpwuwpwswqwuwnyoyoyoyoyoyqyuyqyoyoyoyoymwqwjwmwnypzoyoyoyoyoynwnzqwswqwswqwswqwswrwrwqzqw"
  3445. "rw^}s}swtwtwswtwtwswtwtwK}rwuwe{s~t~s}rwtwqwrwpxowtwrw qwawewtwpwuwpxuwpycwlwnynwOwowswowkwpypwtwpzpzmwoypwsw[y"
  3446. "r}rymwrwtwtwtwrwuwq{qwmwrwq{q{rwm|owlwqxmwnwuwuwuwswuwtwrwqwrwswrwqwswswqylwpwrwqwswrwuwuwuwpwmwmwnwmwlwMwqwswq"
  3447. "wmwswqwswpwnwswqwswowmwowuwmwqwswswswswqwswqwswqwswqxnwswpwnwswrwrwrwtwtwrwtwqwrwmwqxlwlx]xuxrwtyqwuwlwpwtwpwmw"
  3448. "swqwtxpxowswrwqwswtwuxrwtwqwtwtwrwswrwswnwo{pwuwnxpwnwqwswtwtwswrwrwtwtwswuyuwswjwkwowpwrwowcwowuwnwnwswqxuxowo"
  3449. "wtwhwuwrwrzpwtwq}jwuwtwuw_}qyoxfwswpyuwowdyoxowtwryuwqyuwqyuwmwnwuwowuwowuwowuwowuwowuwqwt{twl{q{q{q{nwmwnwmwpz"
  3450. "twswuwtwrwqwswqwswqwswqwswqwqxpwtwtwswrwrwrwrwrwrwrwowowswqwuwkwmwmwmwmwmwowswswmwswqwswqwswqwswnwqwjwmwowswqws"
  3451. "wqwswqwswqwswqwswqwswgwtxqwswqwswqwswqwswrwrwqwswrwrw^wtwtwswqwswuwuwuwswuwswswqwHwowuwf}t~s|r}swrwrwrwqwtwpwtw"
  3452. "r~#zcwewtwoynwuxtwtwswgwlwowuwuwr}gyexowswowlwlwrwswlwqwswowowswpz^yayqwqwtwtwuwrwswrwrwrwmwrwqwmwnwsyswrwowlwq"
  3453. "wuwnwnwuwuwuwswtwuwrwqwrzqwqwszmyowpwrwpwuwqwuwuwuwpwmwmwnwlwmwPzqwswqwmwswq{pwnwswqwswowmwoxlwqwswswswswqwswqw"
  3454. "swqwswqwlxnwnwswqwtwqwuwuwuwqxowtwmwnwmwmwoytwiwtwtwswswpwtxqzpwswpxowswpwuwowuwpwswrwtwtwswtwtwrwtwqwtwtwrwswr"
  3455. "wswnwowswqwswowownwqwswtwtwswrwqwuwuwrwuyuwt~pwq~pwq~pwcwowuwozpwswowewswiwuwrwiwtwjwjwuytw\\wRwswoxuwHwtwpwswq"
  3456. "wtxqwswqxowswqwswqwswqwswqwswqwswrwtwpwlwmwmwmwjwmwnwmwowrwswtwuwrwqwswqwswqwswqwswqwqxpwtwtwswrwrwrwrwrwrwrwow"
  3457. "owswqwtwozpzpzpzpzpzr~swm{q{q{q{nwqwjwmwowswqwswqwswqwswqwswqwswqwswr}rwuwuwqwswqwswqwswqwswqwtwpwswqwtw\\wuwuw"
  3458. "qwswqwswqwswqwswJ}qxf}t~rzp{rwrwrwrwqwtwpwtwrw qwawg}owuwpwuwtwuwswuwfwlwmwmwPwnwswowmwkwr|mwqwswowowswmw^yo}oy"
  3459. "qwqwszq{rwrwrwmwrwqwmwnwqwswrwowlwqwtwownwtwtwswtwuwrwqwrwnwqwswtwkwowpwrwpwuwqwuwuwuwqwuwnwnwmwlwmwQwswqwswqwm"
  3460. "wswqwlwnwswqwswowmwowuwmwqwswswswswqwswqwswqwswqwjwownwswqwtwqwuwuwuwqxowtwnwmwmwmwpwtyhwtwtwswswpwswqwtwpwswqw"
  3461. "mwswpwuwpwtwpwswrwtwtwswtwtwrwtwqwtwtwrwswrwswnwowswqwswpwnwnwqwsxuwuxswrwpyqwqwswjwkwqwuwuwrwrwqwuwuwewowuwnwn"
  3462. "wswq{ownxuwiwtxtwrzpwtwkwjwuwtwuw\\wRwswnwuwSzpwtwowtxqwrwrwtxrxn{q{q{q{q{q{s{pwlwmwmwmwjwmwnwmwowrwswtwuwrwqws"
  3463. "wqwswqwswqwswqwrwtwqwuwswswrwrwrwrwrwrwrwowozpwswqwswqwswqwswqwswqwswqwswswswowmwmwmwmwjwqwjwmwowswqwswqwswqwsw"
  3464. "qwswqwswqwswgwuwuwqwswqwswqwswqwswqwtwpwswqwtw[yoyoyoyoyGwmwdwuwuwpxnxnyqwrwqwtwpwtwoxpw rwswSwuwmwuwpwuwtwuxsw"
  3465. "ewlwcwPwnxuxownwnwswnwlwqwswowowswnwZygygwkwswrwrwqwswrwswpwmwmwrwswrwowlwqwswpwnwqwswsxqwswqwmwswrwswqwrwowpxt"
  3466. "xowowswqwswowowlwlwmwQwswqwswqwmwswqwswpwnwswqwswowmwowtwnwqwswswswswqwswqwswqwswqwmwswpwnwswpxowswqwtwoxnwlwmw"
  3467. "mw[xuxrwtxpwswqwtwpwswqwmwswpypwtwpwswrwtwtwsxuwuxrwtwqwtwtwrwswrwswnwnwuwpwswqwmwmwswq{rwrwowowswqwkwlwoypwtwo"
  3468. "ydwowuwnwn{owmwlwgwrwfwtw^wrw6wswnwuwJwtwowtzswrwrwtzswmwswqwswqwswqwswqwswqwswswswowswqwmwmwmwjwmwnwmwowswrwsx"
  3469. "qwswqwswqwswqwswqwswrwrwqwswrxtxrxtxrxtxrxtxowowmwswqwswqwswqwswqwswqwswqwswswtxowmwswqwswqwswqwswnwqwjwmwowswq"
  3470. "wswqwswqwswqwswqwswqwswowoxtwqwswqwswqwswqwswpxowswpx Wwlwbwnxcwpwrwqzpwtwoxo|!ydwfwtwozpwsxszuxgxnxcwmwcwoxmyp"
  3471. "{q{pymwpzoyowmypymwmwjwiwkwowrwrwqws{oyqzo{qwlzrwrwowlwqwrwq{rwqwswsxpypwlyqwrwqznwoznwowswrxsxpwp}qwkwnwPzqzoy"
  3472. "ozpyowmzqwswowmwowswowqwswswswswpypzozqwlynxozpxowswrwrwpwn{owmwmwQxuxqzoxnyoypwswowpwrwqzpxuxq{qwtxq{qzpylwoyq"
  3473. "}r{qwnyuypwpwrwownydwcwcwcwnzq{rwqwpwmwkwgzHz]}U|owuw@wqwswrytwqwqyqwqwswqwswqwswqwswqwswqwuwr{ryp{q{q{q{nwmwnw"
  3474. "mwozqwsxpyoyoyoyoygwuypzpzpzpznwowmwuypzpzpzpzpzpzryuzryoyoyoyoymwqwjwmwnypwswpyoyoyoyoyfzozpzpzpzpwnzow \\w"
  3475. "OwnwXw[w SwGz kx0x lxdx gw[w=wiw*wbyowoyGwKwowewawcwow YwOwoz Ewjwuwdw 7w 9w Iwnwlw \\w 0|*y[x=wiw,"
  3476. "xWw=wKwowewawcwow Yw hwVx 8w 9w Jxmwnxp" };
  3477. // Define a 26x32 font (normal sans).
  3478. static const char *const data_font_normal[] = {
  3479. " #{}~}a{|y~f{|y~}f{|}|x{}|j{|y}y{|y}g{}y~}|2y~|a{}~}f{}y~|"
  3480. "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~ "
  3481. " 2{}~}c{|y~f{|y~}~}h{}w~y}~|j{}y~y{}y~h{}~y}y~|2y~|c{}~}f{"
  3482. "}~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~}"
  3483. "ey~| g{|}y~} J{}~|dy~|fy~y{}~|i{~}{|}y~}i{}y~y{}y"
  3484. "~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{}"
  3485. "~|e{}~}f{}~|y{|~|iy}|yx}f{}~| fy~y}~} k{|y~| /{|y~| y{}"
  3486. "~} 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{|"
  3487. "}|^{|}|g{|}| s{}~}_{|y~|gy~} Z{}~}_{|y~|gy~} )y}| -{|y~ Jy}|yy}| "
  3488. "X{}y~ 4{|~}y{|~} P{| n{|y~`{|y~fx~}3{}~x{|~|4{}~}`{}~}g{|x~}N{}~}`{|y~|gx~| sy~|`y~|g{}x~| Z{}~}`y~}g{}"
  3489. "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"
  3490. "{|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"
  3491. "~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{|"
  3492. "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"
  3493. "{}~}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{|"
  3494. "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{"
  3495. "|}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{|"
  3496. "}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}"
  3497. "|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~}|"
  3498. "{}~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{|"
  3499. "}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{}"
  3500. "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~|"
  3501. "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{|"
  3502. "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~|"
  3503. "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"
  3504. "{|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"
  3505. "~}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"
  3506. "~}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"
  3507. "~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{"
  3508. "}~}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~"
  3509. "}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{~"
  3510. "}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"
  3511. "~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"
  3512. "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{|"
  3513. "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~"
  3514. "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{"
  3515. "}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"
  3516. "~}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"
  3517. "}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~"
  3518. "}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~}"
  3519. "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"
  3520. "{}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}"
  3521. "|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}"
  3522. "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"
  3523. "~} ^{}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{}"
  3524. "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~|"
  3525. "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{|"
  3526. "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"
  3527. "~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"
  3528. "{}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"
  3529. "{}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{|~"
  3530. "}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{"
  3531. "|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{}"
  3532. "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"
  3533. "{}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~| "
  3534. "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{}"
  3535. "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"
  3536. "{|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~|"
  3537. "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~|"
  3538. "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"
  3539. "~}|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"
  3540. "~}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{|~}"
  3541. "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{|"
  3542. "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{|"
  3543. "}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{|"
  3544. "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{"
  3545. "|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"
  3546. "{}~| 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~"
  3547. "|{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{"
  3548. "|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~|"
  3549. "{|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~}|{}~"
  3550. "}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~"
  3551. "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{}"
  3552. "~}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{|"
  3553. "}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~"
  3554. "}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{"
  3555. "}~|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~"
  3556. "}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~|"
  3557. "}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{}"
  3558. "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"
  3559. "}|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~"
  3560. "}|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"
  3561. "~|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"
  3562. "~} 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{|"
  3563. "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{|"
  3564. "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{|"
  3565. "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"
  3566. "~|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{}"
  3567. "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{"
  3568. "|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"
  3569. "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"
  3570. "{}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"
  3571. "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{"
  3572. "|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"
  3573. "~}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~"
  3574. "|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~|"
  3575. "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"
  3576. "{|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~}<{}~"
  3577. "}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{|"
  3578. "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{|}~}~}"
  3579. "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"
  3580. "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~|"
  3581. "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{|"
  3582. "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"
  3583. "|}|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~"
  3584. "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{|"
  3585. "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"
  3586. "~|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"
  3587. "~}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~"
  3588. "|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{|"
  3589. "~}`{}~|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~}"
  3590. "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{|"
  3591. "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"
  3592. "~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"
  3593. "|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~"
  3594. "|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{}~"
  3595. "}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{}~|"
  3596. "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"
  3597. "{}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"
  3598. "~}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~"
  3599. "|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~|"
  3600. "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~|"
  3601. " 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{}"
  3602. "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{}~"
  3603. "}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{"
  3604. "}~}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~"
  3605. "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{|"
  3606. "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{}~}"
  3607. "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"
  3608. "~|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{"
  3609. "|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{"
  3610. "}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~|"
  3611. "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"
  3612. "~|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{}~}<{"
  3613. "|~}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{|"
  3614. "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"
  3615. "{}|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{|"
  3616. "}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~|"
  3617. "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{"
  3618. "|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~"
  3619. "}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~"
  3620. "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~"
  3621. "|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~|"
  3622. "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~"
  3623. "}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~|"
  3624. "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{|"
  3625. "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"
  3626. "~}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{"
  3627. "|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{"
  3628. "|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{}"
  3629. "~}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{"
  3630. "|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~}"
  3631. "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~"
  3632. "|{|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~"
  3633. "|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"
  3634. "~|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"
  3635. "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"
  3636. "~|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"
  3637. "{}~}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~"
  3638. "|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{"
  3639. "}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"
  3640. "{}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{"
  3641. "~|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"
  3642. "~|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~"
  3643. "}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"
  3644. "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~|"
  3645. "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{"
  3646. "|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{}~"
  3647. "}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"
  3648. "~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}"
  3649. "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~|"
  3650. "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"
  3651. "~|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{"
  3652. "|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~|"
  3653. " 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{}"
  3654. "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{"
  3655. "|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~}"
  3656. "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~|"
  3657. "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"
  3658. "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"
  3659. "{}~|~}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"
  3660. "~|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{|"
  3661. "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"
  3662. "~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"
  3663. "~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~"
  3664. "}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{|"
  3665. "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~"
  3666. "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~"
  3667. "}|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"
  3668. "{|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~"
  3669. "}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{}~"
  3670. "}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{}~}"
  3671. "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"
  3672. "{|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"
  3673. "{|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"
  3674. "{}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~|^{"
  3675. "|~}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~"
  3676. "}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~"
  3677. "|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{|"
  3678. "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~"
  3679. "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~"
  3680. "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"
  3681. "{}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{"
  3682. "}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"
  3683. "~{|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"
  3684. "{|}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{"
  3685. "|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~{|~"
  3686. "}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~"
  3687. "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{}~"
  3688. "}{}~}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{|"
  3689. "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"
  3690. "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~}]"
  3691. "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~}"
  3692. "_{|~}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~"
  3693. "}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"
  3694. "{}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~"
  3695. "}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~"
  3696. "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"
  3697. "{}~}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~"
  3698. "}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"
  3699. "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~"
  3700. "}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"
  3701. "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{"
  3702. "|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{"
  3703. "|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~"
  3704. "|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{"
  3705. "|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~"
  3706. "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"
  3707. "~}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{|"
  3708. "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~"
  3709. "|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"
  3710. "~|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"
  3711. "~|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"
  3712. "~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~|"
  3713. "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{}~"
  3714. "}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~"
  3715. "|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~"
  3716. "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"
  3717. "~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~|"
  3718. "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"
  3719. "{|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{"
  3720. "|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~|"
  3721. "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"
  3722. "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{"
  3723. "}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"
  3724. "~|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{"
  3725. "|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"
  3726. "{|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~"
  3727. "|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"
  3728. "{|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"
  3729. "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"
  3730. "~|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~}{|}"
  3731. "{}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"
  3732. "~}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{"
  3733. "}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~}"
  3734. "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~|"
  3735. "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"
  3736. "~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~"
  3737. "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{}"
  3738. "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{"
  3739. "}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~|"
  3740. "`{|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{}~| /{"
  3741. "}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"
  3742. "~|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{"
  3743. "}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~|"
  3744. "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{"
  3745. "}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~"
  3746. "|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{}~}"
  3747. "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~|"
  3748. "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{|}"
  3749. "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~|"
  3750. "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{}"
  3751. "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"
  3752. "{|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"
  3753. "~}|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~"
  3754. "|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~|`{}"
  3755. "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"
  3756. "~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"
  3757. "|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{|"
  3758. "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|}~"
  3759. "}|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~"
  3760. "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"
  3761. "|}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{|}"
  3762. "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{|}"
  3763. "~}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~}"
  3764. "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"
  3765. "{|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"
  3766. "~}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~"
  3767. "}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"
  3768. "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~|"
  3769. " 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{}~"
  3770. "}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{"
  3771. "|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{"
  3772. "}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"
  3773. "|{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"
  3774. "{|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~}"
  3775. "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{"
  3776. "|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"
  3777. "{|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{|"
  3778. "}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"
  3779. "~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"
  3780. "~}|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~}"
  3781. "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"
  3782. "{}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{}"
  3783. "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{"
  3784. "}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{}~"
  3785. "}{}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{"
  3786. "}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{}~"
  3787. "}|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{}"
  3788. "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~"
  3789. "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~"
  3790. "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"
  3791. "{|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{|"
  3792. "}x~}y{}~}l{|}x~}y{}~}j{|y~}i{}y~{|}x~}|h{|y~} e{|~} V{| .{|~ p{}~}dy~|1{|y~2{}~} U{|y~ ^{}~} ){|y~| {"
  3793. "}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{}"
  3794. "y~ Ry~}c{|~| r{}~}h{}t~|K{| U{| '{}~}^y~y{}~|O{|~| wy|cy}|st|sy|ay~} ^{|~| 9{| Y{|~| 8y| Q{|y~h{}"
  3795. "y~`{|y~ d{|~} c{|~ oy~e{}~}0{}~}2y~| U{}~} ]{}y~|r{|y} 7{}y~ y{}y~| 7{}~}_{|y~f{|y~| .{|y~|6{|y~ "
  3796. "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{"
  3797. "}~|N{}~ 'y~} ]y~ p{~} g{}~}h{}y~`{}~} h{|}|{}~} c{|~ o{}~}fy~/{}~ c{}~ [{}y~}|v{|}y~} "
  3798. " 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"
  3799. "~ Ry~}b{~| r{}~} i{}~}*{}~| (x~uy| e{}~| q{}~ h{|y~|h{}y~a{|y~| h{|y~|y~| c{|~ ny~"
  3800. "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{"
  3801. "|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"
  3802. "{}w~ e{|y}w~}| 8{|w~} ({|n~} m{}s~}7{|w~ @{}y~Z{}~} nv~|F{|y}~}h{}~}h{}~y}| Z{|y~ g{}~}j{"
  3803. "|y~|O{}~}F{|y~J{|y~h{}y~ Rx| W{}~} i{}~}-{}y~}| &{}s~| i{|~y}y~ t{|~y}y~ kv~|g{}y~dv~| ex"
  3804. "} s{|y~}| '{|n~} m{|y}w~}|6{|y~} ?{}y~Z{}~} nx~}|-{}~} B{|y~ g{}~}j{|y~|O{}~}F{|y~J{|y~"
  3805. "h{}y~ q{}~} -{|}x~}| f{}y~}| t{|x~}| kx~}|f{}y~dx~}| "
  3806. " :{}~} I{" };
  3807. // Define a 52x64 font (large sans).
  3808. static const char *const data_font_large[] = {
  3809. " "
  3810. " -{| "
  3811. " [{|x}|Dw}|Pw}| @{}v~} C{|w}|Ew}|Pv}| xv|Ev|Pu| kv|Dw|P{|v} 6{|w}|E{|x}|P{"
  3812. "|w}| pw}| "
  3813. " 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{"
  3814. "|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~ "
  3815. " "
  3816. " 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~"
  3817. "}V{}w~|H{}w~Ps~}St~}w{}y~}X{}w~}w{|v~ Fw~}H{|w~|Q{|t~}Sv~|w{}w~}P{|w~| "
  3818. " D{|w~|J{|w~|Q{|x~}{w~|U"
  3819. "{}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"
  3820. "{}r~|{|x~}X{}w~}w{|v~ Ew~|Iw~}Pw~|}x~}Tv~|w{}w~}Q{|w~| M{| "
  3821. " q{}w~Jw~|Q{|x~}xw~Ux~}y{|}t~}W{|v~vv"
  3822. "~|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~"
  3823. "Ux~}{|}r~W{}w~}w{|v~ E{|w~|K{}x~}Pw~|y{}x~|Uv~|w{}w~}Qw~| O{}v~} "
  3824. " s{}x~}L{}x~|Pw~vw~W{|x~v{|}w~}"
  3825. "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{"
  3826. "|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~| "
  3827. " A{}x~|N{}x~}P"
  3828. "{}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~"
  3829. "}X{|x~u{|}y~}|Vu|vv| C{|x~}N{|w~P{|x~|u{}x~Vv|vu|S{|x~} Op~| Zv~| "
  3830. " ;v~ u{|v~ 6{|y}|N{|y}|P{|x}s{|x} I"
  3831. "{}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"
  3832. "{|w} lv|@v|Rv| 8v}@v}|S{|w} m{}w~| E{|y~x}| ;{|w~} "
  3833. " vv~| J{}y~}t{}y~} e{}w~}B{|w~}Rv~| Dx~|v{|x~| H"
  3834. "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{"
  3835. "|y~| b{}x~|T{|x~} L{|q~} y{}q~| H{|w~} xw~} `{|w~| {{|}t~)w~}Cv~Lv~Tw~}Dv~ G"
  3836. "{|x}w~}Tw~|U{|v~y}| 1{|y}v~y}| cv~y} p{|y}x~y}| {{v|vv| 3{}w~| I{|x~|v{|x~| "
  3837. " %{| 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"
  3838. "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~"
  3839. "} 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|"
  3840. " 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| "
  3841. " 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~| "
  3842. " 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{"
  3843. "}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}"
  3844. "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~|"
  3845. "\"{|}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{|"
  3846. "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"
  3847. "}|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{}"
  3848. "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~"
  3849. "|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{"
  3850. "}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"
  3851. "}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{}"
  3852. "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|"
  3853. "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{}"
  3854. "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{|"
  3855. "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~}"
  3856. "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"
  3857. "~}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~"
  3858. "|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~"
  3859. "|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{}"
  3860. "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"
  3861. "~}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{|"
  3862. "}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"
  3863. "~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~}"
  3864. "|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{|"
  3865. "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~|{"
  3866. "}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~} "
  3867. "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"
  3868. "{}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"
  3869. "~}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~"
  3870. "}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~"
  3871. "}|#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"
  3872. "~}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~"
  3873. "}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"
  3874. "~|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"
  3875. "~}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"
  3876. "{|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~|"
  3877. "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{"
  3878. "|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~| ;{}`~} -"
  3879. "{|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{}"
  3880. "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{"
  3881. "}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{|"
  3882. "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~ "
  3883. " 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~"
  3884. "}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{}"
  3885. "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{"
  3886. "|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~}_"
  3887. "{|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{"
  3888. "|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{|}"
  3889. "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{"
  3890. "|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"
  3891. "|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~}\\{"
  3892. "|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"
  3893. "}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~}"
  3894. "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"
  3895. "|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{|"
  3896. "}| 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"
  3897. "~} 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"
  3898. "~|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{|"
  3899. "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~"
  3900. "}|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~`"
  3901. "{}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~}|"
  3902. "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~}|"
  3903. "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{"
  3904. "|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~"
  3905. "|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"
  3906. "{|}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{|"
  3907. "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"
  3908. "{|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~"
  3909. "}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{|"
  3910. "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{}"
  3911. "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"
  3912. "~}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{|"
  3913. "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"
  3914. "~|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"
  3915. "~} 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~}"
  3916. "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~}^"
  3917. "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"
  3918. "{}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~|"
  3919. "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{|"
  3920. "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~|$"
  3921. "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~}"
  3922. "|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{|"
  3923. "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~"
  3924. "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{|"
  3925. "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"
  3926. "~}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{|"
  3927. "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~|"
  3928. "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{|}"
  3929. "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~}"
  3930. "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{}"
  3931. "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~}"
  3932. "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~ "
  3933. "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~"
  3934. "}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~|\\"
  3935. "{|}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{}"
  3936. "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~"
  3937. "}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~}"
  3938. "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{"
  3939. "}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~}"
  3940. "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{"
  3941. "}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~}"
  3942. "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{}"
  3943. "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"
  3944. "~|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"
  3945. "~} 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"
  3946. "~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~"
  3947. "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~"
  3948. "}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"
  3949. "~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{"
  3950. "|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~}|"
  3951. "\\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{"
  3952. "}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{|~"
  3953. "|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~^{|"
  3954. "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{"
  3955. "}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~"
  3956. "|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"
  3957. "{|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{}"
  3958. "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"
  3959. "~}!{}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~}"
  3960. "-{}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"
  3961. "{|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"
  3962. "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~"
  3963. "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"
  3964. "~}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"
  3965. "{|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~}"
  3966. "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"
  3967. "~}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"
  3968. "~}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"
  3969. "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"
  3970. "~}|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~}"
  3971. "|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~"
  3972. "}|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{}"
  3973. "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~}"
  3974. "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"
  3975. "~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{"
  3976. "}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{|"
  3977. "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~}"
  3978. "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"
  3979. "{|}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~"
  3980. "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{"
  3981. "|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{"
  3982. "}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~}"
  3983. "^{}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~}"
  3984. "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{"
  3985. "}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"
  3986. "~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{|"
  3987. "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{|"
  3988. "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~|"
  3989. "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{"
  3990. "}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{|"
  3991. "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{|"
  3992. "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~"
  3993. "|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"
  3994. "~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"
  3995. "{|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{"
  3996. "}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{|"
  3997. "}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{|"
  3998. "}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~}"
  3999. "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{}"
  4000. "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~"
  4001. "}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"
  4002. "~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"
  4003. "~}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~"
  4004. "}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"
  4005. "~}{|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~}"
  4006. "{|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"
  4007. "~`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"
  4008. "~}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{}"
  4009. "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~|"
  4010. "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~}]"
  4011. "{~|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~}"
  4012. "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~}"
  4013. "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~}["
  4014. "{}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~"
  4015. "}]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"
  4016. "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~}"
  4017. "[{|}|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"
  4018. "{|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"
  4019. "~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"
  4020. "{}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~"
  4021. "}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~"
  4022. "}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~}"
  4023. "|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~}"
  4024. "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"
  4025. "~|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"
  4026. "{|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"
  4027. "{|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"
  4028. "~}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~"
  4029. "|]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"
  4030. "~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"
  4031. "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"
  4032. "~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}"
  4033. "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~"
  4034. "|\\{|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~"
  4035. "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"
  4036. "~}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"
  4037. "{|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}|"
  4038. "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{"
  4039. "}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"
  4040. "{|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{"
  4041. "|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~|"
  4042. "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~|"
  4043. "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"
  4044. "~|]{}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"
  4045. "~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"
  4046. "~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~"
  4047. "}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~|]"
  4048. "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{}"
  4049. "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{"
  4050. "}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"
  4051. "{|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~}|"
  4052. "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"
  4053. "{|}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{"
  4054. "|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~}'{}"
  4055. "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"
  4056. "~|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~|"
  4057. "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{"
  4058. "}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{"
  4059. "}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"
  4060. "~|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~}"
  4061. "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~|"
  4062. "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"
  4063. "~}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{"
  4064. "}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{}"
  4065. "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~}"
  4066. "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"
  4067. "{}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"
  4068. "~}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{|"
  4069. "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"
  4070. "{}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~"
  4071. "|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~"
  4072. "|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~"
  4073. "|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~"
  4074. "}`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"
  4075. "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{"
  4076. "}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{"
  4077. "}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~"
  4078. "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"
  4079. "{|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"
  4080. "~|`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~}_"
  4081. "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{}"
  4082. "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{|"
  4083. "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"
  4084. "{}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~} "
  4085. "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~"
  4086. "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"
  4087. "~|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~"
  4088. "}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{}"
  4089. "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~|"
  4090. "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~}"
  4091. "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~"
  4092. "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"
  4093. "~}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~}"
  4094. "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~|"
  4095. "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~}"
  4096. "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"
  4097. "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~"
  4098. "}|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"
  4099. "{|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"
  4100. "{}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~@{"
  4101. "|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~"
  4102. "}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"
  4103. "~}_{}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~}"
  4104. "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{|"
  4105. "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~|"
  4106. "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{}"
  4107. "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~}`"
  4108. "{|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"
  4109. "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~"
  4110. "|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~ "
  4111. " 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~}"
  4112. "|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{}"
  4113. "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"
  4114. "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~"
  4115. "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~|{"
  4116. "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~}`"
  4117. "{}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~"
  4118. "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{|"
  4119. "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~"
  4120. "|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{}"
  4121. "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"
  4122. "~}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"
  4123. "~|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~}["
  4124. "{}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"
  4125. "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{"
  4126. "}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{}"
  4127. "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~"
  4128. "}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~"
  4129. "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"
  4130. "~}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"
  4131. "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{|"
  4132. "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"
  4133. "{|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{}"
  4134. "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"
  4135. "{}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{}"
  4136. "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~|"
  4137. "[{}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~}|&{|"
  4138. "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~\\"
  4139. "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"
  4140. "~|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{}"
  4141. "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~"
  4142. "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{}"
  4143. "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~}"
  4144. "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~| {{}"
  4145. "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~}]{"
  4146. "|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|}"
  4147. "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"
  4148. "~|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"
  4149. "{}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{"
  4150. "}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"
  4151. "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~"
  4152. "|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~|"
  4153. "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"
  4154. "{}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}"
  4155. "|${|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",
  4156. "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|_{}"
  4157. "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~|"
  4158. "_{|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~"
  4159. "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{}"
  4160. "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~|"
  4161. "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{"
  4162. "}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{"
  4163. "}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{"
  4164. "|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~|"
  4165. "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~"
  4166. "}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~| "
  4167. "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"
  4168. "~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|"
  4169. "}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~"
  4170. "|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~"
  4171. "|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~"
  4172. "|#{|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~}"
  4173. "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{|"
  4174. "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~}"
  4175. "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"
  4176. "~}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{}"
  4177. "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{}"
  4178. "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"
  4179. "~|{}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"
  4180. "~}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"
  4181. "~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~| ]{|}"
  4182. "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{"
  4183. "}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"
  4184. "~}`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~"
  4185. "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~"
  4186. "}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~"
  4187. "`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{"
  4188. "|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"
  4189. "{|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~"
  4190. "}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~^{"
  4191. "}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~}"
  4192. "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~}"
  4193. "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~"
  4194. "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"
  4195. "~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"
  4196. "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{"
  4197. "|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"
  4198. "{}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{}"
  4199. "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"
  4200. "{|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~"
  4201. "|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~"
  4202. "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{|"
  4203. "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~}"
  4204. "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"
  4205. "~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~]"
  4206. "{}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{"
  4207. "|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~|"
  4208. "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~}"
  4209. "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~|_"
  4210. "{}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~|"
  4211. "^{|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~}"
  4212. "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~"
  4213. "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{"
  4214. "}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"
  4215. "~}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"
  4216. "~}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~|"
  4217. "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{|"
  4218. "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~}"
  4219. "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{|"
  4220. "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{"
  4221. "|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"
  4222. "~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~}"
  4223. "]{}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{|"
  4224. "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|"
  4225. "}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{"
  4226. "}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{"
  4227. "|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`~|"
  4228. "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"
  4229. "{}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{|"
  4230. "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~| "
  4231. " 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~}"
  4232. "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"
  4233. "{}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~"
  4234. "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{|"
  4235. "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~"
  4236. "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~"
  4237. "|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~"
  4238. "|^{|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"
  4239. "{}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~}\\"
  4240. "{}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~"
  4241. "} 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"
  4242. "~|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"
  4243. "~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"
  4244. "~}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"
  4245. "~}|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{"
  4246. "|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{}"
  4247. "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"
  4248. "{|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~"
  4249. "}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{"
  4250. "|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"
  4251. "~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{"
  4252. "|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~"
  4253. "}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"
  4254. "{}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~"
  4255. "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"
  4256. "~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~|"
  4257. "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~ "
  4258. "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{"
  4259. "}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"
  4260. "{|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"
  4261. "{}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~"
  4262. "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{}"
  4263. "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~"
  4264. "|[{|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~|"
  4265. " 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{|_~"
  4266. "|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{}"
  4267. "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~}"
  4268. "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"
  4269. "~| 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~"
  4270. "|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~|"
  4271. "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{"
  4272. "|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~}"
  4273. "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"
  4274. "{|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"
  4275. "~|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~"
  4276. "}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{}"
  4277. "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"
  4278. "~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{"
  4279. "|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"
  4280. "~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~|"
  4281. "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"
  4282. "~|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~|"
  4283. " 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~}"
  4284. "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"
  4285. "~|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"
  4286. "~|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~"
  4287. "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{"
  4288. "}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~"
  4289. "|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~}"
  4290. "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|"
  4291. "{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"
  4292. "~}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~"
  4293. "}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~"
  4294. "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"
  4295. "~|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"
  4296. "{|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"
  4297. "~|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~}"
  4298. "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"
  4299. "{|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~}"
  4300. "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{}"
  4301. "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"
  4302. "{|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"
  4303. "~}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"
  4304. "~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"
  4305. "~|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~"
  4306. "|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~"
  4307. "|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~|`{"
  4308. "}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{|"
  4309. "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~|"
  4310. "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{"
  4311. "|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"
  4312. "~|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{"
  4313. "}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"
  4314. "~}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"
  4315. "{}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"
  4316. "~| 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"
  4317. "~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~"
  4318. "}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~"
  4319. "|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"
  4320. "{|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{}"
  4321. "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"
  4322. "~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"
  4323. "{|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"
  4324. "{}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}"
  4325. "|^{}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"
  4326. "~|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"
  4327. "~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"
  4328. "~}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{}"
  4329. "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"
  4330. "{}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"
  4331. "~|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{}"
  4332. "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"
  4333. "~}\"{}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{|"
  4334. "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{|"
  4335. "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~"
  4336. "}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~}"
  4337. "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~|_"
  4338. "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"
  4339. "~|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~"
  4340. "|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{|"
  4341. "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~|"
  4342. "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"
  4343. "~|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~}"
  4344. "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"
  4345. "~}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{"
  4346. "}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"
  4347. "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"
  4348. "~`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~}"
  4349. "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~|"
  4350. "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~"
  4351. "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{"
  4352. "}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"
  4353. "{}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~}"
  4354. "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~}_"
  4355. "{|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"
  4356. "~|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~"
  4357. "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~}_"
  4358. "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"
  4359. "~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"
  4360. "~|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"
  4361. "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~"
  4362. "|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~"
  4363. "|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{"
  4364. "}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{"
  4365. "}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~"
  4366. "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"
  4367. "{|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{"
  4368. "}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{|"
  4369. "}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~"
  4370. "|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"
  4371. "~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{"
  4372. "}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~"
  4373. "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"
  4374. "~}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{}"
  4375. "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"
  4376. "{|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"
  4377. "{}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"
  4378. "~|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"
  4379. "~|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"
  4380. "~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{"
  4381. "|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~|`{"
  4382. "}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~} "
  4383. "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{|"
  4384. "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~}]{}"
  4385. "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{}"
  4386. "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~"
  4387. "|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{|"
  4388. "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~}"
  4389. "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{|"
  4390. "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~"
  4391. "|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~}"
  4392. "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~|#"
  4393. "{}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{"
  4394. "|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"
  4395. "{|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~}"
  4396. "|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~"
  4397. "|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~}"
  4398. "|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~}"
  4399. "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"
  4400. "~}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"
  4401. "{|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"
  4402. "{|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"
  4403. "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"
  4404. "}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~"
  4405. "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"
  4406. "|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"
  4407. "~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{"
  4408. "}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~}"
  4409. "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~|^{"
  4410. "}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~"
  4411. "}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~"
  4412. "} 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{|"
  4413. "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|}"
  4414. "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~}"
  4415. "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{}"
  4416. "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{"
  4417. "}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~}\\{"
  4418. "}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~"
  4419. "}\\{}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"
  4420. "~}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~"
  4421. "|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"
  4422. "~|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{}"
  4423. "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"
  4424. "~}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"
  4425. "~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~"
  4426. "}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~"
  4427. "}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~"
  4428. "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{}"
  4429. "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~}"
  4430. "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~"
  4431. "}[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{|"
  4432. "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~"
  4433. "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~| "
  4434. "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{"
  4435. "}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~"
  4436. "| 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{"
  4437. "|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"
  4438. "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{|"
  4439. "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~}${"
  4440. "}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"
  4441. "{}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~}["
  4442. "{|i~}[{|i~}[{|i~}[{|i~}/{|w~|y{|i~}Z{}i~|[{}i~|[{}i~|[{}i~|U{|v~|V{}w~}Mw~}xm~|^{|l~|{}w~|_{|l~|{}w~|_{|l~|{}w~"
  4443. "|_{|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~"
  4444. "}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"
  4445. "{|}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~"
  4446. "}|!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~}"
  4447. "]{|}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"
  4448. "~}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~}"
  4449. "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}"
  4450. "|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~|"
  4451. "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~"
  4452. "}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"
  4453. "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| "
  4454. "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~|"
  4455. "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{"
  4456. "|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{}"
  4457. "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"
  4458. "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~|\\"
  4459. "{}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~}"
  4460. "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{|"
  4461. "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}"
  4462. "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}"
  4463. "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~"
  4464. "|#{|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}|"
  4465. " 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"
  4466. "{|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"
  4467. "}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}"
  4468. "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~"
  4469. "}|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~"
  4470. "| ?{}w~ Cw~} .{}w~ @{|v~|d{}| Kv~| !u~| J{|w~}C{|w~O{|w~| 9w~} Iv~ bw~}9{|w~| X{|v~ rv"
  4471. "~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~} {"
  4472. "{x~| 0{|v~ ?{}y~} R{|} 5x~| O{|y~} &{|v~Uw~}D{|v~ Lw~| K{|y~| d"
  4473. "{}x~}P{}w~ >w~| D{|w~| .w~| ?{|v~}g{|x~| M{|v~ {|u~| K{|w~}Bw~|P{|w~| :{}w~} Iw~} bw~}9{"
  4474. "|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~"
  4475. "|Ax~}w{|x~} {{}y~} /v~| ?x~| f{|x~ M{} %{}w~|Uw~}D{}w~| Lw~| K"
  4476. "{|y~| d{|w~Pw~| ?{|w~ C{}w~ .{|w~ ={|u~}|l{|u~| N{}v~ {{|u~| L{|q~}H{}x~}V{}q~| :v~| Iw~}"
  4477. " 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~}| "
  4478. " 3{|w~|Ax~}w{|x~} {{|x~| 0v~}m{} N{|x~ e{}y~} Rv~Tw~}Dv~ S{}x~x{|w~| "
  4479. " K{|y~| c{}x~}R{}x~} >{|x~| Cw~} .{|x~| ;{}t~}|sy|}t~| N{|v~} y{|u~| M{|q~}H{|w~V"
  4480. "{}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~} "
  4481. " )v~}Iy~} gw~|Q{|y}v~y}| 1{|w~|Ax~}w{|x~} yx~| 0{}v~|p{|~} N{|x~| f{|x~ "
  4482. " S{}w~}Tw~}E{}w~} S{}x~|y{|w~ J{|y~| bw~|Sw~| >{}y~} K{}y~} 9{|p~x}q~}| N{|u~"
  4483. "| 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~|#{|"
  4484. "w~| j{}w~ s{}w~Uw~} )v~}Iy~} gw~| W{|w~|Aw|vx| y{|x~} 0{|u~|s{}x~} N{|x~| "
  4485. " f{|x~| U{|v~Sw~}F{|v~ R{|x~}y{}w~ J{|y~| b{|x}|T{|x}| w{}g~}| Q"
  4486. "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~}"
  4487. "W{|w~|#{|w~| j{}w~ s{}w~Uw~} )v~}Iy~} gw~| W{|w~| :{|}w|}w~| /t~y}x|y}v~} U{|}|x{|w~| "
  4488. " f{}x~| W{|}v~}Sw~}H{|}v~} Qq~| J{|y} *{|}l~}| O{}q"
  4489. "~ tt| `{|i~} Lr~| aw~}9{|w~| `{}q~ l{}s~}Tw~|U{|s~}| 9v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~"
  4490. "} )v~}Iy~} gw~| W{|w~| :{|q~ .{|i~} U{|q~ ly}w|}w~| [{}q~Rw~}"
  4491. "L{}q~ P{}r~ M{|y}u~y}y| L{}r~| R{|j~} Ks~} `w~}9{|w~| "
  4492. " `{}r~| jy|v}|Tw~|U{|u}| 6v~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} )v~}Iy}| gw~| W{|w~| :{|r~| "
  4493. " -{|k~}| U{|r~} l{}r~} Z{}r~|Rw~}L{}r~| O{}t~ "
  4494. " k{}t~} -{|`}| `{|}m~}| Jt~} _w~}9{|w~| `{}s~| :w~| cv~ S{|w~}W{|w~|#{|w~| j{}w~ s{}"
  4495. "w~Uw~} )v~} d{|w~| 9y}w~y} ){}o~}| S{|}u~}| k{}r~ Y{}s~|Qw~"
  4496. "}L{}s~| M{}w~} j{}w~}| +{}`~} ]{|x}v~y}| Gw~y} ]w~}9{|w~"
  4497. "| `{}v~}| 8w~| cv~ S{|w~}W{|w~|#{|w~| j{}w~ s{}w~Uw~} g{|w~| 8{|}v~y}| Ly| "
  4498. " g{|y}w~}| X{}v~}|Ow~}L{}v~}| Iy| "
  4499. "l{}`~} Ww~| "
  4500. " L{}`~} Ww}| "
  4501. " r{" };
  4502. // Define a 104x128 binary font (huge sans).
  4503. static const char *const data_font_huge[] = {
  4504. " "
  4505. " "
  4506. " "
  4507. " "
  4508. " "
  4509. " "
  4510. " "
  4511. " "
  4512. " FY AY "
  4513. "'Z ;W @Y @Y 'Z Y @Y (Z :Y ?Y (Z 0Y ?Y (Z >X "
  4514. " "
  4515. " "
  4516. " "
  4517. " "
  4518. " )X AX '\\ )XAV 7YDY -] BY BY '[ +YEY 2X AY (\\ -YDY 'XAU 3Y AY (\\ )XAV 8YD"
  4519. "Y LY AY (\\ ,YEY #Y "
  4520. " "
  4521. " "
  4522. " "
  4523. " (X CX '^ +[CU 6ZEY .` C"
  4524. "X CY '] -ZEZ 2X CY (^ .ZEZ )[CU 2Y CY (] *[CU 7ZEZ LY CY (] -ZEZ %Y "
  4525. " "
  4526. " "
  4527. " "
  4528. " "
  4529. " 'Y EY '^ ,^FV 6ZEY /b CX DX '_ .ZEZ 2Y DX '_ /ZEZ +_FV 1X CX (_ ,^FV 7ZEZ "
  4530. " KX CX (_ .ZEZ &Y "
  4531. " "
  4532. " "
  4533. " "
  4534. " %Y GY '` .aHV 6ZEY 1e DY FX"
  4535. " 'a /ZEZ 1Y FX '` /ZEZ +aHV 0X EX '` .aHV 7ZEZ JX EX (a /ZEZ &X "
  4536. " "
  4537. " "
  4538. " "
  4539. " "
  4540. " #X GX 'XNX 0dKW 6ZEY 1f DY HX &WMX 0ZEZ 0X GX 'XMW 0ZEZ ,dLX /X GX 'WMX 0dLX 7ZEZ"
  4541. " IX GX 'WMX 0ZEZ 'X :T "
  4542. " "
  4543. " "
  4544. " "
  4545. " ;X IX 'XLX 1o 5ZEY 2ZLY "
  4546. " CX IX &WKW 0ZEZ /X HX (XLX 1ZEZ ,o .Y HX (WKX 1o 6ZEZ IY IY (WKW 0ZEZ (X <Z "
  4547. " "
  4548. " "
  4549. " "
  4550. " "
  4551. " =X KX 'XJX 3WKd 5ZEY 3XGX CX JX 'WIW 1ZEZ .X JX (XJX 2ZEZ -WKd -X "
  4552. "IX (WIW 2WKd 6ZEZ HX IX (WIW 1ZEZ )X =^ "
  4553. " "
  4554. " "
  4555. " "
  4556. " >X MX &WH"
  4557. "W 3VHa 4ZEY 3WDW CX LX 'WGW 2ZEZ -X LX 'WHW 2ZEZ -VHa +X KX (XHW 3VHa 5ZEZ GX KX (WGW 2ZEZ )X "
  4558. " ?b "
  4559. " "
  4560. " "
  4561. " "
  4562. " ?W MW &WFW 4VF^ 3ZEY 4WBV BW MX 'WEW 3ZEZ ,W M"
  4563. "X 'WFW 3ZEZ -VF^ )X MX 'WFW 4VF^ 4ZEZ FX MX 'WFW 3ZEZ *X ?d "
  4564. " "
  4565. " "
  4566. " "
  4567. " "
  4568. " ?W X 'WDW 5UC[ 2ZEY 4VAV AW X &WDW 4ZEZ +W NW 'WDW 4ZEZ -UC[ 'W MW 'WDW 5UC[ 3ZEZ "
  4569. "EW MW 'WDW 4ZEZ +X ?f "
  4570. " "
  4571. " "
  4572. " "
  4573. " @X \"X 'WBW 6UAW 0ZEY 4V@V B"
  4574. "X !W &WBV 4ZEZ +X !W 'WBW 5ZEZ .VAW $W W 'WBW 6UAW 1ZEZ DW W 'WBV 4ZEZ +W >f "
  4575. " "
  4576. " "
  4577. " "
  4578. " "
  4579. " ?X #W 'W@W U?V AX #W &W@V NX #W &V@W 9W \"W 'W@V .W "
  4580. "\"W 'W@V !W >XHX "
  4581. " 3Y "
  4582. " "
  4583. " "
  4584. " 6W $W &V>V U?V @W $W &W>V "
  4585. " NW $X 'V>V 8W $X (W>V /X $W 'W>V #W >XFX "
  4586. " 5Z "
  4587. " "
  4588. " ,Z "
  4589. " GZ "
  4590. " #U?V NY 7Z ,X CVCW MY "
  4591. " 7Z ,X $Z 7Z ,X >Z 6Y ,X 4Z 7Y +W 7Y @Z "
  4592. " "
  4593. " +Z "
  4594. " "
  4595. " HY \"U?V "
  4596. " MY 8Y ,Y CVBV LY 9Z ,Y #Z 9Z ,Z >Z 8Y ,Y 3Y 8Z ,Y 9Y "
  4597. " ?Z "
  4598. " *Y "
  4599. " "
  4600. " IY !U?V "
  4601. " LY :Y ,[ $R>U ,V@V MZ :Y +Z #Y 9Y +Z ?R"
  4602. ">U 8Y 9Y +Z %S?U HY :Z ,[ ;Y ?[ "
  4603. " "
  4604. " )Y "
  4605. " 8U "
  4606. " 9Y V@U JY <Y"
  4607. " +[ 'XAU ,V@V LY ;Y +\\ #Y ;Y +\\ CXAU 7Y ;Z ,\\ )XAV HY ;Y +[ <Z "
  4608. " ?U ;T $W /W "
  4609. " 8e !f LY Y LX "
  4610. " L] :Y <Y NX 0X >Y @Y /X 0Y K` .X "
  4611. " ^ =ZEY @Y "
  4612. " NVAV <P -X +Y =Y +] )[CU 7YDY 4V@V KY ="
  4613. "Y +] ,YDY 5Y =Y *] .YDY 5[ M[CU 6Y <Y ,] *[CV 7YDY Y =Y +] ,YEZ !Y =Y FYDY 8X "
  4614. " EU :T %W .X "
  4615. " 9e !f KY !Y LY \"a :Y "
  4616. "<Y NX 0X >Y E^ /X 0_ %f 1] 'c "
  4617. " @ZEZ AY MV"
  4618. "CW <R 4a .Y >X *^ +]DU 7ZEZ 5U>U JY ?Y *^ -YEZ 4Y "
  4619. " ?Y *^ .ZEZ 5[ ]DU 5Y >Y +^ ,]DU 6ZEZ Y ?Y +_ .ZEZ \"Y <Y FYEZ :[ FU "
  4620. " 7Y -T 7W#W <Y 9X -W DU KY HZ \"\\ 4Z M[ \""
  4621. "Y LZ +\\ 8] >Z G[ G\\ @e !f JX !Y "
  4622. "LY %d :Y <Y NX 0X >Y Ha /X 0b *j L] D_ "
  4623. " +g A[ LY 8Z -ZEZ \"Y 1o )V FX NZ FY "
  4624. "%Y ,X NX*Z NW 3WEW H\\ #[ !Z \"[ \"[ \"[ G[7T 8g 0Y "
  4625. "@Y +_ ,_FV 7ZEZ 5U>U IY @Y +` .YEZ 3X ?X *` /ZEZ 4[:P 8_FV 4X ?Y +` ._EU 6ZEZ NX @Y *_ .ZEZ #Y ;Y"
  4626. " FYEZ ;] GU <b 1T :]'X @b >W ,X "
  4627. " FV a \"d -g >d (d +b %b 4f Bg Ie \"e \"h "
  4628. " Ge !f IX \"Y LY &e :Y <Y NX 0X >Y Jc /X 0c "
  4629. " -n $g I` .j >a ;e HU .U +b Ac 2ZEZ 'b "
  4630. " 5o -] Na (c KY .Y #_ 8Y!W'Y\"X.c$X 3XGX Mf -e +d "
  4631. ",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"
  4632. "X AY +XNW .`GV 6ZEZ NY AX *XNW /ZEZ $Y :Y FYEZ <_ IU (Q LZ 4Z2Z 1Q "
  4633. " &g %Z +XCX MT <a)W Ah $X HX +X GV GX 3e )_ /j 4n L] ?y /i C~S =i 0g "
  4634. " +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'~^"
  4635. " @e 3X Ff )\\ MY #Y LY (g :Y <Y NX 0X >Y Kd /X 0e 0p "
  4636. " (m Lb 1m ,\\ 5~S E~R Ah 'Z :~]+[;Z;Z Ik LW DX DW /i ?Y(Y 4h 5ZEZ"
  4637. " ,\\ ,h 7\\ -o .` $f -h NY No %_ %c @_\"X-_\"W0h&W .\\ $\\ \"\\ #\\ #\\ )g 5~a Lm D~S I~S "
  4638. "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"
  4639. "X CY *WLW /YEZ 2Y CY *WLW 0ZEZ 3[AW :bHV 3Y BX *WLW 0bHV 6ZEZ MY CX *XMX 0ZEZ $X 9Y FYEZ "
  4640. " =a M~i 7U (Q N_ 9_8_ 3R )k 'Z +XCX +X@X 4T >e,X Cl &X IX *X GV "
  4641. " 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"
  4642. " 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 "
  4643. " Le /X 0e 1r +r c 3o -\\ 5~S E~R Dn *Z :~]+[;Z;Z Ko "
  4644. " Y EX EY 2m @Y)Y 6l 7ZEZ 0e 2k >e 1o 0c 'j /i X !r (b 'g Eb\"W0c#X0i(W -"
  4645. "\\ $] #\\ $] #\\ (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"
  4646. " 1X DX *WKW /WMYJV 6ZEZ 5V@V GY EY *WKX 0YEZ 1Y EY *XKW 1ZEZ 2[EZ :WMZKV 1Y DX *WKX 1WLYKW 6ZEZ L"
  4647. "Y EY *WKW 0ZEZ %X 8Y FYEZ >c M~h 7T (S !a <b:b 6S %| $o "
  4648. ")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!{ :"
  4649. "~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 "
  4650. "LY )h :Y <Y NX 0X >Y >X 8f /X 0f 3t -s c "
  4651. " 4q /^ 6~S E~R Fr ,Z :~]+[;Z;Z Ms #[ FX F[ 4n @Y*Y 6m 7ZEZ 3k 5l Bk 4o 1f )k 0k #"
  4652. "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 "
  4653. " 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"
  4654. "X EX )WJW 2WJZNW 5ZEZ KX FY *WIW 1ZEZ &X 7Y FYEZ ?d M~h 8U )T #e ?d=e 8U "
  4655. " *~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"
  4656. " 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%~^ "
  4657. "@e 2X Gf +a MX %Y LY *i :Y <Y NX 0X >Y >Y 9f /X 0g 5v "
  4658. " 0u d 6_K_ 0^ 6~S E~R Gu .Z :~]+[;Z;Z w &] GX G] 6U &o ?Y+Y 7X )n 7ZEZ "
  4659. "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 "
  4660. "!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"
  4661. "Z /X GX )WHW 2ZEZ 0[M` ;VHb /X GY *WHW 3VHb 5ZEZ JX GX )WHW 2ZEZ 'Y 7Y FYEZ ?e M~f "
  4662. " 7U )U %g Bh@g :W .~T 't +Z +XCX ,X@X 3T Ak1X Er (X JX 'X IV HX 8q"
  4663. " =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{ "
  4664. ";{!~d'Z(Z(Z Z;Z0^0Z3Z.[#[*Z$~^ @X %X :Y ,c MX &Y LY +^ .Y <Y NX 0X >Y >Y "
  4665. " :] %X &] 5]C\\ 1v Nc 7\\D\\ 1_ 6~S E~R Iy 0Z :~]+[;Z;Z!y (_ H"
  4666. "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 "
  4667. " ._ &_ %_ '_ %_ '` 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"
  4668. "J_ 2X IX )WGW 2VG` 5ZEZ 4XFX CX IX )WFW 2YEZ .X IX )WFW 3ZEZ /j 8VG` -X HX *WFW 4VG` 4ZEZ IX IX "
  4669. ")WGW 2ZEZ 'X 6Y FYEZ ?XKX M~f 7T )W 'i DiAi ;X 1~V (w -Z "
  4670. "+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"
  4671. " 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 "
  4672. "LY +[ +Y <Y NX 0X >Y >Y :[ #X #Z 6\\?[ 2v F\\ "
  4673. " 8Z@[ 2` 7~S E~R J{ 1Z :~]+[;Z;Z#} +` HX Ia 8U (q >Y-Y 6X +p 7ZEZ 9bMb ;U@Y JbMb :"
  4674. "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 "
  4675. "!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"
  4676. "W 2YEZ -X KX (WFW 3ZEZ .f 5UE_ ,X JX )WFW 4VF_ 4ZEZ HX KX )WEW 3ZEZ (X 5Y FYEZ @YJW M~"
  4677. "e 7U *X (j EkCk =Y 3~X )x -Z +XCX ,W?X 3T BYEY3X Ft (X KX %X JV "
  4678. " 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"
  4679. "| E~P I} ?}!~d'Z(Z'Z\"Z9Z1^1Z1Z0Z [,Z#~^ @X $X ;Y .g MW 'Y LY +Y )Y <Y NX 0X >Y "
  4680. " >Y :Z \"X \"Z 7[=Z 3aE[ E[ 9Z>[ 3` 7~S E~R L~ 2Z :~]+[;Z;Z$"
  4681. "~P -b IX Jc 9U )r >Y.Y 5X ,]DX 7ZEZ ;\\>\\ <R;X M]>\\ 0XDX ,R=Y MX (X %hEW (SG"
  4682. "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"
  4683. " <~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"
  4684. " KX )WDW 5UD] 3ZEZ GW LX (VCW 4ZEZ )X 4Y FYEZ @XIX M~d 7U *Y *l GmDl ?[ "
  4685. " 6~Z *`C\\ -Z +XCX ,W?W 2T CYCY5X E]CZ (X LX $X JV IX 9]E^ @m 7aGb B^Ec ,b ?y "
  4686. "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"
  4687. "B_!~d'Z(Z'Z#[9Z2_1Z0Z2[ N[.Z\"~^ @X $X ;Y /i MW (Y LY ,Y (Y <Y NX 0X >Y >Y "
  4688. " :Y !X !Y 8[;Z 1\\ 0\\:U D[ ;Z<Z 4b 8~S E~R M~R 4Z :~]+[;Z;Z%bCb "
  4689. "/d JX Ke :U )]BW =Y/Y 5X ,[?U 3Z8[ &W NZ7Z 2XBW EX LW )X %iEW KV -Y?Y @W&X"
  4690. "!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"
  4691. "#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 "
  4692. " 9X MW (WCW MX 3Y GXHW M~d 8U *[ +m HnFn A] 9~\\ +^=Y"
  4693. " -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]@] "
  4694. " <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`"
  4695. "2Z0[4[ LZ/[\"~^ @X #X <Y 0\\N] NX )Y LY ,Y (Y ;X NX 0X >Y >Y ;Z "
  4696. "!X !Y 8Z9Y 6d 4[5R CZ ;Y:Z 5b 8~R D~Q MbAb 8` =~]+[;Z;Z&`=` 1f KX Lg "
  4697. " ;U *\\=T =Y0Y 4X ,Z;R 5Z3Y &W !Y3Y 3W@W EW LX *W %jEW KV -X=X @W'X W'X EX1W ,b "
  4698. "*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"
  4699. " 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 "
  4700. " NW 2Y N\\ #[ \"\\ #\\ #[ MXHW L~b 7U +\\ ,n IoGp C_ ;~] ,]:X -Z "
  4701. "+XCX -X@X 8c LX@X7X E[:T (X MX \"X /TAVAT .X :\\?\\ Am 7Y9] CT4] .c ?Y J]8S Z E\\;\\ E]=[ "
  4702. " <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."
  4703. "Z4Z JZ0Z \\ ?X #X <Y 1\\L] NX *Y LY ,Y (Y 8X >Y >Y ;Y X !Y "
  4704. " 8Y8Y 6f 6Z2P BY <Z9Z 7c 7\\ Z (`;` >j BZ(Z+[;Z;Z'_9_ 3h LX Mi <"
  4705. "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 "
  4706. "*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"
  4707. "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 ,"
  4708. "e +d +d ,e #XHW LZ#Z 7U +] -o KqHp C_ <c 2]7V -Z +XCX -W?X <l#X?X7W E[7R "
  4709. "(X MX \"Y 0VCVCV .X :[<[ B\\IZ 7V5] DQ0] 0XNZ ?Y K\\4Q !Z E\\9\\ F\\;[ =U8[ DdAc =d <Z5^ "
  4710. "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 "
  4711. " <Y 2\\J] NW *Y LY ,X 'Y 8X >Y >Y ;Y X X 9Z7X 6g 7Y"
  4712. " #Z =Y8Z 7d 7[ Z )_7_ Bp EZ(Z+[;Z;Z(^5^ 5j MX Nk =U +[7P <Z3Y 3X -Y "
  4713. " 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 !"
  4714. "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^"
  4715. " 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 "
  4716. " 7U +_ /p KrJr Ea >` .\\5U -Z +XCX -W?W =r'X>W8X EZ ;X NY !X 1XDVDX 2X "
  4717. " &X ;[;[ BWDZ 7T2\\ \"\\ 1XMZ ?Y L\\ 2Z E[7[ G\\9[ >S5[ F`7` ?YNY <Z3\\ F]-W'Z0` IZ MZ )^+W$"
  4718. "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"
  4719. "W +Y LY ,X 'Y 8X >Y >Y ;Y X Y :Y6Y 7i 9Y \"Y "
  4720. " >Y6Y 7YNY 6[ !Z *^3] Dt GZ(Z+[;Z;Z)]2] 6l NX m >U +Z !Y4Z 3X -Y NW(W (W "
  4721. " &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"
  4722. " 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 "
  4723. " 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 "
  4724. " 7U ,a 0q LrJr Fc A_ ,\\2S -Z +XCX .X@X ?u(W=X:X DY :X NX Y 2ZFVFZ 2X "
  4725. "'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"
  4726. " 4Z9] ?Z 2YNX0YNY(d3Z#]0] JZ6\\ N\\/\\ NZ5\\ G[ <Y 9Z(Z%Z&Z6Z4XNX4Z,Z8Z FZ4Z [ <X \"X =Y 4\\F] #Y "
  4727. "LY -Y 'Y 8X >Y >Y ;Y X Y :Y6Y 7j :Y \"Y "
  4728. " >Y6Z 9YMY 5[ \"Z *]1] Hy IZ(Z+[;Z;Z)\\/\\ 8n X !o ?U ,[ Y5Y 2X -Y W&W )W 'W%W 9V"
  4729. "<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 "
  4730. "!Z !Z \"Z :Z/_!d2Y#]0]!]0]\"]0\\!\\/\\\"]0] F\\0]#Z(Z#Z(Z$Z(Z$Y'Y M[5[ EZ \"Y5X +P "
  4731. " %_K[ CY *r 9q 8r 9r 9q *X ;Z%Z >Q JT ,b 0q MsKs Ge "
  4732. "C^ *[0R -Z +XCX .X@X @v)X=X:W CY :X Y NX 1[HVH[ 1X 'X ;Z7Z 0Z 7P,[ ![ 3XLZ ?Y M["
  4733. " 1Z EZ4[ I[5Z ?P1Z I^-] BYLY =Z1[ H\\(T'Z-^ JZ MZ *\\$S$Z(Z :Z \"Z 4Z:] >Z 2YMX1XMY(YNZ4Z$].\\ JZ5"
  4734. "\\!\\-\\ Z4[ GZ ;Y 9Z(Z%Z'Z4Z5XNX5Z*Z:[ F[6Z [ ;X \"X =Y 5\\C[ #Y LY -Y 'Y 8X >Y "
  4735. " >Y ;Y X Y :Y6Y 7k ;Y \"Z @Z5Y 9YLY 5[ #Z +\\.] J| KZ"
  4736. "(Z+[;Z;Z*\\-\\ :p !X \"q @U ,Z NY6Y 1X -X W#V *W (W#W :U;V +X DW LW )mEW KV"
  4737. " /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#\\.]#].\\"
  4738. "#\\-\\#\\-\\#\\-\\ H\\.]$Z(Z#Z(Z$Z(Z$Y'Y L[6Z DZ \"Y5Y /[G[ "
  4739. " DY +u =u <u ;u =u ,X :Y&Z >S LU ,c 1q MtLt Hf E] )[.Q "
  4740. " -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 "
  4741. "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"
  4742. "(Z4Z5WLW5Z*[<[ DZ7[ !\\ ;X \"X =Y 6\\A[ $Y LY -Y 'Y 8X >Y >Y "
  4743. " ;Y X Y :Y6Y 7l <Y !Y @Y4Z :YLY 4[ $Z ,\\,] M~Q MZ(Z+[;Z;Z+\\+\\ <r \"X"
  4744. " #s AU ,Z MY7Y 1X -Y \"W!V :f (V!W ;U;V +X EX MW (mEW KV /W7W BW*W KW+X BW3X "
  4745. " +YLY .YKY -YLY .YKY -YLY .ZLY ;ZFZ 6\\%R NZ !Z Z !Z >Z !Z !Z \"Z :Z,^#YNZ3Y$\\,\\#\\,\\$\\,\\%\\+\\%\\,\\ MP"
  4746. " NP N\\-]$Z(Z#Z(Z$Z(Z$Y'Y KZ7[ Dq :Z4X /XC[ EY "
  4747. " -x @x >x ?x @x -X :Z'Z ?U MU -e 2q MtLt Ig E[ 'Z,P -Z +XCX .W?W By)"
  4748. "X<W;W CZ :X X MY .ZKVKZ -X (Y <Z5Z 1Z A[ !Z 4XKZ ?Y N[ 1Z DZ3Z IZ3Y NY K\\%[ EYKZ >Z0Z"
  4749. " 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"
  4750. " !\\ :X !X >Y 7[>[ %Y LY -Y 'Y 8X >Y >Y ;Y X Y ;Y"
  4751. "5Y 7UH_ <Z \"Z AY3Y ;YKZ 4[ %Z ,[*\\ N~S NZ(Z+[;Z;Z+[*\\ =\\NXM[ #X $\\MXN\\ "
  4752. " 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 /"
  4753. "YKY .ZLZ /YKY .ZKY /YKY <ZEZ 7\\#Q NZ !Z Z !Z >Z !Z !Z \"Z :Z+]#YMZ4Y%\\*\\%\\*\\&\\*[%[)[%[*\\ R!R [-_%Z(Z#Z"
  4754. "(Z$Z(Z$Y'Y K[9[ Ct =Y3X /U@[ \"Q EY .z B{ "
  4755. "B{ Az B{ /X :Z'Y >V U -g 4r NvNu Ji *\\ 5X.X 6\\ 7Z1Z M[ '[ 8Z +XCX /X@X C`MTL_)W;"
  4756. "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 "
  4757. "J[ 'Z)\\ LZ MZ ,\\ \"Z(Z :Z \"Z 4Z=] ;Z 2YLX3XLY(YMZ5Z%[([ LZ3[$\\)\\\"Z3[ IZ :Y 9Z(Z$Z)Z3Z6XLX6Z(Z>[ B[:Z !"
  4758. "\\ 9X !X >Y 8[<[ &Y LY -Y 'Y 8X >Y >Y ;Y X Y ;Y5Y "
  4759. "7RB] =\\ $Z BY2Y ;YJY 3[ &Z -[(\\!~U Z(Z+[;Z;Z,\\)\\ ?\\MXL[ $X %\\LXM\\ CU"
  4760. " ,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"
  4761. "KZ /YJY /YKZ /YJY /YJY =ZEZ 7[!P NZ !Z Z !Z >Z !Z !Z \"Z :Z*]$YMZ4Y%[([%[(['\\)\\'\\)\\'\\)[!T#T\"\\-`&Z(Z#Z("
  4762. "Z$Z(Z$Y'Y J[:Z Bw @Y6[ .Q<[ #S GY /`Da E`C"
  4763. "` 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\\"
  4764. "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"
  4765. "Y >Z0Z K[ &Z(\\ MZ MZ ,[ !Z(Z :Z \"Z 4Z>] :Z 2YLX3XLY(YLZ6Z&['\\ MZ3[$['[\"Z2Z IZ :Y 9Z(Z#Z*Z2Z7XLX7Z'[@[ @Z;"
  4766. "[ ![ 8X !X >Y 9[:[ 'Y LY -Y 'Y 8X >Y >Y ;Y X Y ;Y"
  4767. "5Y %\\ =] %Y BY2Z =ZJY 3\\ 'Z .\\'[#cLZLb!Z(Z+[;Z;Z,['[ @\\LXK[ %X &\\KXL\\ "
  4768. " 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 "
  4769. "0YIY /YJY 0YIY /YJZ 1YIY =ZEZ 8\\ NZ !Z Z !Z >Z !Z !Z \"Z :Z)\\$YLZ5Y&\\'['['\\(['['['['['[#V%V#[-a&Z(Z#Z(Z$"
  4770. "Z(Z$Y'Y IZ;Z Ay BY9^ G[ %U HY 0]<^ G^=^ F"
  4771. "^<] E]<^ G^=^ 1X 9Z)Z @Z \"U .i 5r NvNu Lm 2h ;X.X 7^ 7Y1Y N[ &[ 7Z +XCX /W?X D[GTC"
  4772. "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 "
  4773. "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"
  4774. " X ?Y :[8[ \"\\ 3YBZ \\ ,ZAY 4\\ &Y \"Z 0YAZ \"X >Y .Y3Y 3Z '\\ MZ )Z ;Z 2^ +Y ;Y "
  4775. "X Y 6Y /Y5Y $[ =` G^ !Z IZ M\\ #Y2Z =YIZ 3\\ (Z .[%[%aIZI`\"Z(Z+[;Z;Z-[%[ B\\KXJ["
  4776. " &X '\\JXK\\ H\\ 1Z ,U&V EY;Y /X ,Z 'V@jDV Gp +UDj?V >V<V +X GW )` $nEW KV /W7W "
  4777. "BW-X IW-X N`0W *YIZ 1YIY 0YHY 1YIY 0ZIY 1YIZ ?ZDZ 8[ MZ !Z Z !Z >Z !Z !Z \"Z :Z(\\%YLZ5Y&[&['[&[)\\&[)[%[)"
  4778. "[&[$X'X%[-b&Z(Z#Z(Z$Z(Z$Y'Y I[=[ Az CY;` 5\\ $] $\\ \"\\ #\\ $] 8\\/[ 3\\ '\\ #\\ \"[ \"[ \"[ &Z &[ !["
  4779. " #\\ #[ ![ 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"
  4780. ".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"
  4781. "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'["
  4782. "$[ NZ2Z%[%[#Z2[ JZ :Y 9Z(Z#[,Z1Z8XJW7Z%ZB[ >[>Z !\\ 7X X ?Y ;[6[ (e 7YE` (e 3aEY 8c 2r 5`DX GYEa (X NX "
  4783. "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` "
  4784. "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\\ "
  4785. " (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 )"
  4786. "Y +YHY 2YHZ 1YHY 2ZHY 1YHY 2ZHY ?ZDZ 9[ LZ !Z Z !Z >Z !Z !Z \"Z :Z'[%YKZ6Y'\\%[)[$[*[%[)[%[)[%[%Y)Z&[.d'Z(Z#"
  4787. "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"
  4788. "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_ "
  4789. "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 "
  4790. "!Z 0[ CY1Y JZ1Z 5\\ \\ 'Z!Z FY LZHZ @Z/Y L[ %Z&[ NZ MZ .[ NZ(Z :Z \"Z 4ZA\\ 6Z 2YKX6YKY(YKZ7Z'[$[ NZ"
  4791. "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"
  4792. "\\ 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"
  4793. "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[ ("
  4794. "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 "
  4795. "(Y ,ZHY 2YGY 2ZHZ 3YGY 1YHZ 3YGY @ZCZ 9[ LZ !Z Z !Z >Z !Z !Z \"Z :Z'\\&YJY6Y'[$[)[$[*[$[+[#[+[$[&[+\\([.e'Z("
  4796. "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 "
  4797. "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"
  4798. "Y3Y Z $Z 5Z +XCX 0X@X DYDT EW;W?X ?Y :X #Y IY %^ \"X )X <Z1Z 3Z @Z !Z 8XGZ ?Y !Z"
  4799. " 0Z BY2Z JY0Z 8_ _ *Z!Y DX LYFY @Z/Y M[ $Z&[ NZ MZ .[ NZ(Z :Z \"Z 4ZB\\ 5Z 2YJX7XJY(YJZ8Z([#[ NZ2Z&["
  4800. "#[$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\\ "
  4801. "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"
  4802. " 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"
  4803. " ;Z /[,[ FZ?Y -X +\\ +UBoBU LZ>W -UBnAU >W@W *X JX 'c 1Y1Y HnEW KV /W7W BW.W GW/X Lc5W 'Y ,"
  4804. "YFY 4ZGY 2YFY 3YGZ 3YFY 3YGZ AZCZ 9Z KZ !Z Z !Z >Z !Z !Z \"Z :Z&[&YJZ7Y'[#[*Z\"Z+[#[+[#[+[#[&[-\\'[/YM[(Z(Z#"
  4805. "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"
  4806. "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"
  4807. " #Z 5Z +XCX 0W?X EYCT EW;W@X >Z ;X #Y HX #Z X *X ;Z1Z 3Z @Z !Z 9XFZ ?Y \"Z /Z "
  4808. "BY2Z KZ0[ <b a -[\"Y BX MYFY @Z0Z M[ $Z%[ Z MZ .Z MZ(Z :Z \"Z 4ZD] 4Z 2YJX7XJY(YJZ8Z([\"[ Z2Z&Z\"[$Z2"
  4809. "[ 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"
  4810. "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"
  4811. "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"
  4812. " (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"
  4813. "c7X 'Y ,YFY 4YFZ 3YFY 4YEY 3YFY 4ZFY AYBZ :[ KZ !Z Z !Z >Z !Z !Z \"Z :Z&[&YIZ8Y([\"[+[\"[,[\"Z+Z!Z,[\"[%[/\\"
  4814. "&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"
  4815. " -\\ ,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."
  4816. "X 9a 6Y5Z!Y \"Z 5Z +XCX C~d&YCT EW;W@W =[ <X #Y HY $Z X *X ;Z1Z 3Z @Z !Z :YFZ ?"
  4817. "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![ "
  4818. "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"
  4819. "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"
  4820. "^.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"
  4821. "EXFU %Z )r ;Z -[G^G[ CZAY ,X )] /UC[>TAU NX;W )P9P =UAWAYAU >XDX )X LX HY 3Y1Y HnEW KV /W7W "
  4822. "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![-"
  4823. "[![-[!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 "
  4824. "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"
  4825. " 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"
  4826. " @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"
  4827. "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 "
  4828. "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"
  4829. "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"
  4830. " HU 1X 1U.U @SDXES $Z +t ;Z ,[JbJ[ AYBY +X (^ 2UCZ9QAU NW:W *Q:Q >VAW?XAU ?ZHY (X MX EX 4Y1Y HnE"
  4831. "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"
  4832. "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"
  4833. "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"
  4834. " CZ NU 1u 8m K~m\"w ?^C] CX.X 9b 7Z6Y X \"Z 4Z +XCX C~d&XBT EX=XAW ;[ =X $Y GY ("
  4835. "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"
  4836. "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"
  4837. " 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 "
  4838. " 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"
  4839. "(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 "
  4840. " 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 "
  4841. "!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"
  4842. " =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$"
  4843. "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"
  4844. "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"
  4845. "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[ #\\"
  4846. " 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 "
  4847. ";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"
  4848. "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 >"
  4849. "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"
  4850. "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("
  4851. "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"
  4852. " @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"
  4853. "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 "
  4854. "-j (X +X ;Z/Z 4Z AZ \"Z :XCZ ?YM_ 5ZE^ IZ >Y6Z IZ0[ 5Z \"[ Jj Ci ?\\\"X6\\2X#YBY BZ1Z MZ \"Z$[!Z "
  4855. "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"
  4856. "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\\"
  4857. ";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^"
  4858. " 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"
  4859. "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"
  4860. "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"
  4861. "$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"
  4862. "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"
  4863. "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 "
  4864. "/ZNVNZ *X ,X :Z/Z 4Z AZ #Z :XBZ ?o 9ZGc MZ =Z8[ HY0\\ 6Z \"[ Li >j C\\\"X8aGVBW$ZBZ CZ2Z LZ \"Z#Z!"
  4865. "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"
  4866. "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"
  4867. "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"
  4868. "~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 ("
  4869. "| ;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"
  4870. "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[*"
  4871. "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 "
  4872. "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"
  4873. "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\\ "
  4874. " =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"
  4875. "[ \"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 "
  4876. " LX BY JS4[ C`<\\ A\\5Q D[;` E[9Z 5Y 1\\<` G`<Z 2Y NX 0X=\\ .Y F_=[MV=[)`<[ D\\<\\ E`<[ E[;_ ;` 0Z3Q 5"
  4877. "Y .Y1Y HY1Y%Y<`<Y [?[ DZ2Y $[ 1Y X Y !cBc J[?YLX HY<] JX @Y?_ @[ '`<[ EZ4Z 5` :Y1Y\"Z Z#\\GYI\\ EZ:Z IY@"
  4878. "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"
  4879. " )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 :"
  4880. "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"
  4881. "(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_<"
  4882. "[ C[;[ B\\<\\ C\\<\\ C[;\\ C\\<\\ 3\\ 3\\<\\ FY0X HY0X GX0X GX0Y HY2Z H`<[ FY2Y ;X<X<X)X NX)YKZ?ZJX(X/ZKX)X"
  4883. " NX ;X 3Y2Z >~d#Z6U 3} :h G~k%~P EY5Y FX.X ;ZNY 6Y9Z!X *~R \"Z 0~d&YCT CXAXBW 5] "
  4884. " >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"
  4885. "[ \"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"
  4886. " 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 "
  4887. "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"
  4888. "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"
  4889. "_ >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@"
  4890. "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"
  4891. "$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["
  4892. ":\\ 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 "
  4893. " >~d&^7U 4~ 9f E~i%~R GY4Y FX.X ;ZNZ 7Y9Y!X )~R \"Z NW?W BYCT CYBXCX 6_ ?Y EZ 5ZI"
  4894. "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 "
  4895. " 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 "
  4896. " )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"
  4897. "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"
  4898. ".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="
  4899. "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"
  4900. "?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"
  4901. "[ 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\\"
  4902. " 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"
  4903. "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"
  4904. "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"
  4905. "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] "
  4906. "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"
  4907. " 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/"
  4908. "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 "
  4909. " 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 "
  4910. "\"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"
  4911. "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 "
  4912. " ;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 *~"
  4913. "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"
  4914. " 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("
  4915. "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"
  4916. "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#"
  4917. "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["
  4918. "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"
  4919. "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"
  4920. "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"
  4921. "\\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 "
  4922. "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 "
  4923. " 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"
  4924. "\"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 "
  4925. " '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?"
  4926. "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"
  4927. "[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"
  4928. "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="
  4929. "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$"
  4930. "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"
  4931. "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"
  4932. "~T$~g'~X KY1X GX.X <YKZ 7Z<Y W NZ 3Y NW?W @\\GT @jCW 7f CZ DY 7VCVCV 1X .X "
  4933. "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"
  4934. " 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"
  4935. " 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"
  4936. "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("
  4937. "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"
  4938. " 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"
  4939. " 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"
  4940. " 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["
  4941. "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 "
  4942. " 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"
  4943. "^ 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 "
  4944. "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"
  4945. "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"
  4946. " 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 =\\$\\"
  4947. " =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"
  4948. "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/"
  4949. "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 "
  4950. "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"
  4951. "\\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"
  4952. "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="
  4953. "\\?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] &_ $"
  4954. "\\ (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 "
  4955. "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"
  4956. " 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 "
  4957. "%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"
  4958. ";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"
  4959. "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"
  4960. "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"
  4961. "[ 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"
  4962. "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 "
  4963. "\"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"
  4964. " 9Y 4Y0Z GZ2Y 5Y NX 0XG[ #Y FZ4Z3Y+Z2Y JZ0Z IZ0Y MZ1Z ;Z *Y EY .Y1Y EY8Z NYAXKXAY FZL[ 9Y9Y ([ +Y MX NZ 4b,"
  4965. "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"
  4966. ";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 "
  4967. "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"
  4968. "~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"
  4969. " 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"
  4970. "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 "
  4971. " 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` "
  4972. " =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"
  4973. "$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"
  4974. "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"
  4975. " 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"
  4976. "@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^"
  4977. "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"
  4978. "[ 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"
  4979. "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 /"
  4980. "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"
  4981. " 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"
  4982. " 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"
  4983. "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 *[ +"
  4984. "[ 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 "
  4985. "-[ 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="
  4986. "g5T -X ,o 5k 1Y1Y >dEW 3Z Nl ._ ,ZCZ'lEX6\\>mEWDVCZBkAX6] LY8Y BZ9Z AY8Y BZ9Z AY8Y BZ9Z!Z;~Q3Z H~Q "
  4987. "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"
  4988. " 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"
  4989. "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 "
  4990. "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"
  4991. " 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 "
  4992. "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"
  4993. "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[ "
  4994. ")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"
  4995. " 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",
  4996. "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"
  4997. "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/"
  4998. "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 "
  4999. ">X .Y;Y JZ 1Y?U 6~\\(~e'~]\"~X LX.X HX.X >YFY 7ZAZ N~m 4Z 3Y !W?X 9p +XCi0ZC\\9X "
  5000. " :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\""
  5001. "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 "
  5002. " >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 "
  5003. "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"
  5004. "/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 "
  5005. " JZCZ 8X &T=WIZ6T -X ,o 3e -Y1Y :`EW 3Z Nl (ZCZ)lFW5UNV>mFWCQ;XAe>X6UNW CY 4Y7Z DZ7Y BZ8Z CY7Z CZ7"
  5006. "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[ "
  5007. "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"
  5008. " 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"
  5009. " ?ZFY 6YAZ N~m 4Z 3Y !W?W 6p -WCk1ZB\\;Y :Y CZ %V <~e NX 6Z.Y 4Z M\\ J] EY9Z "
  5010. " 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"
  5011. "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"
  5012. " 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"
  5013. "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["
  5014. "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 "
  5015. " 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"
  5016. "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 "
  5017. " 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"
  5018. "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"
  5019. "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"
  5020. "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 '[ "
  5021. "\"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"
  5022. " 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 "
  5023. "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 "
  5024. "NU M[CZ 6X &T=WFY8T -X EY1Y 1WEW 3Z 7ZC[ 6W6ULV HX+W JX7ULW CY 5Z6Z EY5Y DZ6Z EY5Y DZ6Z E"
  5025. "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?"
  5026. "_ 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"
  5027. "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"
  5028. "CZ L~k :y KY \"X@X 0m 1WCYEY3Y>\\=X 9Y BY %V <~e =l X 5Z.Y 4Z \\ E[ GY8Z JZ I]"
  5029. "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"
  5030. " 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"
  5031. " 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 "
  5032. "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 "
  5033. "BX C~q CX DX 4Z?U -Z (W2W 2Z 'Z7X ITDX U MZCZ 5X &U>WEY9T -X EY1Y 1WEW 3Z 6ZCZ 7X7"
  5034. "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 "
  5035. "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"
  5036. "-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 "
  5037. " LZ -YBU 5~^*~h%~U J~R IX.Y IX.X @ZDY 6YCZ LW 'y JY \"W?X ,j 3WCYCY4Y=\\>X 9Y CZ"
  5038. " %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"
  5039. "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 "
  5040. "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 ,[ "
  5041. "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"
  5042. "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>"
  5043. "WDX:U -X EY1Y 1WEW 3Z 5YBY 7W6UKV IX*W KW6UKW CY 6Z4Y FZ5Z FZ4Z GZ4Y EY4Z GZ4Y%Y8Z <[ IZ !Z "
  5044. " 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{ "
  5045. "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 "
  5046. "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 "
  5047. " (y JY \"W?W (h 5XCXAX5Z<\\@Y 9Y CZ $T ;~e =l X 5Z/Z 4Z \"\\ AZ IX6Z JZ I\\1[ 4Z 8Z3Z AmKZ"
  5048. " ?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"
  5049. "^ 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"
  5050. " 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"
  5051. " 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"
  5052. " DX 4Z?U -Z (W2W 2Z &[9X IUEX T s AXAY 4X &U>WCX;U -X EY1Y 1WEW 3Z Is 0YAX 8W6UJV IW)W"
  5053. " 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 "
  5054. "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"
  5055. " 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[ "
  5056. "+YCT 5~`,~i$~S H~P HX.Y IX.X @YCZ 7ZDY KX )y HX #X@X (TNc 6WCX@X5Y:\\AX 8Y CZ :~e"
  5057. " =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"
  5058. " :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."
  5059. "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"
  5060. "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["
  5061. " /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"
  5062. " -X EY1Y 1WEW \"s 1ZCZ 9X7UIV JX)W LW7UIW CY 6Y2Y HZ3Z GY2Y HZ3Z GY2Y HZ3Z'Z8Z <[ IZ !Z Z !Z"
  5063. " >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"
  5064. "/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 "
  5065. " 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 "
  5066. ")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 "
  5067. " +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"
  5068. "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"
  5069. " Y-X ;Y !m 2Y .Y1Y AZAZ GYGXEXGY >] .ZBY -[ 1e JX Ke LU4k IU8Y8T'Y2X AY0Y EX:[ FY-Z Ah 9Y1Y >XCZ 6YBY AY1Y&"
  5070. "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"
  5071. "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"
  5072. "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"
  5073. "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"
  5074. "-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 "
  5075. " MZ (XDU 5~`,~i\"~ D{ FX.Y IX.X AZBZ 7YEY IX +y GX 6~e 9TG] 8WBW>X6Y8\\DY 8Y CZ "
  5076. " 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"
  5077. "'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"
  5078. "]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 ."
  5079. "[ )_ 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"
  5080. "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"
  5081. "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"
  5082. " !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"
  5083. "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"
  5084. " @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 "
  5085. " Z 3X 6~e 9TF\\ 9WBX=W7Z7\\EX 7Y CZ KX Nl \"X 3Z/Z 4Z &\\ ;Z M~Z %Z I[0Z 6[ 9Z/"
  5086. "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["
  5087. " 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 "
  5088. "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 "
  5089. "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"
  5090. "\\ >X >[ \\ ?X DX 4Z?U -Z 'X6X G~W 9^@X GUDY$T Ns ?[CZ 8X %U?WAY?U ,X EY1Y 1WEW \"s 4"
  5091. "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"
  5092. "[/[ 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"
  5093. " 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["
  5094. "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"
  5095. "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\"[ "
  5096. "#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"
  5097. " 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"
  5098. "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 "
  5099. "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 "
  5100. "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~"
  5101. "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~ "
  5102. "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"
  5103. " 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"
  5104. " IX.X BZ@Y 6YGZ IY Y @~e 9TCZ ;WAX=X8Y4\\HX 6Y CY JX Mj !X 2Y/Y 3Z (\\ 9Z"
  5105. " 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)[ "
  5106. "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"
  5107. " 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"
  5108. " 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"
  5109. "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 "
  5110. " \"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"
  5111. "-[ 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 "
  5112. "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"
  5113. "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<"
  5114. "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 "
  5115. "#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"
  5116. "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"
  5117. "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"
  5118. "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"
  5119. "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"
  5120. ",~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 "
  5121. "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"
  5122. " 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"
  5123. " BY>Y 7ZIZ GY !Z A~e 9TBY <W@W;W8Z3\\KX 5Z DY JX 4X 1Z1Z 3Z *\\ 7Z M~Z %"
  5124. "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 "
  5125. ".[!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"
  5126. "\\ \"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"
  5127. " 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"
  5128. "X 7\\ JVFXFV J[(\\ KUEXFU %Y 3Y?U -Z %Y<Y /Z M`KY BUC[=SAU CZCZ <X #UAW>XCU *X EY1Y 1WEW"
  5129. " 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([\"[+["
  5130. "\"[,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"
  5131. " 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"
  5132. "*WG[0W+X LX =X (YFZ 4~d GYIU 2jLQLj*pNRNq Lt :q AX.Y IY0Y CZ>Y 6YIZ FX !Z A~e 9T"
  5133. "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"
  5134. "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\\"
  5135. " 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"
  5136. "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"
  5137. "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 "
  5138. "/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"
  5139. "~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"
  5140. " 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 "
  5141. " 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"
  5142. "RMo Jr 9q AX.Y HX0Y CZ>Z 7ZJY EY !Z 1X@X &TAY ?X?W;W8Z1\\NX 3Y DY JX 5Y 0"
  5143. "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"
  5144. "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"
  5145. "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"
  5146. "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^"
  5147. "%Z(Z Iw L[#['\\ 8X 9\\ JZHXHZ J[,\\ KYGXHY 'Y 3Z@U -Z $[B[ .Z NW $j @UCpBU @[D[ ?X \"UBW=XEU )X "
  5148. " 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&[&"
  5149. "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"
  5150. " !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"
  5151. "X)XJ[@[KX(XK[/X*X NX <X 'YHZ 3~d FXJU 0hKQKh(nMRMo Jq 7o @X.Y HX0X BY=Z 7ZKZ DY \"Z "
  5152. " 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"
  5153. "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"
  5154. "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"
  5155. " .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 "
  5156. "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 "
  5157. "%^F^ /Z X \"f >VBnCU >[D[ @X \"VCW<XGV )X EY1Y 1WEW I[D[ 5X8UBV!W)X LW8UBW FZ 8~Y!~Z"
  5158. " ~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 "
  5159. "?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"
  5160. ";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"
  5161. "m ?X.Y HY1X CZ<Y 6YKZ DY \"Z 1W?W %TAY @X>W;W7Y/c 2Y EY IX 5X /Z3Z 2Z .\\"
  5162. " 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"
  5163. "Z'[%[ MZ ,[%[#Z1[ N[ 8Y 8Z+[ AZJZ GZKW6WKZ NZ<[ 1Z 3[ EX CX KY NY2Z FZ0Y IZ %Z1[ IY =Y 4Z1Z GY1Y 5Y"
  5164. " 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\\ "
  5165. "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"
  5166. "[%[$\\ :X ;\\ H\\JXJ\\ H\\1\\ J\\IXJ\\ (Y 3Z@U -Z &x 0Z X c <UAmDV =[CZ AX !VDW<YHU (X E"
  5167. "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(\\%"
  5168. "Y6ZKY&[%[)\\&[)[%[)[%[)[%[ L\\>[ Ld.[&Z*Z!Z*Z\"Z+[\"Z+Z 8Z 2s <Y-Y NX1Z IY1Z IY2Z GY2Z HY2Z HX1Z.Y1Z 1Z $Y Y "
  5169. "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("
  5170. "X!X'YJW<WJX&XJW/Y(X!X ;X &YIY #[ LYLU .fJQJf&lLRLm Gn 4k >X.Y HY2Y CZ<Z 7YKY BY #[ "
  5171. " 3X@X %TAY @W=W;W7Z0b 1Y EY IX 5X /Z3Z 2Z /\\ 2Z )Z JZ FZ2Z 8Z 5Z/Z %Z Ki :j >W=X8ZC"
  5172. "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[;"
  5173. "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"
  5174. " 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"
  5175. "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 "
  5176. "2ZAU -Z 'z 1Z X Na ;V@jDV :ZCZ BX UDW;XIU 'X EY2Z 1WEW KZCZ 3X9U@V\"W*X LX9VAW H[ "
  5177. "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,[!"
  5178. "[,[\"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"
  5179. "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"
  5180. " -dIQId%kKRKk El 2j >X.Y HY2Y CY;Z 7ZMZ BZ #Z 3X@X %TAX @W<W;W7Z/a 0Y FY IX "
  5181. " 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"
  5182. " 4Z:] >Z 2Y;[;Y(Y5ZMZ&\\([ LZ +['[\"Z0[ Z 7Y 8[,Z ?YKZ EYLX6XLY [:[ 2Z 5\\ DX BX LY NY3[ F[2Z HZ %Y1"
  5183. "[ 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 "
  5184. "Y #gEf!Z7\\ BY0Y GX9Y BZ1Z FX \"Y1Y :XLZ 7Z9Z FY2Z%Z;\\<[)ZCYCZ GY9Y IZAXAZ L[1Y LZ1Z 3Y 3Y4Y KZ*Z E[ %Z )["
  5185. "/[ MdNZNd!Z(Z Ag B['[!\\ <X =\\ D\\LXL\\ D[4\\ F\\KXL\\ &Z 3ZAU -Z (| 2Z X L^ 9V?fBU 8ZCZ CX V JV "
  5186. " 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"
  5187. " !Z \"Z :Z)\\$Y5ZLY%[(\\'\\(['\\(['['['[(\\ M\\:[ Ma-[$Z,Z NZ,Z![,Z!Z,[ 8Z 2Z #Y-Y NX2[ IY2[ IY2Z GY3[ HX2[ IY2"
  5188. "[.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 ="
  5189. "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 "
  5190. " $[,P )W?X %TBY AX<W;W7[/_ /Y FY IX 6X -Z5Z 1Z 1\\ 1Z (Z K[ EY2Z 9Z 4Z0[ &[ F"
  5191. "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"
  5192. "LZ EZMX6XMZ Z8[ 3Z 6\\ CX BX LY NY3[ F[2Y GZ %Z3\\ HZ ?Y 3Z4\\ GY1Y 5Y NX 0XB\\ )Y FY3Y2Y+Y1Y IZ2Z "
  5193. "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 "
  5194. "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"
  5195. "XM\\ %Z 3ZAU -Z )~ 3Z X J] 9V>a@V 7YBY CX NV LV BZ3Z 1WEW LYBY 2W8U?V#W+X KX9U"
  5196. "?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"
  5197. "[ 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"
  5198. "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 "
  5199. "$Z HYNU +aHSH`!hJRIg Bi /g <X.Y GY4Y CZ:Y 6YMY @[ $Z-Q )W?W $TBY AW;W<X6Z.] .Y GY"
  5200. " 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"
  5201. "'Z(Z :Z \"Z 4Z8] @Z 2Y:Y:Y(Y4ZNZ%\\*[ KZ *\\+\\!Z/[ \"[ 7Y 7Z-Z >ZMZ DZMW4WMZ![7Z 3Z 7\\ BX AX MY NY3"
  5202. "[ 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[ "
  5203. "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"
  5204. "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 "
  5205. " 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 !"
  5206. "Z Z !Z >Z !Z !Z \"Z :Z+]#Y4ZMY$[*\\%\\*[%\\+\\%\\+\\%\\+\\ N\\6[ N^-\\#[.[ N[.[ [.Z NZ-Z 7Z 2Z #Y-Y NY4\\ IY3"
  5207. "\\ 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["
  5208. " 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"
  5209. "Z 7ZNY ?[ %[/R *X@X $TBY BX;X=X6[.] /Y GY HX 7X +Z7Z 0Z 3\\ 0[ (Z L[ DZ4"
  5210. "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 )\\-"
  5211. "\\ Z.Z \"[ 7Y 7[/[ =ZNZ DZNX4XNY![6[ 4Z 7[ AX AX MY NY4\\ F\\4Z F[ &Z5] H[ @Y 2Z6] GY1Y 5Y NX 0X?[ +"
  5212. "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"
  5213. "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 :"
  5214. "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-"
  5215. "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$\\,\\#\\,\\$\\,\\$\\-\\$\\,"
  5216. "\\ 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 "
  5217. "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"
  5218. "X $YMY %[ F^ '\\FSF\\ LcGRGc >f ,c :X.Y FZ7Y BY8Y 7e >[ %[1S -Y 'X@X ;Q:TCZ CX:X=X"
  5219. "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\\ "
  5220. "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"
  5221. " 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"
  5222. "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 "
  5223. "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"
  5224. "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 "
  5225. "!Z >Z !Z !Z \"Z :Z.^!Y3e#\\.\\!\\.\\#].\\#]/]#\\.\\ N\\2[ ]/]![0[ L[0[ M[0[ N\\1[ 6Z 2Z #Y-Y NY5] HY5] IZ6] GY"
  5226. "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\\ "
  5227. "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 =\\ "
  5228. " '\\3T -Z (W?X ;S<TDZ BW8W=W4\\1` 0Y HY HX NZ GZ 'X *Z9Z /Z 5\\ 0\\ 'Z N\\ B[8[ 8Z"
  5229. " 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-"
  5230. "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"
  5231. "+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"
  5232. "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 "
  5233. "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"
  5234. "%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\\"
  5235. "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"
  5236. "[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"
  5237. "\\ 9b )a 9X.Y E[<[ BY7Z 7c ;\\ '\\5U -Z (W?W :U>TE[ CX8X?X3\\3b 1Y IY GX NZ GZ ("
  5238. "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 \""
  5239. "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[,"
  5240. "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 "
  5241. "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"
  5242. "(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 "
  5243. " 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"
  5244. "] 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 "
  5245. "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["
  5246. " L\\3\\ K[2[ K[2[ L\\3\\ 6X #c &Z B\\ ?S /UATAT 4a '_ 8X.Y E\\>\\ BY6Y 7c :] (\\7V "
  5247. "-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^ "
  5248. " ^ ;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"
  5249. "\"Z0[ 7Z ;\\ >X @X NY MZ:` F_:[ B\\3P D[;` E\\1S 7Y 0\\>a GY1Y 5Y NX 0X;\\ 0Y FY3Y2Y+Y1Y F[:[ E_;\\ "
  5250. "F[;_ ;Y *S1Y 6Z .[;_ :e ;`;` G[<[ 5a 6[ >Y X Y F[?YNY F_:[ DX?Z :[;\\ B[ &Y1Y 8a 7Z3Y H]:^#\\BXNWA[#["
  5251. "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 /[,[ "
  5252. "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"
  5253. "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 #"
  5254. "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[:"
  5255. "[ D\\;\\ C\\;\\ 4\\ 3[:\\ DZ;_ EZ;_ EZ;_ EZ;` 8a 8_;\\ 7a %\\6\\ J\\5\\ I\\6\\ I\\6\\ J\\5\\ 5X #c 'Z "
  5256. "@[ @T JT _ %] 7X.Y D^D^ BZ6Y 6b 9_ *];X -Z )X@X :ZCTH] CX7YAX1^:h 2Y JY GX NZ"
  5257. " GZ (X (\\?\\ .Z 7\\ $W7_ %Z BV8` ?\\>] 9[ /];] ET9] -Z \"[ 5[ [ 8Z DX;jLb*Z$Z#Z7a E`7\\'Z9f FZ MZ &`4^"
  5258. "'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"
  5259. " 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 "
  5260. "X Y E[AYMZ G`<[ CX@Z 9\\=\\ A\\3Q EY1Y 7` 7Y2Z I^<_\"[BWMXC\\#]IYI\\ CY9Y F]GXG] Ia=\\ E\\=\\ .[ 2[?Z J"
  5261. "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 "
  5262. " <^<` 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 :"
  5263. "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\\"
  5264. "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[="
  5265. "` E[=a 8a 8`=\\ 6` #]:] H]9] G]:] G]:] H]9] 4W !a 'Z ?Z ?U KT N] $] 7X.Y Cv AZ6Z 7a 7a "
  5266. " -_?Z -Z )W?X :^GTK_ CX5XAX0_>k 3Y JX FX NZ GZ )Y ']C] ?} I~S IZ=b %Z BZ>a =]B^ 8Z ._?^ DX"
  5267. "@_ ,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 "
  5268. "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]"
  5269. "@] 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`@"
  5270. "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"
  5271. " 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*"
  5272. "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_>"
  5273. "_ 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]"
  5274. " @]@] 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 "
  5275. " ?U KT M\\ #[ 6X.Y Bu AY5Z 7a 6f 2aE] -Z )W?W 9~ BW4YCY/bFp 3X KY FX NZ GZ "
  5276. ")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"
  5277. "~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"
  5278. "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 "
  5279. " 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 :"
  5280. "Z 5dGd )X Jd \"e KZ -`MUKY H~U IU&U 6Z X AY 5Z7Z LZ7Z ;~d 3cFk 8WEW "
  5281. " 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"
  5282. " 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]"
  5283. " 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^"
  5284. " dLd CdMd BdLd CdLd DeMd 2X !` %X =Y ?U LV MZ !Y 5X.Y As AZ4Y 6` 5~] )x -Z "
  5285. "*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"
  5286. " ?~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"
  5287. "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 "
  5288. " 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 "
  5289. " Ib c JZ ,u H~U HS$S 5Z X AY 4\\>\\ I]>\\ :~d 3~Y 8WEW CW KV)W0X FX LW"
  5290. "$[ 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} "
  5291. "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"
  5292. "qMX CqMY 6] 6YNr 3^ My Ay @y @z Ay 1X _ $V <X ?V LV LX NW 4X.Y @p ?Z4Z 7_ 2~[ "
  5293. " (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"
  5294. "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 !"
  5295. "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"
  5296. "] 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"
  5297. " :Z 2{ &X H` Ma IZ +t H~U GQ\"Q 4Z X AY 2aLb FaKa 8~d 3YNlN_ 8WEW "
  5298. "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"
  5299. "{ 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 "
  5300. "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 "
  5301. " 't +Z *W?X 8y @X1j)vG] 5X MY EX NZ GZ *X !p <} I~S Iz Z By 6r 5Z )w As (Z \"[ "
  5302. " 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 "
  5303. ">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"
  5304. "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"
  5305. " :Z(Z :Z 1y %X G^ K_ HZ *s H~U *Z X AY 1t Bs 6~d 3YNkM_ 8WEW DW "
  5306. "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"
  5307. " >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 <"
  5308. "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 "
  5309. " %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 "
  5310. " 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"
  5311. " !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"
  5312. "[ 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 "
  5313. ":Z(Z :Z /u #X F\\ I] GZ )r H~U *Z X AY /p >o 4~d 3YMiK^ 8WEW EX "
  5314. "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"
  5315. " <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"
  5316. " 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 "
  5317. " $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 "
  5318. " 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 "
  5319. " =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"
  5320. "\\ 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"
  5321. "Do @Z :Z(Z :Z .s \"X EZ G[ FZ 'p H~U *Z X AY ,k :k 2~d 3YLgJ^ 8WEW "
  5322. " 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 "
  5323. " 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"
  5324. " 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"
  5325. "] )~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 "
  5326. "#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$"
  5327. "[ =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"
  5328. "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 "
  5329. "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"
  5330. "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"
  5331. "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 "
  5332. "<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"
  5333. "[ 6Y0Y 7] &~P Ne $Z +W?X '] 6W)a Mh<\\ 7Y !X CX Y EZ +X Id 6} I~S Cm HZ =l 'e "
  5334. "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 )\\ "
  5335. ";].]'[#Z =Z =~a AX =X \"Y DdFY FYFb *h 6cFY 8j 0Y \"YAY GY1Y 5Y NX 0X1\\ :Y FY3Y2Y+Y1Y ;f :YFb 1cFX ;Y"
  5336. " $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 "
  5337. "FZ 2a D~i$~T FZ 3oDo @Z :Z(Z :Z )i LX CV CW DZ #h D~U *Z X -R9Z #[ *[ *~d 3"
  5338. "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 "
  5339. "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"
  5340. " &e 9X0X :e ,f -f -e ,f 4XFe 0cEX <bEX <bEX <bEY 4Y 4YFb )Z ?` (a '` '` (a %X 'T "
  5341. " L{ K_ 0T 4X&[ Ga AX \"Y :Y EX G_ Ie #e !_ c /a EY "
  5342. " EY Hc ?e FZ +b Ni )d Nc (X =Y #Y A^ J^ %a /] N"
  5343. "c ;Y NX *` 7YD^ ,]CX 1c ^ /Y DY X Y 8] 1YF] *\\ N` %c DY 4Y *YG\\A"
  5344. "X J\\;] 9e A^ =` 7YC] *_ G[ >a NU CZ N` 9X -T<[ "
  5345. " LYG]BX 5WEW %U HW NX MX GZ (d +b (b )b )a )b 9V;a "
  5346. ")c *c *c *c =a 4_ &^ %^ $^ &_ &_ :_/c <b +c *c *c *c 3_ K_ &` '` '_ &` 1WB_ *] $] $^ %^ NZ "
  5347. "4YD^ 'Y 6Q HQ GQ FQ HQ LX &S DQ )T 4W Q :Q"
  5348. " 9Y #X :Y EX ?Q 8R ?R @Q @R MQ =Y DY @R -Q <Z "
  5349. " #R >RM] !R <R X <X #Y ;Q <Q GR !Q @Q 2Y MX #R 0Y=Q Q=X 'Q "
  5350. " @Q *Z DY X Y ;Y <P AQ CQ ;Y 4Y *YAQ8P @Q0Q -Y 8X 7Y 3Y=Q LQ "
  5351. " JQ 4Z IU 3X -W@[ KYAQ8P 1WEW $U IV MW LW FZ "
  5352. " V KQ GR HQ GQ GQ 0T2Q HR GR HR HQ ,Q %Q GP GQ FQ GQ "
  5353. "GP *P NQ ,V MQ GR HR HR #Q =Q FQ HR HQ FQ *V:Q LQ GQ GQ GQ GY 3Y=Q !Y "
  5354. " 9X MT +X #X :Y EX "
  5355. " 5X BZ 7Y 7] 8X <X #Y "
  5356. " HY MX 0Y 'X MY CY X Y ;Y 8Y 4Y *Y 1Y E"
  5357. "X 3Y CZ IU 3X -\\I_ KY 8WEW $V"
  5358. " %Z NU 0R #V "
  5359. " )T <Z 3Y =Y 8X "
  5360. " MT +X $X 9X DX 6Y AZ NR "
  5361. " =Z 6\\ 8X <X #Y HY MX 0Y '"
  5362. "X NZ CY X X :Y 8Y 4Y *Y 1Y EX 3Y "
  5363. " CZ IU 3X -q JY 8WEW #V &Z NV "
  5364. " 0V (R "
  5365. " <Y 2Y =Y 8X MT *X "
  5366. "%X 9X EY 6X @[!T >Z 5\\ "
  5367. " 9X ;X $Y HY NY 0Y 'X NY BY X !Y "
  5368. ":Y 8Y 4Y *Y 1Y EX 3Y CZ IU 3X -p "
  5369. " IY 8WEW #V &Z MV "
  5370. " 0U 'P ;Y 2Y >Z 8X "
  5371. " MT *X &X 9X DX "
  5372. " 5X ?\\%W ?Z 4\\ :X ;X $Y "
  5373. " IZ NY 0Y 'X NY BZ !X !Y :Y 8Y 4Y *Y 1Y EX 3Y "
  5374. " CZ IU 3X -o HY 8WEW \"V "
  5375. " 'Z LU 0V "
  5376. " CZ 2Y >Y 7X "
  5377. " MT )X 'X 9X DX 5W <\\(X ?"
  5378. "Z 3\\ ;Y <X $Y IY MY 0Y 'X "
  5379. " Z AY !X !Y :Y 8Y 4Y *Y 1Y EX 3Y CZ I"
  5380. "U 3X -n GY 8WEW \"V '[3Q <V "
  5381. " 0V DY 1Y "
  5382. "?Z 7X MT )X (X 8W "
  5383. " CX 6X ;],[ AZ 1\\ <e"
  5384. " GX 2f JZ MY 0Y 'X Y @Z \"X \"Z :Y 8"
  5385. "Y 4Y *Y 1Y EX 3Y CZ IU 3X ,k "
  5386. " EY 8WEW !V 'Z4R <V "
  5387. " 0V EZ 1Y ?Y ARGX "
  5388. " MT (X )X 8W DX 5W "
  5389. " 9^1^ AZ 0\\ =e GX 2f KZ LY"
  5390. " 0Y 'X !Z @[ #X #Z 9Y 8Y 4Y *Y 1Y EX 3Y "
  5391. " CZ IU 3X )f CY 8WEW !V '[7T "
  5392. " ;V 1V "
  5393. " EY 0Y ?Y FWGW "
  5394. " LT 'W *X 8W CX 5W 8`7` A[ "
  5395. " /\\ >e GX 2f KZ LY 0Y 'X !Y >"
  5396. "\\ %X &] 9Y 8Y 4Y *Y 1Y EX 3Y CZ IU 3"
  5397. "X $^ @Y 8WEW !V '\\:V ;V "
  5398. " 1W GZ 0Y @Z "
  5399. " FWHX LT 'X +W 7W "
  5400. " V 5b?c A[ -\\ ?e !f "
  5401. " <P2\\ MY /Y 'X \"Z >f /X 0g 9Y 8Y 4Y *Y "
  5402. " 1Y EX 3Y CZ IU 3X 5Y "
  5403. " NV &\\=X ;V "
  5404. "1W GY /Y AZ EWHX "
  5405. " LT &W ,X 7V V 3~T "
  5406. " A] ,\\ @e !f <R5\\ LY /Y "
  5407. "'X #Z =f /X 0f 8Y 8Y 4Y *Y 1Y EX 3Y "
  5408. " CZ IU 3X 5Y NW '^B[ <W "
  5409. " 1W "
  5410. " HZ /Y AZ DWIX LT &X -"
  5411. "W 6U NV 1~P B_ *\\ "
  5412. " Ae !f <U:] LZ /Y 'X #Z <e /X 0e 7Y "
  5413. " 8Y 4Y *Y 1Y EX 3Y CZ IU 3X "
  5414. " 5Y X &aJ_ <W "
  5415. " 2X IZ .Y BZ CWJY "
  5416. " LT %X /X "
  5417. " 7| Hf )\\ Be !f <X?_ N[ "
  5418. " .Y 'X %[ :d /X 0e 7Y 8Y 4Y *Y 1Y EX 3Y "
  5419. " CZ IU 3X 5Y -PDX %v "
  5420. " JQDX ?QEY "
  5421. " J[ .Y D\\ CXLY "
  5422. " KT 7x Fe "
  5423. " -_Me %b .Y 'X /e 9c /X 0c "
  5424. "5Y 8Y 4Y *Y 1Y EX 3Y CZ IU 3X "
  5425. " 5Y -d $u Je "
  5426. " ?d $d -Y Ne Ad "
  5427. " KT "
  5428. " 5s Cd ,v %b -"
  5429. "Y 'X 0e 6a /X 0b 4Y 8Y 4Y *Y 1Y EX 3Y "
  5430. " CZ IU 3X 5Y -d #t Jd "
  5431. " >d "
  5432. " %e -Y Nd @c "
  5433. " (m @c "
  5434. " +u $b -Y 'X 0d 2^ /X 0_ 1Y 8Y 4Y *Y "
  5435. " 1Y EX 3Y CZ IT 2X 5Y "
  5436. "-c !q Hd >c "
  5437. " $d ,Y Nd ?b "
  5438. " %g ="
  5439. "b *t #a ,Y 'X 0d "
  5440. " ,X /X 0Y +Y 8Y 4Y *Y 1Y EX 3Y CZ '"
  5441. "X 5Y -c Nm Fc "
  5442. " =c $c +Y Nc "
  5443. " >a "
  5444. " M\\ 8a \"~Y 1"
  5445. "r !` +Y 'X 0c 1X 1Y 8Y 4Y *Y 1Y EX 3Y "
  5446. " CZ &W 5Y -b Lj "
  5447. " Db <b "
  5448. " #b *Y Nb <_ "
  5449. " (_ "
  5450. " ~Y 1q _ *Y 'X 0b 0X 1Y "
  5451. " 8Y 4Y *Y 1Y EX 3Y CZ "
  5452. " 3Y -` He A` "
  5453. " :` !a )Y Na :] "
  5454. " "
  5455. " '] M~Y .l M] (Y 'X "
  5456. " 0` .X 1Y 8Y 4Y *Y 1Y EX 3Y "
  5457. " KY *Z B^ 9Z "
  5458. " 5Z M` (Y N` "
  5459. " 8Z "
  5460. " %X H~Y "
  5461. " *d I[ &Y 'X 0^ ,X 1Y 8Y 4Y *Y 1Y EX 3Y"
  5462. " KY "
  5463. " "
  5464. " H^ &Y N] 3V "
  5465. " "
  5466. " B~Y #X CU !X &X /Y (X 1"
  5467. "Y 7X 4X )X 0Y EX 2X "
  5468. " KY "
  5469. " HZ \"X MY "
  5470. " "
  5471. " J~Y "
  5472. " 9X "
  5473. " "
  5474. " "
  5475. " "
  5476. " 3~Y "
  5477. " 9X "
  5478. " "
  5479. " "
  5480. " "
  5481. " 3~Y "
  5482. " 9X "
  5483. " "
  5484. " "
  5485. " '" };
  5486. // Define a 40x38 'danger' color logo (used by cimg::dialog()).
  5487. static const unsigned char logo40x38[4576] = {
  5488. 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,
  5489. 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,
  5490. 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,
  5491. 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,
  5492. 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,
  5493. 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,
  5494. 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,
  5495. 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,
  5496. 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,
  5497. 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,
  5498. 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,
  5499. 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,
  5500. 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,
  5501. 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,
  5502. 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,
  5503. 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,
  5504. 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,
  5505. 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,
  5506. 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,
  5507. 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,
  5508. 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,
  5509. 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,
  5510. 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,
  5511. 123,123,123,86,200,200,200,91,49,124,118,124,71,32,124,95,49,56,114,52,82,121,0 };
  5512. //! Get/set default output stream for the \CImg library messages.
  5513. /**
  5514. \param file Desired output stream. Set to \c 0 to get the currently used output stream only.
  5515. \return Currently used output stream.
  5516. **/
  5517. inline std::FILE* output(std::FILE *file) {
  5518. cimg::mutex(1);
  5519. static std::FILE *res = cimg::_stderr();
  5520. if (file) res = file;
  5521. cimg::mutex(1,0);
  5522. return res;
  5523. }
  5524. // Return number of available CPU cores.
  5525. inline unsigned int nb_cpus() {
  5526. unsigned int res = 1;
  5527. #if cimg_OS==2
  5528. SYSTEM_INFO sysinfo;
  5529. GetSystemInfo(&sysinfo);
  5530. res = (unsigned int)sysinfo.dwNumberOfProcessors;
  5531. #elif cimg_OS == 1
  5532. res = (unsigned int)sysconf(_SC_NPROCESSORS_ONLN);
  5533. #endif
  5534. return res?res:1U;
  5535. }
  5536. // Lock/unlock mutex for CImg multi-thread programming.
  5537. inline int mutex(const unsigned int n, const int lock_mode) {
  5538. switch (lock_mode) {
  5539. case 0 : cimg::Mutex_attr().unlock(n); return 0;
  5540. case 1 : cimg::Mutex_attr().lock(n); return 0;
  5541. default : return cimg::Mutex_attr().trylock(n);
  5542. }
  5543. }
  5544. //! Display a warning message on the default output stream.
  5545. /**
  5546. \param format C-string containing the format of the message, as with <tt>std::printf()</tt>.
  5547. \note If configuration macro \c cimg_strict_warnings is set, this function throws a
  5548. \c CImgWarningException instead.
  5549. \warning As the first argument is a format string, it is highly recommended to write
  5550. \code
  5551. cimg::warn("%s",warning_message);
  5552. \endcode
  5553. instead of
  5554. \code
  5555. cimg::warn(warning_message);
  5556. \endcode
  5557. if \c warning_message can be arbitrary, to prevent nasty memory access.
  5558. **/
  5559. inline void warn(const char *const format, ...) {
  5560. if (cimg::exception_mode()>=1) {
  5561. char *const message = new char[16384];
  5562. std::va_list ap;
  5563. va_start(ap,format);
  5564. cimg_vsnprintf(message,16384,format,ap);
  5565. va_end(ap);
  5566. #ifdef cimg_strict_warnings
  5567. throw CImgWarningException(message);
  5568. #else
  5569. std::fprintf(cimg::output(),"\n%s[CImg] *** Warning ***%s%s\n",cimg::t_red,cimg::t_normal,message);
  5570. #endif
  5571. delete[] message;
  5572. }
  5573. }
  5574. // Execute an external system command.
  5575. /**
  5576. \param command C-string containing the command line to execute.
  5577. \param module_name Module name.
  5578. \return Status value of the executed command, whose meaning is OS-dependent.
  5579. \note This function is similar to <tt>std::system()</tt>
  5580. but it does not open an extra console windows
  5581. on Windows-based systems.
  5582. **/
  5583. inline int system(const char *const command, const char *const module_name=0, const bool is_verbose=false) {
  5584. cimg::unused(module_name);
  5585. #ifdef cimg_no_system_calls
  5586. cimg::unused(command,is_verbose);
  5587. return -1;
  5588. #else
  5589. if (is_verbose) return std::system(command);
  5590. #if cimg_OS==1
  5591. const unsigned int l = (unsigned int)std::strlen(command);
  5592. if (l) {
  5593. char *const ncommand = new char[l + 24];
  5594. std::memcpy(ncommand,command,l);
  5595. std::strcpy(ncommand + l," >/dev/null 2>&1"); // Make command silent
  5596. const int out_val = std::system(ncommand);
  5597. delete[] ncommand;
  5598. return out_val;
  5599. } else return -1;
  5600. #elif cimg_OS==2
  5601. PROCESS_INFORMATION pi;
  5602. STARTUPINFOA si;
  5603. std::memset(&pi,0,sizeof(PROCESS_INFORMATION));
  5604. std::memset(&si,0,sizeof(STARTUPINFO));
  5605. GetStartupInfoA(&si);
  5606. si.cb = sizeof(si);
  5607. si.wShowWindow = SW_HIDE;
  5608. si.dwFlags |= SW_HIDE | STARTF_USESHOWWINDOW;
  5609. const BOOL res = CreateProcessA((LPCSTR)module_name,(LPSTR)command,0,0,FALSE,0,0,0,&si,&pi);
  5610. if (res) {
  5611. WaitForSingleObject(pi.hProcess,INFINITE);
  5612. CloseHandle(pi.hThread);
  5613. CloseHandle(pi.hProcess);
  5614. return 0;
  5615. } else {
  5616. char* lpMsgBuf;
  5617. // Get the error message.
  5618. DWORD errorCode = GetLastError();
  5619. FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
  5620. 0,errorCode,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),(LPSTR)&lpMsgBuf,0,0);
  5621. cimg::warn("cimg::system() : Command '%s' (module name '%s) failed with error %lu: %s",
  5622. module_name==0?"(null)":module_name,
  5623. command==0?"(null)":command,
  5624. errorCode,lpMsgBuf);
  5625. return -1;
  5626. }
  5627. #else
  5628. return std::system(command);
  5629. #endif
  5630. #endif
  5631. }
  5632. //! Return a reference to a temporary variable of type T.
  5633. template<typename T>
  5634. inline T& temporary(const T&) {
  5635. static T temp;
  5636. return temp;
  5637. }
  5638. //! Exchange values of variables \c a and \c b.
  5639. template<typename T>
  5640. inline void swap(T& a, T& b) { T t = a; a = b; b = t; }
  5641. //! Exchange values of variables (\c a1,\c a2) and (\c b1,\c b2).
  5642. template<typename T1, typename T2>
  5643. inline void swap(T1& a1, T1& b1, T2& a2, T2& b2) {
  5644. cimg::swap(a1,b1); cimg::swap(a2,b2);
  5645. }
  5646. //! Exchange values of variables (\c a1,\c a2,\c a3) and (\c b1,\c b2,\c b3).
  5647. template<typename T1, typename T2, typename T3>
  5648. inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3) {
  5649. cimg::swap(a1,b1,a2,b2); cimg::swap(a3,b3);
  5650. }
  5651. //! Exchange values of variables (\c a1,\c a2,...,\c a4) and (\c b1,\c b2,...,\c b4).
  5652. template<typename T1, typename T2, typename T3, typename T4>
  5653. inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4) {
  5654. cimg::swap(a1,b1,a2,b2,a3,b3); cimg::swap(a4,b4);
  5655. }
  5656. //! Exchange values of variables (\c a1,\c a2,...,\c a5) and (\c b1,\c b2,...,\c b5).
  5657. template<typename T1, typename T2, typename T3, typename T4, typename T5>
  5658. inline void swap(T1& a1, T1& b1, T2& a2, T2& b2, T3& a3, T3& b3, T4& a4, T4& b4, T5& a5, T5& b5) {
  5659. cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4); cimg::swap(a5,b5);
  5660. }
  5661. //! Exchange values of variables (\c a1,\c a2,...,\c a6) and (\c b1,\c b2,...,\c b6).
  5662. template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6>
  5663. 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) {
  5664. cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5); cimg::swap(a6,b6);
  5665. }
  5666. //! Exchange values of variables (\c a1,\c a2,...,\c a7) and (\c b1,\c b2,...,\c b7).
  5667. template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7>
  5668. 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,
  5669. T7& a7, T7& b7) {
  5670. cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6); cimg::swap(a7,b7);
  5671. }
  5672. //! Exchange values of variables (\c a1,\c a2,...,\c a8) and (\c b1,\c b2,...,\c b8).
  5673. template<typename T1, typename T2, typename T3, typename T4, typename T5, typename T6, typename T7, typename T8>
  5674. 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,
  5675. T7& a7, T7& b7, T8& a8, T8& b8) {
  5676. cimg::swap(a1,b1,a2,b2,a3,b3,a4,b4,a5,b5,a6,b6,a7,b7); cimg::swap(a8,b8);
  5677. }
  5678. //! Return the endianness of the current architecture.
  5679. /**
  5680. \return \c false for <i>Little Endian</i> or \c true for <i>Big Endian</i>.
  5681. **/
  5682. inline bool endianness() {
  5683. const int x = 1;
  5684. return ((unsigned char*)&x)[0]?false:true;
  5685. }
  5686. //! Reverse endianness of all elements in a memory buffer.
  5687. /**
  5688. \param[in,out] buffer Memory buffer whose endianness must be reversed.
  5689. \param size Number of buffer elements to reverse.
  5690. **/
  5691. template<typename T>
  5692. inline void invert_endianness(T* const buffer, const cimg_ulong size) {
  5693. if (size) switch (sizeof(T)) {
  5694. case 1 : break;
  5695. case 2 : {
  5696. for (unsigned short *ptr = (unsigned short*)buffer + size; ptr>(unsigned short*)buffer; ) {
  5697. const unsigned short val = *(--ptr);
  5698. *ptr = (unsigned short)((val>>8) | ((val<<8)));
  5699. }
  5700. } break;
  5701. case 4 : {
  5702. for (unsigned int *ptr = (unsigned int*)buffer + size; ptr>(unsigned int*)buffer; ) {
  5703. const unsigned int val = *(--ptr);
  5704. *ptr = (val>>24) | ((val>>8)&0xff00) | ((val<<8)&0xff0000) | (val<<24);
  5705. }
  5706. } break;
  5707. case 8 : {
  5708. const cimg_uint64
  5709. m0 = (cimg_uint64)0xff, m1 = m0<<8, m2 = m0<<16, m3 = m0<<24,
  5710. m4 = m0<<32, m5 = m0<<40, m6 = m0<<48, m7 = m0<<56;
  5711. for (cimg_uint64 *ptr = (cimg_uint64*)buffer + size; ptr>(cimg_uint64*)buffer; ) {
  5712. const cimg_uint64 val = *(--ptr);
  5713. *ptr = (((val&m7)>>56) | ((val&m6)>>40) | ((val&m5)>>24) | ((val&m4)>>8) |
  5714. ((val&m3)<<8) |((val&m2)<<24) | ((val&m1)<<40) | ((val&m0)<<56));
  5715. }
  5716. } break;
  5717. default : {
  5718. for (T* ptr = buffer + size; ptr>buffer; ) {
  5719. unsigned char *pb = (unsigned char*)(--ptr), *pe = pb + sizeof(T);
  5720. for (int i = 0; i<(int)sizeof(T)/2; ++i) swap(*(pb++),*(--pe));
  5721. }
  5722. }
  5723. }
  5724. }
  5725. inline void invert_endianness(bool* const, const cimg_ulong) {}
  5726. inline void invert_endianness(unsigned char* const, const cimg_ulong) {}
  5727. inline void invert_endianness(char* const, const cimg_ulong) {}
  5728. //! Reverse endianness of a single variable.
  5729. /**
  5730. \param[in,out] a Variable to reverse.
  5731. \return Reference to reversed variable.
  5732. **/
  5733. template<typename T>
  5734. inline T& invert_endianness(T& a) {
  5735. invert_endianness(&a,1);
  5736. return a;
  5737. }
  5738. // Conversion functions to get more precision when trying to store 'unsigned int' values as 'float'.
  5739. inline unsigned int float2uint(const float value) {
  5740. int tmp = 0;
  5741. std::memcpy(&tmp,&value,sizeof(float));
  5742. if (tmp>=0) return (unsigned int)value;
  5743. unsigned int u;
  5744. // use memcpy instead of assignment to avoid undesired optimizations by C++-compiler.
  5745. std::memcpy(&u,&value,sizeof(float));
  5746. return ((u)<<2)>>2; // set sign & exponent bit to 0
  5747. }
  5748. inline float uint2float(const unsigned int value) {
  5749. if (value<(1U<<19)) return (float)value; // Consider 'uint32' safely stored as floats until 19bits (i.e 524287)
  5750. float f;
  5751. const unsigned int v = value | (3U<<(8*sizeof(unsigned int)-2)); // set sign & exponent bit to 1
  5752. // use memcpy instead of simple assignment to avoid undesired optimizations by C++-compiler.
  5753. std::memcpy(&f,&v,sizeof(float));
  5754. return f;
  5755. }
  5756. //! Return the value of a system timer, with a millisecond precision.
  5757. /**
  5758. \note The timer does not necessarily starts from \c 0.
  5759. **/
  5760. inline cimg_uint64 time() {
  5761. #if cimg_OS==1
  5762. struct timeval st_time;
  5763. gettimeofday(&st_time,0);
  5764. return (cimg_uint64)st_time.tv_sec*1000 + (cimg_uint64)st_time.tv_usec/1000;
  5765. #elif cimg_OS==2
  5766. ULARGE_INTEGER ul;
  5767. FILETIME ft;
  5768. GetSystemTimeAsFileTime(&ft);
  5769. ul.LowPart = ft.dwLowDateTime;
  5770. ul.HighPart = ft.dwHighDateTime;
  5771. return (cimg_uint64)ul.QuadPart/10000;
  5772. #else
  5773. return 0;
  5774. #endif
  5775. }
  5776. // Implement a tic/toc mechanism to display elapsed time of algorithms.
  5777. inline cimg_uint64 tictoc(const bool is_tic);
  5778. //! Start tic/toc timer for time measurement between code instructions.
  5779. /**
  5780. \return Current value of the timer (same value as time()).
  5781. **/
  5782. inline cimg_uint64 tic() {
  5783. return cimg::tictoc(true);
  5784. }
  5785. //! End tic/toc timer and displays elapsed time from last call to tic().
  5786. /**
  5787. \return Time elapsed (in ms) since last call to tic().
  5788. **/
  5789. inline cimg_uint64 toc() {
  5790. return cimg::tictoc(false);
  5791. }
  5792. //! Sleep for a given numbers of milliseconds.
  5793. /**
  5794. \param milliseconds Number of milliseconds to wait for.
  5795. \note This function frees the CPU resources during the sleeping time.
  5796. It can be used to temporize your program properly, without wasting CPU time.
  5797. **/
  5798. inline void sleep(const unsigned int milliseconds) {
  5799. #if cimg_OS==1
  5800. struct timespec tv;
  5801. tv.tv_sec = milliseconds/1000;
  5802. tv.tv_nsec = (milliseconds%1000)*1000000;
  5803. nanosleep(&tv,0);
  5804. #elif cimg_OS==2
  5805. Sleep(milliseconds);
  5806. #else
  5807. cimg::unused(milliseconds);
  5808. #endif
  5809. }
  5810. inline unsigned int wait(const unsigned int milliseconds, cimg_uint64 *const p_timer) {
  5811. if (!*p_timer) *p_timer = cimg::time();
  5812. const cimg_uint64 current_time = cimg::time();
  5813. if (current_time<*p_timer || current_time>=*p_timer + milliseconds) { *p_timer = current_time; return 0; }
  5814. const unsigned int time_diff = (unsigned int)(*p_timer + milliseconds - current_time);
  5815. *p_timer = current_time + time_diff;
  5816. cimg::sleep(time_diff);
  5817. return time_diff;
  5818. }
  5819. //! Wait for a given number of milliseconds since the last call to wait().
  5820. /**
  5821. \param milliseconds Number of milliseconds to wait for.
  5822. \return Number of milliseconds elapsed since the last call to wait().
  5823. \note Same as sleep() with a waiting time computed with regard to the last call
  5824. of wait(). It may be used to temporize your program properly, without wasting CPU time.
  5825. **/
  5826. inline unsigned int wait(const unsigned int milliseconds) {
  5827. cimg::mutex(3);
  5828. static cimg_uint64 timer = cimg::time();
  5829. cimg::mutex(3,0);
  5830. return cimg::wait(milliseconds,&timer);
  5831. }
  5832. // Custom random number generator (allow re-entrance).
  5833. inline cimg_uint64& rng() { // Used as a shared global number for rng
  5834. static cimg_uint64 rng = 0xB16B00B5U;
  5835. return rng;
  5836. }
  5837. inline unsigned int _rand(cimg_uint64 *const p_rng) {
  5838. *p_rng = *p_rng*1103515245 + 12345U;
  5839. return (unsigned int)*p_rng;
  5840. }
  5841. inline unsigned int _rand() {
  5842. cimg::mutex(4);
  5843. const unsigned int res = cimg::_rand(&cimg::rng());
  5844. cimg::mutex(4,0);
  5845. return res;
  5846. }
  5847. inline void srand(cimg_uint64 *const p_rng) {
  5848. #if cimg_OS==1 || defined(__BORLANDC__)
  5849. *p_rng = cimg::time() + (cimg_uint64)getpid();
  5850. #elif cimg_OS==2
  5851. *p_rng = cimg::time() + (cimg_uint64)_getpid();
  5852. #endif
  5853. }
  5854. inline void srand() {
  5855. cimg::mutex(4);
  5856. cimg::srand(&cimg::rng());
  5857. cimg::mutex(4,0);
  5858. }
  5859. inline void srand(const cimg_uint64 seed) {
  5860. cimg::mutex(4);
  5861. cimg::rng() = seed;
  5862. cimg::mutex(4,0);
  5863. }
  5864. inline double rand(const double val_min, const double val_max, cimg_uint64 *const p_rng) {
  5865. const double val = cimg::_rand(p_rng)/(double)~0U;
  5866. return val_min + (val_max - val_min)*val;
  5867. }
  5868. inline double rand(const double val_min, const double val_max) {
  5869. cimg::mutex(4);
  5870. const double res = cimg::rand(val_min,val_max,&cimg::rng());
  5871. cimg::mutex(4,0);
  5872. return res;
  5873. }
  5874. inline double rand(const double val_max, cimg_uint64 *const p_rng) {
  5875. const double val = cimg::_rand(p_rng)/(double)~0U;
  5876. return val_max*val;
  5877. }
  5878. inline double rand(const double val_max=1) {
  5879. cimg::mutex(4);
  5880. const double res = cimg::rand(val_max,&cimg::rng());
  5881. cimg::mutex(4,0);
  5882. return res;
  5883. }
  5884. inline double grand(cimg_uint64 *const p_rng) {
  5885. double x1, w;
  5886. do {
  5887. const double x2 = cimg::rand(-1,1,p_rng);
  5888. x1 = cimg::rand(-1,1,p_rng);
  5889. w = x1*x1 + x2*x2;
  5890. } while (w<=0 || w>=1.);
  5891. return x1*std::sqrt((-2*std::log(w))/w);
  5892. }
  5893. inline double grand() {
  5894. cimg::mutex(4);
  5895. const double res = cimg::grand(&cimg::rng());
  5896. cimg::mutex(4,0);
  5897. return res;
  5898. }
  5899. inline unsigned int prand(const double z, cimg_uint64 *const p_rng) {
  5900. if (z<=1.e-10) return 0;
  5901. if (z>100) return (unsigned int)((std::sqrt(z) * cimg::grand(p_rng)) + z);
  5902. unsigned int k = 0;
  5903. const double y = std::exp(-z);
  5904. for (double s = 1.; s>=y; ++k) s*=cimg::rand(1,p_rng);
  5905. return k - 1;
  5906. }
  5907. inline unsigned int prand(const double z) {
  5908. cimg::mutex(4);
  5909. const unsigned int res = cimg::prand(z,&cimg::rng());
  5910. cimg::mutex(4,0);
  5911. return res;
  5912. }
  5913. //! Cut (i.e. clamp) value in specified interval.
  5914. template<typename T, typename t>
  5915. inline T cut(const T& val, const t& val_min, const t& val_max) {
  5916. return val<=val_min?(T)val_min:val>=val_max?(T)val_max:val;
  5917. }
  5918. //! Bitwise-rotate value on the left.
  5919. template<typename T>
  5920. inline T rol(const T& a, const unsigned int n=1) {
  5921. return n?(T)((a<<n)|(a>>((sizeof(T)<<3) - n))):a;
  5922. }
  5923. inline float rol(const float a, const unsigned int n=1) {
  5924. return (float)rol((int)a,n);
  5925. }
  5926. inline double rol(const double a, const unsigned int n=1) {
  5927. return (double)rol((cimg_long)a,n);
  5928. }
  5929. inline double rol(const long double a, const unsigned int n=1) {
  5930. return (double)rol((cimg_long)a,n);
  5931. }
  5932. #ifdef cimg_use_half
  5933. inline half rol(const half a, const unsigned int n=1) {
  5934. return (half)rol((int)a,n);
  5935. }
  5936. #endif
  5937. //! Bitwise-rotate value on the right.
  5938. template<typename T>
  5939. inline T ror(const T& a, const unsigned int n=1) {
  5940. return n?(T)((a>>n)|(a<<((sizeof(T)<<3) - n))):a;
  5941. }
  5942. inline float ror(const float a, const unsigned int n=1) {
  5943. return (float)ror((int)a,n);
  5944. }
  5945. inline double ror(const double a, const unsigned int n=1) {
  5946. return (double)ror((cimg_long)a,n);
  5947. }
  5948. inline double ror(const long double a, const unsigned int n=1) {
  5949. return (double)ror((cimg_long)a,n);
  5950. }
  5951. #ifdef cimg_use_half
  5952. inline half ror(const half a, const unsigned int n=1) {
  5953. return (half)ror((int)a,n);
  5954. }
  5955. #endif
  5956. //! Return absolute value of a value.
  5957. template<typename T>
  5958. inline T abs(const T& a) {
  5959. return a>=0?a:-a;
  5960. }
  5961. inline bool abs(const bool a) {
  5962. return a;
  5963. }
  5964. inline int abs(const unsigned char a) {
  5965. return (int)a;
  5966. }
  5967. inline int abs(const unsigned short a) {
  5968. return (int)a;
  5969. }
  5970. inline int abs(const unsigned int a) {
  5971. return (int)a;
  5972. }
  5973. inline int abs(const int a) {
  5974. return std::abs(a);
  5975. }
  5976. inline cimg_int64 abs(const cimg_uint64 a) {
  5977. return (cimg_int64)a;
  5978. }
  5979. inline double abs(const double a) {
  5980. return std::fabs(a);
  5981. }
  5982. inline float abs(const float a) {
  5983. return (float)std::fabs((double)a);
  5984. }
  5985. //! Return hyperbolic arcosine of a value.
  5986. inline double acosh(const double x) {
  5987. #if cimg_use_cpp11==1 && !defined(_MSC_VER)
  5988. return std::acosh(x);
  5989. #else
  5990. return std::log(x + std::sqrt(x*x - 1));
  5991. #endif
  5992. }
  5993. //! Return hyperbolic arcsine of a value.
  5994. inline double asinh(const double x) {
  5995. #if cimg_use_cpp11==1 && !defined(_MSC_VER)
  5996. return std::asinh(x);
  5997. #else
  5998. return std::log(x + std::sqrt(x*x + 1));
  5999. #endif
  6000. }
  6001. //! Return hyperbolic arctangent of a value.
  6002. inline double atanh(const double x) {
  6003. #if cimg_use_cpp11==1 && !defined(_MSC_VER)
  6004. return std::atanh(x);
  6005. #else
  6006. return 0.5*std::log((1. + x)/(1. - x));
  6007. #endif
  6008. }
  6009. //! Return the sinc of a given value.
  6010. inline double sinc(const double x) {
  6011. return x?std::sin(x)/x:1;
  6012. }
  6013. //! Return base-2 logarithm of a value.
  6014. inline double log2(const double x) {
  6015. #if cimg_use_cpp11==1 && !defined(_MSC_VER)
  6016. return std::log2(x);
  6017. #else
  6018. const double base2 = std::log(2.);
  6019. return std::log(x)/base2;
  6020. #endif
  6021. }
  6022. //! Return square of a value.
  6023. template<typename T>
  6024. inline T sqr(const T& val) {
  6025. return val*val;
  6026. }
  6027. // Return inverse of error function.
  6028. template<typename T>
  6029. inline T erfinv(const T& val) {
  6030. const T
  6031. sgn = val<0?-1:1,
  6032. x = (1 - val)*(1 + val),
  6033. lnx = std::log(x),
  6034. tt1 = (T)(2/(cimg::PI*0.147) + 0.5*lnx),
  6035. tt2 = lnx/(T)0.147;
  6036. return sgn*std::sqrt(-tt1 + std::sqrt(tt1*tt1 - tt2));
  6037. }
  6038. //! Return cubic root of a value.
  6039. template<typename T>
  6040. inline double cbrt(const T& x) {
  6041. #if cimg_use_cpp11==1
  6042. return std::cbrt(x);
  6043. #else
  6044. return x>=0?std::pow((double)x,1./3):-std::pow(-(double)x,1./3);
  6045. #endif
  6046. }
  6047. template<typename T>
  6048. inline T pow3(const T& val) {
  6049. return val*val*val;
  6050. }
  6051. template<typename T>
  6052. inline T pow4(const T& val) {
  6053. return val*val*val*val;
  6054. }
  6055. //! Return the minimum between three values.
  6056. template<typename t>
  6057. inline t min(const t& a, const t& b, const t& c) {
  6058. return std::min(std::min(a,b),c);
  6059. }
  6060. //! Return the minimum between four values.
  6061. template<typename t>
  6062. inline t min(const t& a, const t& b, const t& c, const t& d) {
  6063. return std::min(std::min(a,b),std::min(c,d));
  6064. }
  6065. //! Return the minabs between two values.
  6066. template<typename t>
  6067. inline t minabs(const t& a, const t& b) {
  6068. return cimg::abs(b)<cimg::abs(a)?b:a;
  6069. }
  6070. template<typename t>
  6071. inline t minabs(const t& a, const t& b, const t& abs_b) {
  6072. return abs_b<cimg::abs(a)?b:a;
  6073. }
  6074. //! Return the maximum between three values.
  6075. template<typename t>
  6076. inline t max(const t& a, const t& b, const t& c) {
  6077. return std::max(std::max(a,b),c);
  6078. }
  6079. //! Return the maximum between four values.
  6080. template<typename t>
  6081. inline t max(const t& a, const t& b, const t& c, const t& d) {
  6082. return std::max(std::max(a,b),std::max(c,d));
  6083. }
  6084. //! Return the maxabs between two values.
  6085. template<typename t>
  6086. inline t maxabs(const t& a, const t& b) {
  6087. return cimg::abs(b)>cimg::abs(a)?b:a;
  6088. }
  6089. template<typename t>
  6090. inline t maxabs(const t& a, const t& b, const t& abs_b) {
  6091. return abs_b>cimg::abs(a)?b:a;
  6092. }
  6093. //! Return the sign of a value.
  6094. template<typename T>
  6095. inline T sign(const T& x) {
  6096. return (T)(cimg::type<T>::is_nan(x)?0:x<0?-1:x>0);
  6097. }
  6098. //! Return the nearest power of 2 higher than given value.
  6099. template<typename T>
  6100. inline cimg_uint64 nearest_pow2(const T& x) {
  6101. cimg_uint64 i = 1;
  6102. while (x>i) i<<=1;
  6103. return i;
  6104. }
  6105. //! Return the modulo of a value.
  6106. /**
  6107. \param x Input value.
  6108. \param m Modulo value.
  6109. \note This modulo function accepts negative and floating-points modulo numbers, as well as variables of any type.
  6110. **/
  6111. template<typename T>
  6112. inline T mod(const T& x, const T& m) {
  6113. if (!m) {
  6114. if (cimg::type<T>::is_float()) return cimg::type<T>::nan();
  6115. else throw CImgArgumentException("cimg::mod(): Specified modulo value is 0.");
  6116. }
  6117. const double dx = (double)x, dm = (double)m;
  6118. if (!cimg::type<double>::is_finite(dm)) return x;
  6119. if (cimg::type<double>::is_finite(dx)) return (T)(dx - dm * std::floor(dx / dm));
  6120. return (T)0;
  6121. }
  6122. inline int mod(const bool x, const bool m) {
  6123. if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0.");
  6124. return x?1:0;
  6125. }
  6126. inline int mod(const unsigned char x, const unsigned char m) {
  6127. if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0.");
  6128. return x%m;
  6129. }
  6130. inline int mod(const char x, const char m) {
  6131. if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0.");
  6132. #if defined(CHAR_MAX) && CHAR_MAX==255
  6133. return x%m;
  6134. #else
  6135. return x>=0?x%m:(x%m?m + x%m:0);
  6136. #endif
  6137. }
  6138. inline int mod(const unsigned short x, const unsigned short m) {
  6139. if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0.");
  6140. return (int)(x%m);
  6141. }
  6142. inline int mod(const short x, const short m) {
  6143. if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0.");
  6144. return (int)(x>=0?x%m:(x%m?m + x%m:0));
  6145. }
  6146. inline int mod(const unsigned int x, const unsigned int m) {
  6147. if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0.");
  6148. return (int)(x%m);
  6149. }
  6150. inline int mod(const int x, const int m) {
  6151. if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0.");
  6152. return (int)(x>=0?x%m:(x%m?m + x%m:0));
  6153. }
  6154. inline cimg_int64 mod(const cimg_uint64 x, const cimg_uint64 m) {
  6155. if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0.");
  6156. return (cimg_int64)(x%m);
  6157. }
  6158. inline cimg_int64 mod(const cimg_int64 x, const cimg_int64 m) {
  6159. if (!m) throw CImgArgumentException("cimg::mod(): Specified modulo value is 0.");
  6160. return (cimg_int64)(x>=0?x%m:(x%m?m + x%m:0));
  6161. }
  6162. //! Return the min-mod of two values.
  6163. /**
  6164. \note <i>minmod(\p a,\p b)</i> is defined to be:
  6165. - <i>minmod(\p a,\p b) = min(\p a,\p b)</i>, if \p a and \p b have the same sign.
  6166. - <i>minmod(\p a,\p b) = 0</i>, if \p a and \p b have different signs.
  6167. **/
  6168. template<typename T>
  6169. inline T minmod(const T& a, const T& b) {
  6170. return a*b<=0?0:(a>0?(a<b?a:b):(a<b?b:a));
  6171. }
  6172. template<typename T>
  6173. inline T round(const T& x) {
  6174. return (T)std::floor((_cimg_Tfloat)x + 0.5f);
  6175. }
  6176. template<typename T>
  6177. inline int uiround(const T x) {
  6178. return cimg::type<T>::is_float()?(int)(x + 0.5f):(int)x;
  6179. }
  6180. //! Return rounded value.
  6181. /**
  6182. \param x Value to be rounded.
  6183. \param y Rounding precision.
  6184. \param rounding_type Type of rounding operation (\c 0 = nearest, \c -1 = backward, \c 1 = forward).
  6185. \return Rounded value, having the same type as input value \c x.
  6186. **/
  6187. template<typename T>
  6188. inline T round(const T& x, const double y, const int rounding_type=0) {
  6189. if (y<=0) return x;
  6190. if (y==1) switch (rounding_type) {
  6191. case 0 : return cimg::round(x);
  6192. case 1 : return (T)std::ceil((_cimg_Tfloat)x);
  6193. default : return (T)std::floor((_cimg_Tfloat)x);
  6194. }
  6195. const double sx = (double)x/y, floor = std::floor(sx), delta = sx - floor;
  6196. return (T)(y*(rounding_type<0?floor:rounding_type>0?std::ceil(sx):delta<0.5?floor:std::ceil(sx)));
  6197. }
  6198. // Code to compute fast median from 2,3,5,7,9,13,25 and 49 values.
  6199. // (contribution by RawTherapee: http://rawtherapee.com/).
  6200. template<typename T>
  6201. inline T median(T val0, T val1) {
  6202. return (val0 + val1)/2;
  6203. }
  6204. template<typename T>
  6205. inline T median(T val0, T val1, T val2) {
  6206. return std::max(std::min(val0,val1),std::min(val2,std::max(val0,val1)));
  6207. }
  6208. template<typename T>
  6209. inline T median(T val0, T val1, T val2, T val3, T val4) {
  6210. T tmp = std::min(val0,val1);
  6211. val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4);
  6212. val3 = std::max(val0,tmp); val1 = std::min(val1,val4); tmp = std::min(val1,val2); val2 = std::max(val1,val2);
  6213. val1 = tmp; tmp = std::min(val2,val3);
  6214. return std::max(val1,tmp);
  6215. }
  6216. template<typename T>
  6217. inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6) {
  6218. T tmp = std::min(val0,val5);
  6219. val5 = std::max(val0,val5); val0 = tmp; tmp = std::min(val0,val3); val3 = std::max(val0,val3); val0 = tmp;
  6220. tmp = std::min(val1,val6); val6 = std::max(val1,val6); val1 = tmp; tmp = std::min(val2,val4);
  6221. val4 = std::max(val2,val4); val2 = tmp; val1 = std::max(val0,val1); tmp = std::min(val3,val5);
  6222. val5 = std::max(val3,val5); val3 = tmp; tmp = std::min(val2,val6); val6 = std::max(val2,val6);
  6223. val3 = std::max(tmp,val3); val3 = std::min(val3,val6); tmp = std::min(val4,val5); val4 = std::max(val1,tmp);
  6224. tmp = std::min(val1,tmp); val3 = std::max(tmp,val3);
  6225. return std::min(val3,val4);
  6226. }
  6227. template<typename T>
  6228. inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6, T val7, T val8) {
  6229. T tmp = std::min(val1,val2);
  6230. val2 = std::max(val1,val2); val1 = tmp; tmp = std::min(val4,val5);
  6231. val5 = std::max(val4,val5); val4 = tmp; tmp = std::min(val7,val8);
  6232. val8 = std::max(val7,val8); val7 = tmp; tmp = std::min(val0,val1);
  6233. val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4);
  6234. val4 = std::max(val3,val4); val3 = tmp; tmp = std::min(val6,val7);
  6235. val7 = std::max(val6,val7); val6 = tmp; tmp = std::min(val1,val2);
  6236. val2 = std::max(val1,val2); val1 = tmp; tmp = std::min(val4,val5);
  6237. val5 = std::max(val4,val5); val4 = tmp; tmp = std::min(val7,val8);
  6238. val8 = std::max(val7,val8); val3 = std::max(val0,val3); val5 = std::min(val5,val8);
  6239. val7 = std::max(val4,tmp); tmp = std::min(val4,tmp); val6 = std::max(val3,val6);
  6240. val4 = std::max(val1,tmp); val2 = std::min(val2,val5); val4 = std::min(val4,val7);
  6241. tmp = std::min(val4,val2); val2 = std::max(val4,val2); val4 = std::max(val6,tmp);
  6242. return std::min(val4,val2);
  6243. }
  6244. template<typename T>
  6245. 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,
  6246. T val12) {
  6247. T tmp = std::min(val1,val7);
  6248. val7 = std::max(val1,val7); val1 = tmp; tmp = std::min(val9,val11); val11 = std::max(val9,val11); val9 = tmp;
  6249. tmp = std::min(val3,val4); val4 = std::max(val3,val4); val3 = tmp; tmp = std::min(val5,val8);
  6250. val8 = std::max(val5,val8); val5 = tmp; tmp = std::min(val0,val12); val12 = std::max(val0,val12);
  6251. val0 = tmp; tmp = std::min(val2,val6); val6 = std::max(val2,val6); val2 = tmp; tmp = std::min(val0,val1);
  6252. val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val2,val3); val3 = std::max(val2,val3); val2 = tmp;
  6253. tmp = std::min(val4,val6); val6 = std::max(val4,val6); val4 = tmp; tmp = std::min(val8,val11);
  6254. val11 = std::max(val8,val11); val8 = tmp; tmp = std::min(val7,val12); val12 = std::max(val7,val12); val7 = tmp;
  6255. tmp = std::min(val5,val9); val9 = std::max(val5,val9); val5 = tmp; tmp = std::min(val0,val2);
  6256. val2 = std::max(val0,val2); val0 = tmp; tmp = std::min(val3,val7); val7 = std::max(val3,val7); val3 = tmp;
  6257. tmp = std::min(val10,val11); val11 = std::max(val10,val11); val10 = tmp; tmp = std::min(val1,val4);
  6258. val4 = std::max(val1,val4); val1 = tmp; tmp = std::min(val6,val12); val12 = std::max(val6,val12); val6 = tmp;
  6259. tmp = std::min(val7,val8); val8 = std::max(val7,val8); val7 = tmp; val11 = std::min(val11,val12);
  6260. tmp = std::min(val4,val9); val9 = std::max(val4,val9); val4 = tmp; tmp = std::min(val6,val10);
  6261. val10 = std::max(val6,val10); val6 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4); val3 = tmp;
  6262. tmp = std::min(val5,val6); val6 = std::max(val5,val6); val5 = tmp; val8 = std::min(val8,val9);
  6263. val10 = std::min(val10,val11); tmp = std::min(val1,val7); val7 = std::max(val1,val7); val1 = tmp;
  6264. tmp = std::min(val2,val6); val6 = std::max(val2,val6); val2 = tmp; val3 = std::max(val1,val3);
  6265. tmp = std::min(val4,val7); val7 = std::max(val4,val7); val4 = tmp; val8 = std::min(val8,val10);
  6266. val5 = std::max(val0,val5); val5 = std::max(val2,val5); tmp = std::min(val6,val8); val8 = std::max(val6,val8);
  6267. val5 = std::max(val3,val5); val7 = std::min(val7,val8); val6 = std::max(val4,tmp); tmp = std::min(val4,tmp);
  6268. val5 = std::max(tmp,val5); val6 = std::min(val6,val7);
  6269. return std::max(val5,val6);
  6270. }
  6271. template<typename T>
  6272. inline T median(T val0, T val1, T val2, T val3, T val4,
  6273. T val5, T val6, T val7, T val8, T val9,
  6274. T val10, T val11, T val12, T val13, T val14,
  6275. T val15, T val16, T val17, T val18, T val19,
  6276. T val20, T val21, T val22, T val23, T val24) {
  6277. T tmp = std::min(val0,val1);
  6278. val1 = std::max(val0,val1); val0 = tmp; tmp = std::min(val3,val4); val4 = std::max(val3,val4);
  6279. val3 = tmp; tmp = std::min(val2,val4); val4 = std::max(val2,val4); val2 = std::min(tmp,val3);
  6280. val3 = std::max(tmp,val3); tmp = std::min(val6,val7); val7 = std::max(val6,val7); val6 = tmp;
  6281. tmp = std::min(val5,val7); val7 = std::max(val5,val7); val5 = std::min(tmp,val6); val6 = std::max(tmp,val6);
  6282. tmp = std::min(val9,val10); val10 = std::max(val9,val10); val9 = tmp; tmp = std::min(val8,val10);
  6283. val10 = std::max(val8,val10); val8 = std::min(tmp,val9); val9 = std::max(tmp,val9);
  6284. tmp = std::min(val12,val13); val13 = std::max(val12,val13); val12 = tmp; tmp = std::min(val11,val13);
  6285. val13 = std::max(val11,val13); val11 = std::min(tmp,val12); val12 = std::max(tmp,val12);
  6286. tmp = std::min(val15,val16); val16 = std::max(val15,val16); val15 = tmp; tmp = std::min(val14,val16);
  6287. val16 = std::max(val14,val16); val14 = std::min(tmp,val15); val15 = std::max(tmp,val15);
  6288. tmp = std::min(val18,val19); val19 = std::max(val18,val19); val18 = tmp; tmp = std::min(val17,val19);
  6289. val19 = std::max(val17,val19); val17 = std::min(tmp,val18); val18 = std::max(tmp,val18);
  6290. tmp = std::min(val21,val22); val22 = std::max(val21,val22); val21 = tmp; tmp = std::min(val20,val22);
  6291. val22 = std::max(val20,val22); val20 = std::min(tmp,val21); val21 = std::max(tmp,val21);
  6292. tmp = std::min(val23,val24); val24 = std::max(val23,val24); val23 = tmp; tmp = std::min(val2,val5);
  6293. val5 = std::max(val2,val5); val2 = tmp; tmp = std::min(val3,val6); val6 = std::max(val3,val6); val3 = tmp;
  6294. tmp = std::min(val0,val6); val6 = std::max(val0,val6); val0 = std::min(tmp,val3); val3 = std::max(tmp,val3);
  6295. tmp = std::min(val4,val7); val7 = std::max(val4,val7); val4 = tmp; tmp = std::min(val1,val7);
  6296. val7 = std::max(val1,val7); val1 = std::min(tmp,val4); val4 = std::max(tmp,val4); tmp = std::min(val11,val14);
  6297. val14 = std::max(val11,val14); val11 = tmp; tmp = std::min(val8,val14); val14 = std::max(val8,val14);
  6298. val8 = std::min(tmp,val11); val11 = std::max(tmp,val11); tmp = std::min(val12,val15);
  6299. val15 = std::max(val12,val15); val12 = tmp; tmp = std::min(val9,val15); val15 = std::max(val9,val15);
  6300. val9 = std::min(tmp,val12); val12 = std::max(tmp,val12); tmp = std::min(val13,val16);
  6301. val16 = std::max(val13,val16); val13 = tmp; tmp = std::min(val10,val16); val16 = std::max(val10,val16);
  6302. val10 = std::min(tmp,val13); val13 = std::max(tmp,val13); tmp = std::min(val20,val23);
  6303. val23 = std::max(val20,val23); val20 = tmp; tmp = std::min(val17,val23); val23 = std::max(val17,val23);
  6304. val17 = std::min(tmp,val20); val20 = std::max(tmp,val20); tmp = std::min(val21,val24);
  6305. val24 = std::max(val21,val24); val21 = tmp; tmp = std::min(val18,val24); val24 = std::max(val18,val24);
  6306. val18 = std::min(tmp,val21); val21 = std::max(tmp,val21); tmp = std::min(val19,val22);
  6307. val22 = std::max(val19,val22); val19 = tmp; val17 = std::max(val8,val17); tmp = std::min(val9,val18);
  6308. val18 = std::max(val9,val18); val9 = tmp; tmp = std::min(val0,val18); val18 = std::max(val0,val18);
  6309. val9 = std::max(tmp,val9); tmp = std::min(val10,val19); val19 = std::max(val10,val19); val10 = tmp;
  6310. tmp = std::min(val1,val19); val19 = std::max(val1,val19); val1 = std::min(tmp,val10);
  6311. val10 = std::max(tmp,val10); tmp = std::min(val11,val20); val20 = std::max(val11,val20); val11 = tmp;
  6312. tmp = std::min(val2,val20); val20 = std::max(val2,val20); val11 = std::max(tmp,val11);
  6313. tmp = std::min(val12,val21); val21 = std::max(val12,val21); val12 = tmp; tmp = std::min(val3,val21);
  6314. val21 = std::max(val3,val21); val3 = std::min(tmp,val12); val12 = std::max(tmp,val12);
  6315. tmp = std::min(val13,val22); val22 = std::max(val13,val22); val4 = std::min(val4,val22);
  6316. val13 = std::max(val4,tmp); tmp = std::min(val4,tmp); val4 = tmp; tmp = std::min(val14,val23);
  6317. val23 = std::max(val14,val23); val14 = tmp; tmp = std::min(val5,val23); val23 = std::max(val5,val23);
  6318. val5 = std::min(tmp,val14); val14 = std::max(tmp,val14); tmp = std::min(val15,val24);
  6319. val24 = std::max(val15,val24); val15 = tmp; val6 = std::min(val6,val24); tmp = std::min(val6,val15);
  6320. val15 = std::max(val6,val15); val6 = tmp; tmp = std::min(val7,val16); val7 = std::min(tmp,val19);
  6321. tmp = std::min(val13,val21); val15 = std::min(val15,val23); tmp = std::min(val7,tmp);
  6322. val7 = std::min(tmp,val15); val9 = std::max(val1,val9); val11 = std::max(val3,val11);
  6323. val17 = std::max(val5,val17); val17 = std::max(val11,val17); val17 = std::max(val9,val17);
  6324. tmp = std::min(val4,val10); val10 = std::max(val4,val10); val4 = tmp; tmp = std::min(val6,val12);
  6325. val12 = std::max(val6,val12); val6 = tmp; tmp = std::min(val7,val14); val14 = std::max(val7,val14);
  6326. val7 = tmp; tmp = std::min(val4,val6); val6 = std::max(val4,val6); val7 = std::max(tmp,val7);
  6327. tmp = std::min(val12,val14); val14 = std::max(val12,val14); val12 = tmp; val10 = std::min(val10,val14);
  6328. tmp = std::min(val6,val7); val7 = std::max(val6,val7); val6 = tmp; tmp = std::min(val10,val12);
  6329. val12 = std::max(val10,val12); val10 = std::max(val6,tmp); tmp = std::min(val6,tmp);
  6330. val17 = std::max(tmp,val17); tmp = std::min(val12,val17); val17 = std::max(val12,val17); val12 = tmp;
  6331. val7 = std::min(val7,val17); tmp = std::min(val7,val10); val10 = std::max(val7,val10); val7 = tmp;
  6332. tmp = std::min(val12,val18); val18 = std::max(val12,val18); val12 = std::max(val7,tmp);
  6333. val10 = std::min(val10,val18); tmp = std::min(val12,val20); val20 = std::max(val12,val20); val12 = tmp;
  6334. tmp = std::min(val10,val20);
  6335. return std::max(tmp,val12);
  6336. }
  6337. template<typename T>
  6338. inline T median(T val0, T val1, T val2, T val3, T val4, T val5, T val6,
  6339. T val7, T val8, T val9, T val10, T val11, T val12, T val13,
  6340. T val14, T val15, T val16, T val17, T val18, T val19, T val20,
  6341. T val21, T val22, T val23, T val24, T val25, T val26, T val27,
  6342. T val28, T val29, T val30, T val31, T val32, T val33, T val34,
  6343. T val35, T val36, T val37, T val38, T val39, T val40, T val41,
  6344. T val42, T val43, T val44, T val45, T val46, T val47, T val48) {
  6345. T tmp = std::min(val0,val32);
  6346. val32 = std::max(val0,val32); val0 = tmp; tmp = std::min(val1,val33); val33 = std::max(val1,val33); val1 = tmp;
  6347. tmp = std::min(val2,val34); val34 = std::max(val2,val34); val2 = tmp; tmp = std::min(val3,val35);
  6348. val35 = std::max(val3,val35); val3 = tmp; tmp = std::min(val4,val36); val36 = std::max(val4,val36); val4 = tmp;
  6349. tmp = std::min(val5,val37); val37 = std::max(val5,val37); val5 = tmp; tmp = std::min(val6,val38);
  6350. val38 = std::max(val6,val38); val6 = tmp; tmp = std::min(val7,val39); val39 = std::max(val7,val39); val7 = tmp;
  6351. tmp = std::min(val8,val40); val40 = std::max(val8,val40); val8 = tmp; tmp = std::min(val9,val41);
  6352. val41 = std::max(val9,val41); val9 = tmp; tmp = std::min(val10,val42); val42 = std::max(val10,val42);
  6353. val10 = tmp; tmp = std::min(val11,val43); val43 = std::max(val11,val43); val11 = tmp;
  6354. tmp = std::min(val12,val44); val44 = std::max(val12,val44); val12 = tmp; tmp = std::min(val13,val45);
  6355. val45 = std::max(val13,val45); val13 = tmp; tmp = std::min(val14,val46); val46 = std::max(val14,val46);
  6356. val14 = tmp; tmp = std::min(val15,val47); val47 = std::max(val15,val47); val15 = tmp;
  6357. tmp = std::min(val16,val48); val48 = std::max(val16,val48); val16 = tmp; tmp = std::min(val0,val16);
  6358. val16 = std::max(val0,val16); val0 = tmp; tmp = std::min(val1,val17); val17 = std::max(val1,val17);
  6359. val1 = tmp; tmp = std::min(val2,val18); val18 = std::max(val2,val18); val2 = tmp; tmp = std::min(val3,val19);
  6360. val19 = std::max(val3,val19); val3 = tmp; tmp = std::min(val4,val20); val20 = std::max(val4,val20); val4 = tmp;
  6361. tmp = std::min(val5,val21); val21 = std::max(val5,val21); val5 = tmp; tmp = std::min(val6,val22);
  6362. val22 = std::max(val6,val22); val6 = tmp; tmp = std::min(val7,val23); val23 = std::max(val7,val23); val7 = tmp;
  6363. tmp = std::min(val8,val24); val24 = std::max(val8,val24); val8 = tmp; tmp = std::min(val9,val25);
  6364. val25 = std::max(val9,val25); val9 = tmp; tmp = std::min(val10,val26); val26 = std::max(val10,val26);
  6365. val10 = tmp; tmp = std::min(val11,val27); val27 = std::max(val11,val27); val11 = tmp;
  6366. tmp = std::min(val12,val28); val28 = std::max(val12,val28); val12 = tmp; tmp = std::min(val13,val29);
  6367. val29 = std::max(val13,val29); val13 = tmp; tmp = std::min(val14,val30); val30 = std::max(val14,val30);
  6368. val14 = tmp; tmp = std::min(val15,val31); val31 = std::max(val15,val31); val15 = tmp;
  6369. tmp = std::min(val32,val48); val48 = std::max(val32,val48); val32 = tmp; tmp = std::min(val16,val32);
  6370. val32 = std::max(val16,val32); val16 = tmp; tmp = std::min(val17,val33); val33 = std::max(val17,val33);
  6371. val17 = tmp; tmp = std::min(val18,val34); val34 = std::max(val18,val34); val18 = tmp;
  6372. tmp = std::min(val19,val35); val35 = std::max(val19,val35); val19 = tmp; tmp = std::min(val20,val36);
  6373. val36 = std::max(val20,val36); val20 = tmp; tmp = std::min(val21,val37); val37 = std::max(val21,val37);
  6374. val21 = tmp; tmp = std::min(val22,val38); val38 = std::max(val22,val38); val22 = tmp;
  6375. tmp = std::min(val23,val39); val39 = std::max(val23,val39); val23 = tmp; tmp = std::min(val24,val40);
  6376. val40 = std::max(val24,val40); val24 = tmp; tmp = std::min(val25,val41); val41 = std::max(val25,val41);
  6377. val25 = tmp; tmp = std::min(val26,val42); val42 = std::max(val26,val42); val26 = tmp;
  6378. tmp = std::min(val27,val43); val43 = std::max(val27,val43); val27 = tmp; tmp = std::min(val28,val44);
  6379. val44 = std::max(val28,val44); val28 = tmp; tmp = std::min(val29,val45); val45 = std::max(val29,val45);
  6380. val29 = tmp; tmp = std::min(val30,val46); val46 = std::max(val30,val46); val30 = tmp;
  6381. tmp = std::min(val31,val47); val47 = std::max(val31,val47); val31 = tmp; tmp = std::min(val0,val8);
  6382. val8 = std::max(val0,val8); val0 = tmp; tmp = std::min(val1,val9); val9 = std::max(val1,val9); val1 = tmp;
  6383. tmp = std::min(val2,val10); val10 = std::max(val2,val10); val2 = tmp; tmp = std::min(val3,val11);
  6384. val11 = std::max(val3,val11); val3 = tmp; tmp = std::min(val4,val12); val12 = std::max(val4,val12); val4 = tmp;
  6385. tmp = std::min(val5,val13); val13 = std::max(val5,val13); val5 = tmp; tmp = std::min(val6,val14);
  6386. val14 = std::max(val6,val14); val6 = tmp; tmp = std::min(val7,val15); val15 = std::max(val7,val15); val7 = tmp;
  6387. tmp = std::min(val16,val24); val24 = std::max(val16,val24); val16 = tmp; tmp = std::min(val17,val25);
  6388. val25 = std::max(val17,val25); val17 = tmp; tmp = std::min(val18,val26); val26 = std::max(val18,val26);
  6389. val18 = tmp; tmp = std::min(val19,val27); val27 = std::max(val19,val27); val19 = tmp;
  6390. tmp = std::min(val20,val28); val28 = std::max(val20,val28); val20 = tmp; tmp = std::min(val21,val29);
  6391. val29 = std::max(val21,val29); val21 = tmp; tmp = std::min(val22,val30); val30 = std::max(val22,val30);
  6392. val22 = tmp; tmp = std::min(val23,val31); val31 = std::max(val23,val31); val23 = tmp;
  6393. tmp = std::min(val32,val40); val40 = std::max(val32,val40); val32 = tmp; tmp = std::min(val33,val41);
  6394. val41 = std::max(val33,val41); val33 = tmp; tmp = std::min(val34,val42); val42 = std::max(val34,val42);
  6395. val34 = tmp; tmp = std::min(val35,val43); val43 = std::max(val35,val43); val35 = tmp;
  6396. tmp = std::min(val36,val44); val44 = std::max(val36,val44); val36 = tmp; tmp = std::min(val37,val45);
  6397. val45 = std::max(val37,val45); val37 = tmp; tmp = std::min(val38,val46); val46 = std::max(val38,val46);
  6398. val38 = tmp; tmp = std::min(val39,val47); val47 = std::max(val39,val47); val39 = tmp;
  6399. tmp = std::min(val8,val32); val32 = std::max(val8,val32); val8 = tmp; tmp = std::min(val9,val33);
  6400. val33 = std::max(val9,val33); val9 = tmp; tmp = std::min(val10,val34); val34 = std::max(val10,val34);
  6401. val10 = tmp; tmp = std::min(val11,val35); val35 = std::max(val11,val35); val11 = tmp;
  6402. tmp = std::min(val12,val36); val36 = std::max(val12,val36); val12 = tmp; tmp = std::min(val13,val37);
  6403. val37 = std::max(val13,val37); val13 = tmp; tmp = std::min(val14,val38); val38 = std::max(val14,val38);
  6404. val14 = tmp; tmp = std::min(val15,val39); val39 = std::max(val15,val39); val15 = tmp;
  6405. tmp = std::min(val24,val48); val48 = std::max(val24,val48); val24 = tmp; tmp = std::min(val8,val16);
  6406. val16 = std::max(val8,val16); val8 = tmp; tmp = std::min(val9,val17); val17 = std::max(val9,val17);
  6407. val9 = tmp; tmp = std::min(val10,val18); val18 = std::max(val10,val18); val10 = tmp;
  6408. tmp = std::min(val11,val19); val19 = std::max(val11,val19); val11 = tmp; tmp = std::min(val12,val20);
  6409. val20 = std::max(val12,val20); val12 = tmp; tmp = std::min(val13,val21); val21 = std::max(val13,val21);
  6410. val13 = tmp; tmp = std::min(val14,val22); val22 = std::max(val14,val22); val14 = tmp;
  6411. tmp = std::min(val15,val23); val23 = std::max(val15,val23); val15 = tmp; tmp = std::min(val24,val32);
  6412. val32 = std::max(val24,val32); val24 = tmp; tmp = std::min(val25,val33); val33 = std::max(val25,val33);
  6413. val25 = tmp; tmp = std::min(val26,val34); val34 = std::max(val26,val34); val26 = tmp;
  6414. tmp = std::min(val27,val35); val35 = std::max(val27,val35); val27 = tmp; tmp = std::min(val28,val36);
  6415. val36 = std::max(val28,val36); val28 = tmp; tmp = std::min(val29,val37); val37 = std::max(val29,val37);
  6416. val29 = tmp; tmp = std::min(val30,val38); val38 = std::max(val30,val38); val30 = tmp;
  6417. tmp = std::min(val31,val39); val39 = std::max(val31,val39); val31 = tmp; tmp = std::min(val40,val48);
  6418. val48 = std::max(val40,val48); val40 = tmp; tmp = std::min(val0,val4); val4 = std::max(val0,val4);
  6419. val0 = tmp; tmp = std::min(val1,val5); val5 = std::max(val1,val5); val1 = tmp; tmp = std::min(val2,val6);
  6420. val6 = std::max(val2,val6); val2 = tmp; tmp = std::min(val3,val7); val7 = std::max(val3,val7); val3 = tmp;
  6421. tmp = std::min(val8,val12); val12 = std::max(val8,val12); val8 = tmp; tmp = std::min(val9,val13);
  6422. val13 = std::max(val9,val13); val9 = tmp; tmp = std::min(val10,val14); val14 = std::max(val10,val14);
  6423. val10 = tmp; tmp = std::min(val11,val15); val15 = std::max(val11,val15); val11 = tmp;
  6424. tmp = std::min(val16,val20); val20 = std::max(val16,val20); val16 = tmp; tmp = std::min(val17,val21);
  6425. val21 = std::max(val17,val21); val17 = tmp; tmp = std::min(val18,val22); val22 = std::max(val18,val22);
  6426. val18 = tmp; tmp = std::min(val19,val23); val23 = std::max(val19,val23); val19 = tmp;
  6427. tmp = std::min(val24,val28); val28 = std::max(val24,val28); val24 = tmp; tmp = std::min(val25,val29);
  6428. val29 = std::max(val25,val29); val25 = tmp; tmp = std::min(val26,val30); val30 = std::max(val26,val30);
  6429. val26 = tmp; tmp = std::min(val27,val31); val31 = std::max(val27,val31); val27 = tmp;
  6430. tmp = std::min(val32,val36); val36 = std::max(val32,val36); val32 = tmp; tmp = std::min(val33,val37);
  6431. val37 = std::max(val33,val37); val33 = tmp; tmp = std::min(val34,val38); val38 = std::max(val34,val38);
  6432. val34 = tmp; tmp = std::min(val35,val39); val39 = std::max(val35,val39); val35 = tmp;
  6433. tmp = std::min(val40,val44); val44 = std::max(val40,val44); val40 = tmp; tmp = std::min(val41,val45);
  6434. val45 = std::max(val41,val45); val41 = tmp; tmp = std::min(val42,val46); val46 = std::max(val42,val46);
  6435. val42 = tmp; tmp = std::min(val43,val47); val47 = std::max(val43,val47); val43 = tmp;
  6436. tmp = std::min(val4,val32); val32 = std::max(val4,val32); val4 = tmp; tmp = std::min(val5,val33);
  6437. val33 = std::max(val5,val33); val5 = tmp; tmp = std::min(val6,val34); val34 = std::max(val6,val34);
  6438. val6 = tmp; tmp = std::min(val7,val35); val35 = std::max(val7,val35); val7 = tmp;
  6439. tmp = std::min(val12,val40); val40 = std::max(val12,val40); val12 = tmp; tmp = std::min(val13,val41);
  6440. val41 = std::max(val13,val41); val13 = tmp; tmp = std::min(val14,val42); val42 = std::max(val14,val42);
  6441. val14 = tmp; tmp = std::min(val15,val43); val43 = std::max(val15,val43); val15 = tmp;
  6442. tmp = std::min(val20,val48); val48 = std::max(val20,val48); val20 = tmp; tmp = std::min(val4,val16);
  6443. val16 = std::max(val4,val16); val4 = tmp; tmp = std::min(val5,val17); val17 = std::max(val5,val17);
  6444. val5 = tmp; tmp = std::min(val6,val18); val18 = std::max(val6,val18); val6 = tmp;
  6445. tmp = std::min(val7,val19); val19 = std::max(val7,val19); val7 = tmp; tmp = std::min(val12,val24);
  6446. val24 = std::max(val12,val24); val12 = tmp; tmp = std::min(val13,val25); val25 = std::max(val13,val25);
  6447. val13 = tmp; tmp = std::min(val14,val26); val26 = std::max(val14,val26); val14 = tmp;
  6448. tmp = std::min(val15,val27); val27 = std::max(val15,val27); val15 = tmp; tmp = std::min(val20,val32);
  6449. val32 = std::max(val20,val32); val20 = tmp; tmp = std::min(val21,val33); val33 = std::max(val21,val33);
  6450. val21 = tmp; tmp = std::min(val22,val34); val34 = std::max(val22,val34); val22 = tmp;
  6451. tmp = std::min(val23,val35); val35 = std::max(val23,val35); val23 = tmp; tmp = std::min(val28,val40);
  6452. val40 = std::max(val28,val40); val28 = tmp; tmp = std::min(val29,val41); val41 = std::max(val29,val41);
  6453. val29 = tmp; tmp = std::min(val30,val42); val42 = std::max(val30,val42); val30 = tmp;
  6454. tmp = std::min(val31,val43); val43 = std::max(val31,val43); val31 = tmp; tmp = std::min(val36,val48);
  6455. val48 = std::max(val36,val48); val36 = tmp; tmp = std::min(val4,val8); val8 = std::max(val4,val8);
  6456. val4 = tmp; tmp = std::min(val5,val9); val9 = std::max(val5,val9); val5 = tmp; tmp = std::min(val6,val10);
  6457. val10 = std::max(val6,val10); val6 = tmp; tmp = std::min(val7,val11); val11 = std::max(val7,val11); val7 = tmp;
  6458. tmp = std::min(val12,val16); val16 = std::max(val12,val16); val12 = tmp; tmp = std::min(val13,val17);
  6459. val17 = std::max(val13,val17); val13 = tmp; tmp = std::min(val14,val18); val18 = std::max(val14,val18);
  6460. val14 = tmp; tmp = std::min(val15,val19); val19 = std::max(val15,val19); val15 = tmp;
  6461. tmp = std::min(val20,val24); val24 = std::max(val20,val24); val20 = tmp; tmp = std::min(val21,val25);
  6462. val25 = std::max(val21,val25); val21 = tmp; tmp = std::min(val22,val26); val26 = std::max(val22,val26);
  6463. val22 = tmp; tmp = std::min(val23,val27); val27 = std::max(val23,val27); val23 = tmp;
  6464. tmp = std::min(val28,val32); val32 = std::max(val28,val32); val28 = tmp; tmp = std::min(val29,val33);
  6465. val33 = std::max(val29,val33); val29 = tmp; tmp = std::min(val30,val34); val34 = std::max(val30,val34);
  6466. val30 = tmp; tmp = std::min(val31,val35); val35 = std::max(val31,val35); val31 = tmp;
  6467. tmp = std::min(val36,val40); val40 = std::max(val36,val40); val36 = tmp; tmp = std::min(val37,val41);
  6468. val41 = std::max(val37,val41); val37 = tmp; tmp = std::min(val38,val42); val42 = std::max(val38,val42);
  6469. val38 = tmp; tmp = std::min(val39,val43); val43 = std::max(val39,val43); val39 = tmp;
  6470. tmp = std::min(val44,val48); val48 = std::max(val44,val48); val44 = tmp; tmp = std::min(val0,val2);
  6471. val2 = std::max(val0,val2); val0 = tmp; tmp = std::min(val1,val3); val3 = std::max(val1,val3); val1 = tmp;
  6472. tmp = std::min(val4,val6); val6 = std::max(val4,val6); val4 = tmp; tmp = std::min(val5,val7);
  6473. val7 = std::max(val5,val7); val5 = tmp; tmp = std::min(val8,val10); val10 = std::max(val8,val10); val8 = tmp;
  6474. tmp = std::min(val9,val11); val11 = std::max(val9,val11); val9 = tmp; tmp = std::min(val12,val14);
  6475. val14 = std::max(val12,val14); val12 = tmp; tmp = std::min(val13,val15); val15 = std::max(val13,val15);
  6476. val13 = tmp; tmp = std::min(val16,val18); val18 = std::max(val16,val18); val16 = tmp;
  6477. tmp = std::min(val17,val19); val19 = std::max(val17,val19); val17 = tmp; tmp = std::min(val20,val22);
  6478. val22 = std::max(val20,val22); val20 = tmp; tmp = std::min(val21,val23); val23 = std::max(val21,val23);
  6479. val21 = tmp; tmp = std::min(val24,val26); val26 = std::max(val24,val26); val24 = tmp;
  6480. tmp = std::min(val25,val27); val27 = std::max(val25,val27); val25 = tmp; tmp = std::min(val28,val30);
  6481. val30 = std::max(val28,val30); val28 = tmp; tmp = std::min(val29,val31); val31 = std::max(val29,val31);
  6482. val29 = tmp; tmp = std::min(val32,val34); val34 = std::max(val32,val34); val32 = tmp;
  6483. tmp = std::min(val33,val35); val35 = std::max(val33,val35); val33 = tmp; tmp = std::min(val36,val38);
  6484. val38 = std::max(val36,val38); val36 = tmp; tmp = std::min(val37,val39); val39 = std::max(val37,val39);
  6485. val37 = tmp; tmp = std::min(val40,val42); val42 = std::max(val40,val42); val40 = tmp;
  6486. tmp = std::min(val41,val43); val43 = std::max(val41,val43); val41 = tmp; tmp = std::min(val44,val46);
  6487. val46 = std::max(val44,val46); val44 = tmp; tmp = std::min(val45,val47); val47 = std::max(val45,val47);
  6488. val45 = tmp; tmp = std::min(val2,val32); val32 = std::max(val2,val32); val2 = tmp; tmp = std::min(val3,val33);
  6489. val33 = std::max(val3,val33); val3 = tmp; tmp = std::min(val6,val36); val36 = std::max(val6,val36); val6 = tmp;
  6490. tmp = std::min(val7,val37); val37 = std::max(val7,val37); val7 = tmp; tmp = std::min(val10,val40);
  6491. val40 = std::max(val10,val40); val10 = tmp; tmp = std::min(val11,val41); val41 = std::max(val11,val41);
  6492. val11 = tmp; tmp = std::min(val14,val44); val44 = std::max(val14,val44); val14 = tmp;
  6493. tmp = std::min(val15,val45); val45 = std::max(val15,val45); val15 = tmp; tmp = std::min(val18,val48);
  6494. val48 = std::max(val18,val48); val18 = tmp; tmp = std::min(val2,val16); val16 = std::max(val2,val16);
  6495. val2 = tmp; tmp = std::min(val3,val17); val17 = std::max(val3,val17); val3 = tmp;
  6496. tmp = std::min(val6,val20); val20 = std::max(val6,val20); val6 = tmp; tmp = std::min(val7,val21);
  6497. val21 = std::max(val7,val21); val7 = tmp; tmp = std::min(val10,val24); val24 = std::max(val10,val24);
  6498. val10 = tmp; tmp = std::min(val11,val25); val25 = std::max(val11,val25); val11 = tmp;
  6499. tmp = std::min(val14,val28); val28 = std::max(val14,val28); val14 = tmp; tmp = std::min(val15,val29);
  6500. val29 = std::max(val15,val29); val15 = tmp; tmp = std::min(val18,val32); val32 = std::max(val18,val32);
  6501. val18 = tmp; tmp = std::min(val19,val33); val33 = std::max(val19,val33); val19 = tmp;
  6502. tmp = std::min(val22,val36); val36 = std::max(val22,val36); val22 = tmp; tmp = std::min(val23,val37);
  6503. val37 = std::max(val23,val37); val23 = tmp; tmp = std::min(val26,val40); val40 = std::max(val26,val40);
  6504. val26 = tmp; tmp = std::min(val27,val41); val41 = std::max(val27,val41); val27 = tmp;
  6505. tmp = std::min(val30,val44); val44 = std::max(val30,val44); val30 = tmp; tmp = std::min(val31,val45);
  6506. val45 = std::max(val31,val45); val31 = tmp; tmp = std::min(val34,val48); val48 = std::max(val34,val48);
  6507. val34 = tmp; tmp = std::min(val2,val8); val8 = std::max(val2,val8); val2 = tmp; tmp = std::min(val3,val9);
  6508. val9 = std::max(val3,val9); val3 = tmp; tmp = std::min(val6,val12); val12 = std::max(val6,val12); val6 = tmp;
  6509. tmp = std::min(val7,val13); val13 = std::max(val7,val13); val7 = tmp; tmp = std::min(val10,val16);
  6510. val16 = std::max(val10,val16); val10 = tmp; tmp = std::min(val11,val17); val17 = std::max(val11,val17);
  6511. val11 = tmp; tmp = std::min(val14,val20); val20 = std::max(val14,val20); val14 = tmp;
  6512. tmp = std::min(val15,val21); val21 = std::max(val15,val21); val15 = tmp; tmp = std::min(val18,val24);
  6513. val24 = std::max(val18,val24); val18 = tmp; tmp = std::min(val19,val25); val25 = std::max(val19,val25);
  6514. val19 = tmp; tmp = std::min(val22,val28); val28 = std::max(val22,val28); val22 = tmp;
  6515. tmp = std::min(val23,val29); val29 = std::max(val23,val29); val23 = tmp; tmp = std::min(val26,val32);
  6516. val32 = std::max(val26,val32); val26 = tmp; tmp = std::min(val27,val33); val33 = std::max(val27,val33);
  6517. val27 = tmp; tmp = std::min(val30,val36); val36 = std::max(val30,val36); val30 = tmp;
  6518. tmp = std::min(val31,val37); val37 = std::max(val31,val37); val31 = tmp; tmp = std::min(val34,val40);
  6519. val40 = std::max(val34,val40); val34 = tmp; tmp = std::min(val35,val41); val41 = std::max(val35,val41);
  6520. val35 = tmp; tmp = std::min(val38,val44); val44 = std::max(val38,val44); val38 = tmp;
  6521. tmp = std::min(val39,val45); val45 = std::max(val39,val45); val39 = tmp; tmp = std::min(val42,val48);
  6522. val48 = std::max(val42,val48); val42 = tmp; tmp = std::min(val2,val4); val4 = std::max(val2,val4);
  6523. val2 = tmp; tmp = std::min(val3,val5); val5 = std::max(val3,val5); val3 = tmp; tmp = std::min(val6,val8);
  6524. val8 = std::max(val6,val8); val6 = tmp; tmp = std::min(val7,val9); val9 = std::max(val7,val9); val7 = tmp;
  6525. tmp = std::min(val10,val12); val12 = std::max(val10,val12); val10 = tmp; tmp = std::min(val11,val13);
  6526. val13 = std::max(val11,val13); val11 = tmp; tmp = std::min(val14,val16); val16 = std::max(val14,val16);
  6527. val14 = tmp; tmp = std::min(val15,val17); val17 = std::max(val15,val17); val15 = tmp;
  6528. tmp = std::min(val18,val20); val20 = std::max(val18,val20); val18 = tmp; tmp = std::min(val19,val21);
  6529. val21 = std::max(val19,val21); val19 = tmp; tmp = std::min(val22,val24); val24 = std::max(val22,val24);
  6530. val22 = tmp; tmp = std::min(val23,val25); val25 = std::max(val23,val25); val23 = tmp;
  6531. tmp = std::min(val26,val28); val28 = std::max(val26,val28); val26 = tmp; tmp = std::min(val27,val29);
  6532. val29 = std::max(val27,val29); val27 = tmp; tmp = std::min(val30,val32); val32 = std::max(val30,val32);
  6533. val30 = tmp; tmp = std::min(val31,val33); val33 = std::max(val31,val33); val31 = tmp;
  6534. tmp = std::min(val34,val36); val36 = std::max(val34,val36); val34 = tmp; tmp = std::min(val35,val37);
  6535. val37 = std::max(val35,val37); val35 = tmp; tmp = std::min(val38,val40); val40 = std::max(val38,val40);
  6536. val38 = tmp; tmp = std::min(val39,val41); val41 = std::max(val39,val41); val39 = tmp;
  6537. tmp = std::min(val42,val44); val44 = std::max(val42,val44); val42 = tmp; tmp = std::min(val43,val45);
  6538. val45 = std::max(val43,val45); val43 = tmp; tmp = std::min(val46,val48); val48 = std::max(val46,val48);
  6539. val46 = tmp; val1 = std::max(val0,val1); val3 = std::max(val2,val3); val5 = std::max(val4,val5);
  6540. val7 = std::max(val6,val7); val9 = std::max(val8,val9); val11 = std::max(val10,val11);
  6541. val13 = std::max(val12,val13); val15 = std::max(val14,val15); val17 = std::max(val16,val17);
  6542. val19 = std::max(val18,val19); val21 = std::max(val20,val21); val23 = std::max(val22,val23);
  6543. val24 = std::min(val24,val25); val26 = std::min(val26,val27); val28 = std::min(val28,val29);
  6544. val30 = std::min(val30,val31); val32 = std::min(val32,val33); val34 = std::min(val34,val35);
  6545. val36 = std::min(val36,val37); val38 = std::min(val38,val39); val40 = std::min(val40,val41);
  6546. val42 = std::min(val42,val43); val44 = std::min(val44,val45); val46 = std::min(val46,val47);
  6547. val32 = std::max(val1,val32); val34 = std::max(val3,val34); val36 = std::max(val5,val36);
  6548. val38 = std::max(val7,val38); val9 = std::min(val9,val40); val11 = std::min(val11,val42);
  6549. val13 = std::min(val13,val44); val15 = std::min(val15,val46); val17 = std::min(val17,val48);
  6550. val24 = std::max(val9,val24); val26 = std::max(val11,val26); val28 = std::max(val13,val28);
  6551. val30 = std::max(val15,val30); val17 = std::min(val17,val32); val19 = std::min(val19,val34);
  6552. val21 = std::min(val21,val36); val23 = std::min(val23,val38); val24 = std::max(val17,val24);
  6553. val26 = std::max(val19,val26); val21 = std::min(val21,val28); val23 = std::min(val23,val30);
  6554. val24 = std::max(val21,val24); val23 = std::min(val23,val26);
  6555. return std::max(val23,val24);
  6556. }
  6557. //! Return sqrt(x^2 + y^2).
  6558. template<typename T>
  6559. inline T hypot(const T x, const T y) {
  6560. #if cimg_use_cpp11==1 && !defined(_MSC_VER)
  6561. return std::hypot(x,y);
  6562. #else
  6563. return std::sqrt(x*x + y*y);
  6564. #endif
  6565. }
  6566. //! Return sqrt(x^2 + y^2 + z^2).
  6567. template<typename T>
  6568. inline T hypot(const T x, const T y, const T z) {
  6569. return std::sqrt(x*x + y*y + z*z);
  6570. }
  6571. //! Return the factorial of n
  6572. inline double factorial(const int n) {
  6573. if (n<0) return cimg::type<double>::nan();
  6574. if (n<2) return 1;
  6575. double res = 2;
  6576. for (int i = 3; i<=n; ++i) res*=i;
  6577. return res;
  6578. }
  6579. //! Return the number of permutations of k objects in a set of n objects.
  6580. inline double permutations(const int k, const int n, const bool with_order) {
  6581. if (n<0 || k<0) return cimg::type<double>::nan();
  6582. if (k>n) return 0;
  6583. double res = 1;
  6584. for (int i = n; i>=n - k + 1; --i) res*=i;
  6585. return with_order?res:res/cimg::factorial(k);
  6586. }
  6587. inline double _fibonacci(int exp) {
  6588. double
  6589. base = (1 + std::sqrt(5.))/2,
  6590. result = 1/std::sqrt(5.);
  6591. while (exp) {
  6592. if (exp&1) result*=base;
  6593. exp>>=1;
  6594. base*=base;
  6595. }
  6596. return result;
  6597. }
  6598. //! Calculate fibonacci number.
  6599. // (Precise up to n = 78, less precise for n>78).
  6600. inline double fibonacci(const int n) {
  6601. if (n<0) return cimg::type<double>::nan();
  6602. if (n<3) return 1;
  6603. if (n<11) {
  6604. cimg_uint64 fn1 = 1, fn2 = 1, fn = 0;
  6605. for (int i = 3; i<=n; ++i) { fn = fn1 + fn2; fn2 = fn1; fn1 = fn; }
  6606. return (double)fn;
  6607. }
  6608. if (n<75) // precise up to n = 74, faster than the integer calculation above for n>10
  6609. return (double)((cimg_uint64)(_fibonacci(n) + 0.5));
  6610. if (n<94) { // precise up to n = 78, less precise for n>78 up to n = 93, overflows for n>93
  6611. cimg_uint64
  6612. fn1 = ((cimg_uint64)303836)<<32 | 3861581201UL, // 1304969544928657ULL (avoid C++98 warning with ULL)
  6613. fn2 = ((cimg_uint64)187781)<<32 | 2279239217UL, // 806515533049393ULL
  6614. fn = 0;
  6615. for (int i = 75; i<=n; ++i) { fn = fn1 + fn2; fn2 = fn1; fn1 = fn; }
  6616. return (double)fn;
  6617. }
  6618. return _fibonacci(n); // Not precise, but better than the wrong overflowing calculation
  6619. }
  6620. //! Calculate greatest common divisor.
  6621. inline long gcd(long a, long b) {
  6622. while (a) { const long c = a; a = b%a; b = c; }
  6623. return b;
  6624. }
  6625. //! Convert character to lower case.
  6626. inline char lowercase(const char x) {
  6627. return (char)((x<'A'||x>'Z')?x:x - 'A' + 'a');
  6628. }
  6629. inline double lowercase(const double x) {
  6630. return (double)((x<'A'||x>'Z')?x:x - 'A' + 'a');
  6631. }
  6632. //! Convert C-string to lower case.
  6633. inline void lowercase(char *const str) {
  6634. if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = lowercase(*ptr);
  6635. }
  6636. //! Convert character to upper case.
  6637. inline char uppercase(const char x) {
  6638. return (char)((x<'a'||x>'z')?x:x - 'a' + 'A');
  6639. }
  6640. inline double uppercase(const double x) {
  6641. return (double)((x<'a'||x>'z')?x:x - 'a' + 'A');
  6642. }
  6643. //! Convert C-string to upper case.
  6644. inline void uppercase(char *const str) {
  6645. if (str) for (char *ptr = str; *ptr; ++ptr) *ptr = uppercase(*ptr);
  6646. }
  6647. //! Return \c true if input character is blank (space, tab, or non-printable character).
  6648. inline bool is_blank(const char c) {
  6649. return (unsigned char)c<=' ';
  6650. }
  6651. // Return \c true if specified argument is in set \c [a-zA-Z0-9_].
  6652. inline bool is_varchar(const char c) {
  6653. return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_';
  6654. }
  6655. //! Return \c true if argument \p str can be considered as a regular variable name.
  6656. inline bool is_varname(const char *const str, const unsigned int length=~0U) {
  6657. if (*str>='0' && *str<='9') return false;
  6658. for (unsigned int l = 0; l<length && str[l]; ++l)
  6659. if (!is_varchar(str[l])) return false;
  6660. return true;
  6661. }
  6662. //! Read value in a C-string.
  6663. /**
  6664. \param str C-string containing the float value to read.
  6665. \return Read value.
  6666. \note Same as <tt>std::atof()</tt> extended to manage the retrieval of fractions from C-strings,
  6667. as in <em>"1/2"</em>.
  6668. **/
  6669. inline double atof(const char *const str) {
  6670. double x = 0, y = 1;
  6671. return str && cimg_sscanf(str,"%lf/%lf",&x,&y)>0?x/y:0;
  6672. }
  6673. //! Compare the first \p length characters of two C-strings, ignoring the case.
  6674. /**
  6675. \param str1 C-string.
  6676. \param str2 C-string.
  6677. \param length Number of characters to compare.
  6678. \return \c 0 if the two strings are equal, something else otherwise.
  6679. \note This function has to be defined since it is not provided by all C++-compilers (not ANSI).
  6680. **/
  6681. inline int strncasecmp(const char *const str1, const char *const str2, const int length) {
  6682. if (!length) return 0;
  6683. if (!str1) return str2?-1:0;
  6684. const char *nstr1 = str1, *nstr2 = str2;
  6685. int k, diff = 0;
  6686. for (k = 0; k<length && !(diff = lowercase(*nstr1) - lowercase(*nstr2)); ++k) { ++nstr1; ++nstr2; }
  6687. return k!=length?diff:0;
  6688. }
  6689. //! Compare two C-strings, ignoring the case.
  6690. /**
  6691. \param str1 C-string.
  6692. \param str2 C-string.
  6693. \return \c 0 if the two strings are equal, something else otherwise.
  6694. \note This function has to be defined since it is not provided by all C++-compilers (not ANSI).
  6695. **/
  6696. inline int strcasecmp(const char *const str1, const char *const str2) {
  6697. if (!str1) return str2?-1:0;
  6698. const int
  6699. l1 = (int)std::strlen(str1),
  6700. l2 = (int)std::strlen(str2);
  6701. return cimg::strncasecmp(str1,str2,1 + (l1<l2?l1:l2));
  6702. }
  6703. //! Ellipsize a string.
  6704. /**
  6705. \param str C-string.
  6706. \param l Max number of printed characters.
  6707. \param is_ending Tell if the dots are placed at the end or at the center of the ellipsized string.
  6708. **/
  6709. inline char *strellipsize(char *const str, const unsigned int l=64,
  6710. const bool is_ending=true) {
  6711. if (!str) return str;
  6712. const unsigned int nl = l<5?5:l, ls = (unsigned int)std::strlen(str);
  6713. if (ls<=nl) return str;
  6714. if (is_ending) std::strcpy(str + nl - 5,"(...)");
  6715. else {
  6716. const unsigned int ll = (nl - 5)/2 + 1 - (nl%2), lr = nl - ll - 5;
  6717. std::strcpy(str + ll,"(...)");
  6718. std::memmove(str + ll + 5,str + ls - lr,lr);
  6719. }
  6720. str[nl] = 0;
  6721. return str;
  6722. }
  6723. //! Ellipsize a string.
  6724. /**
  6725. \param str C-string.
  6726. \param res output C-string.
  6727. \param l Max number of printed characters. String 'res' must be a size of at least 'l+1'.
  6728. \param is_ending Tell if the dots are placed at the end or at the center of the ellipsized string.
  6729. **/
  6730. inline char *strellipsize(const char *const str, char *const res, const unsigned int l=64,
  6731. const bool is_ending=true) {
  6732. const unsigned int nl = l<5?5:l, ls = (unsigned int)std::strlen(str);
  6733. if (ls<=nl) { std::strcpy(res,str); return res; }
  6734. if (is_ending) {
  6735. std::strncpy(res,str,nl - 5);
  6736. std::strcpy(res + nl -5,"(...)");
  6737. } else {
  6738. const unsigned int ll = (nl - 5)/2 + 1 - (nl%2), lr = nl - ll - 5;
  6739. std::strncpy(res,str,ll);
  6740. std::strcpy(res + ll,"(...)");
  6741. std::strncpy(res + ll + 5,str + ls - lr,lr);
  6742. }
  6743. res[nl] = 0;
  6744. return res;
  6745. }
  6746. //! Remove delimiters on the start and/or end of a C-string.
  6747. /**
  6748. \param[in,out] str C-string to work with (modified at output).
  6749. \param delimiter Delimiter character code to remove.
  6750. \param is_symmetric Tells if the removal is done only if delimiters are symmetric
  6751. (both at the beginning and the end of \c s).
  6752. \param is_iterative Tells if the removal is done if several iterations are possible.
  6753. \return \c true if delimiters have been removed, \c false otherwise.
  6754. **/
  6755. inline bool strpare(char *const str, const char delimiter,
  6756. const bool is_symmetric, const bool is_iterative) {
  6757. if (!str) return false;
  6758. const int l = (int)std::strlen(str);
  6759. int p, q;
  6760. if (is_symmetric) for (p = 0, q = l - 1; p<q && str[p]==delimiter && str[q]==delimiter; ) {
  6761. --q; ++p; if (!is_iterative) break;
  6762. } else {
  6763. for (p = 0; p<l && str[p]==delimiter; ) { ++p; if (!is_iterative) break; }
  6764. for (q = l - 1; q>p && str[q]==delimiter; ) { --q; if (!is_iterative) break; }
  6765. }
  6766. const int n = q - p + 1;
  6767. if (n!=l) { std::memmove(str,str + p,(unsigned int)n); str[n] = 0; return true; }
  6768. return false;
  6769. }
  6770. //! Remove white spaces on the start and/or end of a C-string.
  6771. inline bool strpare(char *const str, const bool is_symmetric, const bool is_iterative) {
  6772. if (!str) return false;
  6773. const int l = (int)std::strlen(str);
  6774. int p, q;
  6775. if (is_symmetric) for (p = 0, q = l - 1; p<q && is_blank(str[p]) && is_blank(str[q]); ) {
  6776. --q; ++p; if (!is_iterative) break;
  6777. } else {
  6778. for (p = 0; p<l && is_blank(str[p]); ) { ++p; if (!is_iterative) break; }
  6779. for (q = l - 1; q>p && is_blank(str[q]); ) { --q; if (!is_iterative) break; }
  6780. }
  6781. const int n = q - p + 1;
  6782. if (n!=l) { std::memmove(str,str + p,(unsigned int)n); str[n] = 0; return true; }
  6783. return false;
  6784. }
  6785. //! Replace reserved characters (for Windows filename) by another character.
  6786. /**
  6787. \param[in,out] str C-string to work with (modified at output).
  6788. \param[in] c Replacement character.
  6789. **/
  6790. inline void strwindows_reserved(char *const str, const char c='_') {
  6791. for (char *s = str; *s; ++s) {
  6792. const char i = *s;
  6793. if (i=='<' || i=='>' || i==':' || i=='\"' || i=='/' || i=='\\' || i=='|' || i=='?' || i=='*') *s = c;
  6794. }
  6795. }
  6796. //! Replace escape sequences in C-strings by character values.
  6797. /**
  6798. \param[in,out] str C-string to work with (modified at output).
  6799. **/
  6800. inline void strunescape(char *const str) {
  6801. #define cimg_strunescape(ci,co) case ci : *nd = co; ++ns; break;
  6802. unsigned char val = 0;
  6803. for (char *ns = str, *nd = str; *ns || (bool)(*nd = 0); ++nd) if (*ns=='\\') switch (*(++ns)) {
  6804. cimg_strunescape('a','\a');
  6805. cimg_strunescape('b','\b');
  6806. cimg_strunescape('e',0x1B);
  6807. cimg_strunescape('f','\f');
  6808. cimg_strunescape('n','\n');
  6809. cimg_strunescape('r','\r');
  6810. cimg_strunescape('t','\t');
  6811. cimg_strunescape('v','\v');
  6812. cimg_strunescape('\\','\\');
  6813. cimg_strunescape('\'','\'');
  6814. cimg_strunescape('\"','\"');
  6815. cimg_strunescape('\?','\?');
  6816. case '0' : case '1' : case '2' : case '3' : case '4' : case '5' : case '6' : case '7' :
  6817. val = (unsigned char)(*(ns++) - '0');
  6818. if (*ns>='0' && *ns<='7') (val<<=3)|=*(ns++) - '0';
  6819. if (*ns>='0' && *ns<='7') (val<<=3)|=*(ns++) - '0';
  6820. *nd = (char)val;
  6821. break;
  6822. case 'x' : {
  6823. char c = lowercase(*(++ns));
  6824. if ((c>='0' && c<='9') || (c>='a' && c<='f')) {
  6825. val = (unsigned char)(c<='9'?c - '0':c - 'a' + 10);
  6826. c = lowercase(*(++ns));
  6827. if ((c>='0' && c<='9') || (c>='a' && c<='f')) {
  6828. (val<<=4)|=(c<='9'?c - '0':c - 'a' + 10);
  6829. ++ns;
  6830. }
  6831. *nd = (char)val;
  6832. } else *nd = c;
  6833. } break;
  6834. case 'u' : { // UTF-8 BMP
  6835. char c1, c2, c3, c4;
  6836. if ((((c1 = lowercase(ns[1]))>='0' && c1<='9') || (c1>='a' && c1<='f')) &&
  6837. (((c2 = lowercase(ns[2]))>='0' && c2<='9') || (c2>='a' && c2<='f')) &&
  6838. (((c3 = lowercase(ns[3]))>='0' && c3<='9') || (c3>='a' && c3<='f')) &&
  6839. (((c4 = lowercase(ns[4]))>='0' && c4<='9') || (c4>='a' && c4<='f'))) {
  6840. c1 = (c1<='9'?c1 - '0':c1 - 'a' + 10);
  6841. c2 = (c2<='9'?c2 - '0':c2 - 'a' + 10);
  6842. c3 = (c3<='9'?c3 - '0':c3 - 'a' + 10);
  6843. c4 = (c4<='9'?c4 - '0':c4 - 'a' + 10);
  6844. const unsigned int ival =
  6845. ((unsigned int)c1<<12) | ((unsigned int)c2<<8) | ((unsigned int)c3<<4) | c4;
  6846. if (ival<=0x007f) *nd = (char)ival;
  6847. else if (ival<=0x07ff) {
  6848. *(nd++) = (char)((ival>>6)|0xc0);
  6849. *nd = (char)((ival&0x3f)|0x80);
  6850. } else {
  6851. *(nd++) = (char)((ival>>12)|0xe0);
  6852. *(nd++) = (char)(((ival>>6)&0x3f)|0x80);
  6853. *nd = (char)((ival&0x3f)|0x80);
  6854. }
  6855. ns+=5;
  6856. } else *nd = *(ns++);
  6857. } break;
  6858. case 'U' : { // UTF-8 astral planes
  6859. char c1, c2, c3, c4, c5, c6, c7, c8;
  6860. if ((((c1 = lowercase(ns[1]))>='0' && c1<='9') || (c1>='a' && c1<='f')) &&
  6861. (((c2 = lowercase(ns[2]))>='0' && c2<='9') || (c2>='a' && c2<='f')) &&
  6862. (((c3 = lowercase(ns[3]))>='0' && c3<='9') || (c3>='a' && c3<='f')) &&
  6863. (((c4 = lowercase(ns[4]))>='0' && c4<='9') || (c4>='a' && c4<='f')) &&
  6864. (((c5 = lowercase(ns[5]))>='0' && c5<='9') || (c5>='a' && c5<='f')) &&
  6865. (((c6 = lowercase(ns[6]))>='0' && c6<='9') || (c6>='a' && c6<='f')) &&
  6866. (((c7 = lowercase(ns[7]))>='0' && c7<='9') || (c7>='a' && c7<='f')) &&
  6867. (((c8 = lowercase(ns[8]))>='0' && c8<='9') || (c8>='a' && c8<='f'))) {
  6868. c1 = (c1<='9'?c1 - '0':c1 - 'a' + 10);
  6869. c2 = (c2<='9'?c2 - '0':c2 - 'a' + 10);
  6870. c3 = (c3<='9'?c3 - '0':c3 - 'a' + 10);
  6871. c4 = (c4<='9'?c4 - '0':c4 - 'a' + 10);
  6872. c5 = (c5<='9'?c5 - '0':c5 - 'a' + 10);
  6873. c6 = (c6<='9'?c6 - '0':c6 - 'a' + 10);
  6874. c7 = (c7<='9'?c7 - '0':c7 - 'a' + 10);
  6875. c8 = (c8<='9'?c8 - '0':c8 - 'a' + 10);
  6876. const unsigned int ival =
  6877. ((unsigned int)c1<<28) | ((unsigned int)c2<<24) | ((unsigned int)c3<<20) | ((unsigned int)c4<<16) |
  6878. ((unsigned int)c5<<12) | ((unsigned int)c6<<8) | ((unsigned int)c7<<4) | (unsigned int)c8;
  6879. if (ival<=0x007f) *nd = (char)ival;
  6880. else if (ival<=0x07ff) {
  6881. *(nd++) = (char)((ival>>6)|0xc0);
  6882. *nd = (char)((ival&0x3f)|0x80);
  6883. } else if (ival<=0xffff) {
  6884. *(nd++) = (char)((ival>>12)|0xe0);
  6885. *(nd++) = (char)(((ival>>6)&0x3f)|0x80);
  6886. *nd = (char)((ival&0x3f)|0x80);
  6887. } else {
  6888. *(nd++) = (char)((ival>>18)|0xf0);
  6889. *(nd++) = (char)(((ival>>12)&0x3f)|0x80);
  6890. *(nd++) = (char)(((ival>>6)&0x3f)|0x80);
  6891. *nd = (char)((ival&0x3f)|0x80);
  6892. }
  6893. ns+=9;
  6894. } else *nd = *(ns++);
  6895. } break;
  6896. default : if (*ns) *nd = *(ns++);
  6897. }
  6898. else *nd = *(ns++);
  6899. }
  6900. // Return a temporary string describing the size of a memory buffer.
  6901. inline const char *strbuffersize(const cimg_ulong size);
  6902. // Return string that identifies the running OS.
  6903. inline const char *stros() {
  6904. #if defined(linux) || defined(__linux) || defined(__linux__)
  6905. static const char *const str = "Linux";
  6906. #elif defined(sun) || defined(__sun)
  6907. static const char *const str = "Sun OS";
  6908. #elif defined(BSD) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD__) || defined (__DragonFly__)
  6909. static const char *const str = "BSD";
  6910. #elif defined(sgi) || defined(__sgi)
  6911. static const char *const str = "Irix";
  6912. #elif defined(__MACOSX__) || defined(__APPLE__)
  6913. static const char *const str = "Mac OS";
  6914. #elif defined(unix) || defined(__unix) || defined(__unix__)
  6915. static const char *const str = "Generic Unix";
  6916. #elif defined(_MSC_VER) || defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || \
  6917. defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
  6918. static const char *const str = "Windows";
  6919. #else
  6920. const char
  6921. *const _str1 = std::getenv("OSTYPE"),
  6922. *const _str2 = _str1?_str1:std::getenv("OS"),
  6923. *const str = _str2?_str2:"Unknown OS";
  6924. #endif
  6925. return str;
  6926. }
  6927. //! Return the basename of a filename.
  6928. inline const char* basename(const char *const s, const char separator=cimg_file_separator) {
  6929. const char *p = 0, *np = s;
  6930. while (np>=s && (p=np)) np = std::strchr(np,separator) + 1;
  6931. return p;
  6932. }
  6933. // Return a random filename.
  6934. inline const char* filenamerand() {
  6935. cimg::mutex(6);
  6936. static char randomid[9];
  6937. for (unsigned int k = 0; k<8; ++k) {
  6938. const int v = (int)cimg::rand(65535)%3;
  6939. randomid[k] = (char)(v==0?('0' + ((int)cimg::rand(65535)%10)):
  6940. (v==1?('a' + ((int)cimg::rand(65535)%26)):
  6941. ('A' + ((int)cimg::rand(65535)%26))));
  6942. }
  6943. cimg::mutex(6,0);
  6944. return randomid;
  6945. }
  6946. // Convert filename as a Windows-style filename (short path name).
  6947. inline void winformat_string(char *const str) {
  6948. if (str && *str) {
  6949. #if cimg_OS==2
  6950. char *const nstr = new char[MAX_PATH];
  6951. if (GetShortPathNameA(str,nstr,MAX_PATH)) std::strcpy(str,nstr);
  6952. delete[] nstr;
  6953. #endif
  6954. }
  6955. }
  6956. // Open a file (similar to std:: fopen(), but with wide character support on Windows).
  6957. inline std::FILE *std_fopen(const char *const path, const char *const mode);
  6958. //! Open a file.
  6959. /**
  6960. \param path Path of the filename to open.
  6961. \param mode C-string describing the opening mode.
  6962. \return Opened file.
  6963. \note Same as <tt>std::fopen()</tt> but throw a \c CImgIOException when
  6964. the specified file cannot be opened, instead of returning \c 0.
  6965. **/
  6966. inline std::FILE *fopen(const char *const path, const char *const mode) {
  6967. if (!path)
  6968. throw CImgArgumentException("cimg::fopen(): Specified file path is (null).");
  6969. if (!mode)
  6970. throw CImgArgumentException("cimg::fopen(): File '%s', specified mode is (null).",
  6971. path);
  6972. std::FILE *res = 0;
  6973. if (*path=='-' && (!path[1] || path[1]=='.')) {
  6974. res = (*mode=='r')?cimg::_stdin():cimg::_stdout();
  6975. #if cimg_OS==2
  6976. if (*mode && mode[1]=='b') { // Force stdin/stdout to be in binary mode
  6977. #ifdef __BORLANDC__
  6978. if (setmode(_fileno(res),0x8000)==-1) res = 0;
  6979. #else
  6980. if (_setmode(_fileno(res),0x8000)==-1) res = 0;
  6981. #endif
  6982. }
  6983. #endif
  6984. } else res = cimg::std_fopen(path,mode);
  6985. if (!res) throw CImgIOException("cimg::fopen(): Failed to open file '%s' with mode '%s'.",
  6986. path,mode);
  6987. return res;
  6988. }
  6989. //! Close a file.
  6990. /**
  6991. \param file File to close.
  6992. \return \c 0 if file has been closed properly, something else otherwise.
  6993. \note Same as <tt>std::fclose()</tt> but display a warning message if
  6994. the file has not been closed properly.
  6995. **/
  6996. inline int fclose(std::FILE *file) {
  6997. if (!file) { warn("cimg::fclose(): Specified file is (null)."); return 0; }
  6998. if (file==cimg::_stdin(false) || file==cimg::_stdout(false)) return 0;
  6999. const int errn = std::fclose(file);
  7000. if (errn!=0) warn("cimg::fclose(): Error code %d returned during file closing.",
  7001. errn);
  7002. return errn;
  7003. }
  7004. //! Version of 'fseek()' that supports >=64bits offsets everywhere (for Windows).
  7005. inline int fseek(FILE *stream, cimg_long offset, int origin) {
  7006. #if defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
  7007. return _fseeki64(stream,(__int64)offset,origin);
  7008. #else
  7009. return std::fseek(stream,offset,origin);
  7010. #endif
  7011. }
  7012. //! Version of 'ftell()' that supports >=64bits offsets everywhere (for Windows).
  7013. inline cimg_long ftell(FILE *stream) {
  7014. #if defined(WIN64) || defined(_WIN64) || defined(__WIN64__)
  7015. return (cimg_long)_ftelli64(stream);
  7016. #else
  7017. return (cimg_long)std::ftell(stream);
  7018. #endif
  7019. }
  7020. // Get the file or directory attributes with support for UTF-8 paths (Windows only).
  7021. #if cimg_OS==2
  7022. inline DWORD win_getfileattributes(const char *const path);
  7023. #endif
  7024. //! Check if a path is a directory.
  7025. /**
  7026. \param path Specified path to test.
  7027. **/
  7028. inline bool is_directory(const char *const path) {
  7029. if (!path || !*path) return false;
  7030. #if cimg_OS==1
  7031. struct stat st_buf;
  7032. return (!stat(path,&st_buf) && S_ISDIR(st_buf.st_mode));
  7033. #elif cimg_OS==2
  7034. const DWORD res = win_getfileattributes(path);
  7035. return res!=INVALID_FILE_ATTRIBUTES && (res&FILE_ATTRIBUTE_DIRECTORY);
  7036. #else
  7037. return false;
  7038. #endif
  7039. }
  7040. //! Check if a path is a file.
  7041. /**
  7042. \param path Specified path to test.
  7043. **/
  7044. inline bool is_file(const char *const path) {
  7045. if (!path || !*path) return false;
  7046. #if cimg_OS==2
  7047. const DWORD res = cimg::win_getfileattributes(path);
  7048. return res!=INVALID_FILE_ATTRIBUTES && !(res&FILE_ATTRIBUTE_DIRECTORY);
  7049. #else
  7050. std::FILE *const file = cimg::std_fopen(path,"rb");
  7051. if (!file) return false;
  7052. cimg::fclose(file);
  7053. return !is_directory(path);
  7054. #endif
  7055. }
  7056. //! Get file size.
  7057. /**
  7058. \param filename Specified filename to get size from.
  7059. \return File size or '-1' if file does not exist.
  7060. **/
  7061. inline cimg_int64 fsize(std::FILE *const file) {
  7062. if (!file) return (cimg_int64)-1;
  7063. const long pos = std::ftell(file);
  7064. std::fseek(file,0,SEEK_END);
  7065. const cimg_int64 siz = (cimg_int64)std::ftell(file);
  7066. std::fseek(file,pos,SEEK_SET);
  7067. return siz;
  7068. }
  7069. inline cimg_int64 fsize(const char *const filename) {
  7070. std::FILE *const file = cimg::std_fopen(filename,"rb");
  7071. const cimg_int64 siz = fsize(file);
  7072. cimg::fclose(file);
  7073. return siz;
  7074. }
  7075. //! Get last write time of a given file or directory (multiple-attributes version).
  7076. /**
  7077. \param path Specified path to get attributes from.
  7078. \param[in,out] attr Type of requested time attributes.
  7079. Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second }
  7080. Replaced by read attributes after return (or -1 if an error occurred).
  7081. \param nb_attr Number of attributes to read/write.
  7082. \return Latest read attribute.
  7083. **/
  7084. template<typename T>
  7085. inline int fdate(const char *const path, T *attr, const unsigned int nb_attr) {
  7086. #define _cimg_fdate_err() for (unsigned int i = 0; i<nb_attr; ++i) attr[i] = (T)-1
  7087. int res = -1;
  7088. if (!path || !*path) { _cimg_fdate_err(); return -1; }
  7089. cimg::mutex(6);
  7090. #if cimg_OS==2
  7091. HANDLE file = CreateFileA(path,GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
  7092. if (file!=INVALID_HANDLE_VALUE) {
  7093. FILETIME _ft;
  7094. SYSTEMTIME ft;
  7095. if (GetFileTime(file,0,0,&_ft) && FileTimeToSystemTime(&_ft,&ft)) {
  7096. for (unsigned int i = 0; i<nb_attr; ++i) {
  7097. res = (int)(attr[i]==0?ft.wYear:attr[i]==1?ft.wMonth:attr[i]==2?ft.wDay:
  7098. attr[i]==3?ft.wDayOfWeek:attr[i]==4?ft.wHour:attr[i]==5?ft.wMinute:
  7099. attr[i]==6?ft.wSecond:-1);
  7100. attr[i] = (T)res;
  7101. }
  7102. } else _cimg_fdate_err();
  7103. CloseHandle(file);
  7104. } else _cimg_fdate_err();
  7105. #elif cimg_OS==1
  7106. struct stat st_buf;
  7107. if (!stat(path,&st_buf)) {
  7108. const time_t _ft = st_buf.st_mtime;
  7109. const struct tm& ft = *std::localtime(&_ft);
  7110. for (unsigned int i = 0; i<nb_attr; ++i) {
  7111. res = (int)(attr[i]==0?ft.tm_year + 1900:attr[i]==1?ft.tm_mon + 1:attr[i]==2?ft.tm_mday:
  7112. attr[i]==3?ft.tm_wday:attr[i]==4?ft.tm_hour:attr[i]==5?ft.tm_min:
  7113. attr[i]==6?ft.tm_sec:-1);
  7114. attr[i] = (T)res;
  7115. }
  7116. } else _cimg_fdate_err();
  7117. #endif
  7118. cimg::mutex(6,0);
  7119. return res;
  7120. }
  7121. //! Get last write time of a given file or directory (single-attribute version).
  7122. /**
  7123. \param path Specified path to get attributes from.
  7124. \param attr Type of requested time attributes.
  7125. Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second }
  7126. \return Specified attribute or -1 if an error occurred.
  7127. **/
  7128. inline int fdate(const char *const path, unsigned int attr) {
  7129. int out = (int)attr;
  7130. return fdate(path,&out,1);
  7131. }
  7132. //! Get current local time (multiple-attributes version).
  7133. /**
  7134. \param[in,out] attr Type of requested time attributes.
  7135. Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second |
  7136. 7=millisecond }
  7137. Replaced by read attributes after return (or -1 if an error occurred).
  7138. \param nb_attr Number of attributes to read/write.
  7139. \return Latest read attribute.
  7140. **/
  7141. template<typename T>
  7142. inline int date(T *attr, const unsigned int nb_attr) {
  7143. int res = -1;
  7144. cimg::mutex(6);
  7145. #if cimg_OS==2
  7146. SYSTEMTIME st;
  7147. GetLocalTime(&st);
  7148. for (unsigned int i = 0; i<nb_attr; ++i) {
  7149. res = (int)(attr[i]==0?st.wYear:
  7150. attr[i]==1?st.wMonth:
  7151. attr[i]==2?st.wDay:
  7152. attr[i]==3?st.wDayOfWeek:
  7153. attr[i]==4?st.wHour:
  7154. attr[i]==5?st.wMinute:
  7155. attr[i]==6?st.wSecond:
  7156. attr[i]==7?st.wMilliseconds:-1);
  7157. attr[i] = (T)res;
  7158. }
  7159. #else
  7160. struct timeval _st;
  7161. gettimeofday(&_st,0);
  7162. struct tm *st = std::localtime(&_st.tv_sec);
  7163. for (unsigned int i = 0; i<nb_attr; ++i) {
  7164. res = (int)(attr[i]==0?st->tm_year + 1900:
  7165. attr[i]==1?st->tm_mon + 1:
  7166. attr[i]==2?st->tm_mday:
  7167. attr[i]==3?st->tm_wday:
  7168. attr[i]==4?st->tm_hour:
  7169. attr[i]==5?st->tm_min:
  7170. attr[i]==6?st->tm_sec:
  7171. attr[i]==7?_st.tv_usec/1000:-1);
  7172. attr[i] = (T)res;
  7173. }
  7174. #endif
  7175. cimg::mutex(6,0);
  7176. return res;
  7177. }
  7178. //! Get current local time (single-attribute version).
  7179. /**
  7180. \param attr Type of requested time attribute.
  7181. Can be { 0=year | 1=month | 2=day | 3=day of week | 4=hour | 5=minute | 6=second |
  7182. 7=millisecond }
  7183. \return Specified attribute or -1 if an error occurred.
  7184. **/
  7185. inline int date(unsigned int attr) {
  7186. int out = (int)attr;
  7187. return date(&out,1);
  7188. }
  7189. // Get/set path to the \c curl binary.
  7190. inline const char *curl_path(const char *const user_path=0, const bool reinit_path=false);
  7191. // Get/set path to the \c dcraw binary.
  7192. inline const char *dcraw_path(const char *const user_path=0, const bool reinit_path=false);
  7193. // Get/set path to the FFMPEG's \c ffmpeg binary.
  7194. inline const char *ffmpeg_path(const char *const user_path=0, const bool reinit_path=false);
  7195. // Get/set path to the GraphicsMagick's \c gm binary.
  7196. inline const char* graphicsmagick_path(const char *const user_path=0, const bool reinit_path=false);
  7197. // Get/set path to the \c gunzip binary.
  7198. inline const char *gunzip_path(const char *const user_path=0, const bool reinit_path=false);
  7199. // Get/set path to the \c gzip binary.
  7200. inline const char *gzip_path(const char *const user_path=0, const bool reinit_path=false);
  7201. // Get/set path to the ImageMagick's \c convert binary.
  7202. inline const char* imagemagick_path(const char *const user_path=0, const bool reinit_path=false);
  7203. // Get/set path to the Medcon's \c medcon binary.
  7204. inline const char* medcon_path(const char *const user_path=0, const bool reinit_path=false);
  7205. // Get/set path to store temporary files.
  7206. inline const char* temporary_path(const char *const user_path=0, const bool reinit_path=false);
  7207. // Get/set path to the \c wget binary.
  7208. inline const char *wget_path(const char *const user_path=0, const bool reinit_path=false);
  7209. #if cimg_OS==2
  7210. // Get/set path to the \c powershell binary.
  7211. inline const char *powershell_path(const char *const user_path=0, const bool reinit_path=false);
  7212. #endif
  7213. //! Split filename into two C-strings \c body and \c extension.
  7214. /**
  7215. filename and body must not overlap!
  7216. **/
  7217. inline const char *split_filename(const char *const filename, char *const body=0) {
  7218. if (!filename) { if (body) *body = 0; return ""; }
  7219. const char * p = std::strrchr(filename,'.');
  7220. if (!p || std::strchr(p,'/') || std::strchr(p,'\\')) { // No extension.
  7221. if (body) std::strcpy(body,filename);
  7222. return filename + std::strlen(filename);
  7223. }
  7224. const unsigned int l = (unsigned int)(p - filename);
  7225. if (body) { if (l) std::memcpy(body,filename,l); body[l] = 0; }
  7226. return p + 1;
  7227. }
  7228. // Generate a numbered version of a filename.
  7229. inline char* number_filename(const char *const filename, const int number,
  7230. const unsigned int digits, char *const str);
  7231. //! Read data from file.
  7232. /**
  7233. \param[out] ptr Pointer to memory buffer that will contain the binary data read from file.
  7234. \param nmemb Number of elements to read.
  7235. \param stream File to read data from.
  7236. \return Number of read elements.
  7237. \note Same as <tt>std::fread()</tt> but may display warning message if all elements could not be read.
  7238. **/
  7239. template<typename T>
  7240. inline size_t fread(T *const ptr, const size_t nmemb, std::FILE *stream) {
  7241. if (!ptr || !stream)
  7242. throw CImgArgumentException("cimg::fread(): Invalid reading request of %u %s%s from file %p to buffer %p.",
  7243. nmemb,cimg::type<T>::string(),nmemb>1?"s":"",stream,ptr);
  7244. if (!nmemb) return 0;
  7245. const size_t wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T);
  7246. size_t to_read = nmemb, al_read = 0, l_to_read = 0, l_al_read = 0;
  7247. do {
  7248. l_to_read = (to_read*sizeof(T))<wlimitT?to_read:wlimit;
  7249. l_al_read = std::fread((void*)(ptr + al_read),sizeof(T),l_to_read,stream);
  7250. al_read+=l_al_read;
  7251. to_read-=l_al_read;
  7252. } while (l_to_read==l_al_read && to_read>0);
  7253. if (to_read>0)
  7254. warn("cimg::fread(): Only %lu/%lu elements could be read from file.",
  7255. (unsigned long)al_read,(unsigned long)nmemb);
  7256. return al_read;
  7257. }
  7258. //! Write data to file.
  7259. /**
  7260. \param ptr Pointer to memory buffer containing the binary data to write on file.
  7261. \param nmemb Number of elements to write.
  7262. \param[out] stream File to write data on.
  7263. \return Number of written elements.
  7264. \note Similar to <tt>std::fwrite</tt> but may display warning messages if all elements could not be written.
  7265. **/
  7266. template<typename T>
  7267. inline size_t fwrite(const T *ptr, const size_t nmemb, std::FILE *stream) {
  7268. if (!ptr || !stream)
  7269. throw CImgArgumentException("cimg::fwrite(): Invalid writing request of %u %s%s from buffer %p to file %p.",
  7270. nmemb,cimg::type<T>::string(),nmemb>1?"s":"",ptr,stream);
  7271. if (!nmemb) return 0;
  7272. const size_t wlimitT = 63*1024*1024, wlimit = wlimitT/sizeof(T);
  7273. size_t to_write = nmemb, al_write = 0, l_to_write = 0, l_al_write = 0;
  7274. do {
  7275. l_to_write = (to_write*sizeof(T))<wlimitT?to_write:wlimit;
  7276. l_al_write = std::fwrite((void*)(ptr + al_write),sizeof(T),l_to_write,stream);
  7277. al_write+=l_al_write;
  7278. to_write-=l_al_write;
  7279. } while (l_to_write==l_al_write && to_write>0);
  7280. if (to_write>0)
  7281. warn("cimg::fwrite(): Only %lu/%lu elements could be written in file.",
  7282. (unsigned long)al_write,(unsigned long)nmemb);
  7283. return al_write;
  7284. }
  7285. //! Create an empty file.
  7286. /**
  7287. \param file Input file (can be \c 0 if \c filename is set).
  7288. \param filename Filename, as a C-string (can be \c 0 if \c file is set).
  7289. **/
  7290. inline void fempty(std::FILE *const file, const char *const filename) {
  7291. if (!file && !filename)
  7292. throw CImgArgumentException("cimg::fempty(): Specified filename is (null).");
  7293. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  7294. if (!file) cimg::fclose(nfile);
  7295. }
  7296. // Try to guess format from an image file.
  7297. inline const char *ftype(std::FILE *const file, const char *const filename);
  7298. // Get or set load from network mode (can be { 0=disabled | 1=enabled }).
  7299. inline bool& network_mode(const bool value, const bool is_set) {
  7300. static bool mode = true;
  7301. if (is_set) { cimg::mutex(0); mode = value; cimg::mutex(0,0); }
  7302. return mode;
  7303. }
  7304. inline bool& network_mode() {
  7305. return network_mode(false,false);
  7306. }
  7307. // Load file from network as a local temporary file.
  7308. inline char *load_network(const char *const url, char *const filename_local,
  7309. const unsigned int timeout=0, const bool try_fallback=false,
  7310. const char *const referer=0, const char *const user_agent=0);
  7311. //! Return options specified on the command line.
  7312. inline const char* option(const char *const name, const int argc, const char *const *const argv,
  7313. const char *const _default, const char *const usage, const bool reset_static) {
  7314. static bool first = true, visu = false;
  7315. if (reset_static) { first = true; return 0; }
  7316. const char *res = 0;
  7317. if (first) {
  7318. first = false;
  7319. visu = cimg::option("-h",argc,argv,(char*)0,(char*)0,false)!=0;
  7320. visu |= cimg::option("-help",argc,argv,(char*)0,(char*)0,false)!=0;
  7321. visu |= cimg::option("--help",argc,argv,(char*)0,(char*)0,false)!=0;
  7322. }
  7323. if (!name && visu) {
  7324. if (usage) {
  7325. std::fprintf(cimg::output(),"\n %s%s%s",cimg::t_red,cimg::basename(argv[0]),cimg::t_normal);
  7326. std::fprintf(cimg::output(),": %s",usage);
  7327. std::fprintf(cimg::output()," (%s, %s)\n\n",cimg_date,cimg_time);
  7328. }
  7329. if (_default) std::fprintf(cimg::output(),"%s\n",_default);
  7330. }
  7331. if (name) {
  7332. if (argc>0) {
  7333. int k = 0;
  7334. while (k<argc && std::strcmp(argv[k],name)) ++k;
  7335. res = (k++==argc?_default:(k==argc?argv[--k]:argv[k]));
  7336. } else res = _default;
  7337. if (visu && usage) std::fprintf(cimg::output()," %s%-16s%s %-24s %s%s%s\n",
  7338. cimg::t_bold,name,cimg::t_normal,res?res:"0",
  7339. cimg::t_green,usage,cimg::t_normal);
  7340. }
  7341. return res;
  7342. }
  7343. inline const char* option(const char *const name, const int argc, const char *const *const argv,
  7344. const char *const _default, const char *const usage=0) {
  7345. return option(name,argc,argv,_default,usage,false);
  7346. }
  7347. inline bool option(const char *const name, const int argc, const char *const *const argv,
  7348. const bool _default, const char *const usage=0) {
  7349. const char *const s = cimg::option(name,argc,argv,(char*)0);
  7350. const bool res = s?(cimg::strcasecmp(s,"false") && cimg::strcasecmp(s,"off") && cimg::strcasecmp(s,"0")):_default;
  7351. cimg::option(name,0,0,res?"true":"false",usage);
  7352. return res;
  7353. }
  7354. inline int option(const char *const name, const int argc, const char *const *const argv,
  7355. const int _default, const char *const usage=0) {
  7356. const char *const s = cimg::option(name,argc,argv,(char*)0);
  7357. const int res = s?std::atoi(s):_default;
  7358. char *const tmp = new char[256];
  7359. cimg_snprintf(tmp,256,"%d",res);
  7360. cimg::option(name,0,0,tmp,usage);
  7361. delete[] tmp;
  7362. return res;
  7363. }
  7364. inline char option(const char *const name, const int argc, const char *const *const argv,
  7365. const char _default, const char *const usage=0) {
  7366. const char *const s = cimg::option(name,argc,argv,(char*)0);
  7367. const char res = s?*s:_default;
  7368. char tmp[8];
  7369. *tmp = res; tmp[1] = 0;
  7370. cimg::option(name,0,0,tmp,usage);
  7371. return res;
  7372. }
  7373. inline float option(const char *const name, const int argc, const char *const *const argv,
  7374. const float _default, const char *const usage=0) {
  7375. const char *const s = cimg::option(name,argc,argv,(char*)0);
  7376. const float res = s?(float)cimg::atof(s):_default;
  7377. char *const tmp = new char[256];
  7378. cimg_snprintf(tmp,256,"%g",res);
  7379. cimg::option(name,0,0,tmp,usage);
  7380. delete[] tmp;
  7381. return res;
  7382. }
  7383. inline double option(const char *const name, const int argc, const char *const *const argv,
  7384. const double _default, const char *const usage=0) {
  7385. const char *const s = cimg::option(name,argc,argv,(char*)0);
  7386. const double res = s?cimg::atof(s):_default;
  7387. char *const tmp = new char[256];
  7388. cimg_snprintf(tmp,256,"%g",res);
  7389. cimg::option(name,0,0,tmp,usage);
  7390. delete[] tmp;
  7391. return res;
  7392. }
  7393. //! Print information about \CImg environment variables.
  7394. /**
  7395. \note Output is done on the default output stream.
  7396. **/
  7397. inline void info() {
  7398. std::fprintf(cimg::output(),"\n %s%sCImg Library %u.%u.%u%s, compiled %s ( %s ) with the following flags:\n\n",
  7399. cimg::t_red,cimg::t_bold,cimg_version/100,(cimg_version/10)%10,cimg_version%10,
  7400. cimg::t_normal,cimg_date,cimg_time);
  7401. std::fprintf(cimg::output()," > Operating System: %s%-13s%s %s('cimg_OS'=%d)%s\n",
  7402. cimg::t_bold,
  7403. cimg_OS==1?"Unix":(cimg_OS==2?"Windows":"Unknown"),
  7404. cimg::t_normal,cimg::t_green,
  7405. cimg_OS,
  7406. cimg::t_normal);
  7407. std::fprintf(cimg::output()," > CPU endianness: %s%s Endian%s\n",
  7408. cimg::t_bold,
  7409. cimg::endianness()?"Big":"Little",
  7410. cimg::t_normal);
  7411. std::fprintf(cimg::output()," > Verbosity mode: %s%-13s%s %s('cimg_verbosity'=%d)%s\n",
  7412. cimg::t_bold,
  7413. cimg_verbosity==0?"Quiet":
  7414. cimg_verbosity==1?"Console":
  7415. cimg_verbosity==2?"Dialog":
  7416. cimg_verbosity==3?"Console+Warnings":"Dialog+Warnings",
  7417. cimg::t_normal,cimg::t_green,
  7418. cimg_verbosity,
  7419. cimg::t_normal);
  7420. std::fprintf(cimg::output()," > Stricts warnings: %s%-13s%s %s('cimg_strict_warnings' %s)%s\n",
  7421. cimg::t_bold,
  7422. #ifdef cimg_strict_warnings
  7423. "Yes",cimg::t_normal,cimg::t_green,"defined",
  7424. #else
  7425. "No",cimg::t_normal,cimg::t_green,"undefined",
  7426. #endif
  7427. cimg::t_normal);
  7428. std::fprintf(cimg::output()," > Support for C++11: %s%-13s%s %s('cimg_use_cpp11'=%d)%s\n",
  7429. cimg::t_bold,
  7430. cimg_use_cpp11?"Yes":"No",
  7431. cimg::t_normal,cimg::t_green,
  7432. (int)cimg_use_cpp11,
  7433. cimg::t_normal);
  7434. std::fprintf(cimg::output()," > Using VT100 messages: %s%-13s%s %s('cimg_use_vt100' %s)%s\n",
  7435. cimg::t_bold,
  7436. #ifdef cimg_use_vt100
  7437. "Yes",cimg::t_normal,cimg::t_green,"defined",
  7438. #else
  7439. "No",cimg::t_normal,cimg::t_green,"undefined",
  7440. #endif
  7441. cimg::t_normal);
  7442. std::fprintf(cimg::output()," > Display type: %s%-13s%s %s('cimg_display'=%d)%s\n",
  7443. cimg::t_bold,
  7444. cimg_display==0?"No display":cimg_display==1?"X11":cimg_display==2?"Windows GDI":"Unknown",
  7445. cimg::t_normal,cimg::t_green,
  7446. (int)cimg_display,
  7447. cimg::t_normal);
  7448. #if cimg_display==1
  7449. std::fprintf(cimg::output()," > Using XShm for X11: %s%-13s%s %s('cimg_use_xshm' %s)%s\n",
  7450. cimg::t_bold,
  7451. #ifdef cimg_use_xshm
  7452. "Yes",cimg::t_normal,cimg::t_green,"defined",
  7453. #else
  7454. "No",cimg::t_normal,cimg::t_green,"undefined",
  7455. #endif
  7456. cimg::t_normal);
  7457. std::fprintf(cimg::output()," > Using XRand for X11: %s%-13s%s %s('cimg_use_xrandr' %s)%s\n",
  7458. cimg::t_bold,
  7459. #ifdef cimg_use_xrandr
  7460. "Yes",cimg::t_normal,cimg::t_green,"defined",
  7461. #else
  7462. "No",cimg::t_normal,cimg::t_green,"undefined",
  7463. #endif
  7464. cimg::t_normal);
  7465. #endif
  7466. std::fprintf(cimg::output()," > Using OpenMP: %s%-13s%s %s('cimg_use_openmp' %s)%s\n",
  7467. cimg::t_bold,
  7468. #if cimg_use_openmp!=0
  7469. "Yes",cimg::t_normal,cimg::t_green,"defined",
  7470. #else
  7471. "No",cimg::t_normal,cimg::t_green,"undefined",
  7472. #endif
  7473. cimg::t_normal);
  7474. std::fprintf(cimg::output()," > Using PNG library: %s%-13s%s %s('cimg_use_png' %s)%s\n",
  7475. cimg::t_bold,
  7476. #ifdef cimg_use_png
  7477. "Yes",cimg::t_normal,cimg::t_green,"defined",
  7478. #else
  7479. "No",cimg::t_normal,cimg::t_green,"undefined",
  7480. #endif
  7481. cimg::t_normal);
  7482. std::fprintf(cimg::output()," > Using JPEG library: %s%-13s%s %s('cimg_use_jpeg' %s)%s\n",
  7483. cimg::t_bold,
  7484. #ifdef cimg_use_jpeg
  7485. "Yes",cimg::t_normal,cimg::t_green,"defined",
  7486. #else
  7487. "No",cimg::t_normal,cimg::t_green,"undefined",
  7488. #endif
  7489. cimg::t_normal);
  7490. std::fprintf(cimg::output()," > Using TIFF library: %s%-13s%s %s('cimg_use_tiff' %s)%s\n",
  7491. cimg::t_bold,
  7492. #ifdef cimg_use_tiff
  7493. "Yes",cimg::t_normal,cimg::t_green,"defined",
  7494. #else
  7495. "No",cimg::t_normal,cimg::t_green,"undefined",
  7496. #endif
  7497. cimg::t_normal);
  7498. std::fprintf(cimg::output()," > Using Magick++ library: %s%-13s%s %s('cimg_use_magick' %s)%s\n",
  7499. cimg::t_bold,
  7500. #ifdef cimg_use_magick
  7501. "Yes",cimg::t_normal,cimg::t_green,"defined",
  7502. #else
  7503. "No",cimg::t_normal,cimg::t_green,"undefined",
  7504. #endif
  7505. cimg::t_normal);
  7506. std::fprintf(cimg::output()," > Using FFTW3 library: %s%-13s%s %s('cimg_use_fftw3' %s)%s\n",
  7507. cimg::t_bold,
  7508. #ifdef cimg_use_fftw3
  7509. "Yes",cimg::t_normal,cimg::t_green,"defined",
  7510. #else
  7511. "No",cimg::t_normal,cimg::t_green,"undefined",
  7512. #endif
  7513. cimg::t_normal);
  7514. std::fprintf(cimg::output()," > Using LAPACK library: %s%-13s%s %s('cimg_use_lapack' %s)%s\n",
  7515. cimg::t_bold,
  7516. #ifdef cimg_use_lapack
  7517. "Yes",cimg::t_normal,cimg::t_green,"defined",
  7518. #else
  7519. "No",cimg::t_normal,cimg::t_green,"undefined",
  7520. #endif
  7521. cimg::t_normal);
  7522. char *const tmp = new char[1024];
  7523. cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::curl_path());
  7524. std::fprintf(cimg::output()," > Path of 'curl': %s%-13s%s\n",
  7525. cimg::t_bold,
  7526. tmp,
  7527. cimg::t_normal);
  7528. cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::dcraw_path());
  7529. std::fprintf(cimg::output()," > Path of 'dcraw': %s%-13s%s\n",
  7530. cimg::t_bold,
  7531. tmp,
  7532. cimg::t_normal);
  7533. cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::ffmpeg_path());
  7534. std::fprintf(cimg::output()," > Path of 'ffmpeg': %s%-13s%s\n",
  7535. cimg::t_bold,
  7536. tmp,
  7537. cimg::t_normal);
  7538. cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::graphicsmagick_path());
  7539. std::fprintf(cimg::output()," > Path of 'graphicsmagick': %s%-13s%s\n",
  7540. cimg::t_bold,
  7541. tmp,
  7542. cimg::t_normal);
  7543. cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::gunzip_path());
  7544. std::fprintf(cimg::output()," > Path of 'gunzip': %s%-13s%s\n",
  7545. cimg::t_bold,
  7546. tmp,
  7547. cimg::t_normal);
  7548. cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::gzip_path());
  7549. std::fprintf(cimg::output()," > Path of 'gzip': %s%-13s%s\n",
  7550. cimg::t_bold,
  7551. tmp,
  7552. cimg::t_normal);
  7553. cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::imagemagick_path());
  7554. std::fprintf(cimg::output()," > Path of 'imagemagick': %s%-13s%s\n",
  7555. cimg::t_bold,
  7556. tmp,
  7557. cimg::t_normal);
  7558. cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::medcon_path());
  7559. std::fprintf(cimg::output()," > Path of 'medcon': %s%-13s%s\n",
  7560. cimg::t_bold,
  7561. tmp,
  7562. cimg::t_normal);
  7563. cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::temporary_path());
  7564. std::fprintf(cimg::output()," > Temporary path: %s%-13s%s\n",
  7565. cimg::t_bold,
  7566. tmp,
  7567. cimg::t_normal);
  7568. cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::wget_path());
  7569. std::fprintf(cimg::output()," > Path of 'wget': %s%-13s%s\n",
  7570. cimg::t_bold,
  7571. tmp,
  7572. cimg::t_normal);
  7573. #if cimg_OS==2
  7574. cimg_snprintf(tmp,1024,"\"%.1020s\"",cimg::powershell_path());
  7575. std::fprintf(cimg::output()," > Path of 'powershell_path': %s%-13s%s\n",
  7576. cimg::t_bold,
  7577. tmp,
  7578. cimg::t_normal);
  7579. #endif
  7580. std::fprintf(cimg::output(),"\n");
  7581. delete[] tmp;
  7582. }
  7583. // Declare LAPACK function signatures if LAPACK support is enabled.
  7584. #ifdef cimg_use_lapack
  7585. template<typename T>
  7586. inline void getrf(int &N, T *lapA, int *IPIV, int &INFO) {
  7587. dgetrf_(&N,&N,lapA,&N,IPIV,&INFO);
  7588. }
  7589. inline void getrf(int &N, float *lapA, int *IPIV, int &INFO) {
  7590. sgetrf_(&N,&N,lapA,&N,IPIV,&INFO);
  7591. }
  7592. template<typename T>
  7593. inline void getri(int &N, T *lapA, int *IPIV, T* WORK, int &LWORK, int &INFO) {
  7594. dgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);
  7595. }
  7596. inline void getri(int &N, float *lapA, int *IPIV, float* WORK, int &LWORK, int &INFO) {
  7597. sgetri_(&N,lapA,&N,IPIV,WORK,&LWORK,&INFO);
  7598. }
  7599. template<typename T>
  7600. inline void gesvd(char &JOB, int &M, int &N, T *lapA, int &MN,
  7601. T *lapS, T *lapU, T *lapV, T *WORK, int &LWORK, int &INFO) {
  7602. dgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO);
  7603. }
  7604. inline void gesvd(char &JOB, int &M, int &N, float *lapA, int &MN,
  7605. float *lapS, float *lapU, float *lapV, float *WORK, int &LWORK, int &INFO) {
  7606. sgesvd_(&JOB,&JOB,&M,&N,lapA,&MN,lapS,lapU,&M,lapV,&N,WORK,&LWORK,&INFO);
  7607. }
  7608. template<typename T>
  7609. inline void getrs(char &TRANS, int &N, T *lapA, int *IPIV, T *lapB, int &INFO) {
  7610. int one = 1;
  7611. dgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO);
  7612. }
  7613. inline void getrs(char &TRANS, int &N, float *lapA, int *IPIV, float *lapB, int &INFO) {
  7614. int one = 1;
  7615. sgetrs_(&TRANS,&N,&one,lapA,&N,IPIV,lapB,&N,&INFO);
  7616. }
  7617. template<typename T>
  7618. inline void syev(char &JOB, char &UPLO, int &N, T *lapA, T *lapW, T *WORK, int &LWORK, int &INFO) {
  7619. dsyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO);
  7620. }
  7621. inline void syev(char &JOB, char &UPLO, int &N, float *lapA, float *lapW, float *WORK, int &LWORK, int &INFO) {
  7622. ssyev_(&JOB,&UPLO,&N,lapA,&N,lapW,WORK,&LWORK,&INFO);
  7623. }
  7624. template<typename T>
  7625. inline void sgels(char & TRANS, int &M, int &N, int &NRHS, T* lapA, int &LDA,
  7626. T* lapB, int &LDB, T* WORK, int &LWORK, int &INFO) {
  7627. dgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO);
  7628. }
  7629. inline void sgels(char & TRANS, int &M, int &N, int &NRHS, float* lapA, int &LDA,
  7630. float* lapB, int &LDB, float* WORK, int &LWORK, int &INFO) {
  7631. sgels_(&TRANS, &M, &N, &NRHS, lapA, &LDA, lapB, &LDB, WORK, &LWORK, &INFO);
  7632. }
  7633. #endif
  7634. } // namespace cimg { ...
  7635. /*------------------------------------------------
  7636. #
  7637. #
  7638. # Definition of mathematical operators and
  7639. # external functions.
  7640. #
  7641. #
  7642. -------------------------------------------------*/
  7643. #define _cimg_create_operator(typ) \
  7644. template<typename T> \
  7645. inline CImg<typename cimg::superset<T,typ>::type> operator+(const typ val, const CImg<T>& img) { \
  7646. return img + val; \
  7647. } \
  7648. template<typename T> \
  7649. inline CImg<typename cimg::superset<T,typ>::type> operator-(const typ val, const CImg<T>& img) { \
  7650. typedef typename cimg::superset<T,typ>::type Tt; \
  7651. return CImg<Tt>(img._width,img._height,img._depth,img._spectrum,val)-=img; \
  7652. } \
  7653. template<typename T> \
  7654. inline CImg<typename cimg::superset<T,typ>::type> operator*(const typ val, const CImg<T>& img) { \
  7655. return img*val; \
  7656. } \
  7657. template<typename T> \
  7658. inline CImg<typename cimg::superset<T,typ>::type> operator/(const typ val, const CImg<T>& img) { \
  7659. return val*img.get_invert(); \
  7660. } \
  7661. template<typename T> \
  7662. inline CImg<typename cimg::superset<T,typ>::type> operator&(const typ val, const CImg<T>& img) { \
  7663. return img & val; \
  7664. } \
  7665. template<typename T> \
  7666. inline CImg<typename cimg::superset<T,typ>::type> operator|(const typ val, const CImg<T>& img) { \
  7667. return img | val; \
  7668. } \
  7669. template<typename T> \
  7670. inline CImg<typename cimg::superset<T,typ>::type> operator^(const typ val, const CImg<T>& img) { \
  7671. return img ^ val; \
  7672. } \
  7673. template<typename T> \
  7674. inline bool operator==(const typ val, const CImg<T>& img) { \
  7675. return img == val; \
  7676. } \
  7677. template<typename T> \
  7678. inline bool operator!=(const typ val, const CImg<T>& img) { \
  7679. return img != val; \
  7680. }
  7681. _cimg_create_operator(bool)
  7682. _cimg_create_operator(unsigned char)
  7683. _cimg_create_operator(char)
  7684. _cimg_create_operator(signed char)
  7685. _cimg_create_operator(unsigned short)
  7686. _cimg_create_operator(short)
  7687. _cimg_create_operator(unsigned int)
  7688. _cimg_create_operator(int)
  7689. _cimg_create_operator(cimg_uint64)
  7690. _cimg_create_operator(cimg_int64)
  7691. _cimg_create_operator(float)
  7692. _cimg_create_operator(double)
  7693. _cimg_create_operator(long double)
  7694. template<typename T>
  7695. inline CImg<_cimg_Tfloat> operator+(const char *const expression, const CImg<T>& img) {
  7696. return img + expression;
  7697. }
  7698. template<typename T>
  7699. inline CImg<_cimg_Tfloat> operator-(const char *const expression, const CImg<T>& img) {
  7700. return CImg<_cimg_Tfloat>(img,false).fill(expression,true)-=img;
  7701. }
  7702. template<typename T>
  7703. inline CImg<_cimg_Tfloat> operator*(const char *const expression, const CImg<T>& img) {
  7704. return img*expression;
  7705. }
  7706. template<typename T>
  7707. inline CImg<_cimg_Tfloat> operator/(const char *const expression, const CImg<T>& img) {
  7708. return expression*img.get_invert();
  7709. }
  7710. template<typename T>
  7711. inline CImg<T> operator&(const char *const expression, const CImg<T>& img) {
  7712. return img & expression;
  7713. }
  7714. template<typename T>
  7715. inline CImg<T> operator|(const char *const expression, const CImg<T>& img) {
  7716. return img | expression;
  7717. }
  7718. template<typename T>
  7719. inline CImg<T> operator^(const char *const expression, const CImg<T>& img) {
  7720. return img ^ expression;
  7721. }
  7722. template<typename T>
  7723. inline bool operator==(const char *const expression, const CImg<T>& img) {
  7724. return img==expression;
  7725. }
  7726. template<typename T>
  7727. inline bool operator!=(const char *const expression, const CImg<T>& img) {
  7728. return img!=expression;
  7729. }
  7730. template<typename T>
  7731. inline CImg<T> transpose(const CImg<T>& instance) {
  7732. return instance.get_transpose();
  7733. }
  7734. template<typename T>
  7735. inline CImg<_cimg_Tfloat> invert(const CImg<T>& instance, const bool use_LU=false, const float lambda=0) {
  7736. return instance.get_invert(use_LU,lambda);
  7737. }
  7738. #define _cimg_create_pointwise_function(name) \
  7739. template<typename T> \
  7740. inline CImg<_cimg_Tfloat> name(const CImg<T>& instance) { \
  7741. return instance.get_##name(); \
  7742. }
  7743. _cimg_create_pointwise_function(sqr)
  7744. _cimg_create_pointwise_function(sqrt)
  7745. _cimg_create_pointwise_function(erf)
  7746. _cimg_create_pointwise_function(exp)
  7747. _cimg_create_pointwise_function(log)
  7748. _cimg_create_pointwise_function(log2)
  7749. _cimg_create_pointwise_function(log10)
  7750. _cimg_create_pointwise_function(abs)
  7751. _cimg_create_pointwise_function(sign)
  7752. _cimg_create_pointwise_function(cos)
  7753. _cimg_create_pointwise_function(sin)
  7754. _cimg_create_pointwise_function(sinc)
  7755. _cimg_create_pointwise_function(tan)
  7756. _cimg_create_pointwise_function(acos)
  7757. _cimg_create_pointwise_function(asin)
  7758. _cimg_create_pointwise_function(atan)
  7759. _cimg_create_pointwise_function(cosh)
  7760. _cimg_create_pointwise_function(sinh)
  7761. _cimg_create_pointwise_function(tanh)
  7762. _cimg_create_pointwise_function(acosh)
  7763. _cimg_create_pointwise_function(asinh)
  7764. _cimg_create_pointwise_function(atanh)
  7765. /*-----------------------------------
  7766. #
  7767. # Define the CImgDisplay structure
  7768. #
  7769. ----------------------------------*/
  7770. //! Allow the creation of windows, display images on them and manage user events (keyboard, mouse and windows events).
  7771. /**
  7772. CImgDisplay methods rely on a low-level graphic library to perform: it can be either \b X-Window
  7773. (X11, for Unix-based systems) or \b GDI32 (for Windows-based systems).
  7774. If both libraries are missing, CImgDisplay will not be able to display images on screen, and will enter
  7775. a minimal mode where warning messages will be outputted each time the program is trying to call one of the
  7776. CImgDisplay method.
  7777. The configuration variable \c cimg_display tells about the graphic library used.
  7778. It is set automatically by \CImg when one of these graphic libraries has been detected.
  7779. But, you can override its value if necessary. Valid choices are:
  7780. - 0: Disable display capabilities.
  7781. - 1: Use \b X-Window (X11) library.
  7782. - 2: Use \b GDI32 library.
  7783. Remember to link your program against \b X11 or \b GDI32 libraries if you use CImgDisplay.
  7784. **/
  7785. struct CImgDisplay {
  7786. cimg_uint64 _timer, _fps_frames, _fps_timer;
  7787. unsigned int _width, _height, _normalization;
  7788. float _fps_fps, _min, _max;
  7789. bool _is_fullscreen;
  7790. char *_title;
  7791. unsigned int _window_width, _window_height, _button, *_keys, *_released_keys;
  7792. int _window_x, _window_y, _mouse_x, _mouse_y, _wheel;
  7793. bool _is_closed, _is_resized, _is_moved, _is_event,
  7794. _is_keyESC, _is_keyF1, _is_keyF2, _is_keyF3, _is_keyF4, _is_keyF5, _is_keyF6, _is_keyF7,
  7795. _is_keyF8, _is_keyF9, _is_keyF10, _is_keyF11, _is_keyF12, _is_keyPAUSE, _is_key1, _is_key2,
  7796. _is_key3, _is_key4, _is_key5, _is_key6, _is_key7, _is_key8, _is_key9, _is_key0,
  7797. _is_keyBACKSPACE, _is_keyINSERT, _is_keyHOME, _is_keyPAGEUP, _is_keyTAB, _is_keyQ, _is_keyW, _is_keyE,
  7798. _is_keyR, _is_keyT, _is_keyY, _is_keyU, _is_keyI, _is_keyO, _is_keyP, _is_keyDELETE,
  7799. _is_keyEND, _is_keyPAGEDOWN, _is_keyCAPSLOCK, _is_keyA, _is_keyS, _is_keyD, _is_keyF, _is_keyG,
  7800. _is_keyH, _is_keyJ, _is_keyK, _is_keyL, _is_keyENTER, _is_keySHIFTLEFT, _is_keyZ, _is_keyX,
  7801. _is_keyC, _is_keyV, _is_keyB, _is_keyN, _is_keyM, _is_keySHIFTRIGHT, _is_keyARROWUP, _is_keyCTRLLEFT,
  7802. _is_keyAPPLEFT, _is_keyALT, _is_keySPACE, _is_keyALTGR, _is_keyAPPRIGHT, _is_keyMENU, _is_keyCTRLRIGHT,
  7803. _is_keyARROWLEFT, _is_keyARROWDOWN, _is_keyARROWRIGHT, _is_keyPAD0, _is_keyPAD1, _is_keyPAD2, _is_keyPAD3,
  7804. _is_keyPAD4, _is_keyPAD5, _is_keyPAD6, _is_keyPAD7, _is_keyPAD8, _is_keyPAD9, _is_keyPADADD, _is_keyPADSUB,
  7805. _is_keyPADMUL, _is_keyPADDIV;
  7806. //@}
  7807. //---------------------------
  7808. //
  7809. //! \name Plugins
  7810. //@{
  7811. //---------------------------
  7812. #ifdef cimgdisplay_plugin
  7813. #include cimgdisplay_plugin
  7814. #endif
  7815. #ifdef cimgdisplay_plugin1
  7816. #include cimgdisplay_plugin1
  7817. #endif
  7818. #ifdef cimgdisplay_plugin2
  7819. #include cimgdisplay_plugin2
  7820. #endif
  7821. #ifdef cimgdisplay_plugin3
  7822. #include cimgdisplay_plugin3
  7823. #endif
  7824. #ifdef cimgdisplay_plugin4
  7825. #include cimgdisplay_plugin4
  7826. #endif
  7827. #ifdef cimgdisplay_plugin5
  7828. #include cimgdisplay_plugin5
  7829. #endif
  7830. #ifdef cimgdisplay_plugin6
  7831. #include cimgdisplay_plugin6
  7832. #endif
  7833. #ifdef cimgdisplay_plugin7
  7834. #include cimgdisplay_plugin7
  7835. #endif
  7836. #ifdef cimgdisplay_plugin8
  7837. #include cimgdisplay_plugin8
  7838. #endif
  7839. //@}
  7840. //--------------------------------------------------------
  7841. //
  7842. //! \name Constructors / Destructor / Instance Management
  7843. //@{
  7844. //--------------------------------------------------------
  7845. //! Destructor.
  7846. /**
  7847. \note If the associated window is visible on the screen, it is closed by the call to the destructor.
  7848. **/
  7849. ~CImgDisplay() {
  7850. assign();
  7851. delete[] _keys;
  7852. delete[] _released_keys;
  7853. }
  7854. //! Construct an empty display.
  7855. /**
  7856. \note Constructing an empty CImgDisplay instance does not make a window appearing on the screen, until
  7857. display of valid data is performed.
  7858. \par Example
  7859. \code
  7860. CImgDisplay disp; // Does actually nothing
  7861. ...
  7862. disp.display(img); // Construct new window and display image in it
  7863. \endcode
  7864. **/
  7865. CImgDisplay():
  7866. _width(0),_height(0),_normalization(0),
  7867. _min(0),_max(0),
  7868. _is_fullscreen(false),
  7869. _title(0),
  7870. _window_width(0),_window_height(0),_button(0),
  7871. _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
  7872. _window_x(cimg::type<int>::min()),_window_y(cimg::type<int>::min()),
  7873. _mouse_x(-1),_mouse_y(-1),_wheel(0),
  7874. _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
  7875. assign();
  7876. }
  7877. //! Construct a display with specified dimensions.
  7878. /** \param width Window width.
  7879. \param height Window height.
  7880. \param title Window title.
  7881. \param normalization Normalization type
  7882. (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()).
  7883. \param is_fullscreen Tells if fullscreen mode is enabled.
  7884. \param is_closed Tells if associated window is initially visible or not.
  7885. \note A black background is initially displayed on the associated window.
  7886. **/
  7887. CImgDisplay(const unsigned int width, const unsigned int height,
  7888. const char *const title=0, const unsigned int normalization=3,
  7889. const bool is_fullscreen=false, const bool is_closed=false):
  7890. _width(0),_height(0),_normalization(0),
  7891. _min(0),_max(0),
  7892. _is_fullscreen(false),
  7893. _title(0),
  7894. _window_width(0),_window_height(0),_button(0),
  7895. _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
  7896. _window_x(cimg::type<int>::min()),_window_y(cimg::type<int>::min()),
  7897. _mouse_x(-1),_mouse_y(-1),_wheel(0),
  7898. _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
  7899. assign(width,height,title,normalization,is_fullscreen,is_closed);
  7900. }
  7901. //! Construct a display from an image.
  7902. /** \param img Image used as a model to create the window.
  7903. \param title Window title.
  7904. \param normalization Normalization type
  7905. (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()).
  7906. \param is_fullscreen Tells if fullscreen mode is enabled.
  7907. \param is_closed Tells if associated window is initially visible or not.
  7908. \note The pixels of the input image are initially displayed on the associated window.
  7909. **/
  7910. template<typename T>
  7911. explicit CImgDisplay(const CImg<T>& img,
  7912. const char *const title=0, const unsigned int normalization=3,
  7913. const bool is_fullscreen=false, const bool is_closed=false):
  7914. _width(0),_height(0),_normalization(0),
  7915. _min(0),_max(0),
  7916. _is_fullscreen(false),
  7917. _title(0),
  7918. _window_width(0),_window_height(0),_button(0),
  7919. _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
  7920. _window_x(cimg::type<int>::min()),_window_y(cimg::type<int>::min()),
  7921. _mouse_x(-1),_mouse_y(-1),_wheel(0),
  7922. _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
  7923. assign(img,title,normalization,is_fullscreen,is_closed);
  7924. }
  7925. //! Construct a display from an image list.
  7926. /** \param list The images list to display.
  7927. \param title Window title.
  7928. \param normalization Normalization type
  7929. (<tt>0</tt>=none, <tt>1</tt>=always, <tt>2</tt>=once, <tt>3</tt>=pixel type-dependent, see normalization()).
  7930. \param is_fullscreen Tells if fullscreen mode is enabled.
  7931. \param is_closed Tells if associated window is initially visible or not.
  7932. \note All images of the list, appended along the X-axis, are initially displayed on the associated window.
  7933. **/
  7934. template<typename T>
  7935. explicit CImgDisplay(const CImgList<T>& list,
  7936. const char *const title=0, const unsigned int normalization=3,
  7937. const bool is_fullscreen=false, const bool is_closed=false):
  7938. _width(0),_height(0),_normalization(0),
  7939. _min(0),_max(0),
  7940. _is_fullscreen(false),
  7941. _title(0),
  7942. _window_width(0),_window_height(0),_button(0),
  7943. _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
  7944. _window_x(cimg::type<int>::min()),_window_y(cimg::type<int>::min()),
  7945. _mouse_x(-1),_mouse_y(-1),_wheel(0),
  7946. _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
  7947. assign(list,title,normalization,is_fullscreen,is_closed);
  7948. }
  7949. //! Construct a display as a copy of an existing one.
  7950. /**
  7951. \param disp Display instance to copy.
  7952. \note The pixel buffer of the input window is initially displayed on the associated window.
  7953. **/
  7954. CImgDisplay(const CImgDisplay& disp):
  7955. _width(0),_height(0),_normalization(0),
  7956. _min(0),_max(0),
  7957. _is_fullscreen(false),
  7958. _title(0),
  7959. _window_width(0),_window_height(0),_button(0),
  7960. _keys(new unsigned int[128]),_released_keys(new unsigned int[128]),
  7961. _window_x(cimg::type<int>::min()),_window_y(cimg::type<int>::min()),
  7962. _mouse_x(-1),_mouse_y(-1),_wheel(0),
  7963. _is_closed(true),_is_resized(false),_is_moved(false),_is_event(false) {
  7964. assign(disp);
  7965. }
  7966. //! Take a screenshot.
  7967. /**
  7968. \param[out] img Output screenshot. Can be empty on input
  7969. **/
  7970. template<typename T>
  7971. static void screenshot(CImg<T>& img) {
  7972. return screenshot(0,0,cimg::type<int>::max(),cimg::type<int>::max(),img);
  7973. }
  7974. #if cimg_display==0
  7975. static void _no_display_exception() {
  7976. throw CImgDisplayException("CImgDisplay(): No display available.");
  7977. }
  7978. //! Destructor - Empty constructor \inplace.
  7979. /**
  7980. \note Replace the current instance by an empty display.
  7981. **/
  7982. CImgDisplay& assign() {
  7983. return flush();
  7984. }
  7985. //! Construct a display with specified dimensions \inplace.
  7986. /**
  7987. **/
  7988. CImgDisplay& assign(const unsigned int width, const unsigned int height,
  7989. const char *const title=0, const unsigned int normalization=3,
  7990. const bool is_fullscreen=false, const bool is_closed=false) {
  7991. cimg::unused(width,height,title,normalization,is_fullscreen,is_closed);
  7992. _no_display_exception();
  7993. return assign();
  7994. }
  7995. //! Construct a display from an image \inplace.
  7996. /**
  7997. **/
  7998. template<typename T>
  7999. CImgDisplay& assign(const CImg<T>& img,
  8000. const char *const title=0, const unsigned int normalization=3,
  8001. const bool is_fullscreen=false, const bool is_closed=false) {
  8002. _no_display_exception();
  8003. return assign(img._width,img._height,title,normalization,is_fullscreen,is_closed);
  8004. }
  8005. //! Construct a display from an image list \inplace.
  8006. /**
  8007. **/
  8008. template<typename T>
  8009. CImgDisplay& assign(const CImgList<T>& list,
  8010. const char *const title=0, const unsigned int normalization=3,
  8011. const bool is_fullscreen=false, const bool is_closed=false) {
  8012. _no_display_exception();
  8013. return assign(list._width,list._width,title,normalization,is_fullscreen,is_closed);
  8014. }
  8015. //! Construct a display as a copy of another one \inplace.
  8016. /**
  8017. **/
  8018. CImgDisplay& assign(const CImgDisplay &disp) {
  8019. _no_display_exception();
  8020. return assign(disp._width,disp._height);
  8021. }
  8022. #endif
  8023. //! Return a reference to an empty display.
  8024. /**
  8025. \note Can be useful for writing function prototypes where one of the argument (of type CImgDisplay&)
  8026. must have a default value.
  8027. \par Example
  8028. \code
  8029. void foo(CImgDisplay& disp=CImgDisplay::empty());
  8030. \endcode
  8031. **/
  8032. static CImgDisplay& empty() {
  8033. static CImgDisplay _empty;
  8034. return _empty.assign();
  8035. }
  8036. //! Return a reference to an empty display \const.
  8037. static const CImgDisplay& const_empty() {
  8038. static const CImgDisplay _empty;
  8039. return _empty;
  8040. }
  8041. #define cimg_fitscreen(dx,dy,dz) CImgDisplay::_fitscreen(dx,dy,dz,-25,-85,false), \
  8042. CImgDisplay::_fitscreen(dx,dy,dz,-25,-85,true)
  8043. static unsigned int _fitscreen(const unsigned int dx, const unsigned int dy, const unsigned int dz,
  8044. const int dmin, const int dmax, const bool return_y) {
  8045. const int
  8046. u = CImgDisplay::screen_width(),
  8047. v = CImgDisplay::screen_height();
  8048. const float
  8049. mw = dmin<0?cimg::round(u*-dmin/100.f):(float)dmin,
  8050. mh = dmin<0?cimg::round(v*-dmin/100.f):(float)dmin,
  8051. Mw = dmax<0?cimg::round(u*-dmax/100.f):(float)dmax,
  8052. Mh = dmax<0?cimg::round(v*-dmax/100.f):(float)dmax;
  8053. float
  8054. w = (float)std::max(1U,dx),
  8055. h = (float)std::max(1U,dy);
  8056. if (dz>1) { w+=dz; h+=dz; }
  8057. if (w<mw) { h = h*mw/w; w = mw; }
  8058. if (h<mh) { w = w*mh/h; h = mh; }
  8059. if (w>Mw) { h = h*Mw/w; w = Mw; }
  8060. if (h>Mh) { w = w*Mh/h; h = Mh; }
  8061. if (w<mw) w = mw;
  8062. if (h<mh) h = mh;
  8063. return std::max(1U,(unsigned int)cimg::round(return_y?h:w));
  8064. }
  8065. //@}
  8066. //------------------------------------------
  8067. //
  8068. //! \name Overloaded Operators
  8069. //@{
  8070. //------------------------------------------
  8071. //! Display image on associated window.
  8072. /**
  8073. \note <tt>disp = img</tt> is equivalent to <tt>disp.display(img)</tt>.
  8074. **/
  8075. template<typename t>
  8076. CImgDisplay& operator=(const CImg<t>& img) {
  8077. return display(img);
  8078. }
  8079. //! Display list of images on associated window.
  8080. /**
  8081. \note <tt>disp = list</tt> is equivalent to <tt>disp.display(list)</tt>.
  8082. **/
  8083. template<typename t>
  8084. CImgDisplay& operator=(const CImgList<t>& list) {
  8085. return display(list);
  8086. }
  8087. //! Construct a display as a copy of another one \inplace.
  8088. /**
  8089. \note Equivalent to assign(const CImgDisplay&).
  8090. **/
  8091. CImgDisplay& operator=(const CImgDisplay& disp) {
  8092. return assign(disp);
  8093. }
  8094. //! Return \c false if display is empty, \c true otherwise.
  8095. /**
  8096. \note <tt>if (disp) { ... }</tt> is equivalent to <tt>if (!disp.is_empty()) { ... }</tt>.
  8097. **/
  8098. operator bool() const {
  8099. return !is_empty();
  8100. }
  8101. //@}
  8102. //------------------------------------------
  8103. //
  8104. //! \name Instance Checking
  8105. //@{
  8106. //------------------------------------------
  8107. //! Return \c true if display is empty, \c false otherwise.
  8108. /**
  8109. **/
  8110. bool is_empty() const {
  8111. return !(_width && _height);
  8112. }
  8113. //! Return \c true if display is closed (i.e. not visible on the screen), \c false otherwise.
  8114. /**
  8115. \note
  8116. - When a user physically closes the associated window, the display is set to closed.
  8117. - A closed display is not destroyed. Its associated window can be show again on the screen using show().
  8118. **/
  8119. bool is_closed() const {
  8120. return _is_closed;
  8121. }
  8122. //! Return \c true if display is visible (i.e. not closed by the user), \c false otherwise.
  8123. bool is_visible() const {
  8124. return !is_closed();
  8125. }
  8126. //! Return \c true if associated window has been resized on the screen, \c false otherwise.
  8127. /**
  8128. **/
  8129. bool is_resized() const {
  8130. return _is_resized;
  8131. }
  8132. //! Return \c true if associated window has been moved on the screen, \c false otherwise.
  8133. /**
  8134. **/
  8135. bool is_moved() const {
  8136. return _is_moved;
  8137. }
  8138. //! Return \c true if any event has occurred on the associated window, \c false otherwise.
  8139. /**
  8140. **/
  8141. bool is_event() const {
  8142. return _is_event;
  8143. }
  8144. //! Return \c true if current display is in fullscreen mode, \c false otherwise.
  8145. /**
  8146. **/
  8147. bool is_fullscreen() const {
  8148. return _is_fullscreen;
  8149. }
  8150. //! Return \c true if any key is being pressed on the associated window, \c false otherwise.
  8151. /**
  8152. \note The methods below do the same only for specific keys.
  8153. **/
  8154. bool is_key() const {
  8155. return _is_keyESC || _is_keyF1 || _is_keyF2 || _is_keyF3 ||
  8156. _is_keyF4 || _is_keyF5 || _is_keyF6 || _is_keyF7 ||
  8157. _is_keyF8 || _is_keyF9 || _is_keyF10 || _is_keyF11 ||
  8158. _is_keyF12 || _is_keyPAUSE || _is_key1 || _is_key2 ||
  8159. _is_key3 || _is_key4 || _is_key5 || _is_key6 ||
  8160. _is_key7 || _is_key8 || _is_key9 || _is_key0 ||
  8161. _is_keyBACKSPACE || _is_keyINSERT || _is_keyHOME ||
  8162. _is_keyPAGEUP || _is_keyTAB || _is_keyQ || _is_keyW ||
  8163. _is_keyE || _is_keyR || _is_keyT || _is_keyY ||
  8164. _is_keyU || _is_keyI || _is_keyO || _is_keyP ||
  8165. _is_keyDELETE || _is_keyEND || _is_keyPAGEDOWN ||
  8166. _is_keyCAPSLOCK || _is_keyA || _is_keyS || _is_keyD ||
  8167. _is_keyF || _is_keyG || _is_keyH || _is_keyJ ||
  8168. _is_keyK || _is_keyL || _is_keyENTER ||
  8169. _is_keySHIFTLEFT || _is_keyZ || _is_keyX || _is_keyC ||
  8170. _is_keyV || _is_keyB || _is_keyN || _is_keyM ||
  8171. _is_keySHIFTRIGHT || _is_keyARROWUP || _is_keyCTRLLEFT ||
  8172. _is_keyAPPLEFT || _is_keyALT || _is_keySPACE || _is_keyALTGR ||
  8173. _is_keyAPPRIGHT || _is_keyMENU || _is_keyCTRLRIGHT ||
  8174. _is_keyARROWLEFT || _is_keyARROWDOWN || _is_keyARROWRIGHT ||
  8175. _is_keyPAD0 || _is_keyPAD1 || _is_keyPAD2 ||
  8176. _is_keyPAD3 || _is_keyPAD4 || _is_keyPAD5 ||
  8177. _is_keyPAD6 || _is_keyPAD7 || _is_keyPAD8 ||
  8178. _is_keyPAD9 || _is_keyPADADD || _is_keyPADSUB ||
  8179. _is_keyPADMUL || _is_keyPADDIV;
  8180. }
  8181. //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise.
  8182. /**
  8183. \param keycode Keycode to test.
  8184. \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
  8185. your code stay portable (see cimg::keyESC).
  8186. \par Example
  8187. \code
  8188. CImgDisplay disp(400,400);
  8189. while (!disp.is_closed()) {
  8190. if (disp.key(cimg::keyTAB)) { ... } // Equivalent to 'if (disp.is_keyTAB())'
  8191. disp.wait();
  8192. }
  8193. \endcode
  8194. **/
  8195. bool is_key(const unsigned int keycode) const {
  8196. #define _cimg_iskey_test(k) if (keycode==cimg::key##k) return _is_key##k;
  8197. _cimg_iskey_test(ESC); _cimg_iskey_test(F1); _cimg_iskey_test(F2); _cimg_iskey_test(F3);
  8198. _cimg_iskey_test(F4); _cimg_iskey_test(F5); _cimg_iskey_test(F6); _cimg_iskey_test(F7);
  8199. _cimg_iskey_test(F8); _cimg_iskey_test(F9); _cimg_iskey_test(F10); _cimg_iskey_test(F11);
  8200. _cimg_iskey_test(F12); _cimg_iskey_test(PAUSE); _cimg_iskey_test(1); _cimg_iskey_test(2);
  8201. _cimg_iskey_test(3); _cimg_iskey_test(4); _cimg_iskey_test(5); _cimg_iskey_test(6);
  8202. _cimg_iskey_test(7); _cimg_iskey_test(8); _cimg_iskey_test(9); _cimg_iskey_test(0);
  8203. _cimg_iskey_test(BACKSPACE); _cimg_iskey_test(INSERT); _cimg_iskey_test(HOME);
  8204. _cimg_iskey_test(PAGEUP); _cimg_iskey_test(TAB); _cimg_iskey_test(Q); _cimg_iskey_test(W);
  8205. _cimg_iskey_test(E); _cimg_iskey_test(R); _cimg_iskey_test(T); _cimg_iskey_test(Y);
  8206. _cimg_iskey_test(U); _cimg_iskey_test(I); _cimg_iskey_test(O); _cimg_iskey_test(P);
  8207. _cimg_iskey_test(DELETE); _cimg_iskey_test(END); _cimg_iskey_test(PAGEDOWN);
  8208. _cimg_iskey_test(CAPSLOCK); _cimg_iskey_test(A); _cimg_iskey_test(S); _cimg_iskey_test(D);
  8209. _cimg_iskey_test(F); _cimg_iskey_test(G); _cimg_iskey_test(H); _cimg_iskey_test(J);
  8210. _cimg_iskey_test(K); _cimg_iskey_test(L); _cimg_iskey_test(ENTER);
  8211. _cimg_iskey_test(SHIFTLEFT); _cimg_iskey_test(Z); _cimg_iskey_test(X); _cimg_iskey_test(C);
  8212. _cimg_iskey_test(V); _cimg_iskey_test(B); _cimg_iskey_test(N); _cimg_iskey_test(M);
  8213. _cimg_iskey_test(SHIFTRIGHT); _cimg_iskey_test(ARROWUP); _cimg_iskey_test(CTRLLEFT);
  8214. _cimg_iskey_test(APPLEFT); _cimg_iskey_test(ALT); _cimg_iskey_test(SPACE); _cimg_iskey_test(ALTGR);
  8215. _cimg_iskey_test(APPRIGHT); _cimg_iskey_test(MENU); _cimg_iskey_test(CTRLRIGHT);
  8216. _cimg_iskey_test(ARROWLEFT); _cimg_iskey_test(ARROWDOWN); _cimg_iskey_test(ARROWRIGHT);
  8217. _cimg_iskey_test(PAD0); _cimg_iskey_test(PAD1); _cimg_iskey_test(PAD2);
  8218. _cimg_iskey_test(PAD3); _cimg_iskey_test(PAD4); _cimg_iskey_test(PAD5);
  8219. _cimg_iskey_test(PAD6); _cimg_iskey_test(PAD7); _cimg_iskey_test(PAD8);
  8220. _cimg_iskey_test(PAD9); _cimg_iskey_test(PADADD); _cimg_iskey_test(PADSUB);
  8221. _cimg_iskey_test(PADMUL); _cimg_iskey_test(PADDIV);
  8222. return false;
  8223. }
  8224. //! Return \c true if key specified by given keycode is being pressed on the associated window, \c false otherwise.
  8225. /**
  8226. \param keycode C-string containing the keycode label of the key to test.
  8227. \note Use it when the key you want to test can be dynamically set by the user.
  8228. \par Example
  8229. \code
  8230. CImgDisplay disp(400,400);
  8231. const char *const keycode = "TAB";
  8232. while (!disp.is_closed()) {
  8233. if (disp.is_key(keycode)) { ... } // Equivalent to 'if (disp.is_keyTAB())'
  8234. disp.wait();
  8235. }
  8236. \endcode
  8237. **/
  8238. bool& is_key(const char *const keycode) {
  8239. static bool f = false;
  8240. f = false;
  8241. #define _cimg_iskey_test2(k) if (!cimg::strcasecmp(keycode,#k)) return _is_key##k;
  8242. _cimg_iskey_test2(ESC); _cimg_iskey_test2(F1); _cimg_iskey_test2(F2); _cimg_iskey_test2(F3);
  8243. _cimg_iskey_test2(F4); _cimg_iskey_test2(F5); _cimg_iskey_test2(F6); _cimg_iskey_test2(F7);
  8244. _cimg_iskey_test2(F8); _cimg_iskey_test2(F9); _cimg_iskey_test2(F10); _cimg_iskey_test2(F11);
  8245. _cimg_iskey_test2(F12); _cimg_iskey_test2(PAUSE); _cimg_iskey_test2(1); _cimg_iskey_test2(2);
  8246. _cimg_iskey_test2(3); _cimg_iskey_test2(4); _cimg_iskey_test2(5); _cimg_iskey_test2(6);
  8247. _cimg_iskey_test2(7); _cimg_iskey_test2(8); _cimg_iskey_test2(9); _cimg_iskey_test2(0);
  8248. _cimg_iskey_test2(BACKSPACE); _cimg_iskey_test2(INSERT); _cimg_iskey_test2(HOME);
  8249. _cimg_iskey_test2(PAGEUP); _cimg_iskey_test2(TAB); _cimg_iskey_test2(Q); _cimg_iskey_test2(W);
  8250. _cimg_iskey_test2(E); _cimg_iskey_test2(R); _cimg_iskey_test2(T); _cimg_iskey_test2(Y);
  8251. _cimg_iskey_test2(U); _cimg_iskey_test2(I); _cimg_iskey_test2(O); _cimg_iskey_test2(P);
  8252. _cimg_iskey_test2(DELETE); _cimg_iskey_test2(END); _cimg_iskey_test2(PAGEDOWN);
  8253. _cimg_iskey_test2(CAPSLOCK); _cimg_iskey_test2(A); _cimg_iskey_test2(S); _cimg_iskey_test2(D);
  8254. _cimg_iskey_test2(F); _cimg_iskey_test2(G); _cimg_iskey_test2(H); _cimg_iskey_test2(J);
  8255. _cimg_iskey_test2(K); _cimg_iskey_test2(L); _cimg_iskey_test2(ENTER);
  8256. _cimg_iskey_test2(SHIFTLEFT); _cimg_iskey_test2(Z); _cimg_iskey_test2(X); _cimg_iskey_test2(C);
  8257. _cimg_iskey_test2(V); _cimg_iskey_test2(B); _cimg_iskey_test2(N); _cimg_iskey_test2(M);
  8258. _cimg_iskey_test2(SHIFTRIGHT); _cimg_iskey_test2(ARROWUP); _cimg_iskey_test2(CTRLLEFT);
  8259. _cimg_iskey_test2(APPLEFT); _cimg_iskey_test2(ALT); _cimg_iskey_test2(SPACE); _cimg_iskey_test2(ALTGR);
  8260. _cimg_iskey_test2(APPRIGHT); _cimg_iskey_test2(MENU); _cimg_iskey_test2(CTRLRIGHT);
  8261. _cimg_iskey_test2(ARROWLEFT); _cimg_iskey_test2(ARROWDOWN); _cimg_iskey_test2(ARROWRIGHT);
  8262. _cimg_iskey_test2(PAD0); _cimg_iskey_test2(PAD1); _cimg_iskey_test2(PAD2);
  8263. _cimg_iskey_test2(PAD3); _cimg_iskey_test2(PAD4); _cimg_iskey_test2(PAD5);
  8264. _cimg_iskey_test2(PAD6); _cimg_iskey_test2(PAD7); _cimg_iskey_test2(PAD8);
  8265. _cimg_iskey_test2(PAD9); _cimg_iskey_test2(PADADD); _cimg_iskey_test2(PADSUB);
  8266. _cimg_iskey_test2(PADMUL); _cimg_iskey_test2(PADDIV);
  8267. return f;
  8268. }
  8269. //! Return \c true if specified key sequence has been typed on the associated window, \c false otherwise.
  8270. /**
  8271. \param keycodes_sequence Buffer of keycodes to test.
  8272. \param length Number of keys in the \c keycodes_sequence buffer.
  8273. \param remove_sequence Tells if the key sequence must be removed from the key history, if found.
  8274. \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
  8275. your code stay portable (see cimg::keyESC).
  8276. \par Example
  8277. \code
  8278. CImgDisplay disp(400,400);
  8279. const unsigned int key_seq[] = { cimg::keyCTRLLEFT, cimg::keyD };
  8280. while (!disp.is_closed()) {
  8281. if (disp.is_key_sequence(key_seq,2)) { ... } // Test for the 'CTRL+D' keyboard event
  8282. disp.wait();
  8283. }
  8284. \endcode
  8285. **/
  8286. bool is_key_sequence(const unsigned int *const keycodes_sequence, const unsigned int length,
  8287. const bool remove_sequence=false) {
  8288. if (keycodes_sequence && length) {
  8289. const unsigned int
  8290. *const ps_end = keycodes_sequence + length - 1,
  8291. *const pk_end = (unsigned int*)_keys + 1 + 128 - length,
  8292. k = *ps_end;
  8293. for (unsigned int *pk = (unsigned int*)_keys; pk<pk_end; ) {
  8294. if (*(pk++)==k) {
  8295. bool res = true;
  8296. const unsigned int *ps = ps_end, *pk2 = pk;
  8297. for (unsigned int i = 1; i<length; ++i) res = (*(--ps)==*(pk2++));
  8298. if (res) {
  8299. if (remove_sequence) std::memset((void*)(pk - 1),0,sizeof(unsigned int)*length);
  8300. return true;
  8301. }
  8302. }
  8303. }
  8304. }
  8305. return false;
  8306. }
  8307. #define _cimg_iskey_def(k) \
  8308. bool is_key##k() const { \
  8309. return _is_key##k; \
  8310. }
  8311. //! Return \c true if the \c ESC key is being pressed on the associated window, \c false otherwise.
  8312. /**
  8313. \note Similar methods exist for all keys managed by \CImg (see cimg::keyESC).
  8314. **/
  8315. _cimg_iskey_def(ESC); _cimg_iskey_def(F1); _cimg_iskey_def(F2); _cimg_iskey_def(F3);
  8316. _cimg_iskey_def(F4); _cimg_iskey_def(F5); _cimg_iskey_def(F6); _cimg_iskey_def(F7);
  8317. _cimg_iskey_def(F8); _cimg_iskey_def(F9); _cimg_iskey_def(F10); _cimg_iskey_def(F11);
  8318. _cimg_iskey_def(F12); _cimg_iskey_def(PAUSE); _cimg_iskey_def(1); _cimg_iskey_def(2);
  8319. _cimg_iskey_def(3); _cimg_iskey_def(4); _cimg_iskey_def(5); _cimg_iskey_def(6);
  8320. _cimg_iskey_def(7); _cimg_iskey_def(8); _cimg_iskey_def(9); _cimg_iskey_def(0);
  8321. _cimg_iskey_def(BACKSPACE); _cimg_iskey_def(INSERT); _cimg_iskey_def(HOME);
  8322. _cimg_iskey_def(PAGEUP); _cimg_iskey_def(TAB); _cimg_iskey_def(Q); _cimg_iskey_def(W);
  8323. _cimg_iskey_def(E); _cimg_iskey_def(R); _cimg_iskey_def(T); _cimg_iskey_def(Y);
  8324. _cimg_iskey_def(U); _cimg_iskey_def(I); _cimg_iskey_def(O); _cimg_iskey_def(P);
  8325. _cimg_iskey_def(DELETE); _cimg_iskey_def(END); _cimg_iskey_def(PAGEDOWN);
  8326. _cimg_iskey_def(CAPSLOCK); _cimg_iskey_def(A); _cimg_iskey_def(S); _cimg_iskey_def(D);
  8327. _cimg_iskey_def(F); _cimg_iskey_def(G); _cimg_iskey_def(H); _cimg_iskey_def(J);
  8328. _cimg_iskey_def(K); _cimg_iskey_def(L); _cimg_iskey_def(ENTER);
  8329. _cimg_iskey_def(SHIFTLEFT); _cimg_iskey_def(Z); _cimg_iskey_def(X); _cimg_iskey_def(C);
  8330. _cimg_iskey_def(V); _cimg_iskey_def(B); _cimg_iskey_def(N); _cimg_iskey_def(M);
  8331. _cimg_iskey_def(SHIFTRIGHT); _cimg_iskey_def(ARROWUP); _cimg_iskey_def(CTRLLEFT);
  8332. _cimg_iskey_def(APPLEFT); _cimg_iskey_def(ALT); _cimg_iskey_def(SPACE); _cimg_iskey_def(ALTGR);
  8333. _cimg_iskey_def(APPRIGHT); _cimg_iskey_def(MENU); _cimg_iskey_def(CTRLRIGHT);
  8334. _cimg_iskey_def(ARROWLEFT); _cimg_iskey_def(ARROWDOWN); _cimg_iskey_def(ARROWRIGHT);
  8335. _cimg_iskey_def(PAD0); _cimg_iskey_def(PAD1); _cimg_iskey_def(PAD2);
  8336. _cimg_iskey_def(PAD3); _cimg_iskey_def(PAD4); _cimg_iskey_def(PAD5);
  8337. _cimg_iskey_def(PAD6); _cimg_iskey_def(PAD7); _cimg_iskey_def(PAD8);
  8338. _cimg_iskey_def(PAD9); _cimg_iskey_def(PADADD); _cimg_iskey_def(PADSUB);
  8339. _cimg_iskey_def(PADMUL); _cimg_iskey_def(PADDIV);
  8340. //@}
  8341. //------------------------------------------
  8342. //
  8343. //! \name Instance Characteristics
  8344. //@{
  8345. //------------------------------------------
  8346. #if cimg_display==0
  8347. //! Return width of the screen (current resolution along the X-axis).
  8348. /**
  8349. **/
  8350. static int screen_width() {
  8351. _no_display_exception();
  8352. return 0;
  8353. }
  8354. //! Return height of the screen (current resolution along the Y-axis).
  8355. /**
  8356. **/
  8357. static int screen_height() {
  8358. _no_display_exception();
  8359. return 0;
  8360. }
  8361. #endif
  8362. //! Return display width.
  8363. /**
  8364. \note The width of the display (i.e. the width of the pixel data buffer associated to the CImgDisplay instance)
  8365. may be different from the actual width of the associated window.
  8366. **/
  8367. int width() const {
  8368. return (int)_width;
  8369. }
  8370. //! Return display height.
  8371. /**
  8372. \note The height of the display (i.e. the height of the pixel data buffer associated to the CImgDisplay instance)
  8373. may be different from the actual height of the associated window.
  8374. **/
  8375. int height() const {
  8376. return (int)_height;
  8377. }
  8378. //! Return normalization type of the display.
  8379. /**
  8380. The normalization type tells about how the values of an input image are normalized by the CImgDisplay to be
  8381. correctly displayed. The range of values for pixels displayed on screen is <tt>[0,255]</tt>.
  8382. If the range of values of the data to display is different, a normalization may be required for displaying
  8383. the data in a correct way. The normalization type can be one of:
  8384. - \c 0: Value normalization is disabled. It is then assumed that all input data to be displayed by the
  8385. CImgDisplay instance have values in range <tt>[0,255]</tt>.
  8386. - \c 1: Value normalization is always performed (this is the default behavior).
  8387. Before displaying an input image, its values will be (virtually) stretched
  8388. in range <tt>[0,255]</tt>, so that the contrast of the displayed pixels will be maximum.
  8389. Use this mode for images whose minimum and maximum values are not prescribed to known values
  8390. (e.g. float-valued images).
  8391. Note that when normalized versions of images are computed for display purposes, the actual values of these
  8392. images are not modified.
  8393. - \c 2: Value normalization is performed once (on the first image display), then the same normalization
  8394. coefficients are kept for next displayed frames.
  8395. - \c 3: Value normalization depends on the pixel type of the data to display. For integer pixel types,
  8396. the normalization is done regarding the minimum/maximum values of the type (no normalization occurs then
  8397. for <tt>unsigned char</tt>).
  8398. For float-valued pixel types, the normalization is done regarding the minimum/maximum value of the image
  8399. data instead.
  8400. **/
  8401. unsigned int normalization() const {
  8402. return _normalization;
  8403. }
  8404. //! Return title of the associated window as a C-string.
  8405. /**
  8406. \note Window title may be not visible, depending on the used window manager or if the current display is
  8407. in fullscreen mode.
  8408. **/
  8409. const char *title() const {
  8410. return _title?_title:"";
  8411. }
  8412. //! Return width of the associated window.
  8413. /**
  8414. \note The width of the display (i.e. the width of the pixel data buffer associated to the CImgDisplay instance)
  8415. may be different from the actual width of the associated window.
  8416. **/
  8417. int window_width() const {
  8418. return (int)_window_width;
  8419. }
  8420. //! Return height of the associated window.
  8421. /**
  8422. \note The height of the display (i.e. the height of the pixel data buffer associated to the CImgDisplay instance)
  8423. may be different from the actual height of the associated window.
  8424. **/
  8425. int window_height() const {
  8426. return (int)_window_height;
  8427. }
  8428. //! Return X-coordinate of the associated window.
  8429. /**
  8430. \note The returned coordinate corresponds to the location of the upper-left corner of the associated window.
  8431. **/
  8432. int window_x() const {
  8433. return _window_x;
  8434. }
  8435. //! Return Y-coordinate of the associated window.
  8436. /**
  8437. \note The returned coordinate corresponds to the location of the upper-left corner of the associated window.
  8438. **/
  8439. int window_y() const {
  8440. return _window_y;
  8441. }
  8442. //! Return X-coordinate of the mouse pointer.
  8443. /**
  8444. \note
  8445. - If the mouse pointer is outside window area, \c -1 is returned.
  8446. - Otherwise, the returned value is in the range [0,width()-1].
  8447. **/
  8448. int mouse_x() const {
  8449. return _mouse_x;
  8450. }
  8451. //! Return Y-coordinate of the mouse pointer.
  8452. /**
  8453. \note
  8454. - If the mouse pointer is outside window area, \c -1 is returned.
  8455. - Otherwise, the returned value is in the range [0,height()-1].
  8456. **/
  8457. int mouse_y() const {
  8458. return _mouse_y;
  8459. }
  8460. //! Return current state of the mouse buttons.
  8461. /**
  8462. \note Three mouse buttons can be managed. If one button is pressed, its corresponding bit in the returned
  8463. value is set:
  8464. - bit \c 0 (value \c 0x1): State of the left mouse button.
  8465. - bit \c 1 (value \c 0x2): State of the right mouse button.
  8466. - bit \c 2 (value \c 0x4): State of the middle mouse button.
  8467. Several bits can be activated if more than one button are pressed at the same time.
  8468. \par Example
  8469. \code
  8470. CImgDisplay disp(400,400);
  8471. while (!disp.is_closed()) {
  8472. if (disp.button()&1) { // Left button clicked
  8473. ...
  8474. }
  8475. if (disp.button()&2) { // Right button clicked
  8476. ...
  8477. }
  8478. if (disp.button()&4) { // Middle button clicked
  8479. ...
  8480. }
  8481. disp.wait();
  8482. }
  8483. \endcode
  8484. **/
  8485. unsigned int button() const {
  8486. return _button;
  8487. }
  8488. //! Return current state of the mouse wheel.
  8489. /**
  8490. \note
  8491. - The returned value can be positive or negative depending on whether the mouse wheel has been scrolled
  8492. forward or backward.
  8493. - Scrolling the wheel forward add \c 1 to the wheel value.
  8494. - Scrolling the wheel backward subtract \c 1 to the wheel value.
  8495. - The returned value cumulates the number of forward of backward scrolls since the creation of the display,
  8496. or since the last reset of the wheel value (using set_wheel()). It is strongly recommended to quickly reset
  8497. the wheel counter when an action has been performed regarding the current wheel value.
  8498. Otherwise, the returned wheel value may be for instance \c 0 despite the fact that many scrolls have been done
  8499. (as many in forward as in backward directions).
  8500. \par Example
  8501. \code
  8502. CImgDisplay disp(400,400);
  8503. while (!disp.is_closed()) {
  8504. if (disp.wheel()) {
  8505. int counter = disp.wheel(); // Read the state of the mouse wheel
  8506. ... // Do what you want with 'counter'
  8507. disp.set_wheel(); // Reset the wheel value to 0
  8508. }
  8509. disp.wait();
  8510. }
  8511. \endcode
  8512. **/
  8513. int wheel() const {
  8514. return _wheel;
  8515. }
  8516. //! Return one entry from the pressed keys history.
  8517. /**
  8518. \param pos Index to read from the pressed keys history (index \c 0 corresponds to latest entry).
  8519. \return Keycode of a pressed key or \c 0 for a released key.
  8520. \note
  8521. - Each CImgDisplay stores a history of the pressed keys in a buffer of size \c 128. When a new key is pressed,
  8522. its keycode is stored in the pressed keys history. When a key is released, \c 0 is put instead.
  8523. This means that up to the 64 last pressed keys may be read from the pressed keys history.
  8524. When a new value is stored, the pressed keys history is shifted so that the latest entry is always
  8525. stored at position \c 0.
  8526. - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
  8527. your code stay portable (see cimg::keyESC).
  8528. **/
  8529. unsigned int& key(const unsigned int pos=0) const {
  8530. static unsigned int key0;
  8531. return pos<128?_keys[pos]:(key0 = 0);
  8532. }
  8533. //! Return one entry from the released keys history.
  8534. /**
  8535. \param pos Index to read from the released keys history (index \c 0 corresponds to latest entry).
  8536. \return Keycode of a released key or \c 0 for a pressed key.
  8537. \note
  8538. - Each CImgDisplay stores a history of the released keys in a buffer of size \c 128. When a new key is released,
  8539. its keycode is stored in the pressed keys history. When a key is pressed, \c 0 is put instead.
  8540. This means that up to the 64 last released keys may be read from the released keys history.
  8541. When a new value is stored, the released keys history is shifted so that the latest entry is always
  8542. stored at position \c 0.
  8543. - Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
  8544. your code stay portable (see cimg::keyESC).
  8545. **/
  8546. unsigned int& released_key(const unsigned int pos=0) const {
  8547. static unsigned int key0;
  8548. return pos<128?_released_keys[pos]:(key0 = 0);
  8549. }
  8550. //! Return keycode corresponding to the specified string.
  8551. /**
  8552. \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
  8553. your code stay portable (see cimg::keyESC).
  8554. \par Example
  8555. \code
  8556. const unsigned int keyTAB = CImgDisplay::keycode("TAB"); // Return cimg::keyTAB
  8557. \endcode
  8558. **/
  8559. static unsigned int keycode(const char *const keycode) {
  8560. #define _cimg_keycode(k) if (!cimg::strcasecmp(keycode,#k)) return cimg::key##k;
  8561. _cimg_keycode(ESC); _cimg_keycode(F1); _cimg_keycode(F2); _cimg_keycode(F3);
  8562. _cimg_keycode(F4); _cimg_keycode(F5); _cimg_keycode(F6); _cimg_keycode(F7);
  8563. _cimg_keycode(F8); _cimg_keycode(F9); _cimg_keycode(F10); _cimg_keycode(F11);
  8564. _cimg_keycode(F12); _cimg_keycode(PAUSE); _cimg_keycode(1); _cimg_keycode(2);
  8565. _cimg_keycode(3); _cimg_keycode(4); _cimg_keycode(5); _cimg_keycode(6);
  8566. _cimg_keycode(7); _cimg_keycode(8); _cimg_keycode(9); _cimg_keycode(0);
  8567. _cimg_keycode(BACKSPACE); _cimg_keycode(INSERT); _cimg_keycode(HOME);
  8568. _cimg_keycode(PAGEUP); _cimg_keycode(TAB); _cimg_keycode(Q); _cimg_keycode(W);
  8569. _cimg_keycode(E); _cimg_keycode(R); _cimg_keycode(T); _cimg_keycode(Y);
  8570. _cimg_keycode(U); _cimg_keycode(I); _cimg_keycode(O); _cimg_keycode(P);
  8571. _cimg_keycode(DELETE); _cimg_keycode(END); _cimg_keycode(PAGEDOWN);
  8572. _cimg_keycode(CAPSLOCK); _cimg_keycode(A); _cimg_keycode(S); _cimg_keycode(D);
  8573. _cimg_keycode(F); _cimg_keycode(G); _cimg_keycode(H); _cimg_keycode(J);
  8574. _cimg_keycode(K); _cimg_keycode(L); _cimg_keycode(ENTER);
  8575. _cimg_keycode(SHIFTLEFT); _cimg_keycode(Z); _cimg_keycode(X); _cimg_keycode(C);
  8576. _cimg_keycode(V); _cimg_keycode(B); _cimg_keycode(N); _cimg_keycode(M);
  8577. _cimg_keycode(SHIFTRIGHT); _cimg_keycode(ARROWUP); _cimg_keycode(CTRLLEFT);
  8578. _cimg_keycode(APPLEFT); _cimg_keycode(ALT); _cimg_keycode(SPACE); _cimg_keycode(ALTGR);
  8579. _cimg_keycode(APPRIGHT); _cimg_keycode(MENU); _cimg_keycode(CTRLRIGHT);
  8580. _cimg_keycode(ARROWLEFT); _cimg_keycode(ARROWDOWN); _cimg_keycode(ARROWRIGHT);
  8581. _cimg_keycode(PAD0); _cimg_keycode(PAD1); _cimg_keycode(PAD2);
  8582. _cimg_keycode(PAD3); _cimg_keycode(PAD4); _cimg_keycode(PAD5);
  8583. _cimg_keycode(PAD6); _cimg_keycode(PAD7); _cimg_keycode(PAD8);
  8584. _cimg_keycode(PAD9); _cimg_keycode(PADADD); _cimg_keycode(PADSUB);
  8585. _cimg_keycode(PADMUL); _cimg_keycode(PADDIV);
  8586. return 0;
  8587. }
  8588. //! Return the current refresh rate, in frames per second.
  8589. /**
  8590. \note Returns a significant value when the current instance is used to display successive frames.
  8591. It measures the delay between successive calls to frames_per_second().
  8592. **/
  8593. float frames_per_second() {
  8594. if (!_fps_timer) _fps_timer = cimg::time();
  8595. const float delta = (float)((cimg::time() - _fps_timer)/1000.f);
  8596. ++_fps_frames;
  8597. if (delta>=1) {
  8598. _fps_fps = _fps_frames/delta;
  8599. _fps_frames = 0;
  8600. _fps_timer = cimg::time();
  8601. }
  8602. return _fps_fps;
  8603. }
  8604. // Move current display window so that its content stays inside the current screen.
  8605. CImgDisplay& move_inside_screen() {
  8606. if (is_empty()) return *this;
  8607. const int
  8608. x0 = window_x(),
  8609. y0 = window_y(),
  8610. x1 = x0 + window_width() - 1,
  8611. y1 = y0 + window_height() - 1,
  8612. sw = CImgDisplay::screen_width(),
  8613. sh = CImgDisplay::screen_height();
  8614. if (x0<0 || y0<0 || x1>=sw || y1>=sh)
  8615. move(std::max(0,std::min(x0,sw - x1 + x0)),
  8616. std::max(0,std::min(y0,sh - y1 + y0)));
  8617. return *this;
  8618. }
  8619. //@}
  8620. //---------------------------------------
  8621. //
  8622. //! \name Window Manipulation
  8623. //@{
  8624. //---------------------------------------
  8625. #if cimg_display==0
  8626. //! Display image on associated window.
  8627. /**
  8628. \param img Input image to display.
  8629. \note This method returns immediately.
  8630. **/
  8631. template<typename T>
  8632. CImgDisplay& display(const CImg<T>& img) {
  8633. return assign(img);
  8634. }
  8635. #endif
  8636. //! Display list of images on associated window.
  8637. /**
  8638. \param list List of images to display.
  8639. \param axis Axis used to append the images along, for the visualization (can be \c x, \c y, \c z or \c c).
  8640. \param align Relative position of aligned images when displaying lists with images of different sizes
  8641. (\c 0 for upper-left, \c 0.5 for centering and \c 1 for lower-right).
  8642. \note This method returns immediately.
  8643. **/
  8644. template<typename T>
  8645. CImgDisplay& display(const CImgList<T>& list, const char axis='x', const float align=0) {
  8646. if (list._width==1) {
  8647. const CImg<T>& img = list[0];
  8648. if (img._depth==1 && (img._spectrum==1 || img._spectrum>=3) && _normalization!=1) return display(img);
  8649. }
  8650. CImgList<typename CImg<T>::ucharT> visu(list._width);
  8651. unsigned int dims = 0;
  8652. cimglist_for(list,l) {
  8653. const CImg<T>& img = list._data[l];
  8654. img._get_select(*this,_normalization,(img._width - 1)/2,(img._height - 1)/2,
  8655. (img._depth - 1)/2).move_to(visu[l]);
  8656. dims = std::max(dims,visu[l]._spectrum);
  8657. }
  8658. cimglist_for(list,l) if (visu[l]._spectrum<dims) visu[l].resize(-100,-100,-100,dims,1);
  8659. visu.get_append(axis,align).display(*this);
  8660. return *this;
  8661. }
  8662. #if cimg_display==0
  8663. //! Show (closed) associated window on the screen.
  8664. /**
  8665. \note
  8666. - Force the associated window of a display to be visible on the screen, even if it has been closed before.
  8667. - Using show() on a visible display does nothing.
  8668. **/
  8669. CImgDisplay& show() {
  8670. return assign();
  8671. }
  8672. //! Close (visible) associated window and make it disappear from the screen.
  8673. /**
  8674. \note
  8675. - A closed display only means the associated window is not visible anymore. This does not mean the display has
  8676. been destroyed.
  8677. Use show() to make the associated window reappear.
  8678. - Using close() on a closed display does nothing.
  8679. **/
  8680. CImgDisplay& close() {
  8681. return assign();
  8682. }
  8683. //! Move associated window to a new location.
  8684. /**
  8685. \param pos_x X-coordinate of the new window location.
  8686. \param pos_y Y-coordinate of the new window location.
  8687. \note Depending on the window manager behavior, this method may not succeed (no exceptions are thrown
  8688. nevertheless).
  8689. **/
  8690. CImgDisplay& move(const int pos_x, const int pos_y) {
  8691. return assign(pos_x,pos_y);
  8692. }
  8693. #endif
  8694. //! Resize display to the size of the associated window.
  8695. /**
  8696. \param force_redraw Tells if the previous window content must be updated and refreshed as well.
  8697. \note
  8698. - Calling this method ensures that width() and window_width() become equal, as well as height() and
  8699. window_height().
  8700. - The associated window is also resized to specified dimensions.
  8701. **/
  8702. CImgDisplay& resize(const bool force_redraw=true) {
  8703. resize(window_width(),window_height(),force_redraw);
  8704. return *this;
  8705. }
  8706. #if cimg_display==0
  8707. //! Resize display to the specified size.
  8708. /**
  8709. \param width Requested display width.
  8710. \param height Requested display height.
  8711. \param force_redraw Tells if the previous window content must be updated and refreshed as well.
  8712. \note The associated window is also resized to specified dimensions.
  8713. **/
  8714. CImgDisplay& resize(const int width, const int height, const bool force_redraw=true) {
  8715. return assign(width,height,0,3,force_redraw);
  8716. }
  8717. #endif
  8718. //! Resize display to the size of an input image.
  8719. /**
  8720. \param img Input image to take size from.
  8721. \param force_redraw Tells if the previous window content must be resized and updated as well.
  8722. \note
  8723. - Calling this method ensures that width() and <tt>img.width()</tt> become equal, as well as height() and
  8724. <tt>img.height()</tt>.
  8725. - The associated window is also resized to specified dimensions.
  8726. **/
  8727. template<typename T>
  8728. CImgDisplay& resize(const CImg<T>& img, const bool force_redraw=true) {
  8729. return resize(img._width,img._height,force_redraw);
  8730. }
  8731. //! Resize display to the size of another CImgDisplay instance.
  8732. /**
  8733. \param disp Input display to take size from.
  8734. \param force_redraw Tells if the previous window content must be resized and updated as well.
  8735. \note
  8736. - Calling this method ensures that width() and <tt>disp.width()</tt> become equal, as well as height() and
  8737. <tt>disp.height()</tt>.
  8738. - The associated window is also resized to specified dimensions.
  8739. **/
  8740. CImgDisplay& resize(const CImgDisplay& disp, const bool force_redraw=true) {
  8741. return resize(disp.width(),disp.height(),force_redraw);
  8742. }
  8743. // [internal] Render pixel buffer with size (wd,hd) from source buffer of size (ws,hs).
  8744. template<typename t, typename T>
  8745. static void _render_resize(const T *ptrs, const unsigned int ws, const unsigned int hs,
  8746. t *ptrd, const unsigned int wd, const unsigned int hd) {
  8747. typedef typename cimg::last<T,cimg_ulong>::type ulongT;
  8748. const ulongT one = (ulongT)1;
  8749. CImg<ulongT> off_x(wd), off_y(hd + 1);
  8750. if (wd==ws) off_x.fill(1);
  8751. else {
  8752. ulongT *poff_x = off_x._data, curr = 0;
  8753. for (unsigned int x = 0; x<wd; ++x) {
  8754. const ulongT old = curr;
  8755. curr = (x + one)*ws/wd;
  8756. *(poff_x++) = curr - old;
  8757. }
  8758. }
  8759. if (hd==hs) off_y.fill(ws);
  8760. else {
  8761. ulongT *poff_y = off_y._data, curr = 0;
  8762. for (unsigned int y = 0; y<hd; ++y) {
  8763. const ulongT old = curr;
  8764. curr = (y + one)*hs/hd;
  8765. *(poff_y++) = ws*(curr - old);
  8766. }
  8767. *poff_y = 0;
  8768. }
  8769. ulongT *poff_y = off_y._data;
  8770. for (unsigned int y = 0; y<hd; ) {
  8771. const T *ptr = ptrs;
  8772. ulongT *poff_x = off_x._data;
  8773. for (unsigned int x = 0; x<wd; ++x) { *(ptrd++) = *ptr; ptr+=*(poff_x++); }
  8774. ++y;
  8775. ulongT dy = *(poff_y++);
  8776. for ( ; !dy && y<hd; std::memcpy(ptrd,ptrd - wd,sizeof(t)*wd), ++y, ptrd+=wd, dy = *(poff_y++)) {}
  8777. ptrs+=dy;
  8778. }
  8779. }
  8780. //! Set normalization type.
  8781. /**
  8782. \param normalization New normalization mode.
  8783. **/
  8784. CImgDisplay& set_normalization(const unsigned int normalization) {
  8785. _normalization = normalization;
  8786. _min = _max = 0;
  8787. return *this;
  8788. }
  8789. #if cimg_display==0
  8790. //! Set title of the associated window.
  8791. /**
  8792. \param format C-string containing the format of the title, as with <tt>std::printf()</tt>.
  8793. \warning As the first argument is a format string, it is highly recommended to write
  8794. \code
  8795. disp.set_title("%s",window_title);
  8796. \endcode
  8797. instead of
  8798. \code
  8799. disp.set_title(window_title);
  8800. \endcode
  8801. if \c window_title can be arbitrary, to prevent nasty memory access.
  8802. **/
  8803. CImgDisplay& set_title(const char *const format, ...) {
  8804. return assign(0,0,format);
  8805. }
  8806. #endif
  8807. //! Enable or disable fullscreen mode.
  8808. /**
  8809. \param is_fullscreen Tells is the fullscreen mode must be activated or not.
  8810. \param force_redraw Tells if the previous window content must be displayed as well.
  8811. \note
  8812. - When the fullscreen mode is enabled, the associated window fills the entire screen but the size of the
  8813. current display is not modified.
  8814. - The screen resolution may be switched to fit the associated window size and ensure it appears the largest
  8815. as possible.
  8816. For X-Window (X11) users, the configuration flag \c cimg_use_xrandr has to be set to allow the screen
  8817. resolution change (requires the X11 extensions to be enabled).
  8818. **/
  8819. CImgDisplay& set_fullscreen(const bool is_fullscreen, const bool force_redraw=true) {
  8820. if (is_empty() || _is_fullscreen==is_fullscreen) return *this;
  8821. return toggle_fullscreen(force_redraw);
  8822. }
  8823. #if cimg_display==0
  8824. //! Toggle fullscreen mode.
  8825. /**
  8826. \param force_redraw Tells if the previous window content must be displayed as well.
  8827. \note Enable fullscreen mode if it was not enabled, and disable it otherwise.
  8828. **/
  8829. CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
  8830. return assign(_width,_height,0,3,force_redraw);
  8831. }
  8832. //! Show mouse pointer.
  8833. /**
  8834. \note Depending on the window manager behavior, this method may not succeed
  8835. (no exceptions are thrown nevertheless).
  8836. **/
  8837. CImgDisplay& show_mouse() {
  8838. return assign();
  8839. }
  8840. //! Hide mouse pointer.
  8841. /**
  8842. \note Depending on the window manager behavior, this method may not succeed
  8843. (no exceptions are thrown nevertheless).
  8844. **/
  8845. CImgDisplay& hide_mouse() {
  8846. return assign();
  8847. }
  8848. //! Move mouse pointer to a specified location.
  8849. /**
  8850. \note Depending on the window manager behavior, this method may not succeed
  8851. (no exceptions are thrown nevertheless).
  8852. **/
  8853. CImgDisplay& set_mouse(const int pos_x, const int pos_y) {
  8854. return assign(pos_x,pos_y);
  8855. }
  8856. #endif
  8857. //! Simulate a mouse button release event.
  8858. /**
  8859. \note All mouse buttons are considered released at the same time.
  8860. **/
  8861. CImgDisplay& set_button() {
  8862. _button = 0;
  8863. _is_event = true;
  8864. #if cimg_display==1
  8865. pthread_cond_broadcast(&cimg::X11_attr().wait_event);
  8866. #elif cimg_display==2
  8867. SetEvent(cimg::Win32_attr().wait_event);
  8868. #endif
  8869. return *this;
  8870. }
  8871. //! Simulate a mouse button press or release event.
  8872. /**
  8873. \param button Buttons event code, where each button is associated to a single bit.
  8874. \param is_pressed Tells if the mouse button is considered as pressed or released.
  8875. **/
  8876. CImgDisplay& set_button(const unsigned int button, const bool is_pressed=true) {
  8877. const unsigned int buttoncode = button==1U?1U:button==2U?2U:button==3U?4U:0U;
  8878. if (is_pressed) _button |= buttoncode; else _button &= ~buttoncode;
  8879. _is_event = buttoncode?true:false;
  8880. if (buttoncode) {
  8881. #if cimg_display==1
  8882. pthread_cond_broadcast(&cimg::X11_attr().wait_event);
  8883. #elif cimg_display==2
  8884. SetEvent(cimg::Win32_attr().wait_event);
  8885. #endif
  8886. }
  8887. return *this;
  8888. }
  8889. //! Flush all mouse wheel events.
  8890. /**
  8891. \note Make wheel() to return \c 0, if called afterwards.
  8892. **/
  8893. CImgDisplay& set_wheel() {
  8894. _wheel = 0;
  8895. _is_event = true;
  8896. #if cimg_display==1
  8897. pthread_cond_broadcast(&cimg::X11_attr().wait_event);
  8898. #elif cimg_display==2
  8899. SetEvent(cimg::Win32_attr().wait_event);
  8900. #endif
  8901. return *this;
  8902. }
  8903. //! Simulate a wheel event.
  8904. /**
  8905. \param amplitude Amplitude of the wheel scrolling to simulate.
  8906. \note Make wheel() to return \c amplitude, if called afterwards.
  8907. **/
  8908. CImgDisplay& set_wheel(const int amplitude) {
  8909. _wheel+=amplitude;
  8910. _is_event = amplitude?true:false;
  8911. if (amplitude) {
  8912. #if cimg_display==1
  8913. pthread_cond_broadcast(&cimg::X11_attr().wait_event);
  8914. #elif cimg_display==2
  8915. SetEvent(cimg::Win32_attr().wait_event);
  8916. #endif
  8917. }
  8918. return *this;
  8919. }
  8920. //! Flush all key events.
  8921. /**
  8922. \note Make key() to return \c 0, if called afterwards.
  8923. **/
  8924. CImgDisplay& set_key() {
  8925. std::memset((void*)_keys,0,128*sizeof(unsigned int));
  8926. std::memset((void*)_released_keys,0,128*sizeof(unsigned int));
  8927. _is_keyESC = _is_keyF1 = _is_keyF2 = _is_keyF3 = _is_keyF4 = _is_keyF5 = _is_keyF6 = _is_keyF7 = _is_keyF8 =
  8928. _is_keyF9 = _is_keyF10 = _is_keyF11 = _is_keyF12 = _is_keyPAUSE = _is_key1 = _is_key2 = _is_key3 = _is_key4 =
  8929. _is_key5 = _is_key6 = _is_key7 = _is_key8 = _is_key9 = _is_key0 = _is_keyBACKSPACE = _is_keyINSERT =
  8930. _is_keyHOME = _is_keyPAGEUP = _is_keyTAB = _is_keyQ = _is_keyW = _is_keyE = _is_keyR = _is_keyT = _is_keyY =
  8931. _is_keyU = _is_keyI = _is_keyO = _is_keyP = _is_keyDELETE = _is_keyEND = _is_keyPAGEDOWN = _is_keyCAPSLOCK =
  8932. _is_keyA = _is_keyS = _is_keyD = _is_keyF = _is_keyG = _is_keyH = _is_keyJ = _is_keyK = _is_keyL =
  8933. _is_keyENTER = _is_keySHIFTLEFT = _is_keyZ = _is_keyX = _is_keyC = _is_keyV = _is_keyB = _is_keyN =
  8934. _is_keyM = _is_keySHIFTRIGHT = _is_keyARROWUP = _is_keyCTRLLEFT = _is_keyAPPLEFT = _is_keyALT = _is_keySPACE =
  8935. _is_keyALTGR = _is_keyAPPRIGHT = _is_keyMENU = _is_keyCTRLRIGHT = _is_keyARROWLEFT = _is_keyARROWDOWN =
  8936. _is_keyARROWRIGHT = _is_keyPAD0 = _is_keyPAD1 = _is_keyPAD2 = _is_keyPAD3 = _is_keyPAD4 = _is_keyPAD5 =
  8937. _is_keyPAD6 = _is_keyPAD7 = _is_keyPAD8 = _is_keyPAD9 = _is_keyPADADD = _is_keyPADSUB = _is_keyPADMUL =
  8938. _is_keyPADDIV = false;
  8939. _is_event = true;
  8940. #if cimg_display==1
  8941. pthread_cond_broadcast(&cimg::X11_attr().wait_event);
  8942. #elif cimg_display==2
  8943. SetEvent(cimg::Win32_attr().wait_event);
  8944. #endif
  8945. return *this;
  8946. }
  8947. //! Simulate a keyboard press/release event.
  8948. /**
  8949. \param keycode Keycode of the associated key.
  8950. \param is_pressed Tells if the key is considered as pressed or released.
  8951. \note Keycode constants are defined in the cimg namespace and are architecture-dependent. Use them to ensure
  8952. your code stay portable (see cimg::keyESC).
  8953. **/
  8954. CImgDisplay& set_key(const unsigned int keycode, const bool is_pressed=true) {
  8955. #define _cimg_set_key(k) if (keycode==cimg::key##k) _is_key##k = is_pressed;
  8956. _cimg_set_key(ESC); _cimg_set_key(F1); _cimg_set_key(F2); _cimg_set_key(F3);
  8957. _cimg_set_key(F4); _cimg_set_key(F5); _cimg_set_key(F6); _cimg_set_key(F7);
  8958. _cimg_set_key(F8); _cimg_set_key(F9); _cimg_set_key(F10); _cimg_set_key(F11);
  8959. _cimg_set_key(F12); _cimg_set_key(PAUSE); _cimg_set_key(1); _cimg_set_key(2);
  8960. _cimg_set_key(3); _cimg_set_key(4); _cimg_set_key(5); _cimg_set_key(6);
  8961. _cimg_set_key(7); _cimg_set_key(8); _cimg_set_key(9); _cimg_set_key(0);
  8962. _cimg_set_key(BACKSPACE); _cimg_set_key(INSERT); _cimg_set_key(HOME);
  8963. _cimg_set_key(PAGEUP); _cimg_set_key(TAB); _cimg_set_key(Q); _cimg_set_key(W);
  8964. _cimg_set_key(E); _cimg_set_key(R); _cimg_set_key(T); _cimg_set_key(Y);
  8965. _cimg_set_key(U); _cimg_set_key(I); _cimg_set_key(O); _cimg_set_key(P);
  8966. _cimg_set_key(DELETE); _cimg_set_key(END); _cimg_set_key(PAGEDOWN);
  8967. _cimg_set_key(CAPSLOCK); _cimg_set_key(A); _cimg_set_key(S); _cimg_set_key(D);
  8968. _cimg_set_key(F); _cimg_set_key(G); _cimg_set_key(H); _cimg_set_key(J);
  8969. _cimg_set_key(K); _cimg_set_key(L); _cimg_set_key(ENTER);
  8970. _cimg_set_key(SHIFTLEFT); _cimg_set_key(Z); _cimg_set_key(X); _cimg_set_key(C);
  8971. _cimg_set_key(V); _cimg_set_key(B); _cimg_set_key(N); _cimg_set_key(M);
  8972. _cimg_set_key(SHIFTRIGHT); _cimg_set_key(ARROWUP); _cimg_set_key(CTRLLEFT);
  8973. _cimg_set_key(APPLEFT); _cimg_set_key(ALT); _cimg_set_key(SPACE); _cimg_set_key(ALTGR);
  8974. _cimg_set_key(APPRIGHT); _cimg_set_key(MENU); _cimg_set_key(CTRLRIGHT);
  8975. _cimg_set_key(ARROWLEFT); _cimg_set_key(ARROWDOWN); _cimg_set_key(ARROWRIGHT);
  8976. _cimg_set_key(PAD0); _cimg_set_key(PAD1); _cimg_set_key(PAD2);
  8977. _cimg_set_key(PAD3); _cimg_set_key(PAD4); _cimg_set_key(PAD5);
  8978. _cimg_set_key(PAD6); _cimg_set_key(PAD7); _cimg_set_key(PAD8);
  8979. _cimg_set_key(PAD9); _cimg_set_key(PADADD); _cimg_set_key(PADSUB);
  8980. _cimg_set_key(PADMUL); _cimg_set_key(PADDIV);
  8981. if (is_pressed) {
  8982. if (*_keys)
  8983. std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int));
  8984. *_keys = keycode;
  8985. if (*_released_keys) {
  8986. std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int));
  8987. *_released_keys = 0;
  8988. }
  8989. } else {
  8990. if (*_keys) {
  8991. std::memmove((void*)(_keys + 1),(void*)_keys,127*sizeof(unsigned int));
  8992. *_keys = 0;
  8993. }
  8994. if (*_released_keys)
  8995. std::memmove((void*)(_released_keys + 1),(void*)_released_keys,127*sizeof(unsigned int));
  8996. *_released_keys = keycode;
  8997. }
  8998. _is_event = keycode?true:false;
  8999. if (keycode) {
  9000. #if cimg_display==1
  9001. pthread_cond_broadcast(&cimg::X11_attr().wait_event);
  9002. #elif cimg_display==2
  9003. SetEvent(cimg::Win32_attr().wait_event);
  9004. #endif
  9005. }
  9006. return *this;
  9007. }
  9008. //! Flush all display events.
  9009. /**
  9010. \note Remove all passed events from the current display.
  9011. **/
  9012. CImgDisplay& flush() {
  9013. set_key().set_button().set_wheel();
  9014. _is_resized = _is_moved = _is_event = false;
  9015. _fps_timer = _fps_frames = _timer = 0;
  9016. _fps_fps = 0;
  9017. return *this;
  9018. }
  9019. //! Wait for any user event occurring on the current display.
  9020. CImgDisplay& wait() {
  9021. wait(*this);
  9022. return *this;
  9023. }
  9024. //! Wait for a given number of milliseconds since the last call to wait().
  9025. /**
  9026. \param milliseconds Number of milliseconds to wait for.
  9027. \note Similar to cimg::wait().
  9028. **/
  9029. CImgDisplay& wait(const unsigned int milliseconds) {
  9030. cimg::wait(milliseconds,&_timer);
  9031. return *this;
  9032. }
  9033. //! Wait for any event occurring on the display \c disp1.
  9034. static void wait(CImgDisplay& disp1) {
  9035. disp1._is_event = false;
  9036. while (!disp1._is_closed && !disp1._is_event) wait_all();
  9037. }
  9038. //! Wait for any event occurring either on the display \c disp1 or \c disp2.
  9039. static void wait(CImgDisplay& disp1, CImgDisplay& disp2) {
  9040. disp1._is_event = disp2._is_event = false;
  9041. while ((!disp1._is_closed || !disp2._is_closed) &&
  9042. !disp1._is_event && !disp2._is_event) wait_all();
  9043. }
  9044. //! Wait for any event occurring either on the display \c disp1, \c disp2 or \c disp3.
  9045. static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3) {
  9046. disp1._is_event = disp2._is_event = disp3._is_event = false;
  9047. while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed) &&
  9048. !disp1._is_event && !disp2._is_event && !disp3._is_event) wait_all();
  9049. }
  9050. //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3 or \c disp4.
  9051. static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4) {
  9052. disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = false;
  9053. while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed) &&
  9054. !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event) wait_all();
  9055. }
  9056. //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4 or \c disp5.
  9057. static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4,
  9058. CImgDisplay& disp5) {
  9059. disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event = false;
  9060. while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed) &&
  9061. !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event)
  9062. wait_all();
  9063. }
  9064. //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp6.
  9065. static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
  9066. CImgDisplay& disp6) {
  9067. disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
  9068. disp6._is_event = false;
  9069. while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
  9070. !disp6._is_closed) &&
  9071. !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
  9072. !disp6._is_event) wait_all();
  9073. }
  9074. //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp7.
  9075. static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
  9076. CImgDisplay& disp6, CImgDisplay& disp7) {
  9077. disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
  9078. disp6._is_event = disp7._is_event = false;
  9079. while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
  9080. !disp6._is_closed || !disp7._is_closed) &&
  9081. !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
  9082. !disp6._is_event && !disp7._is_event) wait_all();
  9083. }
  9084. //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp8.
  9085. static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
  9086. CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8) {
  9087. disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
  9088. disp6._is_event = disp7._is_event = disp8._is_event = false;
  9089. while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
  9090. !disp6._is_closed || !disp7._is_closed || !disp8._is_closed) &&
  9091. !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
  9092. !disp6._is_event && !disp7._is_event && !disp8._is_event) wait_all();
  9093. }
  9094. //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp9.
  9095. static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
  9096. CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9) {
  9097. disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
  9098. disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = false;
  9099. while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
  9100. !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed) &&
  9101. !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
  9102. !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event) wait_all();
  9103. }
  9104. //! Wait for any event occurring either on the display \c disp1, \c disp2, \c disp3, \c disp4, ... \c disp10.
  9105. static void wait(CImgDisplay& disp1, CImgDisplay& disp2, CImgDisplay& disp3, CImgDisplay& disp4, CImgDisplay& disp5,
  9106. CImgDisplay& disp6, CImgDisplay& disp7, CImgDisplay& disp8, CImgDisplay& disp9,
  9107. CImgDisplay& disp10) {
  9108. disp1._is_event = disp2._is_event = disp3._is_event = disp4._is_event = disp5._is_event =
  9109. disp6._is_event = disp7._is_event = disp8._is_event = disp9._is_event = disp10._is_event = false;
  9110. while ((!disp1._is_closed || !disp2._is_closed || !disp3._is_closed || !disp4._is_closed || !disp5._is_closed ||
  9111. !disp6._is_closed || !disp7._is_closed || !disp8._is_closed || !disp9._is_closed || !disp10._is_closed) &&
  9112. !disp1._is_event && !disp2._is_event && !disp3._is_event && !disp4._is_event && !disp5._is_event &&
  9113. !disp6._is_event && !disp7._is_event && !disp8._is_event && !disp9._is_event && !disp10._is_event)
  9114. wait_all();
  9115. }
  9116. #if cimg_display==0
  9117. //! Wait for any window event occurring in any opened CImgDisplay.
  9118. static void wait_all() {
  9119. return _no_display_exception();
  9120. }
  9121. //! Render image into internal display buffer.
  9122. /**
  9123. \param img Input image data to render.
  9124. \note
  9125. - Convert image data representation into the internal display buffer (architecture-dependent structure).
  9126. - The content of the associated window is not modified, until paint() is called.
  9127. - Should not be used for common CImgDisplay uses, since display() is more useful.
  9128. **/
  9129. template<typename T>
  9130. CImgDisplay& render(const CImg<T>& img) {
  9131. return assign(img);
  9132. }
  9133. //! Paint internal display buffer on associated window.
  9134. /**
  9135. \note
  9136. - Update the content of the associated window with the internal display buffer, e.g. after a render() call.
  9137. - Should not be used for common CImgDisplay uses, since display() is more useful.
  9138. **/
  9139. CImgDisplay& paint() {
  9140. return assign();
  9141. }
  9142. //! Take a snapshot of the current screen content.
  9143. /**
  9144. \param x0 X-coordinate of the upper left corner.
  9145. \param y0 Y-coordinate of the upper left corner.
  9146. \param x1 X-coordinate of the lower right corner.
  9147. \param y1 Y-coordinate of the lower right corner.
  9148. \param[out] img Output screenshot. Can be empty on input
  9149. **/
  9150. template<typename T>
  9151. static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) {
  9152. cimg::unused(x0,y0,x1,y1,&img);
  9153. _no_display_exception();
  9154. }
  9155. //! Take a snapshot of the associated window content.
  9156. /**
  9157. \param[out] img Output snapshot. Can be empty on input.
  9158. **/
  9159. template<typename T>
  9160. const CImgDisplay& snapshot(CImg<T>& img) const {
  9161. cimg::unused(img);
  9162. _no_display_exception();
  9163. return *this;
  9164. }
  9165. #endif
  9166. // X11-based implementation
  9167. //--------------------------
  9168. #if cimg_display==1
  9169. Atom _wm_window_atom, _wm_protocol_atom;
  9170. Window _window, _background_window;
  9171. Colormap _colormap;
  9172. XImage *_image;
  9173. void *_data;
  9174. #ifdef cimg_use_xshm
  9175. XShmSegmentInfo *_shminfo;
  9176. #endif
  9177. static int screen_width() {
  9178. Display *const dpy = cimg::X11_attr().display;
  9179. int res = 0;
  9180. if (!dpy) {
  9181. Display *const _dpy = XOpenDisplay(0);
  9182. if (!_dpy)
  9183. throw CImgDisplayException("CImgDisplay::screen_width(): Failed to open X11 display.");
  9184. res = DisplayWidth(_dpy,DefaultScreen(_dpy));
  9185. XCloseDisplay(_dpy);
  9186. } else {
  9187. #ifdef cimg_use_xrandr
  9188. if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
  9189. res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width;
  9190. else res = DisplayWidth(dpy,DefaultScreen(dpy));
  9191. #else
  9192. res = DisplayWidth(dpy,DefaultScreen(dpy));
  9193. #endif
  9194. }
  9195. return res;
  9196. }
  9197. static int screen_height() {
  9198. Display *const dpy = cimg::X11_attr().display;
  9199. int res = 0;
  9200. if (!dpy) {
  9201. Display *const _dpy = XOpenDisplay(0);
  9202. if (!_dpy)
  9203. throw CImgDisplayException("CImgDisplay::screen_height(): Failed to open X11 display.");
  9204. res = DisplayHeight(_dpy,DefaultScreen(_dpy));
  9205. XCloseDisplay(_dpy);
  9206. } else {
  9207. #ifdef cimg_use_xrandr
  9208. if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution)
  9209. res = cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height;
  9210. else res = DisplayHeight(dpy,DefaultScreen(dpy));
  9211. #else
  9212. res = DisplayHeight(dpy,DefaultScreen(dpy));
  9213. #endif
  9214. }
  9215. return res;
  9216. }
  9217. static void wait_all() {
  9218. if (!cimg::X11_attr().display) return;
  9219. pthread_mutex_lock(&cimg::X11_attr().wait_event_mutex);
  9220. pthread_cond_wait(&cimg::X11_attr().wait_event,&cimg::X11_attr().wait_event_mutex);
  9221. pthread_mutex_unlock(&cimg::X11_attr().wait_event_mutex);
  9222. }
  9223. void _handle_events(const XEvent *const pevent) {
  9224. Display *const dpy = cimg::X11_attr().display;
  9225. XEvent event = *pevent;
  9226. switch (event.type) {
  9227. case ClientMessage : {
  9228. if ((int)event.xclient.message_type==(int)_wm_protocol_atom &&
  9229. (int)event.xclient.data.l[0]==(int)_wm_window_atom) {
  9230. XUnmapWindow(cimg::X11_attr().display,_window);
  9231. _is_closed = _is_event = true;
  9232. pthread_cond_broadcast(&cimg::X11_attr().wait_event);
  9233. }
  9234. } break;
  9235. case ConfigureNotify : {
  9236. while (XCheckWindowEvent(dpy,_window,StructureNotifyMask,&event)) {}
  9237. const unsigned int nw = event.xconfigure.width, nh = event.xconfigure.height;
  9238. const int nx = event.xconfigure.x, ny = event.xconfigure.y;
  9239. if (nw && nh && (nw!=_window_width || nh!=_window_height)) {
  9240. _window_width = nw; _window_height = nh; _mouse_x = _mouse_y = -1;
  9241. XResizeWindow(dpy,_window,_window_width,_window_height);
  9242. _is_resized = _is_event = true;
  9243. pthread_cond_broadcast(&cimg::X11_attr().wait_event);
  9244. }
  9245. if (nx!=_window_x || ny!=_window_y) {
  9246. _window_x = nx;
  9247. _window_y = ny;
  9248. _is_moved = _is_event = true;
  9249. pthread_cond_broadcast(&cimg::X11_attr().wait_event);
  9250. }
  9251. } break;
  9252. case Expose : {
  9253. while (XCheckWindowEvent(dpy,_window,ExposureMask,&event)) {}
  9254. _paint(false);
  9255. if (_is_fullscreen) {
  9256. XWindowAttributes attr;
  9257. do {
  9258. XGetWindowAttributes(dpy,_window,&attr);
  9259. if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); }
  9260. } while (attr.map_state!=IsViewable);
  9261. XSetInputFocus(dpy,_window,RevertToParent,CurrentTime);
  9262. }
  9263. } break;
  9264. case ButtonPress : {
  9265. do {
  9266. _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
  9267. if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
  9268. switch (event.xbutton.button) {
  9269. case 1 : set_button(1); break;
  9270. case 3 : set_button(2); break;
  9271. case 2 : set_button(3); break;
  9272. }
  9273. } while (XCheckWindowEvent(dpy,_window,ButtonPressMask,&event));
  9274. } break;
  9275. case ButtonRelease : {
  9276. do {
  9277. _mouse_x = event.xmotion.x; _mouse_y = event.xmotion.y;
  9278. if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
  9279. switch (event.xbutton.button) {
  9280. case 1 : set_button(1,false); break;
  9281. case 3 : set_button(2,false); break;
  9282. case 2 : set_button(3,false); break;
  9283. case 4 : set_wheel(1); break;
  9284. case 5 : set_wheel(-1); break;
  9285. }
  9286. } while (XCheckWindowEvent(dpy,_window,ButtonReleaseMask,&event));
  9287. } break;
  9288. case KeyPress : {
  9289. char tmp = 0; KeySym ksym;
  9290. XLookupString(&event.xkey,&tmp,1,&ksym,0);
  9291. set_key((unsigned int)ksym,true);
  9292. } break;
  9293. case KeyRelease : {
  9294. char keys_return[32]; // Check that the key has been physically unpressed
  9295. XQueryKeymap(dpy,keys_return);
  9296. const unsigned int kc = event.xkey.keycode, kc1 = kc/8, kc2 = kc%8;
  9297. const bool is_key_pressed = kc1>=32?false:(keys_return[kc1]>>kc2)&1;
  9298. if (!is_key_pressed) {
  9299. char tmp = 0; KeySym ksym;
  9300. XLookupString(&event.xkey,&tmp,1,&ksym,0);
  9301. set_key((unsigned int)ksym,false);
  9302. }
  9303. } break;
  9304. case EnterNotify: {
  9305. while (XCheckWindowEvent(dpy,_window,EnterWindowMask,&event)) {}
  9306. _mouse_x = event.xmotion.x;
  9307. _mouse_y = event.xmotion.y;
  9308. if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
  9309. } break;
  9310. case LeaveNotify : {
  9311. while (XCheckWindowEvent(dpy,_window,LeaveWindowMask,&event)) {}
  9312. _mouse_x = _mouse_y = -1; _is_event = true;
  9313. pthread_cond_broadcast(&cimg::X11_attr().wait_event);
  9314. } break;
  9315. case MotionNotify : {
  9316. while (XCheckWindowEvent(dpy,_window,PointerMotionMask,&event)) {}
  9317. _mouse_x = event.xmotion.x;
  9318. _mouse_y = event.xmotion.y;
  9319. if (_mouse_x<0 || _mouse_y<0 || _mouse_x>=width() || _mouse_y>=height()) _mouse_x = _mouse_y = -1;
  9320. _is_event = true;
  9321. pthread_cond_broadcast(&cimg::X11_attr().wait_event);
  9322. } break;
  9323. }
  9324. }
  9325. static void* _events_thread(void *arg) { // Thread to manage events for all opened display windows
  9326. Display *const dpy = cimg::X11_attr().display;
  9327. XEvent event;
  9328. pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED,0);
  9329. pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,0);
  9330. if (!arg) for ( ; ; ) {
  9331. cimg_lock_display();
  9332. bool event_flag = XCheckTypedEvent(dpy,ClientMessage,&event);
  9333. if (!event_flag) event_flag = XCheckMaskEvent(dpy,
  9334. ExposureMask | StructureNotifyMask | ButtonPressMask |
  9335. KeyPressMask | PointerMotionMask | EnterWindowMask |
  9336. LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask,&event);
  9337. if (event_flag)
  9338. for (unsigned int i = 0; i<cimg::X11_attr().nb_wins; ++i)
  9339. if (!cimg::X11_attr().wins[i]->_is_closed && event.xany.window==cimg::X11_attr().wins[i]->_window)
  9340. cimg::X11_attr().wins[i]->_handle_events(&event);
  9341. cimg_unlock_display();
  9342. pthread_testcancel();
  9343. cimg::sleep(8);
  9344. }
  9345. return 0;
  9346. }
  9347. void _set_colormap(Colormap& cmap, const unsigned int dim) {
  9348. XColor *const colormap = new XColor[256];
  9349. switch (dim) {
  9350. case 1 : { // colormap for greyscale images
  9351. for (unsigned int index = 0; index<256; ++index) {
  9352. colormap[index].pixel = index;
  9353. colormap[index].red = colormap[index].green = colormap[index].blue = (unsigned short)(index<<8);
  9354. colormap[index].flags = DoRed | DoGreen | DoBlue;
  9355. }
  9356. } break;
  9357. case 2 : { // colormap for RG images
  9358. for (unsigned int index = 0, r = 8; r<256; r+=16)
  9359. for (unsigned int g = 8; g<256; g+=16) {
  9360. colormap[index].pixel = index;
  9361. colormap[index].red = colormap[index].blue = (unsigned short)(r<<8);
  9362. colormap[index].green = (unsigned short)(g<<8);
  9363. colormap[index++].flags = DoRed | DoGreen | DoBlue;
  9364. }
  9365. } break;
  9366. default : { // colormap for RGB images
  9367. for (unsigned int index = 0, r = 16; r<256; r+=32)
  9368. for (unsigned int g = 16; g<256; g+=32)
  9369. for (unsigned int b = 32; b<256; b+=64) {
  9370. colormap[index].pixel = index;
  9371. colormap[index].red = (unsigned short)(r<<8);
  9372. colormap[index].green = (unsigned short)(g<<8);
  9373. colormap[index].blue = (unsigned short)(b<<8);
  9374. colormap[index++].flags = DoRed | DoGreen | DoBlue;
  9375. }
  9376. }
  9377. }
  9378. XStoreColors(cimg::X11_attr().display,cmap,colormap,256);
  9379. delete[] colormap;
  9380. }
  9381. void _map_window() {
  9382. Display *const dpy = cimg::X11_attr().display;
  9383. bool is_exposed = false, is_mapped = false;
  9384. XWindowAttributes attr;
  9385. XEvent event;
  9386. XMapRaised(dpy,_window);
  9387. do { // Wait for the window to be mapped
  9388. XWindowEvent(dpy,_window,StructureNotifyMask | ExposureMask,&event);
  9389. switch (event.type) {
  9390. case MapNotify : is_mapped = true; break;
  9391. case Expose : is_exposed = true; break;
  9392. }
  9393. } while (!is_exposed || !is_mapped);
  9394. do { // Wait for the window to be visible
  9395. XGetWindowAttributes(dpy,_window,&attr);
  9396. if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); }
  9397. } while (attr.map_state!=IsViewable);
  9398. _window_x = attr.x;
  9399. _window_y = attr.y;
  9400. }
  9401. void _paint(const bool wait_expose=true) {
  9402. if (_is_closed || !_image) return;
  9403. Display *const dpy = cimg::X11_attr().display;
  9404. if (wait_expose) { // Send an expose event sticked to display window to force repaint
  9405. XEvent event;
  9406. event.xexpose.type = Expose;
  9407. event.xexpose.serial = 0;
  9408. event.xexpose.send_event = 1;
  9409. event.xexpose.display = dpy;
  9410. event.xexpose.window = _window;
  9411. event.xexpose.x = 0;
  9412. event.xexpose.y = 0;
  9413. event.xexpose.width = width();
  9414. event.xexpose.height = height();
  9415. event.xexpose.count = 0;
  9416. XSendEvent(dpy,_window,0,0,&event);
  9417. } else { // Repaint directly (may be called from the expose event)
  9418. GC gc = DefaultGC(dpy,DefaultScreen(dpy));
  9419. #ifdef cimg_use_xshm
  9420. if (_shminfo) XShmPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height,1);
  9421. else XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height);
  9422. #else
  9423. XPutImage(dpy,_window,gc,_image,0,0,0,0,_width,_height);
  9424. #endif
  9425. }
  9426. }
  9427. template<typename T>
  9428. void _resize(T pixel_type, const unsigned int ndimx, const unsigned int ndimy, const bool force_redraw) {
  9429. Display *const dpy = cimg::X11_attr().display;
  9430. cimg::unused(pixel_type);
  9431. #ifdef cimg_use_xshm
  9432. if (_shminfo) {
  9433. XShmSegmentInfo *const nshminfo = new XShmSegmentInfo;
  9434. XImage *const nimage = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),
  9435. cimg::X11_attr().nb_bits,ZPixmap,0,nshminfo,ndimx,ndimy);
  9436. if (!nimage) { delete nshminfo; return; }
  9437. else {
  9438. nshminfo->shmid = shmget(IPC_PRIVATE,ndimx*ndimy*sizeof(T),IPC_CREAT | 0777);
  9439. if (nshminfo->shmid==-1) { XDestroyImage(nimage); delete nshminfo; return; }
  9440. else {
  9441. nshminfo->shmaddr = nimage->data = (char*)shmat(nshminfo->shmid,0,0);
  9442. if (nshminfo->shmaddr==(char*)-1) {
  9443. shmctl(nshminfo->shmid,IPC_RMID,0); XDestroyImage(nimage); delete nshminfo; return;
  9444. } else {
  9445. nshminfo->readOnly = 0;
  9446. cimg::X11_attr().is_shm_enabled = true;
  9447. XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
  9448. XShmAttach(dpy,nshminfo);
  9449. XFlush(dpy);
  9450. XSetErrorHandler(oldXErrorHandler);
  9451. if (!cimg::X11_attr().is_shm_enabled) {
  9452. shmdt(nshminfo->shmaddr);
  9453. shmctl(nshminfo->shmid,IPC_RMID,0);
  9454. XDestroyImage(nimage);
  9455. delete nshminfo;
  9456. return;
  9457. } else {
  9458. T *const ndata = (T*)nimage->data;
  9459. if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
  9460. else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
  9461. XShmDetach(dpy,_shminfo);
  9462. XDestroyImage(_image);
  9463. shmdt(_shminfo->shmaddr);
  9464. shmctl(_shminfo->shmid,IPC_RMID,0);
  9465. delete _shminfo;
  9466. _shminfo = nshminfo;
  9467. _image = nimage;
  9468. _data = (void*)ndata;
  9469. }
  9470. }
  9471. }
  9472. }
  9473. } else
  9474. #endif
  9475. {
  9476. T *ndata = (T*)std::malloc(ndimx*ndimy*sizeof(T));
  9477. if (force_redraw) _render_resize((T*)_data,_width,_height,ndata,ndimx,ndimy);
  9478. else std::memset(ndata,0,sizeof(T)*ndimx*ndimy);
  9479. _data = (void*)ndata;
  9480. XDestroyImage(_image);
  9481. _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),
  9482. cimg::X11_attr().nb_bits,ZPixmap,0,(char*)_data,ndimx,ndimy,8,0);
  9483. }
  9484. }
  9485. void _init_fullscreen() {
  9486. if (!_is_fullscreen || _is_closed) return;
  9487. Display *const dpy = cimg::X11_attr().display;
  9488. _background_window = 0;
  9489. #ifdef cimg_use_xrandr
  9490. int foo;
  9491. if (XRRQueryExtension(dpy,&foo,&foo)) {
  9492. XRRRotations(dpy,DefaultScreen(dpy),&cimg::X11_attr().curr_rotation);
  9493. if (!cimg::X11_attr().resolutions) {
  9494. cimg::X11_attr().resolutions = XRRSizes(dpy,DefaultScreen(dpy),&foo);
  9495. cimg::X11_attr().nb_resolutions = (unsigned int)foo;
  9496. }
  9497. if (cimg::X11_attr().resolutions) {
  9498. cimg::X11_attr().curr_resolution = 0;
  9499. for (unsigned int i = 0; i<cimg::X11_attr().nb_resolutions; ++i) {
  9500. const unsigned int
  9501. nw = (unsigned int)(cimg::X11_attr().resolutions[i].width),
  9502. nh = (unsigned int)(cimg::X11_attr().resolutions[i].height);
  9503. if (nw>=_width && nh>=_height &&
  9504. nw<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].width) &&
  9505. nh<=(unsigned int)(cimg::X11_attr().resolutions[cimg::X11_attr().curr_resolution].height))
  9506. cimg::X11_attr().curr_resolution = i;
  9507. }
  9508. if (cimg::X11_attr().curr_resolution>0) {
  9509. XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy));
  9510. XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),
  9511. cimg::X11_attr().curr_resolution,cimg::X11_attr().curr_rotation,CurrentTime);
  9512. XRRFreeScreenConfigInfo(config);
  9513. XSync(dpy,0);
  9514. }
  9515. }
  9516. }
  9517. if (!cimg::X11_attr().resolutions)
  9518. cimg::warn(_cimgdisplay_instance
  9519. "init_fullscreen(): Xrandr extension not supported by the X server.",
  9520. cimgdisplay_instance);
  9521. #endif
  9522. const unsigned int sx = screen_width(), sy = screen_height();
  9523. if (sx==_width && sy==_height) return;
  9524. XSetWindowAttributes attr_set;
  9525. attr_set.background_pixel = XBlackPixel(dpy,XDefaultScreen(dpy));
  9526. attr_set.override_redirect = 1;
  9527. _background_window = XCreateWindow(dpy,DefaultRootWindow(dpy),0,0,sx,sy,0,0,
  9528. InputOutput,CopyFromParent,CWBackPixel | CWOverrideRedirect,&attr_set);
  9529. XEvent event;
  9530. XSelectInput(dpy,_background_window,StructureNotifyMask);
  9531. XMapRaised(dpy,_background_window);
  9532. do XWindowEvent(dpy,_background_window,StructureNotifyMask,&event);
  9533. while (event.type!=MapNotify);
  9534. XWindowAttributes attr;
  9535. do {
  9536. XGetWindowAttributes(dpy,_background_window,&attr);
  9537. if (attr.map_state!=IsViewable) { XSync(dpy,0); cimg::sleep(10); }
  9538. } while (attr.map_state!=IsViewable);
  9539. }
  9540. void _desinit_fullscreen() {
  9541. if (!_is_fullscreen) return;
  9542. Display *const dpy = cimg::X11_attr().display;
  9543. XUngrabKeyboard(dpy,CurrentTime);
  9544. #ifdef cimg_use_xrandr
  9545. if (cimg::X11_attr().resolutions && cimg::X11_attr().curr_resolution) {
  9546. XRRScreenConfiguration *config = XRRGetScreenInfo(dpy,DefaultRootWindow(dpy));
  9547. XRRSetScreenConfig(dpy,config,DefaultRootWindow(dpy),0,cimg::X11_attr().curr_rotation,CurrentTime);
  9548. XRRFreeScreenConfigInfo(config);
  9549. XSync(dpy,0);
  9550. cimg::X11_attr().curr_resolution = 0;
  9551. }
  9552. #endif
  9553. if (_background_window) XDestroyWindow(dpy,_background_window);
  9554. _background_window = 0;
  9555. _is_fullscreen = false;
  9556. }
  9557. static int _assign_xshm(Display *dpy, XErrorEvent *error) {
  9558. cimg::unused(dpy,error);
  9559. cimg::X11_attr().is_shm_enabled = false;
  9560. return 0;
  9561. }
  9562. void _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
  9563. const unsigned int normalization_type=3,
  9564. const bool fullscreen_flag=false, const bool closed_flag=false) {
  9565. cimg::mutex(14);
  9566. // Allocate space for window title
  9567. const char *const nptitle = ptitle?ptitle:"";
  9568. const unsigned int s = (unsigned int)std::strlen(nptitle) + 1;
  9569. char *const tmp_title = s?new char[s]:0;
  9570. if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
  9571. // Destroy previous display window if existing
  9572. if (!is_empty()) assign();
  9573. // Open X11 display and retrieve graphical properties.
  9574. Display* &dpy = cimg::X11_attr().display;
  9575. if (!dpy) {
  9576. dpy = XOpenDisplay(0);
  9577. if (!dpy)
  9578. throw CImgDisplayException(_cimgdisplay_instance
  9579. "assign(): Failed to open X11 display.",
  9580. cimgdisplay_instance);
  9581. cimg::X11_attr().nb_bits = DefaultDepth(dpy,DefaultScreen(dpy));
  9582. if (cimg::X11_attr().nb_bits!=8 && cimg::X11_attr().nb_bits!=16 &&
  9583. cimg::X11_attr().nb_bits!=24 && cimg::X11_attr().nb_bits!=32)
  9584. throw CImgDisplayException(_cimgdisplay_instance
  9585. "assign(): Invalid %u bits screen mode detected "
  9586. "(only 8, 16, 24 and 32 bits modes are managed).",
  9587. cimgdisplay_instance,
  9588. cimg::X11_attr().nb_bits);
  9589. XVisualInfo vtemplate;
  9590. vtemplate.visualid = XVisualIDFromVisual(DefaultVisual(dpy,DefaultScreen(dpy)));
  9591. int nb_visuals;
  9592. XVisualInfo *vinfo = XGetVisualInfo(dpy,VisualIDMask,&vtemplate,&nb_visuals);
  9593. if (vinfo && vinfo->red_mask<vinfo->blue_mask) cimg::X11_attr().is_blue_first = true;
  9594. cimg::X11_attr().byte_order = ImageByteOrder(dpy);
  9595. XFree(vinfo);
  9596. cimg_lock_display();
  9597. cimg::X11_attr().events_thread = new pthread_t;
  9598. pthread_create(cimg::X11_attr().events_thread,0,_events_thread,0);
  9599. } else cimg_lock_display();
  9600. // Set display variables.
  9601. _width = std::min(dimw,(unsigned int)screen_width());
  9602. _height = std::min(dimh,(unsigned int)screen_height());
  9603. _normalization = normalization_type<4?normalization_type:3;
  9604. _is_fullscreen = fullscreen_flag;
  9605. _window_x = _window_y = cimg::type<int>::min();
  9606. _is_closed = closed_flag;
  9607. _title = tmp_title;
  9608. flush();
  9609. // Create X11 window (and LUT, if 8bits display)
  9610. if (_is_fullscreen) {
  9611. if (!_is_closed) _init_fullscreen();
  9612. const unsigned int sx = screen_width(), sy = screen_height();
  9613. XSetWindowAttributes attr_set;
  9614. attr_set.override_redirect = 1;
  9615. _window = XCreateWindow(dpy,DefaultRootWindow(dpy),(sx - _width)/2,(sy - _height)/2,_width,_height,0,0,
  9616. InputOutput,CopyFromParent,CWOverrideRedirect,&attr_set);
  9617. } else
  9618. _window = XCreateSimpleWindow(dpy,DefaultRootWindow(dpy),0,0,_width,_height,0,0L,0L);
  9619. XSelectInput(dpy,_window,
  9620. ExposureMask | StructureNotifyMask | ButtonPressMask | KeyPressMask | PointerMotionMask |
  9621. EnterWindowMask | LeaveWindowMask | ButtonReleaseMask | KeyReleaseMask);
  9622. XStoreName(dpy,_window,_title?_title:" ");
  9623. if (cimg::X11_attr().nb_bits==8) {
  9624. _colormap = XCreateColormap(dpy,_window,DefaultVisual(dpy,DefaultScreen(dpy)),AllocAll);
  9625. _set_colormap(_colormap,3);
  9626. XSetWindowColormap(dpy,_window,_colormap);
  9627. }
  9628. static const char *const _window_class = cimg_appname;
  9629. XClassHint *const window_class = XAllocClassHint();
  9630. window_class->res_name = (char*)_window_class;
  9631. window_class->res_class = (char*)_window_class;
  9632. XSetClassHint(dpy,_window,window_class);
  9633. XFree(window_class);
  9634. _window_width = _width;
  9635. _window_height = _height;
  9636. // Create XImage
  9637. #ifdef cimg_use_xshm
  9638. _shminfo = 0;
  9639. if (XShmQueryExtension(dpy)) {
  9640. _shminfo = new XShmSegmentInfo;
  9641. _image = XShmCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
  9642. ZPixmap,0,_shminfo,_width,_height);
  9643. if (!_image) { delete _shminfo; _shminfo = 0; }
  9644. else {
  9645. _shminfo->shmid = shmget(IPC_PRIVATE,_image->bytes_per_line*_image->height,IPC_CREAT|0777);
  9646. if (_shminfo->shmid==-1) { XDestroyImage(_image); delete _shminfo; _shminfo = 0; }
  9647. else {
  9648. _shminfo->shmaddr = _image->data = (char*)(_data = shmat(_shminfo->shmid,0,0));
  9649. if (_shminfo->shmaddr==(char*)-1) {
  9650. shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image); delete _shminfo; _shminfo = 0;
  9651. } else {
  9652. _shminfo->readOnly = 0;
  9653. cimg::X11_attr().is_shm_enabled = true;
  9654. XErrorHandler oldXErrorHandler = XSetErrorHandler(_assign_xshm);
  9655. XShmAttach(dpy,_shminfo);
  9656. XSync(dpy,0);
  9657. XSetErrorHandler(oldXErrorHandler);
  9658. if (!cimg::X11_attr().is_shm_enabled) {
  9659. shmdt(_shminfo->shmaddr); shmctl(_shminfo->shmid,IPC_RMID,0); XDestroyImage(_image);
  9660. delete _shminfo; _shminfo = 0;
  9661. }
  9662. }
  9663. }
  9664. }
  9665. }
  9666. if (!_shminfo)
  9667. #endif
  9668. {
  9669. const cimg_ulong buf_size = (cimg_ulong)_width*_height*(cimg::X11_attr().nb_bits==8?1:
  9670. (cimg::X11_attr().nb_bits==16?2:4));
  9671. _data = std::malloc(buf_size);
  9672. _image = XCreateImage(dpy,DefaultVisual(dpy,DefaultScreen(dpy)),cimg::X11_attr().nb_bits,
  9673. ZPixmap,0,(char*)_data,_width,_height,8,0);
  9674. }
  9675. _wm_window_atom = XInternAtom(dpy,"WM_DELETE_WINDOW",0);
  9676. _wm_protocol_atom = XInternAtom(dpy,"WM_PROTOCOLS",0);
  9677. XSetWMProtocols(dpy,_window,&_wm_window_atom,1);
  9678. if (_is_fullscreen) XGrabKeyboard(dpy,_window,1,GrabModeAsync,GrabModeAsync,CurrentTime);
  9679. cimg::X11_attr().wins[cimg::X11_attr().nb_wins++]=this;
  9680. if (!_is_closed) _map_window(); else _window_x = _window_y = cimg::type<int>::min();
  9681. cimg_unlock_display();
  9682. cimg::mutex(14,0);
  9683. }
  9684. CImgDisplay& assign() {
  9685. if (is_empty()) return flush();
  9686. Display *const dpy = cimg::X11_attr().display;
  9687. cimg_lock_display();
  9688. // Remove display window from event thread list.
  9689. unsigned int i;
  9690. for (i = 0; i<cimg::X11_attr().nb_wins && cimg::X11_attr().wins[i]!=this; ++i) {}
  9691. for ( ; i<cimg::X11_attr().nb_wins - 1; ++i) cimg::X11_attr().wins[i] = cimg::X11_attr().wins[i + 1];
  9692. --cimg::X11_attr().nb_wins;
  9693. // Destroy window, image, colormap and title.
  9694. if (_is_fullscreen && !_is_closed) _desinit_fullscreen();
  9695. #ifdef cimg_use_xshm
  9696. if (_shminfo) {
  9697. XShmDetach(dpy,_shminfo);
  9698. shmdt(_shminfo->shmaddr);
  9699. shmctl(_shminfo->shmid,IPC_RMID,0);
  9700. delete _shminfo;
  9701. _shminfo = 0;
  9702. }
  9703. #endif
  9704. XDestroyImage(_image);
  9705. if (cimg::X11_attr().nb_bits==8) XFreeColormap(dpy,_colormap);
  9706. XDestroyWindow(dpy,_window);
  9707. XSync(dpy,0);
  9708. _window = 0; _colormap = 0; _data = 0; _image = 0;
  9709. // Reset display variables.
  9710. delete[] _title;
  9711. _width = _height = _normalization = _window_width = _window_height = 0;
  9712. _window_x = _window_y = cimg::type<int>::min();
  9713. _is_fullscreen = false;
  9714. _is_closed = true;
  9715. _min = _max = 0;
  9716. _title = 0;
  9717. flush();
  9718. cimg_unlock_display();
  9719. return *this;
  9720. }
  9721. CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0,
  9722. const unsigned int normalization_type=3,
  9723. const bool fullscreen_flag=false, const bool closed_flag=false) {
  9724. if (!dimw || !dimh) return assign();
  9725. _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag);
  9726. _min = _max = 0;
  9727. std::memset(_data,0,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char):
  9728. (cimg::X11_attr().nb_bits==16?sizeof(unsigned short):sizeof(unsigned int)))*
  9729. (size_t)_width*_height);
  9730. return paint();
  9731. }
  9732. template<typename T>
  9733. CImgDisplay& assign(const CImg<T>& img, const char *const title=0,
  9734. const unsigned int normalization_type=3,
  9735. const bool fullscreen_flag=false, const bool closed_flag=false) {
  9736. if (!img) return assign();
  9737. CImg<T> tmp;
  9738. const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
  9739. (img._height - 1)/2,
  9740. (img._depth - 1)/2));
  9741. _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
  9742. if (_normalization==2) _min = (float)nimg.min_max(_max);
  9743. return render(nimg).paint();
  9744. }
  9745. template<typename T>
  9746. CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
  9747. const unsigned int normalization_type=3,
  9748. const bool fullscreen_flag=false, const bool closed_flag=false) {
  9749. if (!list) return assign();
  9750. CImg<T> tmp;
  9751. const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
  9752. (img._height - 1)/2,
  9753. (img._depth - 1)/2));
  9754. _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
  9755. if (_normalization==2) _min = (float)nimg.min_max(_max);
  9756. return render(nimg).paint();
  9757. }
  9758. CImgDisplay& assign(const CImgDisplay& disp) {
  9759. if (!disp) return assign();
  9760. _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed);
  9761. std::memcpy(_data,disp._data,(cimg::X11_attr().nb_bits==8?sizeof(unsigned char):
  9762. cimg::X11_attr().nb_bits==16?sizeof(unsigned short):
  9763. sizeof(unsigned int))*(size_t)_width*_height);
  9764. return paint();
  9765. }
  9766. CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) {
  9767. if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign();
  9768. if (is_empty()) return assign(nwidth,nheight);
  9769. Display *const dpy = cimg::X11_attr().display;
  9770. const unsigned int
  9771. tmpdimx = (nwidth>0)?nwidth:(-nwidth*width()/100),
  9772. tmpdimy = (nheight>0)?nheight:(-nheight*height()/100),
  9773. dimx = tmpdimx?tmpdimx:1,
  9774. dimy = tmpdimy?tmpdimy:1;
  9775. if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) {
  9776. show();
  9777. cimg_lock_display();
  9778. if (_window_width!=dimx || _window_height!=dimy) {
  9779. XWindowAttributes attr;
  9780. for (unsigned int i = 0; i<10; ++i) {
  9781. XResizeWindow(dpy,_window,dimx,dimy);
  9782. XGetWindowAttributes(dpy,_window,&attr);
  9783. if (attr.width==(int)dimx && attr.height==(int)dimy) break;
  9784. cimg::wait(5,&_timer);
  9785. }
  9786. }
  9787. if (_width!=dimx || _height!=dimy) switch (cimg::X11_attr().nb_bits) {
  9788. case 8 : { unsigned char pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break;
  9789. case 16 : { unsigned short pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); } break;
  9790. default : { unsigned int pixel_type = 0; _resize(pixel_type,dimx,dimy,force_redraw); }
  9791. }
  9792. _window_width = _width = dimx; _window_height = _height = dimy;
  9793. cimg_unlock_display();
  9794. }
  9795. _is_resized = false;
  9796. if (_is_fullscreen) move((screen_width() - _width)/2,(screen_height() - _height)/2);
  9797. if (force_redraw) return paint();
  9798. return *this;
  9799. }
  9800. CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
  9801. if (is_empty()) return *this;
  9802. if (force_redraw) {
  9803. const cimg_ulong buf_size = (cimg_ulong)_width*_height*
  9804. (cimg::X11_attr().nb_bits==8?1:(cimg::X11_attr().nb_bits==16?2:4));
  9805. void *image_data = std::malloc(buf_size);
  9806. std::memcpy(image_data,_data,buf_size);
  9807. assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
  9808. std::memcpy(_data,image_data,buf_size);
  9809. std::free(image_data);
  9810. return paint();
  9811. }
  9812. return assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
  9813. }
  9814. CImgDisplay& show() {
  9815. if (is_empty() || !_is_closed) return *this;
  9816. cimg_lock_display();
  9817. _is_closed = false;
  9818. if (_is_fullscreen) _init_fullscreen();
  9819. _map_window();
  9820. cimg_unlock_display();
  9821. return paint();
  9822. }
  9823. CImgDisplay& close() {
  9824. if (is_empty() || _is_closed) return *this;
  9825. Display *const dpy = cimg::X11_attr().display;
  9826. cimg_lock_display();
  9827. if (_is_fullscreen) _desinit_fullscreen();
  9828. XUnmapWindow(dpy,_window);
  9829. _window_x = _window_y = cimg::type<int>::min();
  9830. _is_closed = true;
  9831. cimg_unlock_display();
  9832. return *this;
  9833. }
  9834. CImgDisplay& move(const int posx, const int posy) {
  9835. if (is_empty()) return *this;
  9836. show();
  9837. if (_window_x!=posx || _window_y!=posy) {
  9838. Display *const dpy = cimg::X11_attr().display;
  9839. cimg_lock_display();
  9840. XMoveWindow(dpy,_window,posx,posy);
  9841. _window_x = posx;
  9842. _window_y = posy;
  9843. cimg_unlock_display();
  9844. }
  9845. _is_moved = false;
  9846. return paint();
  9847. }
  9848. CImgDisplay& show_mouse() {
  9849. if (is_empty()) return *this;
  9850. Display *const dpy = cimg::X11_attr().display;
  9851. cimg_lock_display();
  9852. XUndefineCursor(dpy,_window);
  9853. cimg_unlock_display();
  9854. return *this;
  9855. }
  9856. CImgDisplay& hide_mouse() {
  9857. if (is_empty()) return *this;
  9858. Display *const dpy = cimg::X11_attr().display;
  9859. cimg_lock_display();
  9860. static const char pix_data[8] = {};
  9861. XColor col;
  9862. col.red = col.green = col.blue = 0;
  9863. Pixmap pix = XCreateBitmapFromData(dpy,_window,pix_data,8,8);
  9864. Cursor cur = XCreatePixmapCursor(dpy,pix,pix,&col,&col,0,0);
  9865. XFreePixmap(dpy,pix);
  9866. XDefineCursor(dpy,_window,cur);
  9867. cimg_unlock_display();
  9868. return *this;
  9869. }
  9870. CImgDisplay& set_mouse(const int posx, const int posy) {
  9871. if (is_empty() || _is_closed) return *this;
  9872. Display *const dpy = cimg::X11_attr().display;
  9873. cimg_lock_display();
  9874. XWarpPointer(dpy,0L,_window,0,0,0,0,posx,posy);
  9875. _mouse_x = posx; _mouse_y = posy;
  9876. _is_moved = false;
  9877. XSync(dpy,0);
  9878. cimg_unlock_display();
  9879. return *this;
  9880. }
  9881. CImgDisplay& set_title(const char *const format, ...) {
  9882. if (is_empty()) return *this;
  9883. char *const tmp = new char[1024];
  9884. va_list ap;
  9885. va_start(ap, format);
  9886. cimg_vsnprintf(tmp,1024,format,ap);
  9887. va_end(ap);
  9888. if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; }
  9889. delete[] _title;
  9890. const unsigned int s = (unsigned int)std::strlen(tmp) + 1;
  9891. _title = new char[s];
  9892. std::memcpy(_title,tmp,s*sizeof(char));
  9893. Display *const dpy = cimg::X11_attr().display;
  9894. cimg_lock_display();
  9895. XStoreName(dpy,_window,tmp);
  9896. cimg_unlock_display();
  9897. delete[] tmp;
  9898. return *this;
  9899. }
  9900. template<typename T>
  9901. CImgDisplay& display(const CImg<T>& img) {
  9902. if (!img)
  9903. throw CImgArgumentException(_cimgdisplay_instance
  9904. "display(): Empty specified image.",
  9905. cimgdisplay_instance);
  9906. if (is_empty()) return assign(img);
  9907. return render(img).paint(false);
  9908. }
  9909. CImgDisplay& paint(const bool wait_expose=true) {
  9910. if (is_empty()) return *this;
  9911. cimg_lock_display();
  9912. _paint(wait_expose);
  9913. cimg_unlock_display();
  9914. return *this;
  9915. }
  9916. template<typename T>
  9917. CImgDisplay& render(const CImg<T>& img, const bool flag8=false) {
  9918. if (!img)
  9919. throw CImgArgumentException(_cimgdisplay_instance
  9920. "render(): Empty specified image.",
  9921. cimgdisplay_instance);
  9922. if (is_empty()) return *this;
  9923. if (img._depth!=1) return render(img.get_projections2d((img._width - 1)/2,(img._height - 1)/2,
  9924. (img._depth - 1)/2));
  9925. if (cimg::X11_attr().nb_bits==8 && (img._width!=_width || img._height!=_height))
  9926. return render(img.get_resize(_width,_height,1,-100,1));
  9927. if (cimg::X11_attr().nb_bits==8 && !flag8 && img._spectrum==3) {
  9928. static const CImg<typename CImg<T>::ucharT> default_colormap = CImg<typename CImg<T>::ucharT>::default_LUT256();
  9929. return render(img.get_index(default_colormap,1,false));
  9930. }
  9931. const T
  9932. *data1 = img._data,
  9933. *data2 = (img._spectrum>1)?img.data(0,0,0,1):data1,
  9934. *data3 = (img._spectrum>2)?img.data(0,0,0,2):data1;
  9935. if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3);
  9936. cimg_lock_display();
  9937. if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) {
  9938. _min = _max = 0;
  9939. switch (cimg::X11_attr().nb_bits) {
  9940. case 8 : { // 256 colormap, no normalization
  9941. _set_colormap(_colormap,img._spectrum);
  9942. unsigned char
  9943. *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:
  9944. new unsigned char[(size_t)img._width*img._height],
  9945. *ptrd = (unsigned char*)ndata;
  9946. switch (img._spectrum) {
  9947. case 1 :
  9948. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
  9949. (*ptrd++) = (unsigned char)*(data1++);
  9950. break;
  9951. case 2 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9952. const unsigned char
  9953. R = (unsigned char)*(data1++),
  9954. G = (unsigned char)*(data2++);
  9955. (*ptrd++) = (R&0xf0) | (G>>4);
  9956. } break;
  9957. default : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9958. const unsigned char
  9959. R = (unsigned char)*(data1++),
  9960. G = (unsigned char)*(data2++),
  9961. B = (unsigned char)*(data3++);
  9962. (*ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6);
  9963. }
  9964. }
  9965. if (ndata!=_data) {
  9966. _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height);
  9967. delete[] ndata;
  9968. }
  9969. } break;
  9970. case 16 : { // 16 bits colors, no normalization
  9971. unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data:
  9972. new unsigned short[(size_t)img._width*img._height];
  9973. unsigned char *ptrd = (unsigned char*)ndata;
  9974. const unsigned int M = 248;
  9975. switch (img._spectrum) {
  9976. case 1 :
  9977. if (cimg::X11_attr().byte_order)
  9978. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9979. const unsigned char val = (unsigned char)*(data1++), G = val>>2;
  9980. ptrd[0] = (val&M) | (G>>3);
  9981. ptrd[1] = (G<<5) | (G>>1);
  9982. ptrd+=2;
  9983. } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9984. const unsigned char val = (unsigned char)*(data1++), G = val>>2;
  9985. ptrd[0] = (G<<5) | (G>>1);
  9986. ptrd[1] = (val&M) | (G>>3);
  9987. ptrd+=2;
  9988. }
  9989. break;
  9990. case 2 :
  9991. if (cimg::X11_attr().byte_order)
  9992. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9993. const unsigned char G = (unsigned char)*(data2++)>>2;
  9994. ptrd[0] = ((unsigned char)*(data1++)&M) | (G>>3);
  9995. ptrd[1] = (G<<5);
  9996. ptrd+=2;
  9997. } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  9998. const unsigned char G = (unsigned char)*(data2++)>>2;
  9999. ptrd[0] = (G<<5);
  10000. ptrd[1] = ((unsigned char)*(data1++)&M) | (G>>3);
  10001. ptrd+=2;
  10002. }
  10003. break;
  10004. default :
  10005. if (cimg::X11_attr().byte_order)
  10006. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10007. const unsigned char G = (unsigned char)*(data2++)>>2;
  10008. ptrd[0] = ((unsigned char)*(data1++)&M) | (G>>3);
  10009. ptrd[1] = (G<<5) | ((unsigned char)*(data3++)>>3);
  10010. ptrd+=2;
  10011. } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10012. const unsigned char G = (unsigned char)*(data2++)>>2;
  10013. ptrd[0] = (G<<5) | ((unsigned char)*(data3++)>>3);
  10014. ptrd[1] = ((unsigned char)*(data1++)&M) | (G>>3);
  10015. ptrd+=2;
  10016. }
  10017. }
  10018. if (ndata!=_data) {
  10019. _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height);
  10020. delete[] ndata;
  10021. }
  10022. } break;
  10023. default : { // 24 bits colors, no normalization
  10024. unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data:
  10025. new unsigned int[(size_t)img._width*img._height];
  10026. if (sizeof(int)==4) { // 32 bits int uses optimized version
  10027. unsigned int *ptrd = ndata;
  10028. switch (img._spectrum) {
  10029. case 1 :
  10030. if (cimg::X11_attr().byte_order==cimg::endianness())
  10031. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10032. const unsigned char val = (unsigned char)*(data1++);
  10033. *(ptrd++) = (val<<16) | (val<<8) | val;
  10034. }
  10035. else
  10036. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10037. const unsigned char val = (unsigned char)*(data1++);
  10038. *(ptrd++) = (val<<16) | (val<<8) | val;
  10039. }
  10040. break;
  10041. case 2 :
  10042. if (cimg::X11_attr().byte_order==cimg::endianness())
  10043. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
  10044. *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8);
  10045. else
  10046. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
  10047. *(ptrd++) = ((unsigned char)*(data2++)<<16) | ((unsigned char)*(data1++)<<8);
  10048. break;
  10049. default :
  10050. if (cimg::X11_attr().byte_order==cimg::endianness())
  10051. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
  10052. *(ptrd++) = ((unsigned char)*(data1++)<<16) | ((unsigned char)*(data2++)<<8) |
  10053. (unsigned char)*(data3++);
  10054. else
  10055. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
  10056. *(ptrd++) = ((unsigned char)*(data3++)<<24) | ((unsigned char)*(data2++)<<16) |
  10057. ((unsigned char)*(data1++)<<8);
  10058. }
  10059. } else {
  10060. unsigned char *ptrd = (unsigned char*)ndata;
  10061. switch (img._spectrum) {
  10062. case 1 :
  10063. if (cimg::X11_attr().byte_order)
  10064. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10065. ptrd[0] = 0;
  10066. ptrd[1] = (unsigned char)*(data1++);
  10067. ptrd[2] = 0;
  10068. ptrd[3] = 0;
  10069. ptrd+=4;
  10070. } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10071. ptrd[0] = 0;
  10072. ptrd[1] = 0;
  10073. ptrd[2] = (unsigned char)*(data1++);
  10074. ptrd[3] = 0;
  10075. ptrd+=4;
  10076. }
  10077. break;
  10078. case 2 :
  10079. if (cimg::X11_attr().byte_order) cimg::swap(data1,data2);
  10080. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10081. ptrd[0] = 0;
  10082. ptrd[1] = (unsigned char)*(data2++);
  10083. ptrd[2] = (unsigned char)*(data1++);
  10084. ptrd[3] = 0;
  10085. ptrd+=4;
  10086. }
  10087. break;
  10088. default :
  10089. if (cimg::X11_attr().byte_order)
  10090. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10091. ptrd[0] = 0;
  10092. ptrd[1] = (unsigned char)*(data1++);
  10093. ptrd[2] = (unsigned char)*(data2++);
  10094. ptrd[3] = (unsigned char)*(data3++);
  10095. ptrd+=4;
  10096. } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10097. ptrd[0] = (unsigned char)*(data3++);
  10098. ptrd[1] = (unsigned char)*(data2++);
  10099. ptrd[2] = (unsigned char)*(data1++);
  10100. ptrd[3] = 0;
  10101. ptrd+=4;
  10102. }
  10103. }
  10104. }
  10105. if (ndata!=_data) {
  10106. _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height);
  10107. delete[] ndata;
  10108. }
  10109. }
  10110. }
  10111. } else {
  10112. if (_normalization==3) {
  10113. if (sizeof(T)>1 && cimg::type<T>::string()!=cimg::type<bool>::string()) _min = (float)img.min_max(_max);
  10114. else { _min = (float)cimg::type<T>::min(); _max = (float)cimg::type<T>::max(); }
  10115. } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max);
  10116. const float delta = _max - _min, mm = 255/(delta?delta:1.f);
  10117. switch (cimg::X11_attr().nb_bits) {
  10118. case 8 : { // 256 colormap, with normalization
  10119. _set_colormap(_colormap,img._spectrum);
  10120. unsigned char *const ndata = (img._width==_width && img._height==_height)?(unsigned char*)_data:
  10121. new unsigned char[(size_t)img._width*img._height];
  10122. unsigned char *ptrd = (unsigned char*)ndata;
  10123. switch (img._spectrum) {
  10124. case 1 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10125. const unsigned char R = (unsigned char)((*(data1++) - _min)*mm);
  10126. *(ptrd++) = R;
  10127. } break;
  10128. case 2 : for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10129. const unsigned char
  10130. R = (unsigned char)((*(data1++) - _min)*mm),
  10131. G = (unsigned char)((*(data2++) - _min)*mm);
  10132. (*ptrd++) = (R&0xf0) | (G>>4);
  10133. } break;
  10134. default :
  10135. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10136. const unsigned char
  10137. R = (unsigned char)((*(data1++) - _min)*mm),
  10138. G = (unsigned char)((*(data2++) - _min)*mm),
  10139. B = (unsigned char)((*(data3++) - _min)*mm);
  10140. *(ptrd++) = (R&0xe0) | ((G>>5)<<2) | (B>>6);
  10141. }
  10142. }
  10143. if (ndata!=_data) {
  10144. _render_resize(ndata,img._width,img._height,(unsigned char*)_data,_width,_height);
  10145. delete[] ndata;
  10146. }
  10147. } break;
  10148. case 16 : { // 16 bits colors, with normalization
  10149. unsigned short *const ndata = (img._width==_width && img._height==_height)?(unsigned short*)_data:
  10150. new unsigned short[(size_t)img._width*img._height];
  10151. unsigned char *ptrd = (unsigned char*)ndata;
  10152. const unsigned int M = 248;
  10153. switch (img._spectrum) {
  10154. case 1 :
  10155. if (cimg::X11_attr().byte_order)
  10156. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10157. const unsigned char val = (unsigned char)((*(data1++) - _min)*mm), G = val>>2;
  10158. ptrd[0] = (val&M) | (G>>3);
  10159. ptrd[1] = (G<<5) | (val>>3);
  10160. ptrd+=2;
  10161. } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10162. const unsigned char val = (unsigned char)((*(data1++) - _min)*mm), G = val>>2;
  10163. ptrd[0] = (G<<5) | (val>>3);
  10164. ptrd[1] = (val&M) | (G>>3);
  10165. ptrd+=2;
  10166. }
  10167. break;
  10168. case 2 :
  10169. if (cimg::X11_attr().byte_order)
  10170. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10171. const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
  10172. ptrd[0] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
  10173. ptrd[1] = (G<<5);
  10174. ptrd+=2;
  10175. } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10176. const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
  10177. ptrd[0] = (G<<5);
  10178. ptrd[1] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
  10179. ptrd+=2;
  10180. }
  10181. break;
  10182. default :
  10183. if (cimg::X11_attr().byte_order)
  10184. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10185. const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
  10186. ptrd[0] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
  10187. ptrd[1] = (G<<5) | ((unsigned char)((*(data3++) - _min)*mm)>>3);
  10188. ptrd+=2;
  10189. } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10190. const unsigned char G = (unsigned char)((*(data2++) - _min)*mm)>>2;
  10191. ptrd[0] = (G<<5) | ((unsigned char)((*(data3++) - _min)*mm)>>3);
  10192. ptrd[1] = ((unsigned char)((*(data1++) - _min)*mm)&M) | (G>>3);
  10193. ptrd+=2;
  10194. }
  10195. }
  10196. if (ndata!=_data) {
  10197. _render_resize(ndata,img._width,img._height,(unsigned short*)_data,_width,_height);
  10198. delete[] ndata;
  10199. }
  10200. } break;
  10201. default : { // 24 bits colors, with normalization
  10202. unsigned int *const ndata = (img._width==_width && img._height==_height)?(unsigned int*)_data:
  10203. new unsigned int[(size_t)img._width*img._height];
  10204. if (sizeof(int)==4) { // 32 bits int uses optimized version
  10205. unsigned int *ptrd = ndata;
  10206. switch (img._spectrum) {
  10207. case 1 :
  10208. if (cimg::X11_attr().byte_order==cimg::endianness())
  10209. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10210. const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
  10211. *(ptrd++) = (val<<16) | (val<<8) | val;
  10212. }
  10213. else
  10214. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10215. const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
  10216. *(ptrd++) = (val<<24) | (val<<16) | (val<<8);
  10217. }
  10218. break;
  10219. case 2 :
  10220. if (cimg::X11_attr().byte_order==cimg::endianness())
  10221. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
  10222. *(ptrd++) =
  10223. ((unsigned char)((*(data1++) - _min)*mm)<<16) |
  10224. ((unsigned char)((*(data2++) - _min)*mm)<<8);
  10225. else
  10226. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
  10227. *(ptrd++) =
  10228. ((unsigned char)((*(data2++) - _min)*mm)<<16) |
  10229. ((unsigned char)((*(data1++) - _min)*mm)<<8);
  10230. break;
  10231. default :
  10232. if (cimg::X11_attr().byte_order==cimg::endianness())
  10233. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
  10234. *(ptrd++) =
  10235. ((unsigned char)((*(data1++) - _min)*mm)<<16) |
  10236. ((unsigned char)((*(data2++) - _min)*mm)<<8) |
  10237. (unsigned char)((*(data3++) - _min)*mm);
  10238. else
  10239. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy)
  10240. *(ptrd++) =
  10241. ((unsigned char)((*(data3++) - _min)*mm)<<24) |
  10242. ((unsigned char)((*(data2++) - _min)*mm)<<16) |
  10243. ((unsigned char)((*(data1++) - _min)*mm)<<8);
  10244. }
  10245. } else {
  10246. unsigned char *ptrd = (unsigned char*)ndata;
  10247. switch (img._spectrum) {
  10248. case 1 :
  10249. if (cimg::X11_attr().byte_order)
  10250. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10251. const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
  10252. ptrd[0] = 0;
  10253. ptrd[1] = val;
  10254. ptrd[2] = val;
  10255. ptrd[3] = val;
  10256. ptrd+=4;
  10257. } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10258. const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
  10259. ptrd[0] = val;
  10260. ptrd[1] = val;
  10261. ptrd[2] = val;
  10262. ptrd[3] = 0;
  10263. ptrd+=4;
  10264. }
  10265. break;
  10266. case 2 :
  10267. if (cimg::X11_attr().byte_order) cimg::swap(data1,data2);
  10268. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10269. ptrd[0] = 0;
  10270. ptrd[1] = (unsigned char)((*(data2++) - _min)*mm);
  10271. ptrd[2] = (unsigned char)((*(data1++) - _min)*mm);
  10272. ptrd[3] = 0;
  10273. ptrd+=4;
  10274. }
  10275. break;
  10276. default :
  10277. if (cimg::X11_attr().byte_order)
  10278. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10279. ptrd[0] = 0;
  10280. ptrd[1] = (unsigned char)((*(data1++) - _min)*mm);
  10281. ptrd[2] = (unsigned char)((*(data2++) - _min)*mm);
  10282. ptrd[3] = (unsigned char)((*(data3++) - _min)*mm);
  10283. ptrd+=4;
  10284. } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10285. ptrd[0] = (unsigned char)((*(data3++) - _min)*mm);
  10286. ptrd[1] = (unsigned char)((*(data2++) - _min)*mm);
  10287. ptrd[2] = (unsigned char)((*(data1++) - _min)*mm);
  10288. ptrd[3] = 0;
  10289. ptrd+=4;
  10290. }
  10291. }
  10292. }
  10293. if (ndata!=_data) {
  10294. _render_resize(ndata,img._width,img._height,(unsigned int*)_data,_width,_height);
  10295. delete[] ndata;
  10296. }
  10297. }
  10298. }
  10299. }
  10300. cimg_unlock_display();
  10301. return *this;
  10302. }
  10303. template<typename T>
  10304. static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) {
  10305. img.assign();
  10306. Display *dpy = cimg::X11_attr().display;
  10307. cimg_lock_display();
  10308. if (!dpy) {
  10309. dpy = XOpenDisplay(0);
  10310. if (!dpy)
  10311. throw CImgDisplayException("CImgDisplay::screenshot(): Failed to open X11 display.");
  10312. }
  10313. Window root = DefaultRootWindow(dpy);
  10314. XWindowAttributes gwa;
  10315. XGetWindowAttributes(dpy,root,&gwa);
  10316. const int width = gwa.width, height = gwa.height;
  10317. int _x0 = x0, _y0 = y0, _x1 = x1, _y1 = y1;
  10318. if (_x0>_x1) cimg::swap(_x0,_x1);
  10319. if (_y0>_y1) cimg::swap(_y0,_y1);
  10320. XImage *image = 0;
  10321. if (_x1>=0 && _x0<width && _y1>=0 && _y0<height) {
  10322. _x0 = std::max(_x0,0);
  10323. _y0 = std::max(_y0,0);
  10324. _x1 = std::min(_x1,width - 1);
  10325. _y1 = std::min(_y1,height - 1);
  10326. image = XGetImage(dpy,root,_x0,_y0,_x1 - _x0 + 1,_y1 - _y0 + 1,AllPlanes,ZPixmap);
  10327. if (image) {
  10328. const unsigned long
  10329. red_mask = image->red_mask,
  10330. green_mask = image->green_mask,
  10331. blue_mask = image->blue_mask;
  10332. img.assign(image->width,image->height,1,3);
  10333. T *pR = img.data(0,0,0,0), *pG = img.data(0,0,0,1), *pB = img.data(0,0,0,2);
  10334. cimg_forXY(img,x,y) {
  10335. const unsigned long pixel = XGetPixel(image,x,y);
  10336. *(pR++) = (T)((pixel & red_mask)>>16);
  10337. *(pG++) = (T)((pixel & green_mask)>>8);
  10338. *(pB++) = (T)(pixel & blue_mask);
  10339. }
  10340. XDestroyImage(image);
  10341. }
  10342. }
  10343. if (!cimg::X11_attr().display) XCloseDisplay(dpy);
  10344. cimg_unlock_display();
  10345. if (img.is_empty())
  10346. throw CImgDisplayException("CImgDisplay::screenshot(): Failed to take screenshot "
  10347. "with coordinates (%d,%d)-(%d,%d).",
  10348. x0,y0,x1,y1);
  10349. }
  10350. template<typename T>
  10351. const CImgDisplay& snapshot(CImg<T>& img) const {
  10352. if (is_empty()) { img.assign(); return *this; }
  10353. const unsigned char *ptrs = (unsigned char*)_data;
  10354. img.assign(_width,_height,1,3);
  10355. T
  10356. *data1 = img.data(0,0,0,0),
  10357. *data2 = img.data(0,0,0,1),
  10358. *data3 = img.data(0,0,0,2);
  10359. if (cimg::X11_attr().is_blue_first) cimg::swap(data1,data3);
  10360. switch (cimg::X11_attr().nb_bits) {
  10361. case 8 : {
  10362. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10363. const unsigned char val = *(ptrs++);
  10364. *(data1++) = (T)(val&0xe0);
  10365. *(data2++) = (T)((val&0x1c)<<3);
  10366. *(data3++) = (T)(val<<6);
  10367. }
  10368. } break;
  10369. case 16 : {
  10370. if (cimg::X11_attr().byte_order) for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10371. const unsigned char
  10372. val0 = ptrs[0],
  10373. val1 = ptrs[1];
  10374. ptrs+=2;
  10375. *(data1++) = (T)(val0&0xf8);
  10376. *(data2++) = (T)((val0<<5) | ((val1&0xe0)>>5));
  10377. *(data3++) = (T)(val1<<3);
  10378. } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10379. const unsigned short
  10380. val0 = ptrs[0],
  10381. val1 = ptrs[1];
  10382. ptrs+=2;
  10383. *(data1++) = (T)(val1&0xf8);
  10384. *(data2++) = (T)((val1<<5) | ((val0&0xe0)>>5));
  10385. *(data3++) = (T)(val0<<3);
  10386. }
  10387. } break;
  10388. default : {
  10389. if (cimg::X11_attr().byte_order) for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10390. ++ptrs;
  10391. *(data1++) = (T)ptrs[0];
  10392. *(data2++) = (T)ptrs[1];
  10393. *(data3++) = (T)ptrs[2];
  10394. ptrs+=3;
  10395. } else for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10396. *(data3++) = (T)ptrs[0];
  10397. *(data2++) = (T)ptrs[1];
  10398. *(data1++) = (T)ptrs[2];
  10399. ptrs+=3;
  10400. ++ptrs;
  10401. }
  10402. }
  10403. }
  10404. return *this;
  10405. }
  10406. // Windows-based implementation.
  10407. //-------------------------------
  10408. #elif cimg_display==2
  10409. bool _is_mouse_tracked, _is_cursor_visible;
  10410. HANDLE _thread, _is_created, _mutex;
  10411. HWND _window, _background_window;
  10412. CLIENTCREATESTRUCT _ccs;
  10413. unsigned int *_data;
  10414. DEVMODE _curr_mode;
  10415. BITMAPINFO _bmi;
  10416. HDC _hdc;
  10417. static int screen_width() {
  10418. DEVMODE mode;
  10419. mode.dmSize = sizeof(DEVMODE);
  10420. mode.dmDriverExtra = 0;
  10421. EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode);
  10422. return (int)mode.dmPelsWidth;
  10423. }
  10424. static int screen_height() {
  10425. DEVMODE mode;
  10426. mode.dmSize = sizeof(DEVMODE);
  10427. mode.dmDriverExtra = 0;
  10428. EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&mode);
  10429. return (int)mode.dmPelsHeight;
  10430. }
  10431. static void wait_all() {
  10432. WaitForSingleObject(cimg::Win32_attr().wait_event,INFINITE);
  10433. }
  10434. static LRESULT APIENTRY _handle_events(HWND window, UINT msg, WPARAM wParam, LPARAM lParam) {
  10435. #ifdef _WIN64
  10436. CImgDisplay *const disp = (CImgDisplay*)GetWindowLongPtr(window,GWLP_USERDATA);
  10437. #else
  10438. CImgDisplay *const disp = (CImgDisplay*)GetWindowLong(window,GWL_USERDATA);
  10439. #endif
  10440. MSG st_msg;
  10441. switch (msg) {
  10442. case WM_CLOSE :
  10443. disp->_mouse_x = disp->_mouse_y = -1;
  10444. disp->_window_x = disp->_window_y = cimg::type<int>::min();
  10445. disp->set_button().set_key(0).set_key(0,false)._is_closed = true;
  10446. ReleaseMutex(disp->_mutex);
  10447. ShowWindow(disp->_window,SW_HIDE);
  10448. disp->_is_event = true;
  10449. SetEvent(cimg::Win32_attr().wait_event);
  10450. return 0;
  10451. case WM_SIZE : {
  10452. while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {}
  10453. WaitForSingleObject(disp->_mutex,INFINITE);
  10454. const unsigned int nw = LOWORD(lParam),nh = HIWORD(lParam);
  10455. if (nw && nh && (nw!=disp->_width || nh!=disp->_height)) {
  10456. disp->_window_width = nw;
  10457. disp->_window_height = nh;
  10458. disp->_mouse_x = disp->_mouse_y = -1;
  10459. disp->_is_resized = disp->_is_event = true;
  10460. SetEvent(cimg::Win32_attr().wait_event);
  10461. }
  10462. ReleaseMutex(disp->_mutex);
  10463. } break;
  10464. case WM_MOVE : {
  10465. while (PeekMessage(&st_msg,window,WM_SIZE,WM_SIZE,PM_REMOVE)) {}
  10466. WaitForSingleObject(disp->_mutex,INFINITE);
  10467. const int nx = (int)(short)(LOWORD(lParam)), ny = (int)(short)(HIWORD(lParam));
  10468. if (nx!=disp->_window_x || ny!=disp->_window_y) {
  10469. disp->_window_x = nx;
  10470. disp->_window_y = ny;
  10471. disp->_is_moved = disp->_is_event = true;
  10472. SetEvent(cimg::Win32_attr().wait_event);
  10473. }
  10474. ReleaseMutex(disp->_mutex);
  10475. } break;
  10476. case WM_PAINT :
  10477. disp->paint();
  10478. cimg_lock_display();
  10479. if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE_WIN)>=0);
  10480. cimg_unlock_display();
  10481. break;
  10482. case WM_ERASEBKGND :
  10483. // return 0;
  10484. break;
  10485. case WM_KEYDOWN :
  10486. disp->set_key((unsigned int)wParam);
  10487. SetEvent(cimg::Win32_attr().wait_event);
  10488. break;
  10489. case WM_KEYUP :
  10490. disp->set_key((unsigned int)wParam,false);
  10491. SetEvent(cimg::Win32_attr().wait_event);
  10492. break;
  10493. case WM_MOUSEMOVE : {
  10494. while (PeekMessage(&st_msg,window,WM_MOUSEMOVE,WM_MOUSEMOVE,PM_REMOVE)) {}
  10495. disp->_mouse_x = LOWORD(lParam);
  10496. disp->_mouse_y = HIWORD(lParam);
  10497. #if (_WIN32_WINNT>=0x0400) && !defined(NOTRACKMOUSEEVENT)
  10498. if (!disp->_is_mouse_tracked) {
  10499. TRACKMOUSEEVENT tme;
  10500. tme.cbSize = sizeof(TRACKMOUSEEVENT);
  10501. tme.dwFlags = TME_LEAVE;
  10502. tme.hwndTrack = disp->_window;
  10503. if (TrackMouseEvent(&tme)) disp->_is_mouse_tracked = true;
  10504. }
  10505. #endif
  10506. if (disp->_mouse_x<0 || disp->_mouse_y<0 || disp->_mouse_x>=disp->width() || disp->_mouse_y>=disp->height())
  10507. disp->_mouse_x = disp->_mouse_y = -1;
  10508. disp->_is_event = true;
  10509. SetEvent(cimg::Win32_attr().wait_event);
  10510. cimg_lock_display();
  10511. if (disp->_is_cursor_visible) while (ShowCursor(TRUE)<0); else while (ShowCursor(FALSE_WIN)>=0);
  10512. cimg_unlock_display();
  10513. } break;
  10514. case WM_MOUSELEAVE : {
  10515. disp->_mouse_x = disp->_mouse_y = -1;
  10516. disp->_is_mouse_tracked = false;
  10517. cimg_lock_display();
  10518. while (ShowCursor(TRUE)<0) {}
  10519. cimg_unlock_display();
  10520. } break;
  10521. case WM_LBUTTONDOWN :
  10522. disp->set_button(1);
  10523. SetEvent(cimg::Win32_attr().wait_event);
  10524. break;
  10525. case WM_RBUTTONDOWN :
  10526. disp->set_button(2);
  10527. SetEvent(cimg::Win32_attr().wait_event);
  10528. break;
  10529. case WM_MBUTTONDOWN :
  10530. disp->set_button(3);
  10531. SetEvent(cimg::Win32_attr().wait_event);
  10532. break;
  10533. case WM_LBUTTONUP :
  10534. disp->set_button(1,false);
  10535. SetEvent(cimg::Win32_attr().wait_event);
  10536. break;
  10537. case WM_RBUTTONUP :
  10538. disp->set_button(2,false);
  10539. SetEvent(cimg::Win32_attr().wait_event);
  10540. break;
  10541. case WM_MBUTTONUP :
  10542. disp->set_button(3,false);
  10543. SetEvent(cimg::Win32_attr().wait_event);
  10544. break;
  10545. case 0x020A : // WM_MOUSEWHEEL:
  10546. disp->set_wheel((int)((short)HIWORD(wParam))/120);
  10547. SetEvent(cimg::Win32_attr().wait_event);
  10548. }
  10549. return DefWindowProc(window,msg,wParam,lParam);
  10550. }
  10551. static DWORD WINAPI _events_thread(void* arg) {
  10552. CImgDisplay *const disp = (CImgDisplay*)(((void**)arg)[0]);
  10553. const char *const title = (const char*)(((void**)arg)[1]);
  10554. MSG msg;
  10555. delete[] (void**)arg;
  10556. disp->_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
  10557. disp->_bmi.bmiHeader.biWidth = disp->width();
  10558. disp->_bmi.bmiHeader.biHeight = -disp->height();
  10559. disp->_bmi.bmiHeader.biPlanes = 1;
  10560. disp->_bmi.bmiHeader.biBitCount = 32;
  10561. disp->_bmi.bmiHeader.biCompression = BI_RGB;
  10562. disp->_bmi.bmiHeader.biSizeImage = 0;
  10563. disp->_bmi.bmiHeader.biXPelsPerMeter = 1;
  10564. disp->_bmi.bmiHeader.biYPelsPerMeter = 1;
  10565. disp->_bmi.bmiHeader.biClrUsed = 0;
  10566. disp->_bmi.bmiHeader.biClrImportant = 0;
  10567. disp->_data = new unsigned int[(size_t)disp->_width*disp->_height];
  10568. if (!disp->_is_fullscreen) { // Normal window
  10569. RECT rect;
  10570. rect.left = rect.top = 0; rect.right = (LONG)disp->_width - 1; rect.bottom = (LONG)disp->_height - 1;
  10571. AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
  10572. const int
  10573. border1 = (int)((rect.right - rect.left + 1 - disp->_width)/2),
  10574. border2 = (int)(rect.bottom - rect.top + 1 - disp->_height - border1),
  10575. ww = disp->width() + 2*border1,
  10576. wh = disp->height() + border1 + border2,
  10577. sw = CImgDisplay::screen_width(),
  10578. sh = CImgDisplay::screen_height();
  10579. int
  10580. wx = (int)cimg::round(cimg::rand(0,sw - ww -1)),
  10581. wy = (int)cimg::round(cimg::rand(64,sh - wh - 65));
  10582. if (wx + ww>=sw) wx = sw - ww;
  10583. if (wy + wh>=sh) wy = sh - wh;
  10584. if (wx<0) wx = 0;
  10585. if (wy<0) wy = 0;
  10586. disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
  10587. (DWORD)(WS_OVERLAPPEDWINDOW | (disp->_is_closed?0:WS_VISIBLE)),
  10588. wx,wy,ww,wh,0,0,0,&(disp->_ccs));
  10589. if (!disp->_is_closed) {
  10590. GetWindowRect(disp->_window,&rect);
  10591. disp->_window_x = rect.left;
  10592. disp->_window_y = rect.top;
  10593. } else disp->_window_x = disp->_window_y = cimg::type<int>::min();
  10594. } else { // Fullscreen window
  10595. const unsigned int
  10596. sx = (unsigned int)screen_width(),
  10597. sy = (unsigned int)screen_height();
  10598. disp->_window = CreateWindowA("MDICLIENT",title?title:" ",
  10599. (DWORD)(WS_POPUP | (disp->_is_closed?0:WS_VISIBLE)),
  10600. (int)(sx - disp->_width)/2,
  10601. (int)(sy - disp->_height)/2,
  10602. disp->width(),disp->height(),0,0,0,&(disp->_ccs));
  10603. disp->_window_x = disp->_window_y = 0;
  10604. }
  10605. SetForegroundWindow(disp->_window);
  10606. disp->_hdc = GetDC(disp->_window);
  10607. disp->_window_width = disp->_width;
  10608. disp->_window_height = disp->_height;
  10609. disp->flush();
  10610. #ifdef _WIN64
  10611. SetWindowLongPtr(disp->_window,GWLP_USERDATA,(LONG_PTR)disp);
  10612. SetWindowLongPtr(disp->_window,GWLP_WNDPROC,(LONG_PTR)_handle_events);
  10613. #else
  10614. SetWindowLong(disp->_window,GWL_USERDATA,(LONG)disp);
  10615. SetWindowLong(disp->_window,GWL_WNDPROC,(LONG)_handle_events);
  10616. #endif
  10617. SetEvent(disp->_is_created);
  10618. while (GetMessage(&msg,0,0,0)) DispatchMessage(&msg);
  10619. return 0;
  10620. }
  10621. CImgDisplay& _update_window_pos() {
  10622. if (_is_closed) _window_x = _window_y = cimg::type<int>::min();
  10623. else {
  10624. RECT rect;
  10625. rect.left = rect.top = 0; rect.right = (LONG)_width - 1; rect.bottom = (LONG)_height - 1;
  10626. AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
  10627. GetWindowRect(_window,&rect);
  10628. _window_x = rect.left;
  10629. _window_y = rect.top;
  10630. }
  10631. return *this;
  10632. }
  10633. void _init_fullscreen() {
  10634. _background_window = 0;
  10635. if (!_is_fullscreen || _is_closed) _curr_mode.dmSize = 0;
  10636. else {
  10637. /* DEVMODE mode;
  10638. unsigned int imode = 0, ibest = 0, bestbpp = 0, bw = ~0U, bh = ~0U;
  10639. for (mode.dmSize = sizeof(DEVMODE), mode.dmDriverExtra = 0; EnumDisplaySettings(0,imode,&mode); ++imode) {
  10640. const unsigned int nw = mode.dmPelsWidth, nh = mode.dmPelsHeight;
  10641. if (nw>=_width && nh>=_height && mode.dmBitsPerPel>=bestbpp && nw<=bw && nh<=bh) {
  10642. bestbpp = mode.dmBitsPerPel;
  10643. ibest = imode;
  10644. bw = nw; bh = nh;
  10645. }
  10646. }
  10647. if (bestbpp) {
  10648. _curr_mode.dmSize = sizeof(DEVMODE); _curr_mode.dmDriverExtra = 0;
  10649. EnumDisplaySettings(0,ENUM_CURRENT_SETTINGS,&_curr_mode);
  10650. EnumDisplaySettings(0,ibest,&mode);
  10651. ChangeDisplaySettings(&mode,0);
  10652. } else _curr_mode.dmSize = 0;
  10653. */
  10654. _curr_mode.dmSize = 0;
  10655. const unsigned int
  10656. sx = (unsigned int)screen_width(),
  10657. sy = (unsigned int)screen_height();
  10658. if (sx!=_width || sy!=_height) {
  10659. CLIENTCREATESTRUCT background_ccs = { 0,0 };
  10660. _background_window = CreateWindowA("MDICLIENT","",WS_POPUP | WS_VISIBLE,
  10661. 0,0,(int)sx,(int)sy,0,0,0,&background_ccs);
  10662. SetForegroundWindow(_background_window);
  10663. }
  10664. }
  10665. }
  10666. void _desinit_fullscreen() {
  10667. if (!_is_fullscreen) return;
  10668. if (_background_window) DestroyWindow(_background_window);
  10669. _background_window = 0;
  10670. if (_curr_mode.dmSize) ChangeDisplaySettings(&_curr_mode,0);
  10671. _is_fullscreen = false;
  10672. }
  10673. CImgDisplay& _assign(const unsigned int dimw, const unsigned int dimh, const char *const ptitle=0,
  10674. const unsigned int normalization_type=3,
  10675. const bool fullscreen_flag=false, const bool closed_flag=false) {
  10676. // Allocate space for window title
  10677. const char *const nptitle = ptitle?ptitle:"";
  10678. const unsigned int s = (unsigned int)std::strlen(nptitle) + 1;
  10679. char *const tmp_title = s?new char[s]:0;
  10680. if (s) std::memcpy(tmp_title,nptitle,s*sizeof(char));
  10681. // Destroy previous window if existing
  10682. if (!is_empty()) assign();
  10683. // Set display variables
  10684. _width = std::min(dimw,(unsigned int)screen_width());
  10685. _height = std::min(dimh,(unsigned int)screen_height());
  10686. _normalization = normalization_type<4?normalization_type:3;
  10687. _is_fullscreen = fullscreen_flag;
  10688. _window_x = _window_y = cimg::type<int>::min();
  10689. _is_closed = closed_flag;
  10690. _is_cursor_visible = true;
  10691. _is_mouse_tracked = false;
  10692. _title = tmp_title;
  10693. flush();
  10694. if (_is_fullscreen) _init_fullscreen();
  10695. // Create event thread
  10696. void *const arg = (void*)(new void*[2]);
  10697. ((void**)arg)[0] = (void*)this;
  10698. ((void**)arg)[1] = (void*)_title;
  10699. _mutex = CreateMutex(0,FALSE_WIN,0);
  10700. _is_created = CreateEvent(0,FALSE_WIN,FALSE_WIN,0);
  10701. _thread = CreateThread(0,0,_events_thread,arg,0,0);
  10702. WaitForSingleObject(_is_created,INFINITE);
  10703. return *this;
  10704. }
  10705. CImgDisplay& assign() {
  10706. if (is_empty()) return flush();
  10707. DestroyWindow(_window);
  10708. TerminateThread(_thread,0);
  10709. delete[] _data;
  10710. delete[] _title;
  10711. _data = 0;
  10712. _title = 0;
  10713. if (_is_fullscreen) _desinit_fullscreen();
  10714. _width = _height = _normalization = _window_width = _window_height = 0;
  10715. _window_x = _window_y = cimg::type<int>::min();
  10716. _is_fullscreen = false;
  10717. _is_closed = true;
  10718. _min = _max = 0;
  10719. _title = 0;
  10720. flush();
  10721. return *this;
  10722. }
  10723. CImgDisplay& assign(const unsigned int dimw, const unsigned int dimh, const char *const title=0,
  10724. const unsigned int normalization_type=3,
  10725. const bool fullscreen_flag=false, const bool closed_flag=false) {
  10726. if (!dimw || !dimh) return assign();
  10727. _assign(dimw,dimh,title,normalization_type,fullscreen_flag,closed_flag);
  10728. _min = _max = 0;
  10729. std::memset(_data,0,sizeof(unsigned int)*_width*_height);
  10730. return paint();
  10731. }
  10732. template<typename T>
  10733. CImgDisplay& assign(const CImg<T>& img, const char *const title=0,
  10734. const unsigned int normalization_type=3,
  10735. const bool fullscreen_flag=false, const bool closed_flag=false) {
  10736. if (!img) return assign();
  10737. CImg<T> tmp;
  10738. const CImg<T>& nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
  10739. (img._height - 1)/2,
  10740. (img._depth - 1)/2));
  10741. _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
  10742. if (_normalization==2) _min = (float)nimg.min_max(_max);
  10743. return display(nimg);
  10744. }
  10745. template<typename T>
  10746. CImgDisplay& assign(const CImgList<T>& list, const char *const title=0,
  10747. const unsigned int normalization_type=3,
  10748. const bool fullscreen_flag=false, const bool closed_flag=false) {
  10749. if (!list) return assign();
  10750. CImg<T> tmp;
  10751. const CImg<T> img = list>'x', &nimg = (img._depth==1)?img:(tmp=img.get_projections2d((img._width - 1)/2,
  10752. (img._height - 1)/2,
  10753. (img._depth - 1)/2));
  10754. _assign(nimg._width,nimg._height,title,normalization_type,fullscreen_flag,closed_flag);
  10755. if (_normalization==2) _min = (float)nimg.min_max(_max);
  10756. return display(nimg);
  10757. }
  10758. CImgDisplay& assign(const CImgDisplay& disp) {
  10759. if (!disp) return assign();
  10760. _assign(disp._width,disp._height,disp._title,disp._normalization,disp._is_fullscreen,disp._is_closed);
  10761. std::memcpy(_data,disp._data,sizeof(unsigned int)*_width*_height);
  10762. return paint();
  10763. }
  10764. CImgDisplay& resize(const int nwidth, const int nheight, const bool force_redraw=true) {
  10765. if (!nwidth || !nheight || (is_empty() && (nwidth<0 || nheight<0))) return assign();
  10766. if (is_empty()) return assign((unsigned int)nwidth,(unsigned int)nheight);
  10767. const unsigned int
  10768. tmpdimx = (nwidth>0)?nwidth:(-nwidth*_width/100),
  10769. tmpdimy = (nheight>0)?nheight:(-nheight*_height/100),
  10770. dimx = tmpdimx?tmpdimx:1,
  10771. dimy = tmpdimy?tmpdimy:1;
  10772. if (_width!=dimx || _height!=dimy || _window_width!=dimx || _window_height!=dimy) {
  10773. if (_window_width!=dimx || _window_height!=dimy) {
  10774. RECT rect; rect.left = rect.top = 0; rect.right = (LONG)dimx - 1; rect.bottom = (LONG)dimy - 1;
  10775. AdjustWindowRect(&rect,WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX,false);
  10776. const int cwidth = rect.right - rect.left + 1, cheight = rect.bottom - rect.top + 1;
  10777. SetWindowPos(_window,0,0,0,cwidth,cheight,SWP_NOMOVE | SWP_NOZORDER | SWP_NOCOPYBITS);
  10778. }
  10779. if (_width!=dimx || _height!=dimy) {
  10780. unsigned int *const ndata = new unsigned int[dimx*dimy];
  10781. if (force_redraw) _render_resize(_data,_width,_height,ndata,dimx,dimy);
  10782. else std::memset(ndata,0x80,sizeof(unsigned int)*dimx*dimy);
  10783. delete[] _data;
  10784. _data = ndata;
  10785. _bmi.bmiHeader.biWidth = (LONG)dimx;
  10786. _bmi.bmiHeader.biHeight = -(int)dimy;
  10787. _width = dimx;
  10788. _height = dimy;
  10789. }
  10790. _window_width = dimx; _window_height = dimy;
  10791. show();
  10792. }
  10793. _is_resized = false;
  10794. if (_is_fullscreen) move((screen_width() - width())/2,(screen_height() - height())/2);
  10795. if (force_redraw) return paint();
  10796. return *this;
  10797. }
  10798. CImgDisplay& toggle_fullscreen(const bool force_redraw=true) {
  10799. if (is_empty()) return *this;
  10800. if (force_redraw) {
  10801. const cimg_ulong buf_size = (cimg_ulong)_width*_height*4;
  10802. void *odata = std::malloc(buf_size);
  10803. if (odata) {
  10804. std::memcpy(odata,_data,buf_size);
  10805. assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
  10806. std::memcpy(_data,odata,buf_size);
  10807. std::free(odata);
  10808. }
  10809. return paint();
  10810. }
  10811. return assign(_width,_height,_title,_normalization,!_is_fullscreen,false);
  10812. }
  10813. CImgDisplay& show() {
  10814. if (is_empty() || !_is_closed) return *this;
  10815. _is_closed = false;
  10816. if (_is_fullscreen) _init_fullscreen();
  10817. ShowWindow(_window,SW_SHOW);
  10818. _update_window_pos();
  10819. return paint();
  10820. }
  10821. CImgDisplay& close() {
  10822. if (is_empty() || _is_closed) return *this;
  10823. _is_closed = true;
  10824. if (_is_fullscreen) _desinit_fullscreen();
  10825. ShowWindow(_window,SW_HIDE);
  10826. _window_x = _window_y = cimg::type<int>::min();
  10827. return *this;
  10828. }
  10829. CImgDisplay& move(const int posx, const int posy) {
  10830. if (is_empty()) return *this;
  10831. if (_window_x!=posx || _window_y!=posy) {
  10832. SetWindowPos(_window,0,posx,posy,0,0,SWP_NOSIZE | SWP_NOZORDER);
  10833. _window_x = posx;
  10834. _window_y = posy;
  10835. }
  10836. show();
  10837. _is_moved = false;
  10838. return *this;
  10839. }
  10840. CImgDisplay& show_mouse() {
  10841. if (is_empty()) return *this;
  10842. _is_cursor_visible = true;
  10843. return *this;
  10844. }
  10845. CImgDisplay& hide_mouse() {
  10846. if (is_empty()) return *this;
  10847. _is_cursor_visible = false;
  10848. return *this;
  10849. }
  10850. CImgDisplay& set_mouse(const int posx, const int posy) {
  10851. if (is_empty() || _is_closed || posx<0 || posy<0) return *this;
  10852. if (!_is_closed) {
  10853. _update_window_pos();
  10854. const int res = (int)SetCursorPos(_window_x + posx,_window_y + posy);
  10855. if (res) { _mouse_x = posx; _mouse_y = posy; }
  10856. }
  10857. return *this;
  10858. }
  10859. CImgDisplay& set_title(const char *const format, ...) {
  10860. if (is_empty()) return *this;
  10861. char *const tmp = new char[1024];
  10862. va_list ap;
  10863. va_start(ap, format);
  10864. cimg_vsnprintf(tmp,1024,format,ap);
  10865. va_end(ap);
  10866. if (!std::strcmp(_title,tmp)) { delete[] tmp; return *this; }
  10867. delete[] _title;
  10868. const unsigned int s = (unsigned int)std::strlen(tmp) + 1;
  10869. _title = new char[s];
  10870. std::memcpy(_title,tmp,s*sizeof(char));
  10871. SetWindowTextA(_window, tmp);
  10872. delete[] tmp;
  10873. return *this;
  10874. }
  10875. template<typename T>
  10876. CImgDisplay& display(const CImg<T>& img) {
  10877. if (!img)
  10878. throw CImgArgumentException(_cimgdisplay_instance
  10879. "display(): Empty specified image.",
  10880. cimgdisplay_instance);
  10881. if (is_empty()) return assign(img);
  10882. return render(img).paint();
  10883. }
  10884. CImgDisplay& paint() {
  10885. if (_is_closed) return *this;
  10886. WaitForSingleObject(_mutex,INFINITE);
  10887. SetDIBitsToDevice(_hdc,0,0,_width,_height,0,0,0,_height,_data,&_bmi,DIB_RGB_COLORS);
  10888. ReleaseMutex(_mutex);
  10889. return *this;
  10890. }
  10891. template<typename T>
  10892. CImgDisplay& render(const CImg<T>& img) {
  10893. if (!img)
  10894. throw CImgArgumentException(_cimgdisplay_instance
  10895. "render(): Empty specified image.",
  10896. cimgdisplay_instance);
  10897. if (is_empty()) return *this;
  10898. if (img._depth!=1) return render(img.get_projections2d((img._width - 1)/2,(img._height - 1)/2,
  10899. (img._depth - 1)/2));
  10900. const T
  10901. *data1 = img._data,
  10902. *data2 = (img._spectrum>=2)?img.data(0,0,0,1):data1,
  10903. *data3 = (img._spectrum>=3)?img.data(0,0,0,2):data1;
  10904. WaitForSingleObject(_mutex,INFINITE);
  10905. unsigned int
  10906. *const ndata = (img._width==_width && img._height==_height)?_data:
  10907. new unsigned int[(size_t)img._width*img._height],
  10908. *ptrd = ndata;
  10909. if (!_normalization || (_normalization==3 && cimg::type<T>::string()==cimg::type<unsigned char>::string())) {
  10910. _min = _max = 0;
  10911. switch (img._spectrum) {
  10912. case 1 : {
  10913. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10914. const unsigned char val = (unsigned char)*(data1++);
  10915. *(ptrd++) = (unsigned int)((val<<16) | (val<<8) | val);
  10916. }
  10917. } break;
  10918. case 2 : {
  10919. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10920. const unsigned char
  10921. R = (unsigned char)*(data1++),
  10922. G = (unsigned char)*(data2++);
  10923. *(ptrd++) = (unsigned int)((R<<16) | (G<<8));
  10924. }
  10925. } break;
  10926. default : {
  10927. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10928. const unsigned char
  10929. R = (unsigned char)*(data1++),
  10930. G = (unsigned char)*(data2++),
  10931. B = (unsigned char)*(data3++);
  10932. *(ptrd++) = (unsigned int)((R<<16) | (G<<8) | B);
  10933. }
  10934. }
  10935. }
  10936. } else {
  10937. if (_normalization==3) {
  10938. if (cimg::type<T>::is_float()) _min = (float)img.min_max(_max);
  10939. else {
  10940. _min = (float)cimg::type<T>::min();
  10941. _max = (float)cimg::type<T>::max();
  10942. }
  10943. } else if ((_min>_max) || _normalization==1) _min = (float)img.min_max(_max);
  10944. const float delta = _max - _min, mm = 255/(delta?delta:1.f);
  10945. switch (img._spectrum) {
  10946. case 1 : {
  10947. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10948. const unsigned char val = (unsigned char)((*(data1++) - _min)*mm);
  10949. *(ptrd++) = (unsigned int)((val<<16) | (val<<8) | val);
  10950. }
  10951. } break;
  10952. case 2 : {
  10953. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10954. const unsigned char
  10955. R = (unsigned char)((*(data1++) - _min)*mm),
  10956. G = (unsigned char)((*(data2++) - _min)*mm);
  10957. *(ptrd++) = (unsigned int)((R<<16) | (G<<8));
  10958. }
  10959. } break;
  10960. default : {
  10961. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  10962. const unsigned char
  10963. R = (unsigned char)((*(data1++) - _min)*mm),
  10964. G = (unsigned char)((*(data2++) - _min)*mm),
  10965. B = (unsigned char)((*(data3++) - _min)*mm);
  10966. *(ptrd++) = (unsigned int)((R<<16) | (G<<8) | B);
  10967. }
  10968. }
  10969. }
  10970. }
  10971. if (ndata!=_data) { _render_resize(ndata,img._width,img._height,_data,_width,_height); delete[] ndata; }
  10972. ReleaseMutex(_mutex);
  10973. return *this;
  10974. }
  10975. template<typename T>
  10976. static void screenshot(const int x0, const int y0, const int x1, const int y1, CImg<T>& img) {
  10977. img.assign();
  10978. HDC hScreen = GetDC(GetDesktopWindow());
  10979. if (hScreen) {
  10980. const int
  10981. width = GetDeviceCaps(hScreen,HORZRES),
  10982. height = GetDeviceCaps(hScreen,VERTRES);
  10983. int _x0 = x0, _y0 = y0, _x1 = x1, _y1 = y1;
  10984. if (_x0>_x1) cimg::swap(_x0,_x1);
  10985. if (_y0>_y1) cimg::swap(_y0,_y1);
  10986. if (_x1>=0 && _x0<width && _y1>=0 && _y0<height) {
  10987. _x0 = std::max(_x0,0);
  10988. _y0 = std::max(_y0,0);
  10989. _x1 = std::min(_x1,width - 1);
  10990. _y1 = std::min(_y1,height - 1);
  10991. const int bw = _x1 - _x0 + 1, bh = _y1 - _y0 + 1;
  10992. HDC hdcMem = CreateCompatibleDC(hScreen);
  10993. if (hdcMem) {
  10994. HBITMAP hBitmap = CreateCompatibleBitmap(hScreen,bw,bh);
  10995. if (hBitmap) {
  10996. HGDIOBJ hOld = SelectObject(hdcMem,hBitmap);
  10997. if (hOld && BitBlt(hdcMem,0,0,bw,bh,hScreen,_x0,_y0,SRCCOPY) && SelectObject(hdcMem,hOld)) {
  10998. BITMAPINFOHEADER bmi;
  10999. bmi.biSize = sizeof(BITMAPINFOHEADER);
  11000. bmi.biWidth = bw;
  11001. bmi.biHeight = -bh;
  11002. bmi.biPlanes = 1;
  11003. bmi.biBitCount = 32;
  11004. bmi.biCompression = BI_RGB;
  11005. bmi.biSizeImage = 0;
  11006. bmi.biXPelsPerMeter = bmi.biYPelsPerMeter = 0;
  11007. bmi.biClrUsed = bmi.biClrImportant = 0;
  11008. unsigned char *buf = new unsigned char[4*bw*bh];
  11009. if (GetDIBits(hdcMem,hBitmap,0,bh,buf,(BITMAPINFO*)&bmi,DIB_RGB_COLORS)) {
  11010. img.assign(bw,bh,1,3);
  11011. const unsigned char *ptrs = buf;
  11012. T *pR = img.data(0,0,0,0), *pG = img.data(0,0,0,1), *pB = img.data(0,0,0,2);
  11013. cimg_forXY(img,x,y) {
  11014. *(pR++) = (T)ptrs[2];
  11015. *(pG++) = (T)ptrs[1];
  11016. *(pB++) = (T)ptrs[0];
  11017. ptrs+=4;
  11018. }
  11019. }
  11020. delete[] buf;
  11021. }
  11022. DeleteObject(hBitmap);
  11023. }
  11024. DeleteDC(hdcMem);
  11025. }
  11026. }
  11027. ReleaseDC(GetDesktopWindow(),hScreen);
  11028. }
  11029. if (img.is_empty())
  11030. throw CImgDisplayException("CImgDisplay::screenshot(): Failed to take screenshot "
  11031. "with coordinates (%d,%d)-(%d,%d).",
  11032. x0,y0,x1,y1);
  11033. }
  11034. template<typename T>
  11035. const CImgDisplay& snapshot(CImg<T>& img) const {
  11036. if (is_empty()) { img.assign(); return *this; }
  11037. const unsigned int *ptrs = _data;
  11038. img.assign(_width,_height,1,3);
  11039. T
  11040. *data1 = img.data(0,0,0,0),
  11041. *data2 = img.data(0,0,0,1),
  11042. *data3 = img.data(0,0,0,2);
  11043. for (cimg_ulong xy = (cimg_ulong)img._width*img._height; xy>0; --xy) {
  11044. const unsigned int val = *(ptrs++);
  11045. *(data1++) = (T)(unsigned char)(val>>16);
  11046. *(data2++) = (T)(unsigned char)((val>>8)&0xFF);
  11047. *(data3++) = (T)(unsigned char)(val&0xFF);
  11048. }
  11049. return *this;
  11050. }
  11051. #endif
  11052. //@}
  11053. }; // struct CImgDisplay { ...
  11054. /*
  11055. #--------------------------------------
  11056. #
  11057. #
  11058. #
  11059. # Definition of the CImg<T> structure
  11060. #
  11061. #
  11062. #
  11063. #--------------------------------------
  11064. */
  11065. //! Class representing an image (up to 4 dimensions wide), each pixel being of type \c T.
  11066. /**
  11067. This is the main class of the %CImg Library. It declares and constructs
  11068. an image, allows access to its pixel values, and is able to perform various image operations.
  11069. \par Image representation
  11070. A %CImg image is defined as an instance of the container \c CImg<T>, which contains a regular grid of pixels,
  11071. each pixel value being of type \c T. The image grid can have up to 4 dimensions: width, height, depth
  11072. and number of channels.
  11073. Usually, the three first dimensions are used to describe spatial coordinates <tt>(x,y,z)</tt>,
  11074. while the number of channels is rather used as a vector-valued dimension
  11075. (it may describe the R,G,B color channels for instance).
  11076. If you need a fifth dimension, you can use image lists \c CImgList<T> rather than simple images \c CImg<T>.
  11077. Thus, the \c CImg<T> class is able to represent volumetric images of vector-valued pixels,
  11078. as well as images with less dimensions (1D scalar signal, 2D color images, ...).
  11079. Most member functions of the class CImg<\c T> are designed to handle this maximum case of (3+1) dimensions.
  11080. Concerning the pixel value type \c T:
  11081. fully supported template types are the basic C++ types: <tt>unsigned char, char, short, unsigned int, int,
  11082. unsigned long, long, float, double, ... </tt>.
  11083. Typically, fast image display can be done using <tt>CImg<unsigned char></tt> images,
  11084. while complex image processing algorithms may be rather coded using <tt>CImg<float></tt> or <tt>CImg<double></tt>
  11085. images that have floating-point pixel values. The default value for the template T is \c float.
  11086. Using your own template types may be possible. However, you will certainly have to define the complete set
  11087. of arithmetic and logical operators for your class.
  11088. \par Image structure
  11089. The \c CImg<T> structure contains \e six fields:
  11090. - \c _width defines the number of \a columns of the image (size along the X-axis).
  11091. - \c _height defines the number of \a rows of the image (size along the Y-axis).
  11092. - \c _depth defines the number of \a slices of the image (size along the Z-axis).
  11093. - \c _spectrum defines the number of \a channels of the image (size along the C-axis).
  11094. - \c _data defines a \a pointer to the \a pixel \a data (of type \c T).
  11095. - \c _is_shared is a boolean that tells if the memory buffer \c data is shared with
  11096. another image.
  11097. You can access these fields publicly although it is recommended to use the dedicated functions
  11098. width(), height(), depth(), spectrum() and ptr() to do so.
  11099. Image dimensions are not limited to a specific range (as long as you got enough available memory).
  11100. A value of \e 1 usually means that the corresponding dimension is \a flat.
  11101. If one of the dimensions is \e 0, or if the data pointer is null, the image is considered as \e empty.
  11102. Empty images should not contain any pixel data and thus, will not be processed by CImg member functions
  11103. (a CImgInstanceException will be thrown instead).
  11104. Pixel data are stored in memory, in a non interlaced mode (See \ref cimg_storage).
  11105. \par Image declaration and construction
  11106. Declaring an image can be done by using one of the several available constructors.
  11107. Here is a list of the most used:
  11108. - Construct images from arbitrary dimensions:
  11109. - <tt>CImg<char> img;</tt> declares an empty image.
  11110. - <tt>CImg<unsigned char> img(128,128);</tt> declares a 128x128 greyscale image with
  11111. \c unsigned \c char pixel values.
  11112. - <tt>CImg<double> img(3,3);</tt> declares a 3x3 matrix with \c double coefficients.
  11113. - <tt>CImg<unsigned char> img(256,256,1,3);</tt> declares a 256x256x1x3 (color) image
  11114. (colors are stored as an image with three channels).
  11115. - <tt>CImg<double> img(128,128,128);</tt> declares a 128x128x128 volumetric and greyscale image
  11116. (with \c double pixel values).
  11117. - <tt>CImg<> img(128,128,128,3);</tt> declares a 128x128x128 volumetric color image
  11118. (with \c float pixels, which is the default value of the template parameter \c T).
  11119. - \b Note: images pixels are <b>not automatically initialized to 0</b>. You may use the function \c fill() to
  11120. do it, or use the specific constructor taking 5 parameters like this:
  11121. <tt>CImg<> img(128,128,128,3,0);</tt> declares a 128x128x128 volumetric color image with all pixel values to 0.
  11122. - Construct images from filenames:
  11123. - <tt>CImg<unsigned char> img("image.jpg");</tt> reads a JPEG color image from the file "image.jpg".
  11124. - <tt>CImg<float> img("analyze.hdr");</tt> reads a volumetric image (ANALYZE7.5 format) from the
  11125. file "analyze.hdr".
  11126. - \b Note: You need to install <a href="http://www.imagemagick.org">ImageMagick</a>
  11127. to be able to read common compressed image formats (JPG,PNG, ...) (See \ref cimg_files_io).
  11128. - Construct images from C-style arrays:
  11129. - <tt>CImg<int> img(data_buffer,256,256);</tt> constructs a 256x256 greyscale image from a \c int* buffer
  11130. \c data_buffer (of size 256x256=65536).
  11131. - <tt>CImg<unsigned char> img(data_buffer,256,256,1,3);</tt> constructs a 256x256 color image
  11132. from a \c unsigned \c char* buffer \c data_buffer (where R,G,B channels follow each others).
  11133. The complete list of constructors can be found <a href="#constructors">here</a>.
  11134. \par Most useful functions
  11135. The \c CImg<T> class contains a lot of functions that operates on images.
  11136. Some of the most useful are:
  11137. - operator()(): Read or write pixel values.
  11138. - display(): displays the image in a new window.
  11139. **/
  11140. template<typename T>
  11141. struct CImg {
  11142. unsigned int _width, _height, _depth, _spectrum;
  11143. bool _is_shared;
  11144. T *_data;
  11145. //! Simple iterator type, to loop through each pixel value of an image instance.
  11146. /**
  11147. \note
  11148. - The \c CImg<T>::iterator type is defined to be a <tt>T*</tt>.
  11149. - You will seldom have to use iterators in %CImg, most classical operations
  11150. being achieved (often in a faster way) using methods of \c CImg<T>.
  11151. \par Example
  11152. \code
  11153. CImg<float> img("reference.jpg"); // Load image from file
  11154. // Set all pixels to '0', with a CImg iterator.
  11155. for (CImg<float>::iterator it = img.begin(), it<img.end(); ++it) *it = 0;
  11156. img.fill(0); // Do the same with a built-in method
  11157. \endcode
  11158. **/
  11159. typedef T* iterator;
  11160. //! Simple const iterator type, to loop through each pixel value of a \c const image instance.
  11161. /**
  11162. \note
  11163. - The \c CImg<T>::const_iterator type is defined to be a \c const \c T*.
  11164. - You will seldom have to use iterators in %CImg, most classical operations
  11165. being achieved (often in a faster way) using methods of \c CImg<T>.
  11166. \par Example
  11167. \code
  11168. const CImg<float> img("reference.jpg"); // Load image from file
  11169. float sum = 0;
  11170. // Compute sum of all pixel values, with a CImg iterator.
  11171. for (CImg<float>::iterator it = img.begin(), it<img.end(); ++it) sum+=*it;
  11172. const float sum2 = img.sum(); // Do the same with a built-in method
  11173. \endcode
  11174. **/
  11175. typedef const T* const_iterator;
  11176. //! Pixel value type.
  11177. /**
  11178. Refer to the type of the pixel values of an image instance.
  11179. \note
  11180. - The \c CImg<T>::value_type type of a \c CImg<T> is defined to be a \c T.
  11181. - \c CImg<T>::value_type is actually not used in %CImg methods. It has been mainly defined for
  11182. compatibility with STL naming conventions.
  11183. **/
  11184. typedef T value_type;
  11185. // Define common types related to template type T.
  11186. typedef typename cimg::superset<T,bool>::type Tbool;
  11187. typedef typename cimg::superset<T,unsigned char>::type Tuchar;
  11188. typedef typename cimg::superset<T,char>::type Tchar;
  11189. typedef typename cimg::superset<T,unsigned short>::type Tushort;
  11190. typedef typename cimg::superset<T,short>::type Tshort;
  11191. typedef typename cimg::superset<T,unsigned int>::type Tuint;
  11192. typedef typename cimg::superset<T,int>::type Tint;
  11193. typedef typename cimg::superset<T,cimg_ulong>::type Tulong;
  11194. typedef typename cimg::superset<T,cimg_long>::type Tlong;
  11195. typedef typename cimg::superset<T,float>::type Tfloat;
  11196. typedef typename cimg::superset<T,double>::type Tdouble;
  11197. typedef typename cimg::last<T,bool>::type boolT;
  11198. typedef typename cimg::last<T,unsigned char>::type ucharT;
  11199. typedef typename cimg::last<T,char>::type charT;
  11200. typedef typename cimg::last<T,unsigned short>::type ushortT;
  11201. typedef typename cimg::last<T,short>::type shortT;
  11202. typedef typename cimg::last<T,unsigned int>::type uintT;
  11203. typedef typename cimg::last<T,int>::type intT;
  11204. typedef typename cimg::last<T,cimg_ulong>::type ulongT;
  11205. typedef typename cimg::last<T,cimg_long>::type longT;
  11206. typedef typename cimg::last<T,cimg_uint64>::type uint64T;
  11207. typedef typename cimg::last<T,cimg_int64>::type int64T;
  11208. typedef typename cimg::last<T,float>::type floatT;
  11209. typedef typename cimg::last<T,double>::type doubleT;
  11210. // Return 'dx*dy*dz*dc' as a 'size_t' and check no overflow occurs.
  11211. static size_t safe_size(const unsigned int dx, const unsigned int dy,
  11212. const unsigned int dz, const unsigned int dc) {
  11213. if (!(dx && dy && dz && dc)) return 0;
  11214. size_t siz = (size_t)dx, osiz = siz;
  11215. if ((dy==1 || (siz*=dy)>osiz) &&
  11216. ((osiz = siz), dz==1 || (siz*=dz)>osiz) &&
  11217. ((osiz = siz), dc==1 || (siz*=dc)>osiz) &&
  11218. ((osiz = siz), sizeof(T)==1 || (siz*sizeof(T))>osiz)) {
  11219. if (siz > cimg_max_buf_size){
  11220. throw CImgArgumentException("CImg<%s>::safe_size(): Specified size (%u,%u,%u,%u) exceeds maximum "
  11221. "allowed buffer size of %lu ",
  11222. pixel_type(),dx,dy,dz,dc,cimg_max_buf_size);
  11223. }
  11224. return siz;
  11225. }
  11226. throw CImgArgumentException("CImg<%s>::safe_size(): Specified size (%u,%u,%u,%u) overflows 'size_t'.",
  11227. pixel_type(),dx,dy,dz,dc);
  11228. }
  11229. //@}
  11230. //---------------------------
  11231. //
  11232. //! \name Plugins
  11233. //@{
  11234. //---------------------------
  11235. #ifdef cimg_plugin
  11236. #include cimg_plugin
  11237. #endif
  11238. #ifdef cimg_plugin1
  11239. #include cimg_plugin1
  11240. #endif
  11241. #ifdef cimg_plugin2
  11242. #include cimg_plugin2
  11243. #endif
  11244. #ifdef cimg_plugin3
  11245. #include cimg_plugin3
  11246. #endif
  11247. #ifdef cimg_plugin4
  11248. #include cimg_plugin4
  11249. #endif
  11250. #ifdef cimg_plugin5
  11251. #include cimg_plugin5
  11252. #endif
  11253. #ifdef cimg_plugin6
  11254. #include cimg_plugin6
  11255. #endif
  11256. #ifdef cimg_plugin7
  11257. #include cimg_plugin7
  11258. #endif
  11259. #ifdef cimg_plugin8
  11260. #include cimg_plugin8
  11261. #endif
  11262. //@}
  11263. //---------------------------------------------------------
  11264. //
  11265. //! \name Constructors / Destructor / Instance Management
  11266. //@{
  11267. //---------------------------------------------------------
  11268. //! Destroy image.
  11269. /**
  11270. \note
  11271. - The pixel buffer data() is deallocated if necessary, e.g. for non-empty and non-shared image instances.
  11272. - Destroying an empty or shared image does nothing actually.
  11273. \warning
  11274. - When destroying a non-shared image, make sure that you will \e not operate on a remaining shared image
  11275. that shares its buffer with the destroyed instance, in order to avoid further invalid memory access
  11276. (to a deallocated buffer).
  11277. **/
  11278. ~CImg() {
  11279. if (!_is_shared) delete[] _data;
  11280. }
  11281. //! Construct empty image.
  11282. /**
  11283. \note
  11284. - An empty image has no pixel data and all of its dimensions width(), height(), depth(), spectrum()
  11285. are set to \c 0, as well as its pixel buffer pointer data().
  11286. - An empty image may be re-assigned afterwards, e.g. with the family of
  11287. assign(unsigned int,unsigned int,unsigned int,unsigned int) methods,
  11288. or by operator=(const CImg<t>&). In all cases, the type of pixels stays \c T.
  11289. - An empty image is never shared.
  11290. \par Example
  11291. \code
  11292. CImg<float> img1, img2; // Construct two empty images
  11293. img1.assign(256,256,1,3); // Re-assign 'img1' to be a 256x256x1x3 (color) image
  11294. img2 = img1.get_rand(0,255); // Re-assign 'img2' to be a random-valued version of 'img1'
  11295. img2.assign(); // Re-assign 'img2' to be an empty image again
  11296. \endcode
  11297. **/
  11298. CImg():_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {}
  11299. //! Construct image with specified size.
  11300. /**
  11301. \param size_x Image width().
  11302. \param size_y Image height().
  11303. \param size_z Image depth().
  11304. \param size_c Image spectrum() (number of channels).
  11305. \note
  11306. - It is able to create only \e non-shared images, and allocates thus a pixel buffer data()
  11307. for each constructed image instance.
  11308. - Setting one dimension \c size_x,\c size_y,\c size_z or \c size_c to \c 0 leads to the construction of
  11309. an \e empty image.
  11310. - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated
  11311. (e.g. when requested size is too big for available memory).
  11312. \warning
  11313. - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values.
  11314. In order to initialize pixel values during construction (e.g. with \c 0), use constructor
  11315. CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) instead.
  11316. \par Example
  11317. \code
  11318. CImg<float> img1(256,256,1,3); // Construct a 256x256x1x3 (color) image, filled with garbage values
  11319. CImg<float> img2(256,256,1,3,0); // Construct a 256x256x1x3 (color) image, filled with value '0'
  11320. \endcode
  11321. **/
  11322. explicit CImg(const unsigned int size_x, const unsigned int size_y=1,
  11323. const unsigned int size_z=1, const unsigned int size_c=1):
  11324. _is_shared(false) {
  11325. const size_t siz = safe_size(size_x,size_y,size_z,size_c);
  11326. if (siz) {
  11327. _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
  11328. try { _data = new T[siz]; } catch (...) {
  11329. _width = _height = _depth = _spectrum = 0; _data = 0;
  11330. throw CImgInstanceException(_cimg_instance
  11331. "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
  11332. cimg_instance,
  11333. cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
  11334. size_x,size_y,size_z,size_c);
  11335. }
  11336. } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
  11337. }
  11338. //! Construct image with specified size and initialize pixel values.
  11339. /**
  11340. \param size_x Image width().
  11341. \param size_y Image height().
  11342. \param size_z Image depth().
  11343. \param size_c Image spectrum() (number of channels).
  11344. \param value Initialization value.
  11345. \note
  11346. - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int),
  11347. but it also fills the pixel buffer with the specified \c value.
  11348. \warning
  11349. - It cannot be used to construct a vector-valued image and initialize it with \e vector-valued pixels
  11350. (e.g. RGB vector, for color images).
  11351. For this task, you may use fillC() after construction.
  11352. **/
  11353. CImg(const unsigned int size_x, const unsigned int size_y,
  11354. const unsigned int size_z, const unsigned int size_c, const T& value):
  11355. _is_shared(false) {
  11356. const size_t siz = safe_size(size_x,size_y,size_z,size_c);
  11357. if (siz) {
  11358. _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
  11359. try { _data = new T[siz]; } catch (...) {
  11360. _width = _height = _depth = _spectrum = 0; _data = 0;
  11361. throw CImgInstanceException(_cimg_instance
  11362. "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
  11363. cimg_instance,
  11364. cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
  11365. size_x,size_y,size_z,size_c);
  11366. }
  11367. fill(value);
  11368. } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
  11369. }
  11370. //! Construct image with specified size and initialize pixel values from a sequence of integers.
  11371. /**
  11372. Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c,
  11373. with pixels of type \c T, and initialize pixel
  11374. values from the specified sequence of integers \c value0,\c value1,\c ...
  11375. \param size_x Image width().
  11376. \param size_y Image height().
  11377. \param size_z Image depth().
  11378. \param size_c Image spectrum() (number of channels).
  11379. \param value0 First value of the initialization sequence (must be an \e integer).
  11380. \param value1 Second value of the initialization sequence (must be an \e integer).
  11381. \param ...
  11382. \note
  11383. - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
  11384. the pixel buffer with a sequence of specified integer values.
  11385. \warning
  11386. - You must specify \e exactly \c size_x*\c size_y*\c size_z*\c size_c integers in the initialization sequence.
  11387. Otherwise, the constructor may crash or fill your image pixels with garbage.
  11388. \par Example
  11389. \code
  11390. const CImg<float> img(2,2,1,3, // Construct a 2x2 color (RGB) image
  11391. 0,255,0,255, // Set the 4 values for the red component
  11392. 0,0,255,255, // Set the 4 values for the green component
  11393. 64,64,64,64); // Set the 4 values for the blue component
  11394. img.resize(150,150).display();
  11395. \endcode
  11396. \image html ref_constructor1.jpg
  11397. **/
  11398. CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
  11399. const int value0, const int value1, ...):
  11400. _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
  11401. #define _CImg_stdarg(img,a0,a1,N,t) { \
  11402. size_t _siz = (size_t)N; \
  11403. if (_siz--) { \
  11404. va_list ap; \
  11405. va_start(ap,a1); \
  11406. T *ptrd = (img)._data; \
  11407. *(ptrd++) = (T)a0; \
  11408. if (_siz--) { \
  11409. *(ptrd++) = (T)a1; \
  11410. for ( ; _siz; --_siz) *(ptrd++) = (T)va_arg(ap,t); \
  11411. } \
  11412. va_end(ap); \
  11413. } \
  11414. }
  11415. assign(size_x,size_y,size_z,size_c);
  11416. _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),int);
  11417. }
  11418. #if cimg_use_cpp11==1
  11419. //! Construct image with specified size and initialize pixel values from an initializer list of integers.
  11420. /**
  11421. Construct a new image instance of size \c size_x x \c size_y x \c size_z x \c size_c,
  11422. with pixels of type \c T, and initialize pixel
  11423. values from the specified initializer list of integers { \c value0,\c value1,\c ... }
  11424. \param size_x Image width().
  11425. \param size_y Image height().
  11426. \param size_z Image depth().
  11427. \param size_c Image spectrum() (number of channels).
  11428. \param { value0, value1, ... } Initialization list
  11429. \param repeat_values Tells if the value filling process is repeated over the image.
  11430. \note
  11431. - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
  11432. the pixel buffer with a sequence of specified integer values.
  11433. \par Example
  11434. \code
  11435. const CImg<float> img(2,2,1,3, // Construct a 2x2 color (RGB) image
  11436. { 0,255,0,255, // Set the 4 values for the red component
  11437. 0,0,255,255, // Set the 4 values for the green component
  11438. 64,64,64,64 }); // Set the 4 values for the blue component
  11439. img.resize(150,150).display();
  11440. \endcode
  11441. \image html ref_constructor1.jpg
  11442. **/
  11443. template<typename t>
  11444. CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
  11445. const std::initializer_list<t> values,
  11446. const bool repeat_values=true):
  11447. _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
  11448. #define _cimg_constructor_cpp11(repeat_values) \
  11449. auto it = values.begin(); \
  11450. size_t siz = size(); \
  11451. if (repeat_values) for (T *ptrd = _data; siz--; ) { \
  11452. *(ptrd++) = (T)(*(it++)); if (it==values.end()) it = values.begin(); } \
  11453. else { siz = std::min(siz,values.size()); for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++)); }
  11454. assign(size_x,size_y,size_z,size_c);
  11455. _cimg_constructor_cpp11(repeat_values);
  11456. }
  11457. template<typename t>
  11458. CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z,
  11459. std::initializer_list<t> values,
  11460. const bool repeat_values=true):
  11461. _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
  11462. assign(size_x,size_y,size_z);
  11463. _cimg_constructor_cpp11(repeat_values);
  11464. }
  11465. template<typename t>
  11466. CImg(const unsigned int size_x, const unsigned int size_y,
  11467. std::initializer_list<t> values,
  11468. const bool repeat_values=true):
  11469. _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
  11470. assign(size_x,size_y);
  11471. _cimg_constructor_cpp11(repeat_values);
  11472. }
  11473. template<typename t>
  11474. CImg(const unsigned int size_x,
  11475. std::initializer_list<t> values,
  11476. const bool repeat_values=true):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
  11477. assign(size_x);
  11478. _cimg_constructor_cpp11(repeat_values);
  11479. }
  11480. //! Construct single channel 1D image with pixel values and width obtained from an initializer list of integers.
  11481. /**
  11482. Construct a new image instance of size \c width x \c 1 x \c 1 x \c 1,
  11483. with pixels of type \c T, and initialize pixel
  11484. values from the specified initializer list of integers { \c value0,\c value1,\c ... }. Image width is
  11485. given by the size of the initializer list.
  11486. \param { value0, value1, ... } Initialization list
  11487. \note
  11488. - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int) with height=1, depth=1, and spectrum=1,
  11489. but it also fills the pixel buffer with a sequence of specified integer values.
  11490. \par Example
  11491. \code
  11492. const CImg<float> img = {10,20,30,20,10 }; // Construct a 5x1 image with one channel, and set its pixel values
  11493. img.resize(150,150).display();
  11494. \endcode
  11495. \image html ref_constructor1.jpg
  11496. **/
  11497. template<typename t>
  11498. CImg(const std::initializer_list<t> values):
  11499. _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
  11500. assign(values.size(),1,1,1);
  11501. auto it = values.begin();
  11502. unsigned int siz = _width;
  11503. for (T *ptrd = _data; siz--; ) *(ptrd++) = (T)(*(it++));
  11504. }
  11505. template<typename t>
  11506. CImg<T>& operator=(std::initializer_list<t> values) {
  11507. _cimg_constructor_cpp11(siz>values.size());
  11508. return *this;
  11509. }
  11510. #endif
  11511. //! Construct image with specified size and initialize pixel values from a sequence of doubles.
  11512. /**
  11513. 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,
  11514. and initialize pixel values from the specified sequence of doubles \c value0,\c value1,\c ...
  11515. \param size_x Image width().
  11516. \param size_y Image height().
  11517. \param size_z Image depth().
  11518. \param size_c Image spectrum() (number of channels).
  11519. \param value0 First value of the initialization sequence (must be a \e double).
  11520. \param value1 Second value of the initialization sequence (must be a \e double).
  11521. \param ...
  11522. \note
  11523. - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...), but
  11524. takes a sequence of double values instead of integers.
  11525. \warning
  11526. - You must specify \e exactly \c dx*\c dy*\c dz*\c dc doubles in the initialization sequence.
  11527. Otherwise, the constructor may crash or fill your image with garbage.
  11528. For instance, the code below will probably crash on most platforms:
  11529. \code
  11530. const CImg<float> img(2,2,1,1, 0.5,0.5,255,255); // FAIL: The two last arguments are 'int', not 'double'!
  11531. \endcode
  11532. **/
  11533. CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
  11534. const double value0, const double value1, ...):
  11535. _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
  11536. assign(size_x,size_y,size_z,size_c);
  11537. _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),double);
  11538. }
  11539. //! Construct image with specified size and initialize pixel values from a value string.
  11540. /**
  11541. 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,
  11542. and initializes pixel values from the specified string \c values.
  11543. \param size_x Image width().
  11544. \param size_y Image height().
  11545. \param size_z Image depth().
  11546. \param size_c Image spectrum() (number of channels).
  11547. \param values Value string describing the way pixel values are set.
  11548. \param repeat_values Tells if the value filling process is repeated over the image.
  11549. \note
  11550. - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it also fills
  11551. the pixel buffer with values described in the value string \c values.
  11552. - Value string \c values may describe two different filling processes:
  11553. - Either \c values is a sequences of values assigned to the image pixels, as in <tt>"1,2,3,7,8,2"</tt>.
  11554. In this case, set \c repeat_values to \c true to periodically fill the image with the value sequence.
  11555. - Either, \c values is a formula, as in <tt>"cos(x/10)*sin(y/20)"</tt>.
  11556. In this case, parameter \c repeat_values is pointless.
  11557. - For both cases, specifying \c repeat_values is mandatory.
  11558. It disambiguates the possible overloading of constructor
  11559. CImg(unsigned int,unsigned int,unsigned int,unsigned int,T) with \c T being a <tt>const char*</tt>.
  11560. - A \c CImgArgumentException is thrown when an invalid value string \c values is specified.
  11561. \par Example
  11562. \code
  11563. const CImg<float> img1(129,129,1,3,"0,64,128,192,255",true), // Construct image from a value sequence
  11564. img2(129,129,1,3,"if(c==0,255*abs(cos(x/10)),1.8*y)",false); // Construct image from a formula
  11565. (img1,img2).display();
  11566. \endcode
  11567. \image html ref_constructor2.jpg
  11568. **/
  11569. CImg(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z, const unsigned int size_c,
  11570. const char *const values, const bool repeat_values):_is_shared(false) {
  11571. const size_t siz = safe_size(size_x,size_y,size_z,size_c);
  11572. if (siz) {
  11573. _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
  11574. try { _data = new T[siz]; } catch (...) {
  11575. _width = _height = _depth = _spectrum = 0; _data = 0;
  11576. throw CImgInstanceException(_cimg_instance
  11577. "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
  11578. cimg_instance,
  11579. cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
  11580. size_x,size_y,size_z,size_c);
  11581. }
  11582. fill(values,repeat_values);
  11583. } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
  11584. }
  11585. //! Construct image with specified size and initialize pixel values from a memory buffer.
  11586. /**
  11587. 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,
  11588. and initializes pixel values from the specified \c t* memory buffer.
  11589. \param values Pointer to the input memory buffer.
  11590. \param size_x Image width().
  11591. \param size_y Image height().
  11592. \param size_z Image depth().
  11593. \param size_c Image spectrum() (number of channels).
  11594. \param is_shared Tells if input memory buffer must be shared by the current instance.
  11595. \note
  11596. - If \c is_shared is \c false, the image instance allocates its own pixel buffer,
  11597. and values from the specified input buffer are copied to the instance buffer.
  11598. If buffer types \c T and \c t are different, a regular static cast is performed during buffer copy.
  11599. - Otherwise, the image instance does \e not allocate a new buffer, and uses the input memory buffer as its
  11600. own pixel buffer. This case requires that types \c T and \c t are the same. Later, destroying such a shared
  11601. image will not deallocate the pixel buffer, this task being obviously charged to the initial buffer allocator.
  11602. - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated
  11603. (e.g. when requested size is too big for available memory).
  11604. \warning
  11605. - You must take care when operating on a shared image, since it may have an invalid pixel buffer pointer data()
  11606. (e.g. already deallocated).
  11607. \par Example
  11608. \code
  11609. unsigned char tab[256*256] = {};
  11610. CImg<unsigned char> img1(tab,256,256,1,1,false), // Construct new non-shared image from buffer 'tab'
  11611. img2(tab,256,256,1,1,true); // Construct new shared-image from buffer 'tab'
  11612. tab[1024] = 255; // Here, 'img2' is indirectly modified, but not 'img1'
  11613. \endcode
  11614. **/
  11615. template<typename t>
  11616. CImg(const t *const values, const unsigned int size_x, const unsigned int size_y=1,
  11617. const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false):_is_shared(false) {
  11618. if (is_shared) {
  11619. _width = _height = _depth = _spectrum = 0; _data = 0;
  11620. throw CImgArgumentException(_cimg_instance
  11621. "CImg(): Invalid construction request of a (%u,%u,%u,%u) shared instance "
  11622. "from a (%s*) buffer (pixel types are different).",
  11623. cimg_instance,
  11624. size_x,size_y,size_z,size_c,CImg<t>::pixel_type());
  11625. }
  11626. const size_t siz = safe_size(size_x,size_y,size_z,size_c);
  11627. if (values && siz) {
  11628. _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
  11629. try { _data = new T[siz]; } catch (...) {
  11630. _width = _height = _depth = _spectrum = 0; _data = 0;
  11631. throw CImgInstanceException(_cimg_instance
  11632. "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
  11633. cimg_instance,
  11634. cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
  11635. size_x,size_y,size_z,size_c);
  11636. }
  11637. const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
  11638. } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
  11639. }
  11640. //! Construct image with specified size and initialize pixel values from a memory buffer \specialization.
  11641. CImg(const T *const values, const unsigned int size_x, const unsigned int size_y=1,
  11642. const unsigned int size_z=1, const unsigned int size_c=1, const bool is_shared=false) {
  11643. const size_t siz = safe_size(size_x,size_y,size_z,size_c);
  11644. if (values && siz) {
  11645. _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = is_shared;
  11646. if (_is_shared) _data = const_cast<T*>(values);
  11647. else {
  11648. try { _data = new T[siz]; } catch (...) {
  11649. _width = _height = _depth = _spectrum = 0; _data = 0;
  11650. throw CImgInstanceException(_cimg_instance
  11651. "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
  11652. cimg_instance,
  11653. cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
  11654. size_x,size_y,size_z,size_c);
  11655. }
  11656. std::memcpy(_data,values,siz*sizeof(T));
  11657. }
  11658. } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
  11659. }
  11660. //! Construct image from memory buffer with specified size and pixel ordering scheme.
  11661. template<typename t>
  11662. CImg(const t *const values, const unsigned int size_x, const unsigned int size_y,
  11663. const unsigned int size_z, const unsigned int size_c,
  11664. const char *const axes_order):_data(0),_is_shared(false) {
  11665. const size_t siz = safe_size(size_x,size_y,size_z,size_c);
  11666. if (values && siz) {
  11667. unsigned char s_code[4] = { 0,1,2,3 }, n_code[4] = {};
  11668. for (unsigned int l = 0; axes_order[l]; ++l) {
  11669. int c = cimg::lowercase(axes_order[l]);
  11670. if (l>=4 || (c!='x' && c!='y' && c!='z' && c!='c')) { *s_code = 4; break; }
  11671. else { ++n_code[c%=4]; s_code[l] = c; }
  11672. }
  11673. if (*axes_order && *s_code<4 && *n_code<=1 && n_code[1]<=1 && n_code[2]<=1 && n_code[3]<=1) {
  11674. const unsigned int code = (s_code[0]<<12) | (s_code[1]<<8) | (s_code[2]<<4) | (s_code[3]);
  11675. int s0 = 0, s1 = 0, s2 = 0, s3 = 0;
  11676. const char *inv_order = 0;
  11677. switch (code) {
  11678. case 0x0123 : inv_order = "xyzc"; s0 = size_x; s1 = size_y; s2 = size_z; s3 = size_c; break; // xyzc
  11679. case 0x0132 : inv_order = "xyzc"; s0 = size_x; s1 = size_y; s2 = size_c; s3 = size_z; break; // xycz
  11680. case 0x0213 : inv_order = "xzyc"; s0 = size_x; s1 = size_z; s2 = size_y; s3 = size_c; break; // xzyc
  11681. case 0x0231 : inv_order = "xcyz"; s0 = size_x; s1 = size_z; s2 = size_c; s3 = size_y; break; // xzcy
  11682. case 0x0312 : inv_order = "xzcy"; s0 = size_x; s1 = size_c; s2 = size_y; s3 = size_z; break; // xcyz
  11683. case 0x0321 : inv_order = "xczy"; s0 = size_x; s1 = size_c; s2 = size_z; s3 = size_y; break; // xczy
  11684. case 0x1023 : inv_order = "yxzc"; s0 = size_y; s1 = size_x; s2 = size_z; s3 = size_c; break; // yxzc
  11685. case 0x1032 : inv_order = "yxcz"; s0 = size_y; s1 = size_x; s2 = size_c; s3 = size_z; break; // yxcz
  11686. case 0x1203 : inv_order = "zxyc"; s0 = size_y; s1 = size_z; s2 = size_x; s3 = size_c; break; // yzxc
  11687. case 0x1230 : inv_order = "cxyz"; s0 = size_y; s1 = size_z; s2 = size_c; s3 = size_x; break; // yzcx
  11688. case 0x1302 : inv_order = "zxcy"; s0 = size_y; s1 = size_c; s2 = size_x; s3 = size_z; break; // ycxz
  11689. case 0x1320 : inv_order = "cxzy"; s0 = size_y; s1 = size_c; s2 = size_z; s3 = size_x; break; // yczx
  11690. case 0x2013 : inv_order = "yzxc"; s0 = size_z; s1 = size_x; s2 = size_y; s3 = size_c; break; // zxyc
  11691. case 0x2031 : inv_order = "ycxz"; s0 = size_z; s1 = size_x; s2 = size_c; s3 = size_y; break; // zxcy
  11692. case 0x2103 : inv_order = "zyxc"; s0 = size_z; s1 = size_y; s2 = size_x; s3 = size_c; break; // zyxc
  11693. case 0x2130 : inv_order = "cyxz"; s0 = size_z; s1 = size_y; s2 = size_c; s3 = size_x; break; // zycx
  11694. case 0x2301 : inv_order = "zcxy"; s0 = size_z; s1 = size_c; s2 = size_x; s3 = size_y; break; // zcxy
  11695. case 0x2310 : inv_order = "czxy"; s0 = size_z; s1 = size_c; s2 = size_y; s3 = size_x; break; // zcyx
  11696. case 0x3012 : inv_order = "yzcx"; s0 = size_c; s1 = size_x; s2 = size_y; s3 = size_z; break; // cxyz
  11697. case 0x3021 : inv_order = "yczx"; s0 = size_c; s1 = size_x; s2 = size_z; s3 = size_y; break; // cxzy
  11698. case 0x3102 : inv_order = "zycx"; s0 = size_c; s1 = size_y; s2 = size_x; s3 = size_z; break; // cyxz
  11699. case 0x3120 : inv_order = "cyzx"; s0 = size_c; s1 = size_y; s2 = size_z; s3 = size_x; break; // cyzx
  11700. case 0x3201 : inv_order = "zcyx"; s0 = size_c; s1 = size_z; s2 = size_x; s3 = size_y; break; // czxy
  11701. case 0x3210 : inv_order = "czyx"; s0 = size_c; s1 = size_z; s2 = size_y; s3 = size_x; break; // czyx
  11702. }
  11703. CImg<t>(values,s0,s1,s2,s3,true).get_permute_axes(inv_order).move_to(*this);
  11704. } else {
  11705. _width = _height = _depth = _spectrum = 0; _data = 0;
  11706. throw CImgArgumentException(_cimg_instance
  11707. "CImg(): Invalid specified axes order '%s'.",
  11708. cimg_instance,
  11709. axes_order);
  11710. }
  11711. } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
  11712. }
  11713. //! Construct image from reading an image file.
  11714. /**
  11715. Construct a new image instance with pixels of type \c T, and initialize pixel values with the data read from
  11716. an image file.
  11717. \param filename Filename, as a C-string.
  11718. \note
  11719. - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it reads the image
  11720. dimensions and pixel values from the specified image file.
  11721. - The recognition of the image file format by %CImg higlhy depends on the tools installed on your system
  11722. and on the external libraries you used to link your code against.
  11723. - Considered pixel type \c T should better fit the file format specification, or data loss may occur during
  11724. file load (e.g. constructing a \c CImg<unsigned char> from a float-valued image file).
  11725. - A \c CImgIOException is thrown when the specified \c filename cannot be read, or if the file format is not
  11726. recognized.
  11727. \par Example
  11728. \code
  11729. const CImg<float> img("reference.jpg");
  11730. img.display();
  11731. \endcode
  11732. \image html ref_image.jpg
  11733. **/
  11734. explicit CImg(const char *const filename):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
  11735. assign(filename);
  11736. }
  11737. //! Construct image copy.
  11738. /**
  11739. Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg<t> instance.
  11740. \param img Input image to copy.
  11741. \note
  11742. - Constructed copy has the same size width() x height() x depth() x spectrum() and pixel values as the
  11743. input image \c img.
  11744. - If input image \c img is \e shared and if types \c T and \c t are the same, the constructed copy is also
  11745. \e shared, and shares its pixel buffer with \c img.
  11746. Modifying a pixel value in the constructed copy will thus also modifies it in the input image \c img.
  11747. This behavior is needful to allow functions to return shared images.
  11748. - Otherwise, the constructed copy allocates its own pixel buffer, and copies pixel values from the input
  11749. image \c img into its buffer. The copied pixel values may be eventually statically casted if types \c T and
  11750. \c t are different.
  11751. - Constructing a copy from an image \c img when types \c t and \c T are the same is significantly faster than
  11752. with different types.
  11753. - A \c CImgInstanceException is thrown when the pixel buffer cannot be allocated
  11754. (e.g. not enough available memory).
  11755. **/
  11756. template<typename t>
  11757. CImg(const CImg<t>& img):_is_shared(false) {
  11758. const size_t siz = (size_t)img.size();
  11759. if (img._data && siz) {
  11760. _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
  11761. try { _data = new T[siz]; } catch (...) {
  11762. _width = _height = _depth = _spectrum = 0; _data = 0;
  11763. throw CImgInstanceException(_cimg_instance
  11764. "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
  11765. cimg_instance,
  11766. cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
  11767. img._width,img._height,img._depth,img._spectrum);
  11768. }
  11769. const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
  11770. } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
  11771. }
  11772. //! Construct image copy \specialization.
  11773. CImg(const CImg<T>& img) {
  11774. const size_t siz = (size_t)img.size();
  11775. if (img._data && siz) {
  11776. _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
  11777. _is_shared = img._is_shared;
  11778. if (_is_shared) _data = const_cast<T*>(img._data);
  11779. else {
  11780. try { _data = new T[siz]; } catch (...) {
  11781. _width = _height = _depth = _spectrum = 0; _data = 0;
  11782. throw CImgInstanceException(_cimg_instance
  11783. "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
  11784. cimg_instance,
  11785. cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
  11786. img._width,img._height,img._depth,img._spectrum);
  11787. }
  11788. std::memcpy(_data,img._data,siz*sizeof(T));
  11789. }
  11790. } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
  11791. }
  11792. //! Advanced copy constructor.
  11793. /**
  11794. Construct a new image instance with pixels of type \c T, as a copy of an existing \c CImg<t> instance,
  11795. while forcing the shared state of the constructed copy.
  11796. \param img Input image to copy.
  11797. \param is_shared Tells about the shared state of the constructed copy.
  11798. \note
  11799. - Similar to CImg(const CImg<t>&), except that it allows to decide the shared state of
  11800. the constructed image, which does not depend anymore on the shared state of the input image \c img:
  11801. - If \c is_shared is \c true, the constructed copy will share its pixel buffer with the input image \c img.
  11802. For that case, the pixel types \c T and \c t \e must be the same.
  11803. - If \c is_shared is \c false, the constructed copy will allocate its own pixel buffer, whether the input
  11804. image \c img is shared or not.
  11805. - A \c CImgArgumentException is thrown when a shared copy is requested with different pixel types \c T and \c t.
  11806. **/
  11807. template<typename t>
  11808. CImg(const CImg<t>& img, const bool is_shared):_is_shared(false) {
  11809. if (is_shared) {
  11810. _width = _height = _depth = _spectrum = 0; _data = 0;
  11811. throw CImgArgumentException(_cimg_instance
  11812. "CImg(): Invalid construction request of a shared instance from a "
  11813. "CImg<%s> image (%u,%u,%u,%u,%p) (pixel types are different).",
  11814. cimg_instance,
  11815. CImg<t>::pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data);
  11816. }
  11817. const size_t siz = (size_t)img.size();
  11818. if (img._data && siz) {
  11819. _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
  11820. try { _data = new T[siz]; } catch (...) {
  11821. _width = _height = _depth = _spectrum = 0; _data = 0;
  11822. throw CImgInstanceException(_cimg_instance
  11823. "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
  11824. cimg_instance,
  11825. cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
  11826. img._width,img._height,img._depth,img._spectrum);
  11827. }
  11828. const t *ptrs = img._data; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
  11829. } else { _width = _height = _depth = _spectrum = 0; _data = 0; }
  11830. }
  11831. //! Advanced copy constructor \specialization.
  11832. CImg(const CImg<T>& img, const bool is_shared) {
  11833. const size_t siz = (size_t)img.size();
  11834. if (img._data && siz) {
  11835. _width = img._width; _height = img._height; _depth = img._depth; _spectrum = img._spectrum;
  11836. _is_shared = is_shared;
  11837. if (_is_shared) _data = const_cast<T*>(img._data);
  11838. else {
  11839. try { _data = new T[siz]; } catch (...) {
  11840. _width = _height = _depth = _spectrum = 0; _data = 0;
  11841. throw CImgInstanceException(_cimg_instance
  11842. "CImg(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
  11843. cimg_instance,
  11844. cimg::strbuffersize(sizeof(T)*img._width*img._height*img._depth*img._spectrum),
  11845. img._width,img._height,img._depth,img._spectrum);
  11846. }
  11847. std::memcpy(_data,img._data,siz*sizeof(T));
  11848. }
  11849. } else { _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0; }
  11850. }
  11851. //! Construct image with dimensions borrowed from another image.
  11852. /**
  11853. Construct a new image instance with pixels of type \c T, and size get from some dimensions of an existing
  11854. \c CImg<t> instance.
  11855. \param img Input image from which dimensions are borrowed.
  11856. \param dimensions C-string describing the image size along the X,Y,Z and C-dimensions.
  11857. \note
  11858. - Similar to CImg(unsigned int,unsigned int,unsigned int,unsigned int), but it takes the image dimensions
  11859. (\e not its pixel values) from an existing \c CImg<t> instance.
  11860. - The allocated pixel buffer is \e not filled with a default value, and is likely to contain garbage values.
  11861. In order to initialize pixel values (e.g. with \c 0), use constructor CImg(const CImg<t>&,const char*,T)
  11862. instead.
  11863. \par Example
  11864. \code
  11865. const CImg<float> img1(256,128,1,3), // 'img1' is a 256x128x1x3 image
  11866. img2(img1,"xyzc"), // 'img2' is a 256x128x1x3 image
  11867. img3(img1,"y,x,z,c"), // 'img3' is a 128x256x1x3 image
  11868. img4(img1,"c,x,y,3",0), // 'img4' is a 3x128x256x3 image (with pixels initialized to '0')
  11869. \endcode
  11870. **/
  11871. template<typename t>
  11872. CImg(const CImg<t>& img, const char *const dimensions):
  11873. _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
  11874. assign(img,dimensions);
  11875. }
  11876. //! Construct image with dimensions borrowed from another image and initialize pixel values.
  11877. /**
  11878. Construct a new image instance with pixels of type \c T, and size get from the dimensions of an existing
  11879. \c CImg<t> instance, and set all pixel values to specified \c value.
  11880. \param img Input image from which dimensions are borrowed.
  11881. \param dimensions String describing the image size along the X,Y,Z and V-dimensions.
  11882. \param value Value used for initialization.
  11883. \note
  11884. - Similar to CImg(const CImg<t>&,const char*), but it also fills the pixel buffer with the specified \c value.
  11885. **/
  11886. template<typename t>
  11887. CImg(const CImg<t>& img, const char *const dimensions, const T& value):
  11888. _width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
  11889. assign(img,dimensions).fill(value);
  11890. }
  11891. //! Construct image from a display window.
  11892. /**
  11893. Construct a new image instance with pixels of type \c T, as a snapshot of an existing \c CImgDisplay instance.
  11894. \param disp Input display window.
  11895. \note
  11896. - The width() and height() of the constructed image instance are the same as the specified \c CImgDisplay.
  11897. - The depth() and spectrum() of the constructed image instance are respectively set to \c 1 and \c 3
  11898. (i.e. a 2D color image).
  11899. - The image pixels are read as 8-bits RGB values.
  11900. **/
  11901. explicit CImg(const CImgDisplay &disp):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
  11902. disp.snapshot(*this);
  11903. }
  11904. // Constructor and assignment operator for rvalue references (c++11).
  11905. // This avoids an additional image copy for methods returning new images. Can save RAM for big images !
  11906. #if cimg_use_cpp11==1
  11907. CImg(CImg<T>&& img):_width(0),_height(0),_depth(0),_spectrum(0),_is_shared(false),_data(0) {
  11908. swap(img);
  11909. }
  11910. CImg<T>& operator=(CImg<T>&& img) {
  11911. if (_is_shared) return assign(img);
  11912. return img.swap(*this);
  11913. }
  11914. #endif
  11915. //! Construct empty image \inplace.
  11916. /**
  11917. In-place version of the default constructor CImg(). It simply resets the instance to an empty image.
  11918. **/
  11919. CImg<T>& assign() {
  11920. if (!_is_shared) delete[] _data;
  11921. _width = _height = _depth = _spectrum = 0; _is_shared = false; _data = 0;
  11922. return *this;
  11923. }
  11924. //! Construct image with specified size \inplace.
  11925. /**
  11926. In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int).
  11927. **/
  11928. CImg<T>& assign(const unsigned int size_x, const unsigned int size_y=1,
  11929. const unsigned int size_z=1, const unsigned int size_c=1) {
  11930. const size_t siz = safe_size(size_x,size_y,size_z,size_c);
  11931. if (!siz) return assign();
  11932. const size_t curr_siz = (size_t)size();
  11933. if (siz!=curr_siz) {
  11934. if (_is_shared)
  11935. throw CImgArgumentException(_cimg_instance
  11936. "assign(): Invalid assignment request of shared instance from specified "
  11937. "image (%u,%u,%u,%u).",
  11938. cimg_instance,
  11939. size_x,size_y,size_z,size_c);
  11940. else {
  11941. delete[] _data;
  11942. try { _data = new T[siz]; } catch (...) {
  11943. _width = _height = _depth = _spectrum = 0; _data = 0;
  11944. throw CImgInstanceException(_cimg_instance
  11945. "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
  11946. cimg_instance,
  11947. cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
  11948. size_x,size_y,size_z,size_c);
  11949. }
  11950. }
  11951. }
  11952. _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
  11953. return *this;
  11954. }
  11955. //! Construct image with specified size and initialize pixel values \inplace.
  11956. /**
  11957. In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,T).
  11958. **/
  11959. CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
  11960. const unsigned int size_z, const unsigned int size_c, const T& value) {
  11961. return assign(size_x,size_y,size_z,size_c).fill(value);
  11962. }
  11963. //! Construct image with specified size and initialize pixel values from a sequence of integers \inplace.
  11964. /**
  11965. In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,int,int,...).
  11966. **/
  11967. CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
  11968. const unsigned int size_z, const unsigned int size_c,
  11969. const int value0, const int value1, ...) {
  11970. assign(size_x,size_y,size_z,size_c);
  11971. _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),int);
  11972. return *this;
  11973. }
  11974. //! Construct image with specified size and initialize pixel values from a sequence of doubles \inplace.
  11975. /**
  11976. In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,double,double,...).
  11977. **/
  11978. CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
  11979. const unsigned int size_z, const unsigned int size_c,
  11980. const double value0, const double value1, ...) {
  11981. assign(size_x,size_y,size_z,size_c);
  11982. _CImg_stdarg(*this,value0,value1,safe_size(size_x,size_y,size_z,size_c),double);
  11983. return *this;
  11984. }
  11985. //! Construct image with specified size and initialize pixel values from a value string \inplace.
  11986. /**
  11987. In-place version of the constructor CImg(unsigned int,unsigned int,unsigned int,unsigned int,const char*,bool).
  11988. **/
  11989. CImg<T>& assign(const unsigned int size_x, const unsigned int size_y,
  11990. const unsigned int size_z, const unsigned int size_c,
  11991. const char *const values, const bool repeat_values) {
  11992. return assign(size_x,size_y,size_z,size_c).fill(values,repeat_values);
  11993. }
  11994. //! Construct image with specified size and initialize pixel values from a memory buffer \inplace.
  11995. /**
  11996. In-place version of the constructor CImg(const t*,unsigned int,unsigned int,unsigned int,unsigned int).
  11997. **/
  11998. template<typename t>
  11999. CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y=1,
  12000. const unsigned int size_z=1, const unsigned int size_c=1) {
  12001. const size_t siz = safe_size(size_x,size_y,size_z,size_c);
  12002. if (!values || !siz) return assign();
  12003. assign(size_x,size_y,size_z,size_c);
  12004. const t *ptrs = values; cimg_for(*this,ptrd,T) *ptrd = (T)*(ptrs++);
  12005. return *this;
  12006. }
  12007. //! Construct image with specified size and initialize pixel values from a memory buffer \specialization.
  12008. CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y=1,
  12009. const unsigned int size_z=1, const unsigned int size_c=1) {
  12010. const size_t siz = safe_size(size_x,size_y,size_z,size_c);
  12011. if (!values || !siz) return assign();
  12012. const size_t curr_siz = (size_t)size();
  12013. if (values==_data && siz==curr_siz) return assign(size_x,size_y,size_z,size_c);
  12014. if (_is_shared || values + siz<_data || values>=_data + size()) {
  12015. assign(size_x,size_y,size_z,size_c);
  12016. if (_is_shared) std::memmove((void*)_data,(void*)values,siz*sizeof(T));
  12017. else std::memcpy((void*)_data,(void*)values,siz*sizeof(T));
  12018. } else {
  12019. T *new_data = 0;
  12020. try { new_data = new T[siz]; } catch (...) {
  12021. _width = _height = _depth = _spectrum = 0; _data = 0;
  12022. throw CImgInstanceException(_cimg_instance
  12023. "assign(): Failed to allocate memory (%s) for image (%u,%u,%u,%u).",
  12024. cimg_instance,
  12025. cimg::strbuffersize(sizeof(T)*size_x*size_y*size_z*size_c),
  12026. size_x,size_y,size_z,size_c);
  12027. }
  12028. std::memcpy((void*)new_data,(void*)values,siz*sizeof(T));
  12029. delete[] _data; _data = new_data; _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c;
  12030. }
  12031. return *this;
  12032. }
  12033. //! Construct image with specified size and initialize pixel values from a memory buffer \overloading.
  12034. template<typename t>
  12035. CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y,
  12036. const unsigned int size_z, const unsigned int size_c, const bool is_shared) {
  12037. if (is_shared)
  12038. throw CImgArgumentException(_cimg_instance
  12039. "assign(): Invalid assignment request of shared instance from (%s*) buffer"
  12040. "(pixel types are different).",
  12041. cimg_instance,
  12042. CImg<t>::pixel_type());
  12043. return assign(values,size_x,size_y,size_z,size_c);
  12044. }
  12045. //! Construct image with specified size and initialize pixel values from a memory buffer \overloading.
  12046. CImg<T>& assign(const T *const values, const unsigned int size_x, const unsigned int size_y,
  12047. const unsigned int size_z, const unsigned int size_c, const bool is_shared) {
  12048. const size_t siz = safe_size(size_x,size_y,size_z,size_c);
  12049. if (!values || !siz) return assign();
  12050. if (!is_shared) { if (_is_shared) assign(); assign(values,size_x,size_y,size_z,size_c); }
  12051. else {
  12052. if (!_is_shared) {
  12053. if (values + siz<_data || values>=_data + size()) assign();
  12054. else cimg::warn(_cimg_instance
  12055. "assign(): Shared image instance has overlapping memory.",
  12056. cimg_instance);
  12057. }
  12058. _width = size_x; _height = size_y; _depth = size_z; _spectrum = size_c; _is_shared = true;
  12059. _data = const_cast<T*>(values);
  12060. }
  12061. return *this;
  12062. }
  12063. //! Construct image from memory buffer with specified size and pixel ordering scheme.
  12064. template<typename t>
  12065. CImg<T>& assign(const t *const values, const unsigned int size_x, const unsigned int size_y,
  12066. const unsigned int size_z, const unsigned int size_c,
  12067. const char *const axes_order) {
  12068. CImg<T>(values,size_x,size_y,size_z,size_c,axes_order).move_to(*this);
  12069. }
  12070. //! Construct image from reading an image file \inplace.
  12071. /**
  12072. In-place version of the constructor CImg(const char*).
  12073. **/
  12074. CImg<T>& assign(const char *const filename) {
  12075. return load(filename);
  12076. }
  12077. //! Construct image copy \inplace.
  12078. /**
  12079. In-place version of the constructor CImg(const CImg<t>&).
  12080. **/
  12081. template<typename t>
  12082. CImg<T>& assign(const CImg<t>& img) {
  12083. return assign(img._data,img._width,img._height,img._depth,img._spectrum);
  12084. }
  12085. //! In-place version of the advanced copy constructor.
  12086. /**
  12087. In-place version of the constructor CImg(const CImg<t>&,bool).
  12088. **/
  12089. template<typename t>
  12090. CImg<T>& assign(const CImg<t>& img, const bool is_shared) {
  12091. return assign(img._data,img._width,img._height,img._depth,img._spectrum,is_shared);
  12092. }
  12093. //! Construct image with dimensions borrowed from another image \inplace.
  12094. /**
  12095. In-place version of the constructor CImg(const CImg<t>&,const char*).
  12096. **/
  12097. template<typename t>
  12098. CImg<T>& assign(const CImg<t>& img, const char *const dimensions) {
  12099. if (!dimensions || !*dimensions) return assign(img._width,img._height,img._depth,img._spectrum);
  12100. unsigned int siz[4] = { 0,1,1,1 }, k = 0;
  12101. CImg<charT> item(256);
  12102. for (const char *s = dimensions; *s && k<4; ++k) {
  12103. if (cimg_sscanf(s,"%255[^0-9%xyzvwhdcXYZVWHDC]",item._data)>0) s+=std::strlen(item);
  12104. if (*s) {
  12105. unsigned int val = 0; char sep = 0;
  12106. if (cimg_sscanf(s,"%u%c",&val,&sep)>0) {
  12107. if (sep=='%') siz[k] = val*(k==0?_width:k==1?_height:k==2?_depth:_spectrum)/100;
  12108. else siz[k] = val;
  12109. while (*s>='0' && *s<='9') ++s;
  12110. if (sep=='%') ++s;
  12111. } else switch (cimg::lowercase(*s)) {
  12112. case 'x' : case 'w' : siz[k] = img._width; ++s; break;
  12113. case 'y' : case 'h' : siz[k] = img._height; ++s; break;
  12114. case 'z' : case 'd' : siz[k] = img._depth; ++s; break;
  12115. case 'c' : case 's' : siz[k] = img._spectrum; ++s; break;
  12116. default :
  12117. throw CImgArgumentException(_cimg_instance
  12118. "assign(): Invalid character '%c' detected in specified dimension string '%s'.",
  12119. cimg_instance,
  12120. *s,dimensions);
  12121. }
  12122. }
  12123. }
  12124. return assign(siz[0],siz[1],siz[2],siz[3]);
  12125. }
  12126. //! Construct image with dimensions borrowed from another image and initialize pixel values \inplace.
  12127. /**
  12128. In-place version of the constructor CImg(const CImg<t>&,const char*,T).
  12129. **/
  12130. template<typename t>
  12131. CImg<T>& assign(const CImg<t>& img, const char *const dimensions, const T& value) {
  12132. return assign(img,dimensions).fill(value);
  12133. }
  12134. //! Construct image from a display window \inplace.
  12135. /**
  12136. In-place version of the constructor CImg(const CImgDisplay&).
  12137. **/
  12138. CImg<T>& assign(const CImgDisplay &disp) {
  12139. disp.snapshot(*this);
  12140. return *this;
  12141. }
  12142. //! Construct empty image \inplace.
  12143. /**
  12144. Equivalent to assign().
  12145. \note
  12146. - It has been defined for compatibility with STL naming conventions.
  12147. **/
  12148. CImg<T>& clear() {
  12149. return assign();
  12150. }
  12151. //! Transfer content of an image instance into another one.
  12152. /**
  12153. Transfer the dimensions and the pixel buffer content of an image instance into another one,
  12154. and replace instance by an empty image. It avoids the copy of the pixel buffer
  12155. when possible.
  12156. \param img Destination image.
  12157. \note
  12158. - Pixel types \c T and \c t of source and destination images can be different, though the process is
  12159. designed to be instantaneous when \c T and \c t are the same.
  12160. \par Example
  12161. \code
  12162. CImg<float> src(256,256,1,3,0), // Construct a 256x256x1x3 (color) image filled with value '0'
  12163. dest(16,16); // Construct a 16x16x1x1 (scalar) image
  12164. src.move_to(dest); // Now, 'src' is empty and 'dest' is the 256x256x1x3 image
  12165. \endcode
  12166. **/
  12167. template<typename t>
  12168. CImg<t>& move_to(CImg<t>& img) {
  12169. img.assign(*this);
  12170. assign();
  12171. return img;
  12172. }
  12173. //! Transfer content of an image instance into another one \specialization.
  12174. CImg<T>& move_to(CImg<T>& img) {
  12175. if (_is_shared || img._is_shared) img.assign(*this);
  12176. else swap(img);
  12177. assign();
  12178. return img;
  12179. }
  12180. //! Transfer content of an image instance into a new image in an image list.
  12181. /**
  12182. Transfer the dimensions and the pixel buffer content of an image instance
  12183. into a newly inserted image at position \c pos in specified \c CImgList<t> instance.
  12184. \param list Destination list.
  12185. \param pos Position of the newly inserted image in the list.
  12186. \note
  12187. - When optional parameter \c pos is omitted, the image instance is transferred as a new
  12188. image at the end of the specified \c list.
  12189. - It is convenient to sequentially insert new images into image lists, with no
  12190. additional copies of memory buffer.
  12191. \par Example
  12192. \code
  12193. CImgList<float> list; // Construct an empty image list
  12194. CImg<float> img("reference.jpg"); // Read image from filename
  12195. img.move_to(list); // Transfer image content as a new item in the list (no buffer copy)
  12196. \endcode
  12197. **/
  12198. template<typename t>
  12199. CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos=~0U) {
  12200. const unsigned int npos = pos>list._width?list._width:pos;
  12201. move_to(list.insert(1,npos)[npos]);
  12202. return list;
  12203. }
  12204. //! Swap fields of two image instances.
  12205. /**
  12206. \param img Image to swap fields with.
  12207. \note
  12208. - It can be used to interchange the content of two images in a very fast way. Can be convenient when dealing
  12209. with algorithms requiring two swapping buffers.
  12210. \par Example
  12211. \code
  12212. CImg<float> img1("lena.jpg"),
  12213. img2("milla.jpg");
  12214. img1.swap(img2); // Now, 'img1' is 'milla' and 'img2' is 'lena'
  12215. \endcode
  12216. **/
  12217. CImg<T>& swap(CImg<T>& img) {
  12218. cimg::swap(_width,img._width,_height,img._height,_depth,img._depth,_spectrum,img._spectrum);
  12219. cimg::swap(_data,img._data);
  12220. cimg::swap(_is_shared,img._is_shared);
  12221. return img;
  12222. }
  12223. //! Return a reference to an empty image.
  12224. /**
  12225. \note
  12226. This function is useful mainly to declare optional parameters having type \c CImg<T> in functions prototypes,
  12227. e.g.
  12228. \code
  12229. void f(const int x=0, const int y=0, const CImg<float>& img=CImg<float>::empty());
  12230. \endcode
  12231. **/
  12232. static CImg<T>& empty() {
  12233. static CImg<T> _empty;
  12234. return _empty.assign();
  12235. }
  12236. //! Return a reference to an empty image \const.
  12237. static const CImg<T>& const_empty() {
  12238. static const CImg<T> _empty;
  12239. return _empty;
  12240. }
  12241. //@}
  12242. //------------------------------------------
  12243. //
  12244. //! \name Overloaded Operators
  12245. //@{
  12246. //------------------------------------------
  12247. //! Access to a pixel value.
  12248. /**
  12249. Return a reference to a located pixel value of the image instance,
  12250. being possibly \e const, whether the image instance is \e const or not.
  12251. This is the standard method to get/set pixel values in \c CImg<T> images.
  12252. \param x X-coordinate of the pixel value.
  12253. \param y Y-coordinate of the pixel value.
  12254. \param z Z-coordinate of the pixel value.
  12255. \param c C-coordinate of the pixel value.
  12256. \note
  12257. - Range of pixel coordinates start from <tt>(0,0,0,0)</tt> to
  12258. <tt>(width() - 1,height() - 1,depth() - 1,spectrum() - 1)</tt>.
  12259. - Due to the particular arrangement of the pixel buffers defined in %CImg, you can omit one coordinate if the
  12260. corresponding dimension is equal to \c 1.
  12261. For instance, pixels of a 2D image (depth() equal to \c 1) can be accessed by <tt>img(x,y,c)</tt> instead of
  12262. <tt>img(x,y,0,c)</tt>.
  12263. \warning
  12264. - There is \e no boundary checking done in this operator, to make it as fast as possible.
  12265. You \e must take care of out-of-bounds access by yourself, if necessary.
  12266. For debugging purposes, you may want to define macro \c 'cimg_verbosity'>=3 to enable additional boundary
  12267. checking operations in this operator. In that case, warning messages will be printed on the error output
  12268. when accessing out-of-bounds pixels.
  12269. \par Example
  12270. \code
  12271. CImg<float> img(100,100,1,3,0); // Construct a 100x100x1x3 (color) image with pixels set to '0'
  12272. const float
  12273. valR = img(10,10,0,0), // Read red value at coordinates (10,10)
  12274. valG = img(10,10,0,1), // Read green value at coordinates (10,10)
  12275. valB = img(10,10,2), // Read blue value at coordinates (10,10) (Z-coordinate can be omitted)
  12276. avg = (valR + valG + valB)/3; // Compute average pixel value
  12277. img(10,10,0) = img(10,10,1) = img(10,10,2) = avg; // Replace the color pixel (10,10) by the average grey value
  12278. \endcode
  12279. **/
  12280. #if cimg_verbosity>=3
  12281. T& operator()(const unsigned int x, const unsigned int y=0,
  12282. const unsigned int z=0, const unsigned int c=0) {
  12283. const ulongT off = (ulongT)offset(x,y,z,c);
  12284. if (!_data || off>=size()) {
  12285. cimg::warn(_cimg_instance
  12286. "operator(): Invalid pixel request, at coordinates (%d,%d,%d,%d) [offset=%u].",
  12287. cimg_instance,
  12288. (int)x,(int)y,(int)z,(int)c,off);
  12289. return *_data;
  12290. }
  12291. else return _data[off];
  12292. }
  12293. //! Access to a pixel value \const.
  12294. const T& operator()(const unsigned int x, const unsigned int y=0,
  12295. const unsigned int z=0, const unsigned int c=0) const {
  12296. return const_cast<CImg<T>*>(this)->operator()(x,y,z,c);
  12297. }
  12298. //! Access to a pixel value.
  12299. /**
  12300. \param x X-coordinate of the pixel value.
  12301. \param y Y-coordinate of the pixel value.
  12302. \param z Z-coordinate of the pixel value.
  12303. \param c C-coordinate of the pixel value.
  12304. \param wh Precomputed offset, must be equal to <tt>width()*\ref height()</tt>.
  12305. \param whd Precomputed offset, must be equal to <tt>width()*\ref height()*\ref depth()</tt>.
  12306. \note
  12307. - Similar to (but faster than) operator()().
  12308. It uses precomputed offsets to optimize memory access. You may use it to optimize
  12309. the reading/writing of several pixel values in the same image (e.g. in a loop).
  12310. **/
  12311. T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
  12312. const ulongT wh, const ulongT whd=0) {
  12313. cimg::unused(wh,whd);
  12314. return (*this)(x,y,z,c);
  12315. }
  12316. //! Access to a pixel value \const.
  12317. const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
  12318. const ulongT wh, const ulongT whd=0) const {
  12319. cimg::unused(wh,whd);
  12320. return (*this)(x,y,z,c);
  12321. }
  12322. #else
  12323. T& operator()(const unsigned int x) {
  12324. return _data[x];
  12325. }
  12326. const T& operator()(const unsigned int x) const {
  12327. return _data[x];
  12328. }
  12329. T& operator()(const unsigned int x, const unsigned int y) {
  12330. return _data[x + y*_width];
  12331. }
  12332. const T& operator()(const unsigned int x, const unsigned int y) const {
  12333. return _data[x + y*_width];
  12334. }
  12335. T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) {
  12336. return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height];
  12337. }
  12338. const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z) const {
  12339. return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height];
  12340. }
  12341. T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) {
  12342. return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height + c*(ulongT)_width*_height*_depth];
  12343. }
  12344. const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c) const {
  12345. return _data[x + y*(ulongT)_width + z*(ulongT)_width*_height + c*(ulongT)_width*_height*_depth];
  12346. }
  12347. T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int,
  12348. const ulongT wh) {
  12349. return _data[x + y*_width + z*wh];
  12350. }
  12351. const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int,
  12352. const ulongT wh) const {
  12353. return _data[x + y*_width + z*wh];
  12354. }
  12355. T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
  12356. const ulongT wh, const ulongT whd) {
  12357. return _data[x + y*_width + z*wh + c*whd];
  12358. }
  12359. const T& operator()(const unsigned int x, const unsigned int y, const unsigned int z, const unsigned int c,
  12360. const ulongT wh, const ulongT whd) const {
  12361. return _data[x + y*_width + z*wh + c*whd];
  12362. }
  12363. #endif
  12364. //! Implicitly cast an image into a \c T*.
  12365. /**
  12366. Implicitly cast a \c CImg<T> instance into a \c T* or \c const \c T* pointer, whether the image instance
  12367. is \e const or not. The returned pointer points on the first value of the image pixel buffer.
  12368. \note
  12369. - It simply returns the pointer data() to the pixel buffer.
  12370. - This implicit conversion is convenient to test the empty state of images (data() being \c 0 in this case), e.g.
  12371. \code
  12372. CImg<float> img1(100,100), img2; // 'img1' is a 100x100 image, 'img2' is an empty image
  12373. if (img1) { // Test succeeds, 'img1' is not an empty image
  12374. if (!img2) { // Test succeeds, 'img2' is an empty image
  12375. std::printf("'img1' is not empty, 'img2' is empty.");
  12376. }
  12377. }
  12378. \endcode
  12379. - It also allows to use brackets to access pixel values, without need for a \c CImg<T>::operator[](), e.g.
  12380. \code
  12381. CImg<float> img(100,100);
  12382. const float value = img[99]; // Access to value of the last pixel on the first row
  12383. img[510] = 255; // Set pixel value at (10,5)
  12384. \endcode
  12385. **/
  12386. operator T*() {
  12387. return _data;
  12388. }
  12389. //! Implicitly cast an image into a \c T* \const.
  12390. operator const T*() const {
  12391. return _data;
  12392. }
  12393. //! Assign a value to all image pixels.
  12394. /**
  12395. Assign specified \c value to each pixel value of the image instance.
  12396. \param value Value that will be assigned to image pixels.
  12397. \note
  12398. - The image size is never modified.
  12399. - The \c value may be casted to pixel type \c T if necessary.
  12400. \par Example
  12401. \code
  12402. CImg<char> img(100,100); // Declare image (with garbage values)
  12403. img = 0; // Set all pixel values to '0'
  12404. img = 1.2; // Set all pixel values to '1' (cast of '1.2' as a 'char')
  12405. \endcode
  12406. **/
  12407. CImg<T>& operator=(const T& value) {
  12408. return fill(value);
  12409. }
  12410. //! Assign pixels values from a specified expression.
  12411. /**
  12412. Initialize all pixel values from the specified string \c expression.
  12413. \param expression Value string describing the way pixel values are set.
  12414. \note
  12415. - String parameter \c expression may describe different things:
  12416. - 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"),
  12417. the pixel values are set from specified \c expression and the image size is not modified.
  12418. - If \c expression is a filename (as in \c "reference.jpg"), the corresponding image file is loaded and
  12419. replace the image instance. The image size is modified if necessary.
  12420. \par Example
  12421. \code
  12422. CImg<float> img1(100,100), img2(img1), img3(img1); // Declare 3 scalar images 100x100 with uninitialized values
  12423. img1 = "0,50,100,150,200,250,200,150,100,50"; // Set pixel values of 'img1' from a value sequence
  12424. img2 = "10*((x*y)%25)"; // Set pixel values of 'img2' from a formula
  12425. img3 = "reference.jpg"; // Set pixel values of 'img3' from a file (image size is modified)
  12426. (img1,img2,img3).display();
  12427. \endcode
  12428. \image html ref_operator_eq.jpg
  12429. **/
  12430. CImg<T>& operator=(const char *const expression) {
  12431. const unsigned int omode = cimg::exception_mode();
  12432. cimg::exception_mode(0);
  12433. try {
  12434. _fill(expression,true,3,0,"operator=",0,0);
  12435. } catch (CImgException&) {
  12436. cimg::exception_mode(omode);
  12437. load(expression);
  12438. }
  12439. cimg::exception_mode(omode);
  12440. return *this;
  12441. }
  12442. //! Copy an image into the current image instance.
  12443. /**
  12444. Similar to the in-place copy constructor assign(const CImg<t>&).
  12445. **/
  12446. template<typename t>
  12447. CImg<T>& operator=(const CImg<t>& img) {
  12448. return assign(img);
  12449. }
  12450. //! Copy an image into the current image instance \specialization.
  12451. CImg<T>& operator=(const CImg<T>& img) {
  12452. return assign(img);
  12453. }
  12454. //! Copy the content of a display window to the current image instance.
  12455. /**
  12456. Similar to assign(const CImgDisplay&).
  12457. **/
  12458. CImg<T>& operator=(const CImgDisplay& disp) {
  12459. disp.snapshot(*this);
  12460. return *this;
  12461. }
  12462. //! In-place addition operator.
  12463. /**
  12464. Add specified \c value to all pixels of an image instance.
  12465. \param value Value to add.
  12466. \note
  12467. - Resulting pixel values are casted to fit the pixel type \c T.
  12468. For instance, adding \c 0.2 to a \c CImg<char> is possible but does nothing indeed.
  12469. - Overflow values are treated as with standard C++ numeric types. For instance,
  12470. \code
  12471. CImg<unsigned char> img(100,100,1,1,255); // Construct a 100x100 image with pixel values '255'
  12472. img+=1; // Add '1' to each pixels -> Overflow
  12473. // here all pixels of image 'img' are equal to '0'.
  12474. \endcode
  12475. - To prevent value overflow, you may want to consider pixel type \c T as \c float or \c double,
  12476. and use cut() after addition.
  12477. \par Example
  12478. \code
  12479. CImg<unsigned char> img1("reference.jpg"); // Load a 8-bits RGB image (values in [0,255])
  12480. CImg<float> img2(img1); // Construct a float-valued copy of 'img1'
  12481. img2+=100; // Add '100' to pixel values -> goes out of [0,255] but no problems with floats
  12482. img2.cut(0,255); // Cut values in [0,255] to fit the 'unsigned char' constraint
  12483. img1 = img2; // Rewrite safe result in 'unsigned char' version 'img1'
  12484. const CImg<unsigned char> img3 = (img1 + 100).cut(0,255); // Do the same in a more simple and elegant way
  12485. (img1,img2,img3).display();
  12486. \endcode
  12487. \image html ref_operator_plus.jpg
  12488. **/
  12489. template<typename t>
  12490. CImg<T>& operator+=(const t value) {
  12491. if (is_empty()) return *this;
  12492. cimg_openmp_for(*this,*ptr + value,524288);
  12493. return *this;
  12494. }
  12495. //! In-place addition operator.
  12496. /**
  12497. Add values to image pixels, according to the specified string \c expression.
  12498. \param expression Value string describing the way pixel values are added.
  12499. \note
  12500. - Similar to operator=(const char*), except that it adds values to the pixels of the current image instance,
  12501. instead of assigning them.
  12502. **/
  12503. CImg<T>& operator+=(const char *const expression) {
  12504. return *this+=(+*this)._fill(expression,true,3,0,"operator+=",this,0);
  12505. }
  12506. //! In-place addition operator.
  12507. /**
  12508. Add values to image pixels, according to the values of the input image \c img.
  12509. \param img Input image to add.
  12510. \note
  12511. - The size of the image instance is never modified.
  12512. - It is not mandatory that input image \c img has the same size as the image instance.
  12513. If less values are available in \c img, then the values are added periodically. For instance, adding one
  12514. WxH scalar image (spectrum() equal to \c 1) to one WxH color image (spectrum() equal to \c 3)
  12515. means each color channel will be incremented with the same values at the same locations.
  12516. \par Example
  12517. \code
  12518. CImg<float> img1("reference.jpg"); // Load a RGB color image (img1.spectrum()==3)
  12519. // Construct a scalar shading (img2.spectrum()==1).
  12520. const CImg<float> img2(img1.width(),img.height(),1,1,"255*(x/w)^2");
  12521. img1+=img2; // Add shading to each channel of 'img1'
  12522. img1.cut(0,255); // Prevent [0,255] overflow
  12523. (img2,img1).display();
  12524. \endcode
  12525. \image html ref_operator_plus1.jpg
  12526. **/
  12527. template<typename t>
  12528. CImg<T>& operator+=(const CImg<t>& img) {
  12529. const ulongT siz = size(), isiz = img.size();
  12530. if (siz && isiz) {
  12531. if (is_overlapped(img)) return *this+=+img;
  12532. T *ptrd = _data, *const ptre = _data + siz;
  12533. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  12534. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  12535. *ptrd = (T)(*ptrd + *(ptrs++));
  12536. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd + *(ptrs++));
  12537. }
  12538. return *this;
  12539. }
  12540. //! In-place increment operator (prefix).
  12541. /**
  12542. Add \c 1 to all image pixels, and return a reference to the current incremented image instance.
  12543. \note
  12544. - Writing \c ++img is equivalent to \c img+=1.
  12545. **/
  12546. CImg<T>& operator++() {
  12547. if (is_empty()) return *this;
  12548. cimg_openmp_for(*this,*ptr + 1,524288);
  12549. return *this;
  12550. }
  12551. //! In-place increment operator (postfix).
  12552. /**
  12553. Add \c 1 to all image pixels, and return a new copy of the initial (pre-incremented) image instance.
  12554. \note
  12555. - Use the prefixed version operator++() if you don't need a copy of the initial
  12556. (pre-incremented) image instance, since a useless image copy may be expensive in terms of memory usage.
  12557. **/
  12558. CImg<T> operator++(int) {
  12559. const CImg<T> copy(*this,false);
  12560. ++*this;
  12561. return copy;
  12562. }
  12563. //! Return a non-shared copy of the image instance.
  12564. /**
  12565. \note
  12566. - Use this operator to ensure you get a non-shared copy of an image instance with same pixel type \c T.
  12567. Indeed, the usual copy constructor CImg<T>(const CImg<T>&) returns a shared copy of a shared input image,
  12568. and it may be not desirable to work on a regular copy (e.g. for a resize operation) if you have no
  12569. information about the shared state of the input image.
  12570. - Writing \c (+img) is equivalent to \c CImg<T>(img,false).
  12571. **/
  12572. CImg<T> operator+() const {
  12573. return CImg<T>(*this,false);
  12574. }
  12575. //! Addition operator.
  12576. /**
  12577. Similar to operator+=(const t), except that it returns a new image instance instead of operating in-place.
  12578. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  12579. **/
  12580. template<typename t>
  12581. CImg<_cimg_Tt> operator+(const t value) const {
  12582. return CImg<_cimg_Tt>(*this,false)+=value;
  12583. }
  12584. //! Addition operator.
  12585. /**
  12586. Similar to operator+=(const char*), except that it returns a new image instance instead of operating in-place.
  12587. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  12588. **/
  12589. CImg<Tfloat> operator+(const char *const expression) const {
  12590. return CImg<Tfloat>(*this,false)+=expression;
  12591. }
  12592. //! Addition operator.
  12593. /**
  12594. Similar to operator+=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
  12595. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  12596. **/
  12597. template<typename t>
  12598. CImg<_cimg_Tt> operator+(const CImg<t>& img) const {
  12599. return CImg<_cimg_Tt>(*this,false)+=img;
  12600. }
  12601. //! In-place subtraction operator.
  12602. /**
  12603. Similar to operator+=(const t), except that it performs a subtraction instead of an addition.
  12604. **/
  12605. template<typename t>
  12606. CImg<T>& operator-=(const t value) {
  12607. if (is_empty()) return *this;
  12608. cimg_openmp_for(*this,*ptr - value,524288);
  12609. return *this;
  12610. }
  12611. //! In-place subtraction operator.
  12612. /**
  12613. Similar to operator+=(const char*), except that it performs a subtraction instead of an addition.
  12614. **/
  12615. CImg<T>& operator-=(const char *const expression) {
  12616. return *this-=(+*this)._fill(expression,true,3,0,"operator-=",this,0);
  12617. }
  12618. //! In-place subtraction operator.
  12619. /**
  12620. Similar to operator+=(const CImg<t>&), except that it performs a subtraction instead of an addition.
  12621. **/
  12622. template<typename t>
  12623. CImg<T>& operator-=(const CImg<t>& img) {
  12624. const ulongT siz = size(), isiz = img.size();
  12625. if (siz && isiz) {
  12626. if (is_overlapped(img)) return *this-=+img;
  12627. T *ptrd = _data, *const ptre = _data + siz;
  12628. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  12629. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  12630. *ptrd = (T)(*ptrd - *(ptrs++));
  12631. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd - *(ptrs++));
  12632. }
  12633. return *this;
  12634. }
  12635. //! In-place decrement operator (prefix).
  12636. /**
  12637. Similar to operator++(), except that it performs a decrement instead of an increment.
  12638. **/
  12639. CImg<T>& operator--() {
  12640. if (is_empty()) return *this;
  12641. cimg_openmp_for(*this,*ptr - 1,524288);
  12642. return *this;
  12643. }
  12644. //! In-place decrement operator (postfix).
  12645. /**
  12646. Similar to operator++(int), except that it performs a decrement instead of an increment.
  12647. **/
  12648. CImg<T> operator--(int) {
  12649. const CImg<T> copy(*this,false);
  12650. --*this;
  12651. return copy;
  12652. }
  12653. //! Replace each pixel by its opposite value.
  12654. /**
  12655. \note
  12656. - If the computed opposite values are out-of-range, they are treated as with standard C++ numeric types.
  12657. For instance, the \c unsigned \c char opposite of \c 1 is \c 255.
  12658. \par Example
  12659. \code
  12660. const CImg<unsigned char>
  12661. img1("reference.jpg"), // Load a RGB color image
  12662. img2 = -img1; // Compute its opposite (in 'unsigned char')
  12663. (img1,img2).display();
  12664. \endcode
  12665. \image html ref_operator_minus.jpg
  12666. **/
  12667. CImg<T> operator-() const {
  12668. return CImg<T>(_width,_height,_depth,_spectrum,(T)0)-=*this;
  12669. }
  12670. //! Subtraction operator.
  12671. /**
  12672. Similar to operator-=(const t), except that it returns a new image instance instead of operating in-place.
  12673. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  12674. **/
  12675. template<typename t>
  12676. CImg<_cimg_Tt> operator-(const t value) const {
  12677. return CImg<_cimg_Tt>(*this,false)-=value;
  12678. }
  12679. //! Subtraction operator.
  12680. /**
  12681. Similar to operator-=(const char*), except that it returns a new image instance instead of operating in-place.
  12682. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  12683. **/
  12684. CImg<Tfloat> operator-(const char *const expression) const {
  12685. return CImg<Tfloat>(*this,false)-=expression;
  12686. }
  12687. //! Subtraction operator.
  12688. /**
  12689. Similar to operator-=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
  12690. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  12691. **/
  12692. template<typename t>
  12693. CImg<_cimg_Tt> operator-(const CImg<t>& img) const {
  12694. return CImg<_cimg_Tt>(*this,false)-=img;
  12695. }
  12696. //! In-place multiplication operator.
  12697. /**
  12698. Similar to operator+=(const t), except that it performs a multiplication instead of an addition.
  12699. **/
  12700. template<typename t>
  12701. CImg<T>& operator*=(const t value) {
  12702. if (is_empty()) return *this;
  12703. cimg_openmp_for(*this,*ptr * value,262144);
  12704. return *this;
  12705. }
  12706. //! In-place multiplication operator.
  12707. /**
  12708. Similar to operator+=(const char*), except that it performs a multiplication instead of an addition.
  12709. **/
  12710. CImg<T>& operator*=(const char *const expression) {
  12711. return mul((+*this)._fill(expression,true,3,0,"operator*=",this,0));
  12712. }
  12713. //! In-place multiplication operator.
  12714. /**
  12715. Replace the image instance by the matrix multiplication between the image instance and the specified matrix
  12716. \c img.
  12717. \param img Second operand of the matrix multiplication.
  12718. \note
  12719. - It does \e not compute a pointwise multiplication between two images. For this purpose, use
  12720. mul(const CImg<t>&) instead.
  12721. - The size of the image instance can be modified by this operator.
  12722. \par Example
  12723. \code
  12724. CImg<float> A(2,2,1,1, 1,2,3,4); // Construct 2x2 matrix A = [1,2;3,4]
  12725. const CImg<float> X(1,2,1,1, 1,2); // Construct 1x2 vector X = [1;2]
  12726. A*=X; // Assign matrix multiplication A*X to 'A'
  12727. // 'A' is now a 1x2 vector whose values are [5;11].
  12728. \endcode
  12729. **/
  12730. template<typename t>
  12731. CImg<T>& operator*=(const CImg<t>& img) {
  12732. return ((*this)*img).move_to(*this);
  12733. }
  12734. //! Multiplication operator.
  12735. /**
  12736. Similar to operator*=(const t), except that it returns a new image instance instead of operating in-place.
  12737. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  12738. **/
  12739. template<typename t>
  12740. CImg<_cimg_Tt> operator*(const t value) const {
  12741. return CImg<_cimg_Tt>(*this,false)*=value;
  12742. }
  12743. //! Multiplication operator.
  12744. /**
  12745. Similar to operator*=(const char*), except that it returns a new image instance instead of operating in-place.
  12746. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  12747. **/
  12748. CImg<Tfloat> operator*(const char *const expression) const {
  12749. return CImg<Tfloat>(*this,false)*=expression;
  12750. }
  12751. //! Multiplication operator.
  12752. /**
  12753. Similar to operator*=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
  12754. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  12755. **/
  12756. template<typename t>
  12757. CImg<_cimg_Tt> operator*(const CImg<t>& img) const {
  12758. typedef _cimg_Ttdouble Ttdouble;
  12759. typedef _cimg_Tt Tt;
  12760. if (_width!=img._height || _depth!=1 || _spectrum!=1)
  12761. throw CImgArgumentException(_cimg_instance
  12762. "operator*(): Invalid multiplication of instance by specified "
  12763. "matrix (%u,%u,%u,%u,%p).",
  12764. cimg_instance,
  12765. img._width,img._height,img._depth,img._spectrum,img._data);
  12766. CImg<Tt> res(img._width,_height);
  12767. // Check for common cases to optimize.
  12768. if (img._width==1) { // Matrix * Vector
  12769. if (_height==1) switch (_width) { // Vector^T * Vector
  12770. case 1 :
  12771. res[0] = (Tt)((Ttdouble)_data[0]*img[0]);
  12772. return res;
  12773. case 2 :
  12774. res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1]);
  12775. return res;
  12776. case 3 :
  12777. res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] +
  12778. (Ttdouble)_data[2]*img[2]);
  12779. return res;
  12780. case 4 :
  12781. res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] +
  12782. (Ttdouble)_data[2]*img[2] + (Ttdouble)_data[3]*img[3]);
  12783. return res;
  12784. default : {
  12785. Ttdouble val = 0;
  12786. cimg_pragma_openmp(parallel for reduction(+:val) cimg_openmp_if_size(size(),4096))
  12787. cimg_forX(*this,i) val+=(Ttdouble)_data[i]*img[i];
  12788. res[0] = val;
  12789. return res;
  12790. }
  12791. } else if (_height==_width) switch (_width) { // Square_matrix * Vector
  12792. case 2 : // 2x2_matrix * Vector
  12793. res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1]);
  12794. res[1] = (Tt)((Ttdouble)_data[2]*img[0] + (Ttdouble)_data[3]*img[1]);
  12795. return res;
  12796. case 3 : // 3x3_matrix * Vector
  12797. res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] +
  12798. (Ttdouble)_data[2]*img[2]);
  12799. res[1] = (Tt)((Ttdouble)_data[3]*img[0] + (Ttdouble)_data[4]*img[1] +
  12800. (Ttdouble)_data[5]*img[2]);
  12801. res[2] = (Tt)((Ttdouble)_data[6]*img[0] + (Ttdouble)_data[7]*img[1] +
  12802. (Ttdouble)_data[8]*img[2]);
  12803. return res;
  12804. case 4 : // 4x4_matrix * Vector
  12805. res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[1] +
  12806. (Ttdouble)_data[2]*img[2] + (Ttdouble)_data[3]*img[3]);
  12807. res[1] = (Tt)((Ttdouble)_data[4]*img[0] + (Ttdouble)_data[5]*img[1] +
  12808. (Ttdouble)_data[6]*img[2] + (Ttdouble)_data[7]*img[3]);
  12809. res[2] = (Tt)((Ttdouble)_data[8]*img[0] + (Ttdouble)_data[9]*img[1] +
  12810. (Ttdouble)_data[10]*img[2] + (Ttdouble)_data[11]*img[3]);
  12811. res[3] = (Tt)((Ttdouble)_data[12]*img[0] + (Ttdouble)_data[13]*img[1] +
  12812. (Ttdouble)_data[14]*img[2] + (Ttdouble)_data[15]*img[3]);
  12813. return res;
  12814. }
  12815. } else if (_height==_width) {
  12816. if (img._height==img._width) switch (_width) { // Square_matrix * Square_matrix
  12817. case 2 : // 2x2_matrix * 2x2_matrix
  12818. res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[2]);
  12819. res[1] = (Tt)((Ttdouble)_data[0]*img[1] + (Ttdouble)_data[1]*img[3]);
  12820. res[2] = (Tt)((Ttdouble)_data[2]*img[0] + (Ttdouble)_data[3]*img[2]);
  12821. res[3] = (Tt)((Ttdouble)_data[2]*img[1] + (Ttdouble)_data[3]*img[3]);
  12822. return res;
  12823. case 3 : // 3x3_matrix * 3x3_matrix
  12824. res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[3] +
  12825. (Ttdouble)_data[2]*img[6]);
  12826. res[1] = (Tt)((Ttdouble)_data[0]*img[1] + (Ttdouble)_data[1]*img[4] +
  12827. (Ttdouble)_data[2]*img[7]);
  12828. res[2] = (Tt)((Ttdouble)_data[0]*img[2] + (Ttdouble)_data[1]*img[5] +
  12829. (Ttdouble)_data[2]*img[8]);
  12830. res[3] = (Tt)((Ttdouble)_data[3]*img[0] + (Ttdouble)_data[4]*img[3] +
  12831. (Ttdouble)_data[5]*img[6]);
  12832. res[4] = (Tt)((Ttdouble)_data[3]*img[1] + (Ttdouble)_data[4]*img[4] +
  12833. (Ttdouble)_data[5]*img[7]);
  12834. res[5] = (Tt)((Ttdouble)_data[3]*img[2] + (Ttdouble)_data[4]*img[5] +
  12835. (Ttdouble)_data[5]*img[8]);
  12836. res[6] = (Tt)((Ttdouble)_data[6]*img[0] + (Ttdouble)_data[7]*img[3] +
  12837. (Ttdouble)_data[8]*img[6]);
  12838. res[7] = (Tt)((Ttdouble)_data[6]*img[1] + (Ttdouble)_data[7]*img[4] +
  12839. (Ttdouble)_data[8]*img[7]);
  12840. res[8] = (Tt)((Ttdouble)_data[6]*img[2] + (Ttdouble)_data[7]*img[5] +
  12841. (Ttdouble)_data[8]*img[8]);
  12842. return res;
  12843. case 4 : // 4x4_matrix * 4x4_matrix
  12844. res[0] = (Tt)((Ttdouble)_data[0]*img[0] + (Ttdouble)_data[1]*img[4] +
  12845. (Ttdouble)_data[2]*img[8] + (Ttdouble)_data[3]*img[12]);
  12846. res[1] = (Tt)((Ttdouble)_data[0]*img[1] + (Ttdouble)_data[1]*img[5] +
  12847. (Ttdouble)_data[2]*img[9] + (Ttdouble)_data[3]*img[13]);
  12848. res[2] = (Tt)((Ttdouble)_data[0]*img[2] + (Ttdouble)_data[1]*img[6] +
  12849. (Ttdouble)_data[2]*img[10] + (Ttdouble)_data[3]*img[14]);
  12850. res[3] = (Tt)((Ttdouble)_data[0]*img[3] + (Ttdouble)_data[1]*img[7] +
  12851. (Ttdouble)_data[2]*img[11] + (Ttdouble)_data[3]*img[15]);
  12852. res[4] = (Tt)((Ttdouble)_data[4]*img[0] + (Ttdouble)_data[5]*img[4] +
  12853. (Ttdouble)_data[6]*img[8] + (Ttdouble)_data[7]*img[12]);
  12854. res[5] = (Tt)((Ttdouble)_data[4]*img[1] + (Ttdouble)_data[5]*img[5] +
  12855. (Ttdouble)_data[6]*img[9] + (Ttdouble)_data[7]*img[13]);
  12856. res[6] = (Tt)((Ttdouble)_data[4]*img[2] + (Ttdouble)_data[5]*img[6] +
  12857. (Ttdouble)_data[6]*img[10] + (Ttdouble)_data[7]*img[14]);
  12858. res[7] = (Tt)((Ttdouble)_data[4]*img[3] + (Ttdouble)_data[5]*img[7] +
  12859. (Ttdouble)_data[6]*img[11] + (Ttdouble)_data[7]*img[15]);
  12860. res[8] = (Tt)((Ttdouble)_data[8]*img[0] + (Ttdouble)_data[9]*img[4] +
  12861. (Ttdouble)_data[10]*img[8] + (Ttdouble)_data[11]*img[12]);
  12862. res[9] = (Tt)((Ttdouble)_data[8]*img[1] + (Ttdouble)_data[9]*img[5] +
  12863. (Ttdouble)_data[10]*img[9] + (Ttdouble)_data[11]*img[13]);
  12864. res[10] = (Tt)((Ttdouble)_data[8]*img[2] + (Ttdouble)_data[9]*img[6] +
  12865. (Ttdouble)_data[10]*img[10] + (Ttdouble)_data[11]*img[14]);
  12866. res[11] = (Tt)((Ttdouble)_data[8]*img[3] + (Ttdouble)_data[9]*img[7] +
  12867. (Ttdouble)_data[10]*img[11] + (Ttdouble)_data[11]*img[15]);
  12868. res[12] = (Tt)((Ttdouble)_data[12]*img[0] + (Ttdouble)_data[13]*img[4] +
  12869. (Ttdouble)_data[14]*img[8] + (Ttdouble)_data[15]*img[12]);
  12870. res[13] = (Tt)((Ttdouble)_data[12]*img[1] + (Ttdouble)_data[13]*img[5] +
  12871. (Ttdouble)_data[14]*img[9] + (Ttdouble)_data[15]*img[13]);
  12872. res[14] = (Tt)((Ttdouble)_data[12]*img[2] + (Ttdouble)_data[13]*img[6] +
  12873. (Ttdouble)_data[14]*img[10] + (Ttdouble)_data[15]*img[14]);
  12874. res[15] = (Tt)((Ttdouble)_data[12]*img[3] + (Ttdouble)_data[13]*img[7] +
  12875. (Ttdouble)_data[14]*img[11] + (Ttdouble)_data[15]*img[15]);
  12876. return res;
  12877. } else switch (_width) { // Square_matrix * Matrix
  12878. case 2 : { // 2x2_matrix * Matrix
  12879. const t *const ps0 = img.data(), *const ps1 = img.data(0,1);
  12880. Tt *const pd0 = res.data(), *const pd1 = res.data(0,1);
  12881. const Ttdouble
  12882. a0 = (Ttdouble)_data[0], a1 = (Ttdouble)_data[1],
  12883. a2 = (Ttdouble)_data[2], a3 = (Ttdouble)_data[3];
  12884. cimg_pragma_openmp(parallel for cimg_openmp_if_size(img.width(),4096))
  12885. cimg_forX(img,i) {
  12886. const Ttdouble x = (Ttdouble)ps0[i], y = (Ttdouble)ps1[i];
  12887. pd0[i] = (Tt)(a0*x + a1*y);
  12888. pd1[i] = (Tt)(a2*x + a3*y);
  12889. }
  12890. return res;
  12891. }
  12892. case 3 : { // 3x3_matrix * Matrix
  12893. const t *const ps0 = img.data(), *const ps1 = img.data(0,1), *const ps2 = img.data(0,2);
  12894. Tt *const pd0 = res.data(), *const pd1 = res.data(0,1), *const pd2 = res.data(0,2);
  12895. const Ttdouble
  12896. a0 = (Ttdouble)_data[0], a1 = (Ttdouble)_data[1], a2 = (Ttdouble)_data[2],
  12897. a3 = (Ttdouble)_data[3], a4 = (Ttdouble)_data[4], a5 = (Ttdouble)_data[5],
  12898. a6 = (Ttdouble)_data[6], a7 = (Ttdouble)_data[7], a8 = (Ttdouble)_data[8];
  12899. cimg_pragma_openmp(parallel for cimg_openmp_if_size(img.width(),1024))
  12900. cimg_forX(img,i) {
  12901. const Ttdouble x = (Ttdouble)ps0[i], y = (Ttdouble)ps1[i], z = (Ttdouble)ps2[i];
  12902. pd0[i] = (Tt)(a0*x + a1*y + a2*z);
  12903. pd1[i] = (Tt)(a3*x + a4*y + a5*z);
  12904. pd2[i] = (Tt)(a6*x + a7*y + a8*z);
  12905. }
  12906. return res;
  12907. }
  12908. case 4 : { // 4x4_matrix * Matrix
  12909. const t
  12910. *const ps0 = img.data(), *const ps1 = img.data(0,1),
  12911. *const ps2 = img.data(0,2), *const ps3 = img.data(0,3);
  12912. Tt
  12913. *const pd0 = res.data(), *const pd1 = res.data(0,1),
  12914. *const pd2 = res.data(0,2), *const pd3 = res.data(0,3);
  12915. const Ttdouble
  12916. a0 = (Ttdouble)_data[0], a1 = (Ttdouble)_data[1], a2 = (Ttdouble)_data[2], a3 = (Ttdouble)_data[3],
  12917. a4 = (Ttdouble)_data[4], a5 = (Ttdouble)_data[5], a6 = (Ttdouble)_data[6], a7 = (Ttdouble)_data[7],
  12918. a8 = (Ttdouble)_data[8], a9 = (Ttdouble)_data[9], a10 = (Ttdouble)_data[10], a11 = (Ttdouble)_data[11],
  12919. a12 = (Ttdouble)_data[12], a13 = (Ttdouble)_data[13], a14 = (Ttdouble)_data[14],
  12920. a15 = (Ttdouble)_data[15];
  12921. cimg_pragma_openmp(parallel for cimg_openmp_if_size(img.width(),512))
  12922. cimg_forX(img,i) {
  12923. const Ttdouble x = (Ttdouble)ps0[i], y = (Ttdouble)ps1[i], z = (Ttdouble)ps2[i], c = (Ttdouble)ps3[i];
  12924. pd0[i] = (Tt)(a0*x + a1*y + a2*z + a3*c);
  12925. pd1[i] = (Tt)(a4*x + a5*y + a6*z + a7*c);
  12926. pd2[i] = (Tt)(a8*x + a9*y + a10*z + a11*c);
  12927. pd3[i] = (Tt)(a12*x + a13*y + a14*z + a15*c);
  12928. }
  12929. return res;
  12930. }
  12931. }
  12932. }
  12933. // Fallback to generic version.
  12934. #if cimg_use_openmp!=0
  12935. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
  12936. cimg_openmp_if(size()>(cimg_openmp_sizefactor)*1024 &&
  12937. img.size()>(cimg_openmp_sizefactor)*1024))
  12938. cimg_forXY(res,i,j) {
  12939. Ttdouble value = 0;
  12940. cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k);
  12941. res(i,j) = (Tt)value;
  12942. }
  12943. #else
  12944. Tt *ptrd = res._data;
  12945. cimg_forXY(res,i,j) {
  12946. Ttdouble value = 0;
  12947. cimg_forX(*this,k) value+=(*this)(k,j)*img(i,k);
  12948. *(ptrd++) = (Tt)value;
  12949. }
  12950. #endif
  12951. return res;
  12952. }
  12953. //! In-place division operator.
  12954. /**
  12955. Similar to operator+=(const t), except that it performs a division instead of an addition.
  12956. **/
  12957. template<typename t>
  12958. CImg<T>& operator/=(const t value) {
  12959. if (is_empty()) return *this;
  12960. cimg_openmp_for(*this,*ptr / value,32768);
  12961. return *this;
  12962. }
  12963. //! In-place division operator.
  12964. /**
  12965. Similar to operator+=(const char*), except that it performs a division instead of an addition.
  12966. **/
  12967. CImg<T>& operator/=(const char *const expression) {
  12968. return div((+*this)._fill(expression,true,3,0,"operator/=",this,0));
  12969. }
  12970. //! In-place division operator.
  12971. /**
  12972. Replace the image instance by the (right) matrix division between the image instance and the specified
  12973. matrix \c img.
  12974. \param img Second operand of the matrix division.
  12975. \note
  12976. - It does \e not compute a pointwise division between two images. For this purpose, use
  12977. div(const CImg<t>&) instead.
  12978. - It returns the matrix operation \c A*inverse(img).
  12979. - The size of the image instance can be modified by this operator.
  12980. **/
  12981. template<typename t>
  12982. CImg<T>& operator/=(const CImg<t>& img) {
  12983. return (*this*img.get_invert()).move_to(*this);
  12984. }
  12985. //! Division operator.
  12986. /**
  12987. Similar to operator/=(const t), except that it returns a new image instance instead of operating in-place.
  12988. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  12989. **/
  12990. template<typename t>
  12991. CImg<_cimg_Tt> operator/(const t value) const {
  12992. return CImg<_cimg_Tt>(*this,false)/=value;
  12993. }
  12994. //! Division operator.
  12995. /**
  12996. Similar to operator/=(const char*), except that it returns a new image instance instead of operating in-place.
  12997. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  12998. **/
  12999. CImg<Tfloat> operator/(const char *const expression) const {
  13000. return CImg<Tfloat>(*this,false)/=expression;
  13001. }
  13002. //! Division operator.
  13003. /**
  13004. Similar to operator/=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
  13005. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  13006. **/
  13007. template<typename t>
  13008. CImg<_cimg_Tt> operator/(const CImg<t>& img) const {
  13009. return (*this)*img.get_invert();
  13010. }
  13011. //! In-place modulo operator.
  13012. /**
  13013. Similar to operator+=(const t), except that it performs a modulo operation instead of an addition.
  13014. **/
  13015. template<typename t>
  13016. CImg<T>& operator%=(const t value) {
  13017. if (is_empty()) return *this;
  13018. cimg_openmp_for(*this,cimg::mod(*ptr,(T)value),16384);
  13019. return *this;
  13020. }
  13021. //! In-place modulo operator.
  13022. /**
  13023. Similar to operator+=(const char*), except that it performs a modulo operation instead of an addition.
  13024. **/
  13025. CImg<T>& operator%=(const char *const expression) {
  13026. return *this%=(+*this)._fill(expression,true,3,0,"operator%=",this,0);
  13027. }
  13028. //! In-place modulo operator.
  13029. /**
  13030. Similar to operator+=(const CImg<t>&), except that it performs a modulo operation instead of an addition.
  13031. **/
  13032. template<typename t>
  13033. CImg<T>& operator%=(const CImg<t>& img) {
  13034. const ulongT siz = size(), isiz = img.size();
  13035. if (siz && isiz) {
  13036. if (is_overlapped(img)) return *this%=+img;
  13037. T *ptrd = _data, *const ptre = _data + siz;
  13038. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  13039. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  13040. *ptrd = cimg::mod(*ptrd,(T)*(ptrs++));
  13041. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::mod(*ptrd,(T)*(ptrs++));
  13042. }
  13043. return *this;
  13044. }
  13045. //! Modulo operator.
  13046. /**
  13047. Similar to operator%=(const t), except that it returns a new image instance instead of operating in-place.
  13048. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  13049. **/
  13050. template<typename t>
  13051. CImg<_cimg_Tt> operator%(const t value) const {
  13052. return CImg<_cimg_Tt>(*this,false)%=value;
  13053. }
  13054. //! Modulo operator.
  13055. /**
  13056. Similar to operator%=(const char*), except that it returns a new image instance instead of operating in-place.
  13057. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  13058. **/
  13059. CImg<Tfloat> operator%(const char *const expression) const {
  13060. return CImg<Tfloat>(*this,false)%=expression;
  13061. }
  13062. //! Modulo operator.
  13063. /**
  13064. Similar to operator%=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
  13065. The pixel type of the returned image may be a superset of the initial pixel type \c T, if necessary.
  13066. **/
  13067. template<typename t>
  13068. CImg<_cimg_Tt> operator%(const CImg<t>& img) const {
  13069. return CImg<_cimg_Tt>(*this,false)%=img;
  13070. }
  13071. //! In-place bitwise AND operator.
  13072. /**
  13073. Similar to operator+=(const t), except that it performs a bitwise AND operation instead of an addition.
  13074. **/
  13075. template<typename t>
  13076. CImg<T>& operator&=(const t value) {
  13077. if (is_empty()) return *this;
  13078. cimg_openmp_for(*this,(longT)*ptr & (longT)value,32768);
  13079. return *this;
  13080. }
  13081. //! In-place bitwise AND operator.
  13082. /**
  13083. Similar to operator+=(const char*), except that it performs a bitwise AND operation instead of an addition.
  13084. **/
  13085. CImg<T>& operator&=(const char *const expression) {
  13086. return *this&=(+*this)._fill(expression,true,3,0,"operator&=",this,0);
  13087. }
  13088. //! In-place bitwise AND operator.
  13089. /**
  13090. Similar to operator+=(const CImg<t>&), except that it performs a bitwise AND operation instead of an addition.
  13091. **/
  13092. template<typename t>
  13093. CImg<T>& operator&=(const CImg<t>& img) {
  13094. const ulongT siz = size(), isiz = img.size();
  13095. if (siz && isiz) {
  13096. if (is_overlapped(img)) return *this&=+img;
  13097. T *ptrd = _data, *const ptre = _data + siz;
  13098. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  13099. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  13100. *ptrd = (T)((longT)*ptrd & (longT)*(ptrs++));
  13101. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd & (longT)*(ptrs++));
  13102. }
  13103. return *this;
  13104. }
  13105. //! Bitwise AND operator.
  13106. /**
  13107. Similar to operator&=(const t), except that it returns a new image instance instead of operating in-place.
  13108. The pixel type of the returned image is \c T.
  13109. **/
  13110. template<typename t>
  13111. CImg<T> operator&(const t value) const {
  13112. return (+*this)&=value;
  13113. }
  13114. //! Bitwise AND operator.
  13115. /**
  13116. Similar to operator&=(const char*), except that it returns a new image instance instead of operating in-place.
  13117. The pixel type of the returned image is \c T.
  13118. **/
  13119. CImg<T> operator&(const char *const expression) const {
  13120. return (+*this)&=expression;
  13121. }
  13122. //! Bitwise AND operator.
  13123. /**
  13124. Similar to operator&=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
  13125. The pixel type of the returned image is \c T.
  13126. **/
  13127. template<typename t>
  13128. CImg<T> operator&(const CImg<t>& img) const {
  13129. return (+*this)&=img;
  13130. }
  13131. //! In-place bitwise OR operator.
  13132. /**
  13133. Similar to operator+=(const t), except that it performs a bitwise OR operation instead of an addition.
  13134. **/
  13135. template<typename t>
  13136. CImg<T>& operator|=(const t value) {
  13137. if (is_empty()) return *this;
  13138. cimg_openmp_for(*this,(longT)*ptr | (longT)value,32768);
  13139. return *this;
  13140. }
  13141. //! In-place bitwise OR operator.
  13142. /**
  13143. Similar to operator+=(const char*), except that it performs a bitwise OR operation instead of an addition.
  13144. **/
  13145. CImg<T>& operator|=(const char *const expression) {
  13146. return *this|=(+*this)._fill(expression,true,3,0,"operator|=",this,0);
  13147. }
  13148. //! In-place bitwise OR operator.
  13149. /**
  13150. Similar to operator+=(const CImg<t>&), except that it performs a bitwise OR operation instead of an addition.
  13151. **/
  13152. template<typename t>
  13153. CImg<T>& operator|=(const CImg<t>& img) {
  13154. const ulongT siz = size(), isiz = img.size();
  13155. if (siz && isiz) {
  13156. if (is_overlapped(img)) return *this|=+img;
  13157. T *ptrd = _data, *const ptre = _data + siz;
  13158. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  13159. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  13160. *ptrd = (T)((longT)*ptrd | (longT)*(ptrs++));
  13161. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd | (longT)*(ptrs++));
  13162. }
  13163. return *this;
  13164. }
  13165. //! Bitwise OR operator.
  13166. /**
  13167. Similar to operator|=(const t), except that it returns a new image instance instead of operating in-place.
  13168. The pixel type of the returned image is \c T.
  13169. **/
  13170. template<typename t>
  13171. CImg<T> operator|(const t value) const {
  13172. return (+*this)|=value;
  13173. }
  13174. //! Bitwise OR operator.
  13175. /**
  13176. Similar to operator|=(const char*), except that it returns a new image instance instead of operating in-place.
  13177. The pixel type of the returned image is \c T.
  13178. **/
  13179. CImg<T> operator|(const char *const expression) const {
  13180. return (+*this)|=expression;
  13181. }
  13182. //! Bitwise OR operator.
  13183. /**
  13184. Similar to operator|=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
  13185. The pixel type of the returned image is \c T.
  13186. **/
  13187. template<typename t>
  13188. CImg<T> operator|(const CImg<t>& img) const {
  13189. return (+*this)|=img;
  13190. }
  13191. //! In-place bitwise XOR operator.
  13192. /**
  13193. Similar to operator+=(const t), except that it performs a bitwise XOR operation instead of an addition.
  13194. \warning
  13195. - It does \e not compute the \e power of pixel values. For this purpose, use pow(const t) instead.
  13196. **/
  13197. template<typename t>
  13198. CImg<T>& operator^=(const t value) {
  13199. if (is_empty()) return *this;
  13200. cimg_openmp_for(*this,(longT)*ptr ^ (longT)value,32768);
  13201. return *this;
  13202. }
  13203. //! In-place bitwise XOR operator.
  13204. /**
  13205. Similar to operator+=(const char*), except that it performs a bitwise XOR operation instead of an addition.
  13206. \warning
  13207. - It does \e not compute the \e power of pixel values. For this purpose, use pow(const char*) instead.
  13208. **/
  13209. CImg<T>& operator^=(const char *const expression) {
  13210. return *this^=(+*this)._fill(expression,true,3,0,"operator^=",this,0);
  13211. }
  13212. //! In-place bitwise XOR operator.
  13213. /**
  13214. Similar to operator+=(const CImg<t>&), except that it performs a bitwise XOR operation instead of an addition.
  13215. \warning
  13216. - It does \e not compute the \e power of pixel values. For this purpose, use pow(const CImg<t>&) instead.
  13217. **/
  13218. template<typename t>
  13219. CImg<T>& operator^=(const CImg<t>& img) {
  13220. const ulongT siz = size(), isiz = img.size();
  13221. if (siz && isiz) {
  13222. if (is_overlapped(img)) return *this^=+img;
  13223. T *ptrd = _data, *const ptre = _data + siz;
  13224. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  13225. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  13226. *ptrd = (T)((longT)*ptrd ^ (longT)*(ptrs++));
  13227. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd ^ (longT)*(ptrs++));
  13228. }
  13229. return *this;
  13230. }
  13231. //! Bitwise XOR operator.
  13232. /**
  13233. Similar to operator^=(const t), except that it returns a new image instance instead of operating in-place.
  13234. The pixel type of the returned image is \c T.
  13235. **/
  13236. template<typename t>
  13237. CImg<T> operator^(const t value) const {
  13238. return (+*this)^=value;
  13239. }
  13240. //! Bitwise XOR operator.
  13241. /**
  13242. Similar to operator^=(const char*), except that it returns a new image instance instead of operating in-place.
  13243. The pixel type of the returned image is \c T.
  13244. **/
  13245. CImg<T> operator^(const char *const expression) const {
  13246. return (+*this)^=expression;
  13247. }
  13248. //! Bitwise XOR operator.
  13249. /**
  13250. Similar to operator^=(const CImg<t>&), except that it returns a new image instance instead of operating in-place.
  13251. The pixel type of the returned image is \c T.
  13252. **/
  13253. template<typename t>
  13254. CImg<T> operator^(const CImg<t>& img) const {
  13255. return (+*this)^=img;
  13256. }
  13257. //! In-place bitwise left shift operator.
  13258. /**
  13259. Similar to operator+=(const t), except that it performs a bitwise left shift instead of an addition.
  13260. **/
  13261. template<typename t>
  13262. CImg<T>& operator<<=(const t value) {
  13263. if (is_empty()) return *this;
  13264. cimg_openmp_for(*this,((longT)*ptr) << (int)value,65536);
  13265. return *this;
  13266. }
  13267. //! In-place bitwise left shift operator.
  13268. /**
  13269. Similar to operator+=(const char*), except that it performs a bitwise left shift instead of an addition.
  13270. **/
  13271. CImg<T>& operator<<=(const char *const expression) {
  13272. return *this<<=(+*this)._fill(expression,true,3,0,"operator<<=",this,0);
  13273. }
  13274. //! In-place bitwise left shift operator.
  13275. /**
  13276. Similar to operator+=(const CImg<t>&), except that it performs a bitwise left shift instead of an addition.
  13277. **/
  13278. template<typename t>
  13279. CImg<T>& operator<<=(const CImg<t>& img) {
  13280. const ulongT siz = size(), isiz = img.size();
  13281. if (siz && isiz) {
  13282. if (is_overlapped(img)) return *this^=+img;
  13283. T *ptrd = _data, *const ptre = _data + siz;
  13284. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  13285. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  13286. *ptrd = (T)((longT)*ptrd << (int)*(ptrs++));
  13287. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd << (int)*(ptrs++));
  13288. }
  13289. return *this;
  13290. }
  13291. //! Bitwise left shift operator.
  13292. /**
  13293. Similar to operator<<=(const t), except that it returns a new image instance instead of operating in-place.
  13294. The pixel type of the returned image is \c T.
  13295. **/
  13296. template<typename t>
  13297. CImg<T> operator<<(const t value) const {
  13298. return (+*this)<<=value;
  13299. }
  13300. //! Bitwise left shift operator.
  13301. /**
  13302. Similar to operator<<=(const char*), except that it returns a new image instance instead of operating in-place.
  13303. The pixel type of the returned image is \c T.
  13304. **/
  13305. CImg<T> operator<<(const char *const expression) const {
  13306. return (+*this)<<=expression;
  13307. }
  13308. //! Bitwise left shift operator.
  13309. /**
  13310. Similar to operator<<=(const CImg<t>&), except that it returns a new image instance instead of
  13311. operating in-place.
  13312. The pixel type of the returned image is \c T.
  13313. **/
  13314. template<typename t>
  13315. CImg<T> operator<<(const CImg<t>& img) const {
  13316. return (+*this)<<=img;
  13317. }
  13318. //! In-place bitwise right shift operator.
  13319. /**
  13320. Similar to operator+=(const t), except that it performs a bitwise right shift instead of an addition.
  13321. **/
  13322. template<typename t>
  13323. CImg<T>& operator>>=(const t value) {
  13324. if (is_empty()) return *this;
  13325. cimg_openmp_for(*this,((longT)*ptr) >> (int)value,65536);
  13326. return *this;
  13327. }
  13328. //! In-place bitwise right shift operator.
  13329. /**
  13330. Similar to operator+=(const char*), except that it performs a bitwise right shift instead of an addition.
  13331. **/
  13332. CImg<T>& operator>>=(const char *const expression) {
  13333. return *this>>=(+*this)._fill(expression,true,3,0,"operator>>=",this,0);
  13334. }
  13335. //! In-place bitwise right shift operator.
  13336. /**
  13337. Similar to operator+=(const CImg<t>&), except that it performs a bitwise right shift instead of an addition.
  13338. **/
  13339. template<typename t>
  13340. CImg<T>& operator>>=(const CImg<t>& img) {
  13341. const ulongT siz = size(), isiz = img.size();
  13342. if (siz && isiz) {
  13343. if (is_overlapped(img)) return *this^=+img;
  13344. T *ptrd = _data, *const ptre = _data + siz;
  13345. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  13346. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  13347. *ptrd = (T)((longT)*ptrd >> (int)*(ptrs++));
  13348. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)((longT)*ptrd >> (int)*(ptrs++));
  13349. }
  13350. return *this;
  13351. }
  13352. //! Bitwise right shift operator.
  13353. /**
  13354. Similar to operator>>=(const t), except that it returns a new image instance instead of operating in-place.
  13355. The pixel type of the returned image is \c T.
  13356. **/
  13357. template<typename t>
  13358. CImg<T> operator>>(const t value) const {
  13359. return (+*this)>>=value;
  13360. }
  13361. //! Bitwise right shift operator.
  13362. /**
  13363. Similar to operator>>=(const char*), except that it returns a new image instance instead of operating in-place.
  13364. The pixel type of the returned image is \c T.
  13365. **/
  13366. CImg<T> operator>>(const char *const expression) const {
  13367. return (+*this)>>=expression;
  13368. }
  13369. //! Bitwise right shift operator.
  13370. /**
  13371. Similar to operator>>=(const CImg<t>&), except that it returns a new image instance instead of
  13372. operating in-place.
  13373. The pixel type of the returned image is \c T.
  13374. **/
  13375. template<typename t>
  13376. CImg<T> operator>>(const CImg<t>& img) const {
  13377. return (+*this)>>=img;
  13378. }
  13379. //! Bitwise inversion operator.
  13380. /**
  13381. Similar to operator-(), except that it compute the bitwise inverse instead of the opposite value.
  13382. **/
  13383. CImg<T> operator~() const {
  13384. CImg<T> res(_width,_height,_depth,_spectrum);
  13385. const T *ptrs = _data;
  13386. cimg_for(res,ptrd,T) { const ulongT value = (ulongT)*(ptrs++); *ptrd = (T)~value; }
  13387. return res;
  13388. }
  13389. //! Test if all pixels of an image have the same value.
  13390. /**
  13391. Return \c true is all pixels of the image instance are equal to the specified \c value.
  13392. \param value Reference value to compare with.
  13393. **/
  13394. template<typename t>
  13395. bool operator==(const t value) const {
  13396. if (is_empty()) return false;
  13397. typedef _cimg_Tt Tt;
  13398. bool is_equal = true;
  13399. for (T *ptrd = _data + size(); is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)value)) {}
  13400. return is_equal;
  13401. }
  13402. //! Test if all pixel values of an image follow a specified expression.
  13403. /**
  13404. Return \c true is all pixels of the image instance are equal to the specified \c expression.
  13405. \param expression Value string describing the way pixel values are compared.
  13406. **/
  13407. bool operator==(const char *const expression) const {
  13408. return *this==(+*this)._fill(expression,true,3,0,"operator==",this,0);
  13409. }
  13410. //! Test if two images have the same size and values.
  13411. /**
  13412. Return \c true if the image instance and the input image \c img have the same pixel values,
  13413. even if the dimensions of the two images do not match. It returns \c false otherwise.
  13414. \param img Input image to compare with.
  13415. \note
  13416. - The pixel buffer pointers data() of the two compared images do not have to be the same for operator==()
  13417. to return \c true.
  13418. Only the dimensions and the pixel values matter. Thus, the comparison can be \c true even for different
  13419. pixel types \c T and \c t.
  13420. \par Example
  13421. \code
  13422. const CImg<float> img1(1,3,1,1, 0,1,2); // Construct a 1x3 vector [0;1;2] (with 'float' pixel values)
  13423. const CImg<char> img2(1,3,1,1, 0,1,2); // Construct a 1x3 vector [0;1;2] (with 'char' pixel values)
  13424. if (img1==img2) { // Test succeeds, image dimensions and values are the same
  13425. std::printf("'img1' and 'img2' have same dimensions and values.");
  13426. }
  13427. \endcode
  13428. **/
  13429. template<typename t>
  13430. bool operator==(const CImg<t>& img) const {
  13431. typedef _cimg_Tt Tt;
  13432. const ulongT siz = size();
  13433. bool is_equal = true;
  13434. if (siz!=img.size()) return false;
  13435. t *ptrs = img._data + siz;
  13436. for (T *ptrd = _data + siz; is_equal && ptrd>_data; is_equal = ((Tt)*(--ptrd)==(Tt)*(--ptrs))) {}
  13437. return is_equal;
  13438. }
  13439. //! Test if pixels of an image are all different from a value.
  13440. /**
  13441. Return \c true is all pixels of the image instance are different than the specified \c value.
  13442. \param value Reference value to compare with.
  13443. **/
  13444. template<typename t>
  13445. bool operator!=(const t value) const {
  13446. return !((*this)==value);
  13447. }
  13448. //! Test if all pixel values of an image are different from a specified expression.
  13449. /**
  13450. Return \c true is all pixels of the image instance are different to the specified \c expression.
  13451. \param expression Value string describing the way pixel values are compared.
  13452. **/
  13453. bool operator!=(const char *const expression) const {
  13454. return !((*this)==expression);
  13455. }
  13456. //! Test if two images have different sizes or values.
  13457. /**
  13458. Return \c true if the image instance and the input image \c img have different dimensions or pixel values,
  13459. and \c false otherwise.
  13460. \param img Input image to compare with.
  13461. \note
  13462. - Writing \c img1!=img2 is equivalent to \c !(img1==img2).
  13463. **/
  13464. template<typename t>
  13465. bool operator!=(const CImg<t>& img) const {
  13466. return !((*this)==img);
  13467. }
  13468. //! Construct an image list from two images.
  13469. /**
  13470. Return a new list of image (\c CImgList instance) containing exactly two elements:
  13471. - A copy of the image instance, at position [\c 0].
  13472. - A copy of the specified image \c img, at position [\c 1].
  13473. \param img Input image that will be the second image of the resulting list.
  13474. \note
  13475. - The family of operator,() is convenient to easily create list of images, but it is also \e quite \e slow
  13476. in practice (see warning below).
  13477. - Constructed lists contain no shared images. If image instance or input image \c img are shared, they are
  13478. inserted as new non-shared copies in the resulting list.
  13479. - The pixel type of the returned list may be a superset of the initial pixel type \c T, if necessary.
  13480. \warning
  13481. - Pipelining operator,() \c N times will perform \c N copies of the entire content of a (growing) image list.
  13482. This may become very expensive in terms of speed and used memory. You should avoid using this technique to
  13483. build a new CImgList instance from several images, if you are seeking for performance.
  13484. Fast insertions of images in an image list are possible with
  13485. CImgList<T>::insert(const CImg<t>&,unsigned int,bool) or move_to(CImgList<t>&,unsigned int).
  13486. \par Example
  13487. \code
  13488. const CImg<float>
  13489. img1("reference.jpg"),
  13490. img2 = img1.get_mirror('x'),
  13491. img3 = img2.get_blur(5);
  13492. const CImgList<float> list = (img1,img2); // Create list of two elements from 'img1' and 'img2'
  13493. (list,img3).display(); // Display image list containing copies of 'img1','img2' and 'img3'
  13494. \endcode
  13495. \image html ref_operator_comma.jpg
  13496. **/
  13497. template<typename t>
  13498. CImgList<_cimg_Tt> operator,(const CImg<t>& img) const {
  13499. return CImgList<_cimg_Tt>(*this,img);
  13500. }
  13501. //! Construct an image list from image instance and an input image list.
  13502. /**
  13503. Return a new list of images (\c CImgList instance) containing exactly \c list.size() \c + \c 1 elements:
  13504. - A copy of the image instance, at position [\c 0].
  13505. - A copy of the specified image list \c list, from positions [\c 1] to [\c list.size()].
  13506. \param list Input image list that will be appended to the image instance.
  13507. \note
  13508. - Similar to operator,(const CImg<t>&) const, except that it takes an image list as an argument.
  13509. **/
  13510. template<typename t>
  13511. CImgList<_cimg_Tt> operator,(const CImgList<t>& list) const {
  13512. return CImgList<_cimg_Tt>(list,false).insert(*this,0);
  13513. }
  13514. //! Split image along specified axis.
  13515. /**
  13516. Return a new list of images (\c CImgList instance) containing the split components
  13517. of the instance image along the specified axis.
  13518. \param axis Splitting axis (can be '\c x','\c y','\c z' or '\c c')
  13519. \note
  13520. - Similar to get_split(char,int) const, with default second argument.
  13521. \par Example
  13522. \code
  13523. const CImg<unsigned char> img("reference.jpg"); // Load a RGB color image
  13524. const CImgList<unsigned char> list = (img<'c'); // Get a list of its three R,G,B channels
  13525. (img,list).display();
  13526. \endcode
  13527. \image html ref_operator_less.jpg
  13528. **/
  13529. CImgList<T> operator<(const char axis) const {
  13530. return get_split(axis);
  13531. }
  13532. //@}
  13533. //-------------------------------------
  13534. //
  13535. //! \name Instance Characteristics
  13536. //@{
  13537. //-------------------------------------
  13538. //! Return the type of image pixel values as a C string.
  13539. /**
  13540. Return a \c char* string containing the usual type name of the image pixel values
  13541. (i.e. a stringified version of the template parameter \c T).
  13542. \note
  13543. - The returned string does not contain any spaces.
  13544. - If the pixel type \c T does not correspond to a registered type, the string <tt>"unknown"</tt> is returned.
  13545. **/
  13546. static const char* pixel_type() {
  13547. return cimg::type<T>::string();
  13548. }
  13549. //! Return the number of image columns.
  13550. /**
  13551. Return the image width, i.e. the image dimension along the X-axis.
  13552. \note
  13553. - The width() of an empty image is equal to \c 0.
  13554. - width() is typically equal to \c 1 when considering images as \e vectors for matrix calculations.
  13555. - width() returns an \c int, although the image width is internally stored as an \c unsigned \c int.
  13556. Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
  13557. \c unsigned \c int variables.
  13558. Access to the initial \c unsigned \c int variable is possible (though not recommended) by
  13559. <tt>(*this)._width</tt>.
  13560. **/
  13561. int width() const {
  13562. return (int)_width;
  13563. }
  13564. //! Return the number of image rows.
  13565. /**
  13566. Return the image height, i.e. the image dimension along the Y-axis.
  13567. \note
  13568. - The height() of an empty image is equal to \c 0.
  13569. - height() returns an \c int, although the image height is internally stored as an \c unsigned \c int.
  13570. Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
  13571. \c unsigned \c int variables.
  13572. Access to the initial \c unsigned \c int variable is possible (though not recommended) by
  13573. <tt>(*this)._height</tt>.
  13574. **/
  13575. int height() const {
  13576. return (int)_height;
  13577. }
  13578. //! Return the number of image slices.
  13579. /**
  13580. Return the image depth, i.e. the image dimension along the Z-axis.
  13581. \note
  13582. - The depth() of an empty image is equal to \c 0.
  13583. - depth() is typically equal to \c 1 when considering usual 2D images. When depth()\c > \c 1, the image
  13584. is said to be \e volumetric.
  13585. - depth() returns an \c int, although the image depth is internally stored as an \c unsigned \c int.
  13586. Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
  13587. \c unsigned \c int variables.
  13588. Access to the initial \c unsigned \c int variable is possible (though not recommended) by
  13589. <tt>(*this)._depth</tt>.
  13590. **/
  13591. int depth() const {
  13592. return (int)_depth;
  13593. }
  13594. //! Return the number of image channels.
  13595. /**
  13596. Return the number of image channels, i.e. the image dimension along the C-axis.
  13597. \note
  13598. - The spectrum() of an empty image is equal to \c 0.
  13599. - spectrum() is typically equal to \c 1 when considering scalar-valued images, to \c 3
  13600. for RGB-coded color images, and to \c 4 for RGBA-coded color images (with alpha-channel).
  13601. The number of channels of an image instance is not limited. The meaning of the pixel values is not linked
  13602. up to the number of channels (e.g. a 4-channel image may indifferently stands for a RGBA or CMYK color image).
  13603. - spectrum() returns an \c int, although the image spectrum is internally stored as an \c unsigned \c int.
  13604. Using an \c int is safer and prevents arithmetic traps possibly encountered when doing calculations involving
  13605. \c unsigned \c int variables.
  13606. Access to the initial \c unsigned \c int variable is possible (though not recommended) by
  13607. <tt>(*this)._spectrum</tt>.
  13608. **/
  13609. int spectrum() const {
  13610. return (int)_spectrum;
  13611. }
  13612. //! Return the total number of pixel values.
  13613. /**
  13614. Return <tt>width()*\ref height()*\ref depth()*\ref spectrum()</tt>,
  13615. i.e. the total number of values of type \c T in the pixel buffer of the image instance.
  13616. \note
  13617. - The size() of an empty image is equal to \c 0.
  13618. - The allocated memory size for a pixel buffer of a non-shared \c CImg<T> instance is equal to
  13619. <tt>size()*sizeof(T)</tt>.
  13620. \par Example
  13621. \code
  13622. const CImg<float> img(100,100,1,3); // Construct new 100x100 color image
  13623. if (img.size()==30000) // Test succeeds
  13624. std::printf("Pixel buffer uses %lu bytes",
  13625. img.size()*sizeof(float));
  13626. \endcode
  13627. **/
  13628. ulongT size() const {
  13629. return (ulongT)_width*_height*_depth*_spectrum;
  13630. }
  13631. //! Return a pointer to the first pixel value.
  13632. /**
  13633. Return a \c T*, or a \c const \c T* pointer to the first value in the pixel buffer of the image instance,
  13634. whether the instance is \c const or not.
  13635. \note
  13636. - The data() of an empty image is equal to \c 0 (null pointer).
  13637. - The allocated pixel buffer for the image instance starts from \c data()
  13638. and goes to <tt>data()+\ref size() - 1</tt> (included).
  13639. - To get the pointer to one particular location of the pixel buffer, use
  13640. data(unsigned int,unsigned int,unsigned int,unsigned int) instead.
  13641. **/
  13642. T* data() {
  13643. return _data;
  13644. }
  13645. //! Return a pointer to the first pixel value \const.
  13646. const T* data() const {
  13647. return _data;
  13648. }
  13649. //! Return a pointer to a located pixel value.
  13650. /**
  13651. 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
  13652. of the image instance,
  13653. whether the instance is \c const or not.
  13654. \param x X-coordinate of the pixel value.
  13655. \param y Y-coordinate of the pixel value.
  13656. \param z Z-coordinate of the pixel value.
  13657. \param c C-coordinate of the pixel value.
  13658. \note
  13659. - Writing \c img.data(x,y,z,c) is equivalent to <tt>&(img(x,y,z,c))</tt>. Thus, this method has the same
  13660. properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int).
  13661. **/
  13662. #if cimg_verbosity>=3
  13663. T *data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
  13664. const ulongT off = (ulongT)offset(x,y,z,c);
  13665. if (off>=size())
  13666. cimg::warn(_cimg_instance
  13667. "data(): Invalid pointer request, at coordinates (%u,%u,%u,%u) [offset=%u].",
  13668. cimg_instance,
  13669. x,y,z,c,off);
  13670. return _data + off;
  13671. }
  13672. //! Return a pointer to a located pixel value \const.
  13673. const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
  13674. return const_cast<CImg<T>*>(this)->data(x,y,z,c);
  13675. }
  13676. #else
  13677. T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) {
  13678. return _data + x + (ulongT)y*_width + (ulongT)z*_width*_height + (ulongT)c*_width*_height*_depth;
  13679. }
  13680. const T* data(const unsigned int x, const unsigned int y=0, const unsigned int z=0, const unsigned int c=0) const {
  13681. return _data + x + (ulongT)y*_width + (ulongT)z*_width*_height + (ulongT)c*_width*_height*_depth;
  13682. }
  13683. #endif
  13684. //! Return the offset to a located pixel value, with respect to the beginning of the pixel buffer.
  13685. /**
  13686. \param x X-coordinate of the pixel value.
  13687. \param y Y-coordinate of the pixel value.
  13688. \param z Z-coordinate of the pixel value.
  13689. \param c C-coordinate of the pixel value.
  13690. \note
  13691. - Writing \c img.data(x,y,z,c) is equivalent to <tt>&(img(x,y,z,c)) - img.data()</tt>.
  13692. Thus, this method has the same properties as operator()(unsigned int,unsigned int,unsigned int,unsigned int).
  13693. \par Example
  13694. \code
  13695. const CImg<float> img(100,100,1,3); // Define a 100x100 RGB-color image
  13696. const long off = img.offset(10,10,0,2); // Get the offset of the blue value of the pixel located at (10,10)
  13697. const float val = img[off]; // Get the blue value of this pixel
  13698. \endcode
  13699. **/
  13700. longT offset(const int x, const int y=0, const int z=0, const int c=0) const {
  13701. return x + (longT)y*_width + (longT)z*_width*_height + (longT)c*_width*_height*_depth;
  13702. }
  13703. //! Return a CImg<T>::iterator pointing to the first pixel value.
  13704. /**
  13705. \note
  13706. - Equivalent to data().
  13707. - It has been mainly defined for compatibility with STL naming conventions.
  13708. **/
  13709. iterator begin() {
  13710. return _data;
  13711. }
  13712. //! Return a CImg<T>::iterator pointing to the first value of the pixel buffer \const.
  13713. const_iterator begin() const {
  13714. return _data;
  13715. }
  13716. //! Return a CImg<T>::iterator pointing next to the last pixel value.
  13717. /**
  13718. \note
  13719. - Writing \c img.end() is equivalent to <tt>img.data() + img.size()</tt>.
  13720. - It has been mainly defined for compatibility with STL naming conventions.
  13721. \warning
  13722. - The returned iterator actually points to a value located \e outside the acceptable bounds of the pixel buffer.
  13723. Trying to read or write the content of the returned iterator will probably result in a crash.
  13724. Use it mainly as a strict upper bound for a CImg<T>::iterator.
  13725. \par Example
  13726. \code
  13727. CImg<float> img(100,100,1,3); // Define a 100x100 RGB color image
  13728. // 'img.end()' used below as an upper bound for the iterator.
  13729. for (CImg<float>::iterator it = img.begin(); it<img.end(); ++it)
  13730. *it = 0;
  13731. \endcode
  13732. **/
  13733. iterator end() {
  13734. return _data + size();
  13735. }
  13736. //! Return a CImg<T>::iterator pointing next to the last pixel value \const.
  13737. const_iterator end() const {
  13738. return _data + size();
  13739. }
  13740. //! Return a reference to the first pixel value.
  13741. /**
  13742. \note
  13743. - Writing \c img.front() is equivalent to <tt>img[0]</tt>, or <tt>img(0,0,0,0)</tt>.
  13744. - It has been mainly defined for compatibility with STL naming conventions.
  13745. **/
  13746. T& front() {
  13747. return *_data;
  13748. }
  13749. //! Return a reference to the first pixel value \const.
  13750. const T& front() const {
  13751. return *_data;
  13752. }
  13753. //! Return a reference to the last pixel value.
  13754. /**
  13755. \note
  13756. - Writing \c img.back() is equivalent to <tt>img[img.size() - 1]</tt>, or
  13757. <tt>img(img.width() - 1,img.height() - 1,img.depth() - 1,img.spectrum() - 1)</tt>.
  13758. - It has been mainly defined for compatibility with STL naming conventions.
  13759. **/
  13760. T& back() {
  13761. return *(_data + size() - 1);
  13762. }
  13763. //! Return a reference to the last pixel value \const.
  13764. const T& back() const {
  13765. return *(_data + size() - 1);
  13766. }
  13767. //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions.
  13768. /**
  13769. Return a reference to the pixel value of the image instance located at a specified \c offset,
  13770. or to a specified default value in case of out-of-bounds access.
  13771. \param offset Offset to the desired pixel value.
  13772. \param out_value Default value returned if \c offset is outside image bounds.
  13773. \note
  13774. - Writing \c img.at(offset,out_value) is similar to <tt>img[offset]</tt>, except that if \c offset
  13775. is outside bounds (e.g. \c offset<0 or \c offset>=img.size()), a reference to a value \c out_value
  13776. is safely returned instead.
  13777. - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
  13778. you are \e not sure about the validity of the specified pixel offset.
  13779. **/
  13780. T& at(const int offset, const T& out_value) {
  13781. return (offset<0 || offset>=(int)size())?(cimg::temporary(out_value)=out_value):(*this)[offset];
  13782. }
  13783. //! Access to a pixel value at a specified offset, using Dirichlet boundary conditions \const.
  13784. T at(const int offset, const T& out_value) const {
  13785. return (offset<0 || offset>=(int)size())?out_value:(*this)[offset];
  13786. }
  13787. //! Access to a pixel value at a specified offset, using Neumann boundary conditions.
  13788. /**
  13789. Return a reference to the pixel value of the image instance located at a specified \c offset,
  13790. or to the nearest pixel location in the image instance in case of out-of-bounds access.
  13791. \param offset Offset to the desired pixel value.
  13792. \note
  13793. - Similar to at(int,const T), except that an out-of-bounds access returns the value of the
  13794. nearest pixel in the image instance, regarding the specified offset, i.e.
  13795. - If \c offset<0, then \c img[0] is returned.
  13796. - If \c offset>=img.size(), then \c img[img.size() - 1] is returned.
  13797. - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
  13798. you are \e not sure about the validity of the specified pixel offset.
  13799. - If you know your image instance is \e not empty, you may rather use the slightly faster method \c _at(int).
  13800. **/
  13801. T& at(const int offset) {
  13802. if (is_empty())
  13803. throw CImgInstanceException(_cimg_instance
  13804. "at(): Empty instance.",
  13805. cimg_instance);
  13806. return _at(offset);
  13807. }
  13808. T& _at(const int offset) {
  13809. const unsigned int siz = (unsigned int)size();
  13810. return (*this)[offset<0?0:(unsigned int)offset>=siz?siz - 1:offset];
  13811. }
  13812. //! Access to a pixel value at a specified offset, using Neumann boundary conditions \const.
  13813. const T& at(const int offset) const {
  13814. if (is_empty())
  13815. throw CImgInstanceException(_cimg_instance
  13816. "at(): Empty instance.",
  13817. cimg_instance);
  13818. return _at(offset);
  13819. }
  13820. const T& _at(const int offset) const {
  13821. const unsigned int siz = (unsigned int)size();
  13822. return (*this)[offset<0?0:(unsigned int)offset>=siz?siz - 1:offset];
  13823. }
  13824. //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate.
  13825. /**
  13826. Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c),
  13827. or to a specified default value in case of out-of-bounds access along the X-axis.
  13828. \param x X-coordinate of the pixel value.
  13829. \param y Y-coordinate of the pixel value.
  13830. \param z Z-coordinate of the pixel value.
  13831. \param c C-coordinate of the pixel value.
  13832. \param out_value Default value returned if \c (\c x,\c y,\c z,\c c) is outside image bounds.
  13833. \note
  13834. - Similar to operator()(), except that an out-of-bounds access along the X-axis returns the specified value
  13835. \c out_value.
  13836. - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
  13837. you are \e not sure about the validity of the specified pixel coordinates.
  13838. \warning
  13839. - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
  13840. **/
  13841. T& atX(const int x, const int y, const int z, const int c, const T& out_value) {
  13842. return (x<0 || x>=width())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
  13843. }
  13844. //! Access to a pixel value, using Dirichlet boundary conditions for the X-coordinate \const.
  13845. T atX(const int x, const int y, const int z, const int c, const T& out_value) const {
  13846. return (x<0 || x>=width())?out_value:(*this)(x,y,z,c);
  13847. }
  13848. //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate.
  13849. /**
  13850. Return a reference to the pixel value of the image instance located at (\c x,\c y,\c z,\c c),
  13851. or to the nearest pixel location in the image instance in case of out-of-bounds access along the X-axis.
  13852. \param x X-coordinate of the pixel value.
  13853. \param y Y-coordinate of the pixel value.
  13854. \param z Z-coordinate of the pixel value.
  13855. \param c C-coordinate of the pixel value.
  13856. \note
  13857. - Similar to at(int,int,int,int,const T), except that an out-of-bounds access returns the value of the
  13858. nearest pixel in the image instance, regarding the specified X-coordinate.
  13859. - Due to the additional boundary checking operation, this method is slower than operator()(). Use it when
  13860. you are \e not sure about the validity of the specified pixel coordinates.
  13861. - If you know your image instance is \e not empty, you may rather use the slightly faster method
  13862. \c _at(int,int,int,int).
  13863. \warning
  13864. - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
  13865. **/
  13866. T& atX(const int x, const int y=0, const int z=0, const int c=0) {
  13867. if (is_empty())
  13868. throw CImgInstanceException(_cimg_instance
  13869. "atX(): Empty instance.",
  13870. cimg_instance);
  13871. return _atX(x,y,z,c);
  13872. }
  13873. T& _atX(const int x, const int y=0, const int z=0, const int c=0) {
  13874. return (*this)(x<0?0:(x>=width()?width() - 1:x),y,z,c);
  13875. }
  13876. //! Access to a pixel value, using Neumann boundary conditions for the X-coordinate \const.
  13877. const T& atX(const int x, const int y=0, const int z=0, const int c=0) const {
  13878. if (is_empty())
  13879. throw CImgInstanceException(_cimg_instance
  13880. "atX(): Empty instance.",
  13881. cimg_instance);
  13882. return _atX(x,y,z,c);
  13883. }
  13884. const T& _atX(const int x, const int y=0, const int z=0, const int c=0) const {
  13885. return (*this)(x<0?0:(x>=width()?width() - 1:x),y,z,c);
  13886. }
  13887. //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y-coordinates.
  13888. /**
  13889. Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on X and Y-coordinates.
  13890. **/
  13891. T& atXY(const int x, const int y, const int z, const int c, const T& out_value) {
  13892. return (x<0 || y<0 || x>=width() || y>=height())?(cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
  13893. }
  13894. //! Access to a pixel value, using Dirichlet boundary conditions for the X and Y coordinates \const.
  13895. T atXY(const int x, const int y, const int z, const int c, const T& out_value) const {
  13896. return (x<0 || y<0 || x>=width() || y>=height())?out_value:(*this)(x,y,z,c);
  13897. }
  13898. //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates.
  13899. /**
  13900. Similar to atX(int,int,int,int), except that boundary checking is performed both on X and Y-coordinates.
  13901. \note
  13902. - If you know your image instance is \e not empty, you may rather use the slightly faster method
  13903. \c _atXY(int,int,int,int).
  13904. **/
  13905. T& atXY(const int x, const int y, const int z=0, const int c=0) {
  13906. if (is_empty())
  13907. throw CImgInstanceException(_cimg_instance
  13908. "atXY(): Empty instance.",
  13909. cimg_instance);
  13910. return _atXY(x,y,z,c);
  13911. }
  13912. T& _atXY(const int x, const int y, const int z=0, const int c=0) {
  13913. return (*this)(cimg::cut(x,0,width() - 1),
  13914. cimg::cut(y,0,height() - 1),z,c);
  13915. }
  13916. //! Access to a pixel value, using Neumann boundary conditions for the X and Y-coordinates \const.
  13917. const T& atXY(const int x, const int y, const int z=0, const int c=0) const {
  13918. if (is_empty())
  13919. throw CImgInstanceException(_cimg_instance
  13920. "atXY(): Empty instance.",
  13921. cimg_instance);
  13922. return _atXY(x,y,z,c);
  13923. }
  13924. const T& _atXY(const int x, const int y, const int z=0, const int c=0) const {
  13925. return (*this)(cimg::cut(x,0,width() - 1),
  13926. cimg::cut(y,0,height() - 1),z,c);
  13927. }
  13928. //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates.
  13929. /**
  13930. Similar to atX(int,int,int,int,const T), except that boundary checking is performed both on
  13931. X,Y and Z-coordinates.
  13932. **/
  13933. T& atXYZ(const int x, const int y, const int z, const int c, const T& out_value) {
  13934. return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?
  13935. (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
  13936. }
  13937. //! Access to a pixel value, using Dirichlet boundary conditions for the X,Y and Z-coordinates \const.
  13938. T atXYZ(const int x, const int y, const int z, const int c, const T& out_value) const {
  13939. return (x<0 || y<0 || z<0 || x>=width() || y>=height() || z>=depth())?out_value:(*this)(x,y,z,c);
  13940. }
  13941. //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates.
  13942. /**
  13943. Similar to atX(int,int,int,int), except that boundary checking is performed both on X,Y and Z-coordinates.
  13944. \note
  13945. - If you know your image instance is \e not empty, you may rather use the slightly faster method
  13946. \c _atXYZ(int,int,int,int).
  13947. **/
  13948. T& atXYZ(const int x, const int y, const int z, const int c=0) {
  13949. if (is_empty())
  13950. throw CImgInstanceException(_cimg_instance
  13951. "atXYZ(): Empty instance.",
  13952. cimg_instance);
  13953. return _atXYZ(x,y,z,c);
  13954. }
  13955. T& _atXYZ(const int x, const int y, const int z, const int c=0) {
  13956. return (*this)(cimg::cut(x,0,width() - 1),
  13957. cimg::cut(y,0,height() - 1),
  13958. cimg::cut(z,0,depth() - 1),c);
  13959. }
  13960. //! Access to a pixel value, using Neumann boundary conditions for the X,Y and Z-coordinates \const.
  13961. const T& atXYZ(const int x, const int y, const int z, const int c=0) const {
  13962. if (is_empty())
  13963. throw CImgInstanceException(_cimg_instance
  13964. "atXYZ(): Empty instance.",
  13965. cimg_instance);
  13966. return _atXYZ(x,y,z,c);
  13967. }
  13968. const T& _atXYZ(const int x, const int y, const int z, const int c=0) const {
  13969. return (*this)(cimg::cut(x,0,width() - 1),
  13970. cimg::cut(y,0,height() - 1),
  13971. cimg::cut(z,0,depth() - 1),c);
  13972. }
  13973. //! Access to a pixel value, using Dirichlet boundary conditions.
  13974. /**
  13975. Similar to atX(int,int,int,int,const T), except that boundary checking is performed on all
  13976. X,Y,Z and C-coordinates.
  13977. **/
  13978. T& atXYZC(const int x, const int y, const int z, const int c, const T& out_value) {
  13979. return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?
  13980. (cimg::temporary(out_value)=out_value):(*this)(x,y,z,c);
  13981. }
  13982. //! Access to a pixel value, using Dirichlet boundary conditions \const.
  13983. T atXYZC(const int x, const int y, const int z, const int c, const T& out_value) const {
  13984. return (x<0 || y<0 || z<0 || c<0 || x>=width() || y>=height() || z>=depth() || c>=spectrum())?out_value:
  13985. (*this)(x,y,z,c);
  13986. }
  13987. //! Access to a pixel value, using Neumann boundary conditions.
  13988. /**
  13989. Similar to atX(int,int,int,int), except that boundary checking is performed on all X,Y,Z and C-coordinates.
  13990. \note
  13991. - If you know your image instance is \e not empty, you may rather use the slightly faster method
  13992. \c _atXYZC(int,int,int,int).
  13993. **/
  13994. T& atXYZC(const int x, const int y, const int z, const int c) {
  13995. if (is_empty())
  13996. throw CImgInstanceException(_cimg_instance
  13997. "atXYZC(): Empty instance.",
  13998. cimg_instance);
  13999. return _atXYZC(x,y,z,c);
  14000. }
  14001. T& _atXYZC(const int x, const int y, const int z, const int c) {
  14002. return (*this)(cimg::cut(x,0,width() - 1),
  14003. cimg::cut(y,0,height() - 1),
  14004. cimg::cut(z,0,depth() - 1),
  14005. cimg::cut(c,0,spectrum() - 1));
  14006. }
  14007. //! Access to a pixel value, using Neumann boundary conditions \const.
  14008. const T& atXYZC(const int x, const int y, const int z, const int c) const {
  14009. if (is_empty())
  14010. throw CImgInstanceException(_cimg_instance
  14011. "atXYZC(): Empty instance.",
  14012. cimg_instance);
  14013. return _atXYZC(x,y,z,c);
  14014. }
  14015. const T& _atXYZC(const int x, const int y, const int z, const int c) const {
  14016. return (*this)(cimg::cut(x,0,width() - 1),
  14017. cimg::cut(y,0,height() - 1),
  14018. cimg::cut(z,0,depth() - 1),
  14019. cimg::cut(c,0,spectrum() - 1));
  14020. }
  14021. //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X-coordinate.
  14022. /**
  14023. Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
  14024. or a specified default value in case of out-of-bounds access along the X-axis.
  14025. \param fx X-coordinate of the pixel value (float-valued).
  14026. \param y Y-coordinate of the pixel value.
  14027. \param z Z-coordinate of the pixel value.
  14028. \param c C-coordinate of the pixel value.
  14029. \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds.
  14030. \note
  14031. - Similar to atX(int,int,int,int,const T), except that the returned pixel value is approximated by
  14032. a linear interpolation along the X-axis, if corresponding coordinates are not integers.
  14033. - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued.
  14034. \warning
  14035. - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
  14036. **/
  14037. Tfloat linear_atX(const float fx, const int y, const int z, const int c, const T& out_value) const {
  14038. const int
  14039. x = (int)fx - (fx>=0?0:1), nx = x + 1;
  14040. const float
  14041. dx = fx - x;
  14042. const Tfloat
  14043. Ic = (Tfloat)atX(x,y,z,c,out_value), In = (Tfloat)atXY(nx,y,z,c,out_value);
  14044. return Ic + dx*(In - Ic);
  14045. }
  14046. //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X-coordinate.
  14047. /**
  14048. Return a linearly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
  14049. or the value of the nearest pixel location in the image instance in case of out-of-bounds access along
  14050. the X-axis.
  14051. \param fx X-coordinate of the pixel value (float-valued).
  14052. \param y Y-coordinate of the pixel value.
  14053. \param z Z-coordinate of the pixel value.
  14054. \param c C-coordinate of the pixel value.
  14055. \note
  14056. - Similar to linear_atX(float,int,int,int,const T) const, except that an out-of-bounds access returns
  14057. the value of the nearest pixel in the image instance, regarding the specified X-coordinate.
  14058. - If you know your image instance is \e not empty, you may rather use the slightly faster method
  14059. \c _linear_atX(float,int,int,int).
  14060. \warning
  14061. - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
  14062. **/
  14063. Tfloat linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
  14064. if (is_empty())
  14065. throw CImgInstanceException(_cimg_instance
  14066. "linear_atX(): Empty instance.",
  14067. cimg_instance);
  14068. return _linear_atX(fx,y,z,c);
  14069. }
  14070. Tfloat _linear_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
  14071. const float
  14072. nfx = cimg::cut(fx,0,width() - 1);
  14073. const unsigned int
  14074. x = (unsigned int)nfx;
  14075. const float
  14076. dx = nfx - x;
  14077. const unsigned int
  14078. nx = dx>0?x + 1:x;
  14079. const Tfloat
  14080. Ic = (Tfloat)(*this)(x,y,z,c), In = (Tfloat)(*this)(nx,y,z,c);
  14081. return Ic + dx*(In - Ic);
  14082. }
  14083. //! Return pixel value, using linear interpolation and periodic boundary conditions for the X-coordinate.
  14084. Tfloat linear_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const {
  14085. if (is_empty())
  14086. throw CImgInstanceException(_cimg_instance
  14087. "linear_atX_p(): Empty instance.",
  14088. cimg_instance);
  14089. return _linear_atX_p(fx,y,z,c);
  14090. }
  14091. Tfloat _linear_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const {
  14092. const float
  14093. nfx = cimg::mod(fx,_width - 0.5f);
  14094. const unsigned int
  14095. x = (unsigned int)nfx;
  14096. const float
  14097. dx = nfx - x;
  14098. const unsigned int
  14099. nx = cimg::mod(x + 1,_width);
  14100. const Tfloat
  14101. Ic = (Tfloat)(*this)(x,y,z,c), In = (Tfloat)(*this)(nx,y,z,c);
  14102. return Ic + dx*(In - Ic);
  14103. }
  14104. //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X and Y-coordinates.
  14105. /**
  14106. Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the
  14107. boundary checking are achieved both for X and Y-coordinates.
  14108. **/
  14109. Tfloat linear_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const {
  14110. const int
  14111. x = (int)fx - (fx>=0?0:1), nx = x + 1,
  14112. y = (int)fy - (fy>=0?0:1), ny = y + 1;
  14113. const float
  14114. dx = fx - x,
  14115. dy = fy - y;
  14116. const Tfloat
  14117. Icc = (Tfloat)atXY(x,y,z,c,out_value), Inc = (Tfloat)atXY(nx,y,z,c,out_value),
  14118. Icn = (Tfloat)atXY(x,ny,z,c,out_value), Inn = (Tfloat)atXY(nx,ny,z,c,out_value);
  14119. return Icc + (Inc - Icc + (Icc + Inn - Icn - Inc)*dy)*dx + (Icn - Icc)*dy;
  14120. }
  14121. //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X and Y-coordinates.
  14122. /**
  14123. Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
  14124. are achieved both for X and Y-coordinates.
  14125. \note
  14126. - If you know your image instance is \e not empty, you may rather use the slightly faster method
  14127. \c _linear_atXY(float,float,int,int).
  14128. **/
  14129. Tfloat linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
  14130. if (is_empty())
  14131. throw CImgInstanceException(_cimg_instance
  14132. "linear_atXY(): Empty instance.",
  14133. cimg_instance);
  14134. return _linear_atXY(fx,fy,z,c);
  14135. }
  14136. Tfloat _linear_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
  14137. const float
  14138. nfx = cimg::cut(fx,0,width() - 1),
  14139. nfy = cimg::cut(fy,0,height() - 1);
  14140. const unsigned int
  14141. x = (unsigned int)nfx,
  14142. y = (unsigned int)nfy;
  14143. const float
  14144. dx = nfx - x,
  14145. dy = nfy - y;
  14146. const unsigned int
  14147. nx = dx>0?x + 1:x,
  14148. ny = dy>0?y + 1:y;
  14149. const Tfloat
  14150. Icc = (Tfloat)(*this)(x,y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c),
  14151. Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c);
  14152. return Icc + (Inc - Icc + (Icc + Inn - Icn - Inc)*dy)*dx + (Icn - Icc)*dy;
  14153. }
  14154. //! Return pixel value, using linear interpolation and periodic boundary conditions for the X and Y-coordinates.
  14155. Tfloat linear_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const {
  14156. if (is_empty())
  14157. throw CImgInstanceException(_cimg_instance
  14158. "linear_atXY_p(): Empty instance.",
  14159. cimg_instance);
  14160. return _linear_atXY_p(fx,fy,z,c);
  14161. }
  14162. Tfloat _linear_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const {
  14163. const float
  14164. nfx = cimg::mod(fx,_width - 0.5f),
  14165. nfy = cimg::mod(fy,_height - 0.5f);
  14166. const unsigned int
  14167. x = (unsigned int)nfx,
  14168. y = (unsigned int)nfy;
  14169. const float
  14170. dx = nfx - x,
  14171. dy = nfy - y;
  14172. const unsigned int
  14173. nx = cimg::mod(x + 1,_width),
  14174. ny = cimg::mod(y + 1,_height);
  14175. const Tfloat
  14176. Icc = (Tfloat)(*this)(x,y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c),
  14177. Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c);
  14178. return Icc + (Inc - Icc + (Icc + Inn - Icn - Inc)*dy)*dx + (Icn - Icc)*dy;
  14179. }
  14180. //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates.
  14181. /**
  14182. Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the
  14183. boundary checking are achieved both for X,Y and Z-coordinates.
  14184. **/
  14185. Tfloat linear_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const {
  14186. const int
  14187. x = (int)fx - (fx>=0?0:1), nx = x + 1,
  14188. y = (int)fy - (fy>=0?0:1), ny = y + 1,
  14189. z = (int)fz - (fz>=0?0:1), nz = z + 1;
  14190. const float
  14191. dx = fx - x,
  14192. dy = fy - y,
  14193. dz = fz - z;
  14194. const Tfloat
  14195. Iccc = (Tfloat)atXYZ(x,y,z,c,out_value), Incc = (Tfloat)atXYZ(nx,y,z,c,out_value),
  14196. Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value), Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value),
  14197. Iccn = (Tfloat)atXYZ(x,y,nz,c,out_value), Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value),
  14198. Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value), Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value);
  14199. return Iccc +
  14200. (Incc - Iccc +
  14201. (Iccc + Innc - Icnc - Incc +
  14202. (Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)*dz)*dy +
  14203. (Iccc + Incn - Iccn - Incc)*dz)*dx +
  14204. (Icnc - Iccc +
  14205. (Iccc + Icnn - Iccn - Icnc)*dz)*dy +
  14206. (Iccn - Iccc)*dz;
  14207. }
  14208. //! Return pixel value, using linear interpolation and Neumann boundary conditions for the X,Y and Z-coordinates.
  14209. /**
  14210. Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
  14211. are achieved both for X,Y and Z-coordinates.
  14212. \note
  14213. - If you know your image instance is \e not empty, you may rather use the slightly faster method
  14214. \c _linear_atXYZ(float,float,float,int).
  14215. **/
  14216. Tfloat linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const {
  14217. if (is_empty())
  14218. throw CImgInstanceException(_cimg_instance
  14219. "linear_atXYZ(): Empty instance.",
  14220. cimg_instance);
  14221. return _linear_atXYZ(fx,fy,fz,c);
  14222. }
  14223. Tfloat _linear_atXYZ(const float fx, const float fy=0, const float fz=0, const int c=0) const {
  14224. const float
  14225. nfx = cimg::cut(fx,0,width() - 1),
  14226. nfy = cimg::cut(fy,0,height() - 1),
  14227. nfz = cimg::cut(fz,0,depth() - 1);
  14228. const unsigned int
  14229. x = (unsigned int)nfx,
  14230. y = (unsigned int)nfy,
  14231. z = (unsigned int)nfz;
  14232. const float
  14233. dx = nfx - x,
  14234. dy = nfy - y,
  14235. dz = nfz - z;
  14236. const unsigned int
  14237. nx = dx>0?x + 1:x,
  14238. ny = dy>0?y + 1:y,
  14239. nz = dz>0?z + 1:z;
  14240. const Tfloat
  14241. Iccc = (Tfloat)(*this)(x,y,z,c), Incc = (Tfloat)(*this)(nx,y,z,c),
  14242. Icnc = (Tfloat)(*this)(x,ny,z,c), Innc = (Tfloat)(*this)(nx,ny,z,c),
  14243. Iccn = (Tfloat)(*this)(x,y,nz,c), Incn = (Tfloat)(*this)(nx,y,nz,c),
  14244. Icnn = (Tfloat)(*this)(x,ny,nz,c), Innn = (Tfloat)(*this)(nx,ny,nz,c);
  14245. return Iccc +
  14246. (Incc - Iccc +
  14247. (Iccc + Innc - Icnc - Incc +
  14248. (Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)*dz)*dy +
  14249. (Iccc + Incn - Iccn - Incc)*dz)*dx +
  14250. (Icnc - Iccc +
  14251. (Iccc + Icnn - Iccn - Icnc)*dz)*dy +
  14252. (Iccn - Iccc)*dz;
  14253. }
  14254. //! Return pixel value, using linear interpolation and periodic boundary conditions for the X,Y and Z-coordinates.
  14255. Tfloat linear_atXYZ_p(const float fx, const float fy=0, const float fz=0, const int c=0) const {
  14256. if (is_empty())
  14257. throw CImgInstanceException(_cimg_instance
  14258. "linear_atXYZ_p(): Empty instance.",
  14259. cimg_instance);
  14260. return _linear_atXYZ_p(fx,fy,fz,c);
  14261. }
  14262. Tfloat _linear_atXYZ_p(const float fx, const float fy=0, const float fz=0, const int c=0) const {
  14263. const float
  14264. nfx = cimg::mod(fx,_width - 0.5f),
  14265. nfy = cimg::mod(fy,_height - 0.5f),
  14266. nfz = cimg::mod(fz,_depth - 0.5f);
  14267. const unsigned int
  14268. x = (unsigned int)nfx,
  14269. y = (unsigned int)nfy,
  14270. z = (unsigned int)nfz;
  14271. const float
  14272. dx = nfx - x,
  14273. dy = nfy - y,
  14274. dz = nfz - z;
  14275. const unsigned int
  14276. nx = cimg::mod(x + 1,_width),
  14277. ny = cimg::mod(y + 1,_height),
  14278. nz = cimg::mod(z + 1,_depth);
  14279. const Tfloat
  14280. Iccc = (Tfloat)(*this)(x,y,z,c), Incc = (Tfloat)(*this)(nx,y,z,c),
  14281. Icnc = (Tfloat)(*this)(x,ny,z,c), Innc = (Tfloat)(*this)(nx,ny,z,c),
  14282. Iccn = (Tfloat)(*this)(x,y,nz,c), Incn = (Tfloat)(*this)(nx,y,nz,c),
  14283. Icnn = (Tfloat)(*this)(x,ny,nz,c), Innn = (Tfloat)(*this)(nx,ny,nz,c);
  14284. return Iccc +
  14285. (Incc - Iccc +
  14286. (Iccc + Innc - Icnc - Incc +
  14287. (Iccn + Innn + Icnc + Incc - Icnn - Incn - Iccc - Innc)*dz)*dy +
  14288. (Iccc + Incn - Iccn - Incc)*dz)*dx +
  14289. (Icnc - Iccc +
  14290. (Iccc + Icnn - Iccn - Icnc)*dz)*dy +
  14291. (Iccn - Iccc)*dz;
  14292. }
  14293. //! Return pixel value, using linear interpolation and Dirichlet boundary conditions for all X,Y,Z,C-coordinates.
  14294. /**
  14295. Similar to linear_atX(float,int,int,int,const T) const, except that the linear interpolation and the
  14296. boundary checking are achieved for all X,Y,Z and C-coordinates.
  14297. **/
  14298. Tfloat linear_atXYZC(const float fx, const float fy, const float fz, const float fc, const T& out_value) const {
  14299. const int
  14300. x = (int)fx - (fx>=0?0:1), nx = x + 1,
  14301. y = (int)fy - (fy>=0?0:1), ny = y + 1,
  14302. z = (int)fz - (fz>=0?0:1), nz = z + 1,
  14303. c = (int)fc - (fc>=0?0:1), nc = c + 1;
  14304. const float
  14305. dx = fx - x,
  14306. dy = fy - y,
  14307. dz = fz - z,
  14308. dc = fc - c;
  14309. const Tfloat
  14310. Icccc = (Tfloat)atXYZC(x,y,z,c,out_value), Inccc = (Tfloat)atXYZC(nx,y,z,c,out_value),
  14311. Icncc = (Tfloat)atXYZC(x,ny,z,c,out_value), Inncc = (Tfloat)atXYZC(nx,ny,z,c,out_value),
  14312. Iccnc = (Tfloat)atXYZC(x,y,nz,c,out_value), Incnc = (Tfloat)atXYZC(nx,y,nz,c,out_value),
  14313. Icnnc = (Tfloat)atXYZC(x,ny,nz,c,out_value), Innnc = (Tfloat)atXYZC(nx,ny,nz,c,out_value),
  14314. Icccn = (Tfloat)atXYZC(x,y,z,nc,out_value), Inccn = (Tfloat)atXYZC(nx,y,z,nc,out_value),
  14315. Icncn = (Tfloat)atXYZC(x,ny,z,nc,out_value), Inncn = (Tfloat)atXYZC(nx,ny,z,nc,out_value),
  14316. Iccnn = (Tfloat)atXYZC(x,y,nz,nc,out_value), Incnn = (Tfloat)atXYZC(nx,y,nz,nc,out_value),
  14317. Icnnn = (Tfloat)atXYZC(x,ny,nz,nc,out_value), Innnn = (Tfloat)atXYZC(nx,ny,nz,nc,out_value);
  14318. return Icccc +
  14319. dx*(Inccc - Icccc +
  14320. dy*(Icccc + Inncc - Icncc - Inccc +
  14321. dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc +
  14322. dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc -
  14323. Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) +
  14324. dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) +
  14325. dz*(Icccc + Incnc - Iccnc - Inccc +
  14326. dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) +
  14327. dc*(Icccc + Inccn - Inccc - Icccn)) +
  14328. dy*(Icncc - Icccc +
  14329. dz*(Icccc + Icnnc - Iccnc - Icncc +
  14330. dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) +
  14331. dc*(Icccc + Icncn - Icncc - Icccn)) +
  14332. dz*(Iccnc - Icccc +
  14333. dc*(Icccc + Iccnn - Iccnc - Icccn)) +
  14334. dc*(Icccn -Icccc);
  14335. }
  14336. //! Return pixel value, using linear interpolation and Neumann boundary conditions for all X,Y,Z and C-coordinates.
  14337. /**
  14338. Similar to linear_atX(float,int,int,int) const, except that the linear interpolation and the boundary checking
  14339. are achieved for all X,Y,Z and C-coordinates.
  14340. \note
  14341. - If you know your image instance is \e not empty, you may rather use the slightly faster method
  14342. \c _linear_atXYZC(float,float,float,float).
  14343. **/
  14344. Tfloat linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
  14345. if (is_empty())
  14346. throw CImgInstanceException(_cimg_instance
  14347. "linear_atXYZC(): Empty instance.",
  14348. cimg_instance);
  14349. return _linear_atXYZC(fx,fy,fz,fc);
  14350. }
  14351. Tfloat _linear_atXYZC(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
  14352. const float
  14353. nfx = cimg::cut(fx,0,width() - 1),
  14354. nfy = cimg::cut(fy,0,height() - 1),
  14355. nfz = cimg::cut(fz,0,depth() - 1),
  14356. nfc = cimg::cut(fc,0,spectrum() - 1);
  14357. const unsigned int
  14358. x = (unsigned int)nfx,
  14359. y = (unsigned int)nfy,
  14360. z = (unsigned int)nfz,
  14361. c = (unsigned int)nfc;
  14362. const float
  14363. dx = nfx - x,
  14364. dy = nfy - y,
  14365. dz = nfz - z,
  14366. dc = nfc - c;
  14367. const unsigned int
  14368. nx = dx>0?x + 1:x,
  14369. ny = dy>0?y + 1:y,
  14370. nz = dz>0?z + 1:z,
  14371. nc = dc>0?c + 1:c;
  14372. const Tfloat
  14373. Icccc = (Tfloat)(*this)(x,y,z,c), Inccc = (Tfloat)(*this)(nx,y,z,c),
  14374. Icncc = (Tfloat)(*this)(x,ny,z,c), Inncc = (Tfloat)(*this)(nx,ny,z,c),
  14375. Iccnc = (Tfloat)(*this)(x,y,nz,c), Incnc = (Tfloat)(*this)(nx,y,nz,c),
  14376. Icnnc = (Tfloat)(*this)(x,ny,nz,c), Innnc = (Tfloat)(*this)(nx,ny,nz,c),
  14377. Icccn = (Tfloat)(*this)(x,y,z,nc), Inccn = (Tfloat)(*this)(nx,y,z,nc),
  14378. Icncn = (Tfloat)(*this)(x,ny,z,nc), Inncn = (Tfloat)(*this)(nx,ny,z,nc),
  14379. Iccnn = (Tfloat)(*this)(x,y,nz,nc), Incnn = (Tfloat)(*this)(nx,y,nz,nc),
  14380. Icnnn = (Tfloat)(*this)(x,ny,nz,nc), Innnn = (Tfloat)(*this)(nx,ny,nz,nc);
  14381. return Icccc +
  14382. dx*(Inccc - Icccc +
  14383. dy*(Icccc + Inncc - Icncc - Inccc +
  14384. dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc +
  14385. dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc -
  14386. Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) +
  14387. dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) +
  14388. dz*(Icccc + Incnc - Iccnc - Inccc +
  14389. dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) +
  14390. dc*(Icccc + Inccn - Inccc - Icccn)) +
  14391. dy*(Icncc - Icccc +
  14392. dz*(Icccc + Icnnc - Iccnc - Icncc +
  14393. dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) +
  14394. dc*(Icccc + Icncn - Icncc - Icccn)) +
  14395. dz*(Iccnc - Icccc +
  14396. dc*(Icccc + Iccnn - Iccnc - Icccn)) +
  14397. dc*(Icccn - Icccc);
  14398. }
  14399. //! Return pixel value, using linear interpolation and periodic boundary conditions for all X,Y,Z and C-coordinates.
  14400. Tfloat linear_atXYZC_p(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
  14401. if (is_empty())
  14402. throw CImgInstanceException(_cimg_instance
  14403. "linear_atXYZC_p(): Empty instance.",
  14404. cimg_instance);
  14405. return _linear_atXYZC_p(fx,fy,fz,fc);
  14406. }
  14407. Tfloat _linear_atXYZC_p(const float fx, const float fy=0, const float fz=0, const float fc=0) const {
  14408. const float
  14409. nfx = cimg::mod(fx,_width - 0.5f),
  14410. nfy = cimg::mod(fy,_height - 0.5f),
  14411. nfz = cimg::mod(fz,_depth - 0.5f),
  14412. nfc = cimg::mod(fc,_spectrum - 0.5f);
  14413. const unsigned int
  14414. x = (unsigned int)nfx,
  14415. y = (unsigned int)nfy,
  14416. z = (unsigned int)nfz,
  14417. c = (unsigned int)nfc;
  14418. const float
  14419. dx = nfx - x,
  14420. dy = nfy - y,
  14421. dz = nfz - z,
  14422. dc = nfc - c;
  14423. const unsigned int
  14424. nx = cimg::mod(x + 1,_width),
  14425. ny = cimg::mod(y + 1,_height),
  14426. nz = cimg::mod(z + 1,_depth),
  14427. nc = cimg::mod(c + 1,_spectrum);
  14428. const Tfloat
  14429. Icccc = (Tfloat)(*this)(x,y,z,c), Inccc = (Tfloat)(*this)(nx,y,z,c),
  14430. Icncc = (Tfloat)(*this)(x,ny,z,c), Inncc = (Tfloat)(*this)(nx,ny,z,c),
  14431. Iccnc = (Tfloat)(*this)(x,y,nz,c), Incnc = (Tfloat)(*this)(nx,y,nz,c),
  14432. Icnnc = (Tfloat)(*this)(x,ny,nz,c), Innnc = (Tfloat)(*this)(nx,ny,nz,c),
  14433. Icccn = (Tfloat)(*this)(x,y,z,nc), Inccn = (Tfloat)(*this)(nx,y,z,nc),
  14434. Icncn = (Tfloat)(*this)(x,ny,z,nc), Inncn = (Tfloat)(*this)(nx,ny,z,nc),
  14435. Iccnn = (Tfloat)(*this)(x,y,nz,nc), Incnn = (Tfloat)(*this)(nx,y,nz,nc),
  14436. Icnnn = (Tfloat)(*this)(x,ny,nz,nc), Innnn = (Tfloat)(*this)(nx,ny,nz,nc);
  14437. return Icccc +
  14438. dx*(Inccc - Icccc +
  14439. dy*(Icccc + Inncc - Icncc - Inccc +
  14440. dz*(Iccnc + Innnc + Icncc + Inccc - Icnnc - Incnc - Icccc - Inncc +
  14441. dc*(Iccnn + Innnn + Icncn + Inccn + Icnnc + Incnc + Icccc + Inncc -
  14442. Icnnn - Incnn - Icccn - Inncn - Iccnc - Innnc - Icncc - Inccc)) +
  14443. dc*(Icccn + Inncn + Icncc + Inccc - Icncn - Inccn - Icccc - Inncc)) +
  14444. dz*(Icccc + Incnc - Iccnc - Inccc +
  14445. dc*(Icccn + Incnn + Iccnc + Inccc - Iccnn - Inccn - Icccc - Incnc)) +
  14446. dc*(Icccc + Inccn - Inccc - Icccn)) +
  14447. dy*(Icncc - Icccc +
  14448. dz*(Icccc + Icnnc - Iccnc - Icncc +
  14449. dc*(Icccn + Icnnn + Iccnc + Icncc - Iccnn - Icncn - Icccc - Icnnc)) +
  14450. dc*(Icccc + Icncn - Icncc - Icccn)) +
  14451. dz*(Iccnc - Icccc +
  14452. dc*(Icccc + Iccnn - Iccnc - Icccn)) +
  14453. dc*(Icccn - Icccc);
  14454. }
  14455. //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate.
  14456. /**
  14457. Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
  14458. or a specified default value in case of out-of-bounds access along the X-axis.
  14459. The cubic interpolation uses Hermite splines.
  14460. \param fx d X-coordinate of the pixel value (float-valued).
  14461. \param y Y-coordinate of the pixel value.
  14462. \param z Z-coordinate of the pixel value.
  14463. \param c C-coordinate of the pixel value.
  14464. \param out_value Default value returned if \c (\c fx,\c y,\c z,\c c) is outside image bounds.
  14465. \note
  14466. - Similar to linear_atX(float,int,int,int,const T) const, except that the returned pixel value is
  14467. approximated by a \e cubic interpolation along the X-axis.
  14468. - The type of the returned pixel value is extended to \c float, if the pixel type \c T is not float-valued.
  14469. \warning
  14470. - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
  14471. **/
  14472. Tfloat cubic_atX(const float fx, const int y, const int z, const int c, const T& out_value) const {
  14473. const int
  14474. x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2;
  14475. const float
  14476. dx = fx - x;
  14477. const Tfloat
  14478. Ip = (Tfloat)atX(px,y,z,c,out_value), Ic = (Tfloat)atX(x,y,z,c,out_value),
  14479. In = (Tfloat)atX(nx,y,z,c,out_value), Ia = (Tfloat)atX(ax,y,z,c,out_value);
  14480. 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));
  14481. }
  14482. //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X-coordinate.
  14483. /**
  14484. Similar to cubic_atX(float,int,int,int,const T) const, except that the return value is clamped to stay in the
  14485. min/max range of the datatype \c T.
  14486. **/
  14487. T cubic_atX_c(const float fx, const int y, const int z, const int c, const T& out_value) const {
  14488. return cimg::type<T>::cut(cubic_atX(fx,y,z,c,out_value));
  14489. }
  14490. //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate.
  14491. /**
  14492. Return a cubicly-interpolated pixel value of the image instance located at (\c fx,\c y,\c z,\c c),
  14493. or the value of the nearest pixel location in the image instance in case of out-of-bounds access
  14494. along the X-axis. The cubic interpolation uses Hermite splines.
  14495. \param fx X-coordinate of the pixel value (float-valued).
  14496. \param y Y-coordinate of the pixel value.
  14497. \param z Z-coordinate of the pixel value.
  14498. \param c C-coordinate of the pixel value.
  14499. \note
  14500. - Similar to cubic_atX(float,int,int,int,const T) const, except that the returned pixel value is
  14501. approximated by a cubic interpolation along the X-axis.
  14502. - If you know your image instance is \e not empty, you may rather use the slightly faster method
  14503. \c _cubic_atX(float,int,int,int).
  14504. \warning
  14505. - There is \e no boundary checking performed for the Y,Z and C-coordinates, so they must be inside image bounds.
  14506. **/
  14507. Tfloat cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
  14508. if (is_empty())
  14509. throw CImgInstanceException(_cimg_instance
  14510. "cubic_atX(): Empty instance.",
  14511. cimg_instance);
  14512. return _cubic_atX(fx,y,z,c);
  14513. }
  14514. Tfloat _cubic_atX(const float fx, const int y=0, const int z=0, const int c=0) const {
  14515. const float
  14516. nfx = cimg::type<float>::is_nan(fx)?0:cimg::cut(fx,0,width() - 1);
  14517. const int
  14518. x = (int)nfx;
  14519. const float
  14520. dx = nfx - x;
  14521. const int
  14522. px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2;
  14523. const Tfloat
  14524. Ip = (Tfloat)(*this)(px,y,z,c), Ic = (Tfloat)(*this)(x,y,z,c),
  14525. In = (Tfloat)(*this)(nx,y,z,c), Ia = (Tfloat)(*this)(ax,y,z,c);
  14526. 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));
  14527. }
  14528. //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the X-coordinate.
  14529. /**
  14530. Similar to cubic_atX(float,int,int,int) const, except that the return value is clamped to stay in the
  14531. min/max range of the datatype \c T.
  14532. **/
  14533. T cubic_atX_c(const float fx, const int y, const int z, const int c) const {
  14534. return cimg::type<T>::cut(cubic_atX(fx,y,z,c));
  14535. }
  14536. T _cubic_atX_c(const float fx, const int y, const int z, const int c) const {
  14537. return cimg::type<T>::cut(_cubic_atX(fx,y,z,c));
  14538. }
  14539. //! Return pixel value, using cubic interpolation and periodic boundary conditions for the X-coordinate.
  14540. Tfloat cubic_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const {
  14541. if (is_empty())
  14542. throw CImgInstanceException(_cimg_instance
  14543. "cubic_atX_p(): Empty instance.",
  14544. cimg_instance);
  14545. return _cubic_atX_p(fx,y,z,c);
  14546. }
  14547. Tfloat _cubic_atX_p(const float fx, const int y=0, const int z=0, const int c=0) const {
  14548. const float
  14549. nfx = cimg::type<float>::is_nan(fx)?0:cimg::mod(fx,_width - 0.5f);
  14550. const int
  14551. x = (int)nfx;
  14552. const float
  14553. dx = nfx - x;
  14554. const int
  14555. px = cimg::mod(x - 1,width()), nx = cimg::mod(x + 1,width()), ax = cimg::mod(x + 2,width());
  14556. const Tfloat
  14557. Ip = (Tfloat)(*this)(px,y,z,c), Ic = (Tfloat)(*this)(x,y,z,c),
  14558. In = (Tfloat)(*this)(nx,y,z,c), Ia = (Tfloat)(*this)(ax,y,z,c);
  14559. 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));
  14560. }
  14561. T cubic_atX_pc(const float fx, const int y, const int z, const int c) const {
  14562. return cimg::type<T>::cut(cubic_atX_p(fx,y,z,c));
  14563. }
  14564. T _cubic_atX_pc(const float fx, const int y, const int z, const int c) const {
  14565. return cimg::type<T>::cut(_cubic_atX_p(fx,y,z,c));
  14566. }
  14567. //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X and Y-coordinates.
  14568. /**
  14569. Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking
  14570. are achieved both for X and Y-coordinates.
  14571. **/
  14572. Tfloat cubic_atXY(const float fx, const float fy, const int z, const int c, const T& out_value) const {
  14573. const int
  14574. x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2,
  14575. y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2;
  14576. const float dx = fx - x, dy = fy - y;
  14577. const Tfloat
  14578. Ipp = (Tfloat)atXY(px,py,z,c,out_value), Icp = (Tfloat)atXY(x,py,z,c,out_value),
  14579. Inp = (Tfloat)atXY(nx,py,z,c,out_value), Iap = (Tfloat)atXY(ax,py,z,c,out_value),
  14580. 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)),
  14581. Ipc = (Tfloat)atXY(px,y,z,c,out_value), Icc = (Tfloat)atXY(x, y,z,c,out_value),
  14582. Inc = (Tfloat)atXY(nx,y,z,c,out_value), Iac = (Tfloat)atXY(ax,y,z,c,out_value),
  14583. 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)),
  14584. Ipn = (Tfloat)atXY(px,ny,z,c,out_value), Icn = (Tfloat)atXY(x,ny,z,c,out_value),
  14585. Inn = (Tfloat)atXY(nx,ny,z,c,out_value), Ian = (Tfloat)atXY(ax,ny,z,c,out_value),
  14586. 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)),
  14587. Ipa = (Tfloat)atXY(px,ay,z,c,out_value), Ica = (Tfloat)atXY(x,ay,z,c,out_value),
  14588. Ina = (Tfloat)atXY(nx,ay,z,c,out_value), Iaa = (Tfloat)atXY(ax,ay,z,c,out_value),
  14589. 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));
  14590. 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));
  14591. }
  14592. //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y-coordinates.
  14593. /**
  14594. Similar to cubic_atXY(float,float,int,int,const T) const, except that the return value is clamped to stay in the
  14595. min/max range of the datatype \c T.
  14596. **/
  14597. T cubic_atXY_c(const float fx, const float fy, const int z, const int c, const T& out_value) const {
  14598. return cimg::type<T>::cut(cubic_atXY(fx,fy,z,c,out_value));
  14599. }
  14600. //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X and Y-coordinates.
  14601. /**
  14602. Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking
  14603. are achieved for both X and Y-coordinates.
  14604. \note
  14605. - If you know your image instance is \e not empty, you may rather use the slightly faster method
  14606. \c _cubic_atXY(float,float,int,int).
  14607. **/
  14608. Tfloat cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
  14609. if (is_empty())
  14610. throw CImgInstanceException(_cimg_instance
  14611. "cubic_atXY(): Empty instance.",
  14612. cimg_instance);
  14613. return _cubic_atXY(fx,fy,z,c);
  14614. }
  14615. Tfloat _cubic_atXY(const float fx, const float fy, const int z=0, const int c=0) const {
  14616. const float
  14617. nfx = cimg::type<float>::is_nan(fx)?0:cimg::cut(fx,0,width() - 1),
  14618. nfy = cimg::type<float>::is_nan(fy)?0:cimg::cut(fy,0,height() - 1);
  14619. const int x = (int)nfx, y = (int)nfy;
  14620. const float dx = nfx - x, dy = nfy - y;
  14621. const int
  14622. px = x - 1<0?0:x - 1, nx = dx<=0?x:x + 1, ax = x + 2>=width()?width() - 1:x + 2,
  14623. py = y - 1<0?0:y - 1, ny = dy<=0?y:y + 1, ay = y + 2>=height()?height() - 1:y + 2;
  14624. const Tfloat
  14625. Ipp = (Tfloat)(*this)(px,py,z,c), Icp = (Tfloat)(*this)(x,py,z,c), Inp = (Tfloat)(*this)(nx,py,z,c),
  14626. Iap = (Tfloat)(*this)(ax,py,z,c),
  14627. 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)),
  14628. Ipc = (Tfloat)(*this)(px,y,z,c), Icc = (Tfloat)(*this)(x, y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c),
  14629. Iac = (Tfloat)(*this)(ax,y,z,c),
  14630. 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)),
  14631. Ipn = (Tfloat)(*this)(px,ny,z,c), Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c),
  14632. Ian = (Tfloat)(*this)(ax,ny,z,c),
  14633. 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)),
  14634. Ipa = (Tfloat)(*this)(px,ay,z,c), Ica = (Tfloat)(*this)(x,ay,z,c), Ina = (Tfloat)(*this)(nx,ay,z,c),
  14635. Iaa = (Tfloat)(*this)(ax,ay,z,c),
  14636. 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));
  14637. 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));
  14638. }
  14639. //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y-coordinates.
  14640. /**
  14641. Similar to cubic_atXY(float,float,int,int) const, except that the return value is clamped to stay in the
  14642. min/max range of the datatype \c T.
  14643. **/
  14644. T cubic_atXY_c(const float fx, const float fy, const int z, const int c) const {
  14645. return cimg::type<T>::cut(cubic_atXY(fx,fy,z,c));
  14646. }
  14647. T _cubic_atXY_c(const float fx, const float fy, const int z, const int c) const {
  14648. return cimg::type<T>::cut(_cubic_atXY(fx,fy,z,c));
  14649. }
  14650. //! Return pixel value, using cubic interpolation and periodic boundary conditions for the X and Y-coordinates.
  14651. Tfloat cubic_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const {
  14652. if (is_empty())
  14653. throw CImgInstanceException(_cimg_instance
  14654. "cubic_atXY_p(): Empty instance.",
  14655. cimg_instance);
  14656. return _cubic_atXY_p(fx,fy,z,c);
  14657. }
  14658. Tfloat _cubic_atXY_p(const float fx, const float fy, const int z=0, const int c=0) const {
  14659. const float
  14660. nfx = cimg::type<float>::is_nan(fx)?0:cimg::mod(fx,_width - 0.5f),
  14661. nfy = cimg::type<float>::is_nan(fy)?0:cimg::mod(fy,_height - 0.5f);
  14662. const int x = (int)nfx, y = (int)nfy;
  14663. const float dx = nfx - x, dy = nfy - y;
  14664. const int
  14665. px = cimg::mod(x - 1,width()), nx = cimg::mod(x + 1,width()), ax = cimg::mod(x + 2,width()),
  14666. py = cimg::mod(y - 1,height()), ny = cimg::mod(y + 1,height()), ay = cimg::mod(y + 2,height());
  14667. const Tfloat
  14668. Ipp = (Tfloat)(*this)(px,py,z,c), Icp = (Tfloat)(*this)(x,py,z,c), Inp = (Tfloat)(*this)(nx,py,z,c),
  14669. Iap = (Tfloat)(*this)(ax,py,z,c),
  14670. 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)),
  14671. Ipc = (Tfloat)(*this)(px,y,z,c), Icc = (Tfloat)(*this)(x, y,z,c), Inc = (Tfloat)(*this)(nx,y,z,c),
  14672. Iac = (Tfloat)(*this)(ax,y,z,c),
  14673. 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)),
  14674. Ipn = (Tfloat)(*this)(px,ny,z,c), Icn = (Tfloat)(*this)(x,ny,z,c), Inn = (Tfloat)(*this)(nx,ny,z,c),
  14675. Ian = (Tfloat)(*this)(ax,ny,z,c),
  14676. 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)),
  14677. Ipa = (Tfloat)(*this)(px,ay,z,c), Ica = (Tfloat)(*this)(x,ay,z,c), Ina = (Tfloat)(*this)(nx,ay,z,c),
  14678. Iaa = (Tfloat)(*this)(ax,ay,z,c),
  14679. 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));
  14680. 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));
  14681. }
  14682. T cubic_atXY_pc(const float fx, const float fy, const int z, const int c) const {
  14683. return cimg::type<T>::cut(cubic_atXY_p(fx,fy,z,c));
  14684. }
  14685. T _cubic_atXY_pc(const float fx, const float fy, const int z, const int c) const {
  14686. return cimg::type<T>::cut(_cubic_atXY_p(fx,fy,z,c));
  14687. }
  14688. //! Return pixel value, using cubic interpolation and Dirichlet boundary conditions for the X,Y and Z-coordinates.
  14689. /**
  14690. Similar to cubic_atX(float,int,int,int,const T) const, except that the cubic interpolation and boundary checking
  14691. are achieved both for X,Y and Z-coordinates.
  14692. **/
  14693. Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c, const T& out_value) const {
  14694. const int
  14695. x = (int)fx - (fx>=0?0:1), px = x - 1, nx = x + 1, ax = x + 2,
  14696. y = (int)fy - (fy>=0?0:1), py = y - 1, ny = y + 1, ay = y + 2,
  14697. z = (int)fz - (fz>=0?0:1), pz = z - 1, nz = z + 1, az = z + 2;
  14698. const float dx = fx - x, dy = fy - y, dz = fz - z;
  14699. const Tfloat
  14700. Ippp = (Tfloat)atXYZ(px,py,pz,c,out_value), Icpp = (Tfloat)atXYZ(x,py,pz,c,out_value),
  14701. Inpp = (Tfloat)atXYZ(nx,py,pz,c,out_value), Iapp = (Tfloat)atXYZ(ax,py,pz,c,out_value),
  14702. Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) +
  14703. dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)),
  14704. Ipcp = (Tfloat)atXYZ(px,y,pz,c,out_value), Iccp = (Tfloat)atXYZ(x, y,pz,c,out_value),
  14705. Incp = (Tfloat)atXYZ(nx,y,pz,c,out_value), Iacp = (Tfloat)atXYZ(ax,y,pz,c,out_value),
  14706. Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) +
  14707. dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)),
  14708. Ipnp = (Tfloat)atXYZ(px,ny,pz,c,out_value), Icnp = (Tfloat)atXYZ(x,ny,pz,c,out_value),
  14709. Innp = (Tfloat)atXYZ(nx,ny,pz,c,out_value), Ianp = (Tfloat)atXYZ(ax,ny,pz,c,out_value),
  14710. Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) +
  14711. dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)),
  14712. Ipap = (Tfloat)atXYZ(px,ay,pz,c,out_value), Icap = (Tfloat)atXYZ(x,ay,pz,c,out_value),
  14713. Inap = (Tfloat)atXYZ(nx,ay,pz,c,out_value), Iaap = (Tfloat)atXYZ(ax,ay,pz,c,out_value),
  14714. Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) +
  14715. dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)),
  14716. Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) +
  14717. dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)),
  14718. Ippc = (Tfloat)atXYZ(px,py,z,c,out_value), Icpc = (Tfloat)atXYZ(x,py,z,c,out_value),
  14719. Inpc = (Tfloat)atXYZ(nx,py,z,c,out_value), Iapc = (Tfloat)atXYZ(ax,py,z,c,out_value),
  14720. Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) +
  14721. dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)),
  14722. Ipcc = (Tfloat)atXYZ(px,y,z,c,out_value), Iccc = (Tfloat)atXYZ(x, y,z,c,out_value),
  14723. Incc = (Tfloat)atXYZ(nx,y,z,c,out_value), Iacc = (Tfloat)atXYZ(ax,y,z,c,out_value),
  14724. Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) +
  14725. dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)),
  14726. Ipnc = (Tfloat)atXYZ(px,ny,z,c,out_value), Icnc = (Tfloat)atXYZ(x,ny,z,c,out_value),
  14727. Innc = (Tfloat)atXYZ(nx,ny,z,c,out_value), Ianc = (Tfloat)atXYZ(ax,ny,z,c,out_value),
  14728. Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) +
  14729. dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)),
  14730. Ipac = (Tfloat)atXYZ(px,ay,z,c,out_value), Icac = (Tfloat)atXYZ(x,ay,z,c,out_value),
  14731. Inac = (Tfloat)atXYZ(nx,ay,z,c,out_value), Iaac = (Tfloat)atXYZ(ax,ay,z,c,out_value),
  14732. Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) +
  14733. dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)),
  14734. Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) +
  14735. dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)),
  14736. Ippn = (Tfloat)atXYZ(px,py,nz,c,out_value), Icpn = (Tfloat)atXYZ(x,py,nz,c,out_value),
  14737. Inpn = (Tfloat)atXYZ(nx,py,nz,c,out_value), Iapn = (Tfloat)atXYZ(ax,py,nz,c,out_value),
  14738. Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) +
  14739. dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)),
  14740. Ipcn = (Tfloat)atXYZ(px,y,nz,c,out_value), Iccn = (Tfloat)atXYZ(x, y,nz,c,out_value),
  14741. Incn = (Tfloat)atXYZ(nx,y,nz,c,out_value), Iacn = (Tfloat)atXYZ(ax,y,nz,c,out_value),
  14742. Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) +
  14743. dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)),
  14744. Ipnn = (Tfloat)atXYZ(px,ny,nz,c,out_value), Icnn = (Tfloat)atXYZ(x,ny,nz,c,out_value),
  14745. Innn = (Tfloat)atXYZ(nx,ny,nz,c,out_value), Iann = (Tfloat)atXYZ(ax,ny,nz,c,out_value),
  14746. Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) +
  14747. dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)),
  14748. Ipan = (Tfloat)atXYZ(px,ay,nz,c,out_value), Ican = (Tfloat)atXYZ(x,ay,nz,c,out_value),
  14749. Inan = (Tfloat)atXYZ(nx,ay,nz,c,out_value), Iaan = (Tfloat)atXYZ(ax,ay,nz,c,out_value),
  14750. Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) +
  14751. dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)),
  14752. In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) +
  14753. dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)),
  14754. Ippa = (Tfloat)atXYZ(px,py,az,c,out_value), Icpa = (Tfloat)atXYZ(x,py,az,c,out_value),
  14755. Inpa = (Tfloat)atXYZ(nx,py,az,c,out_value), Iapa = (Tfloat)atXYZ(ax,py,az,c,out_value),
  14756. Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) +
  14757. dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)),
  14758. Ipca = (Tfloat)atXYZ(px,y,az,c,out_value), Icca = (Tfloat)atXYZ(x, y,az,c,out_value),
  14759. Inca = (Tfloat)atXYZ(nx,y,az,c,out_value), Iaca = (Tfloat)atXYZ(ax,y,az,c,out_value),
  14760. Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) +
  14761. dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)),
  14762. Ipna = (Tfloat)atXYZ(px,ny,az,c,out_value), Icna = (Tfloat)atXYZ(x,ny,az,c,out_value),
  14763. Inna = (Tfloat)atXYZ(nx,ny,az,c,out_value), Iana = (Tfloat)atXYZ(ax,ny,az,c,out_value),
  14764. Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) +
  14765. dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)),
  14766. Ipaa = (Tfloat)atXYZ(px,ay,az,c,out_value), Icaa = (Tfloat)atXYZ(x,ay,az,c,out_value),
  14767. Inaa = (Tfloat)atXYZ(nx,ay,az,c,out_value), Iaaa = (Tfloat)atXYZ(ax,ay,az,c,out_value),
  14768. Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) +
  14769. dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)),
  14770. Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) +
  14771. dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa));
  14772. 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));
  14773. }
  14774. //! Return clamped pixel value, using cubic interpolation and Dirichlet boundary conditions for the XYZ-coordinates.
  14775. /**
  14776. Similar to cubic_atXYZ(float,float,float,int,const T) const, except that the return value is clamped to stay
  14777. in the min/max range of the datatype \c T.
  14778. **/
  14779. T cubic_atXYZ_c(const float fx, const float fy, const float fz, const int c, const T& out_value) const {
  14780. return cimg::type<T>::cut(cubic_atXYZ(fx,fy,fz,c,out_value));
  14781. }
  14782. //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates.
  14783. /**
  14784. Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking
  14785. are achieved both for X,Y and Z-coordinates.
  14786. \note
  14787. - If you know your image instance is \e not empty, you may rather use the slightly faster method
  14788. \c _cubic_atXYZ(float,float,float,int).
  14789. **/
  14790. Tfloat cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const {
  14791. if (is_empty())
  14792. throw CImgInstanceException(_cimg_instance
  14793. "cubic_atXYZ(): Empty instance.",
  14794. cimg_instance);
  14795. return _cubic_atXYZ(fx,fy,fz,c);
  14796. }
  14797. Tfloat _cubic_atXYZ(const float fx, const float fy, const float fz, const int c=0) const {
  14798. const float
  14799. nfx = cimg::type<float>::is_nan(fx)?0:cimg::cut(fx,0,width() - 1),
  14800. nfy = cimg::type<float>::is_nan(fy)?0:cimg::cut(fy,0,height() - 1),
  14801. nfz = cimg::type<float>::is_nan(fz)?0:cimg::cut(fz,0,depth() - 1);
  14802. const int x = (int)nfx, y = (int)nfy, z = (int)nfz;
  14803. const float dx = nfx - x, dy = nfy - y, dz = nfz - z;
  14804. const int
  14805. px = x - 1<0?0:x - 1, nx = dx>0?x + 1:x, ax = x + 2>=width()?width() - 1:x + 2,
  14806. py = y - 1<0?0:y - 1, ny = dy>0?y + 1:y, ay = y + 2>=height()?height() - 1:y + 2,
  14807. pz = z - 1<0?0:z - 1, nz = dz>0?z + 1:z, az = z + 2>=depth()?depth() - 1:z + 2;
  14808. const Tfloat
  14809. Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c),
  14810. Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c),
  14811. Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) +
  14812. dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)),
  14813. Ipcp = (Tfloat)(*this)(px,y,pz,c), Iccp = (Tfloat)(*this)(x, y,pz,c),
  14814. Incp = (Tfloat)(*this)(nx,y,pz,c), Iacp = (Tfloat)(*this)(ax,y,pz,c),
  14815. Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) +
  14816. dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)),
  14817. Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c),
  14818. Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c),
  14819. Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) +
  14820. dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)),
  14821. Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c),
  14822. Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c),
  14823. Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) +
  14824. dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)),
  14825. Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) +
  14826. dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)),
  14827. Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c),
  14828. Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c),
  14829. Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) +
  14830. dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)),
  14831. Ipcc = (Tfloat)(*this)(px,y,z,c), Iccc = (Tfloat)(*this)(x, y,z,c),
  14832. Incc = (Tfloat)(*this)(nx,y,z,c), Iacc = (Tfloat)(*this)(ax,y,z,c),
  14833. Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) +
  14834. dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)),
  14835. Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c),
  14836. Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c),
  14837. Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) +
  14838. dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)),
  14839. Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c),
  14840. Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c),
  14841. Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) +
  14842. dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)),
  14843. Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) +
  14844. dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)),
  14845. Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c),
  14846. Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c),
  14847. Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) +
  14848. dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)),
  14849. Ipcn = (Tfloat)(*this)(px,y,nz,c), Iccn = (Tfloat)(*this)(x, y,nz,c),
  14850. Incn = (Tfloat)(*this)(nx,y,nz,c), Iacn = (Tfloat)(*this)(ax,y,nz,c),
  14851. Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) +
  14852. dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)),
  14853. Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c),
  14854. Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c),
  14855. Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) +
  14856. dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)),
  14857. Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c),
  14858. Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c),
  14859. Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) +
  14860. dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)),
  14861. In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) +
  14862. dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)),
  14863. Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c),
  14864. Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c),
  14865. Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) +
  14866. dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)),
  14867. Ipca = (Tfloat)(*this)(px,y,az,c), Icca = (Tfloat)(*this)(x, y,az,c),
  14868. Inca = (Tfloat)(*this)(nx,y,az,c), Iaca = (Tfloat)(*this)(ax,y,az,c),
  14869. Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) +
  14870. dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)),
  14871. Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c),
  14872. Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c),
  14873. Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) +
  14874. dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)),
  14875. Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c),
  14876. Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c),
  14877. Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) +
  14878. dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)),
  14879. Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) +
  14880. dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa));
  14881. 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));
  14882. }
  14883. //! Return clamped pixel value, using cubic interpolation and Neumann boundary conditions for the XYZ-coordinates.
  14884. /**
  14885. Similar to cubic_atXYZ(float,float,float,int) const, except that the return value is clamped to stay in the
  14886. min/max range of the datatype \c T.
  14887. **/
  14888. T cubic_atXYZ_c(const float fx, const float fy, const float fz, const int c) const {
  14889. return cimg::type<T>::cut(cubic_atXYZ(fx,fy,fz,c));
  14890. }
  14891. T _cubic_atXYZ_c(const float fx, const float fy, const float fz, const int c) const {
  14892. return cimg::type<T>::cut(_cubic_atXYZ(fx,fy,fz,c));
  14893. }
  14894. //! Return pixel value, using cubic interpolation and Neumann boundary conditions for the X,Y and Z-coordinates.
  14895. /**
  14896. Similar to cubic_atX(float,int,int,int) const, except that the cubic interpolation and boundary checking
  14897. are achieved both for X,Y and Z-coordinates.
  14898. \note
  14899. - If you know your image instance is \e not empty, you may rather use the slightly faster method
  14900. \c _cubic_atXYZ(float,float,float,int).
  14901. **/
  14902. Tfloat cubic_atXYZ_p(const float fx, const float fy, const float fz, const int c=0) const {
  14903. if (is_empty())
  14904. throw CImgInstanceException(_cimg_instance
  14905. "cubic_atXYZ_p(): Empty instance.",
  14906. cimg_instance);
  14907. return _cubic_atXYZ_p(fx,fy,fz,c);
  14908. }
  14909. Tfloat _cubic_atXYZ_p(const float fx, const float fy, const float fz, const int c=0) const {
  14910. const float
  14911. nfx = cimg::type<float>::is_nan(fx)?0:cimg::mod(fx,_width - 0.5f),
  14912. nfy = cimg::type<float>::is_nan(fy)?0:cimg::mod(fy,_height - 0.5f),
  14913. nfz = cimg::type<float>::is_nan(fz)?0:cimg::mod(fz,_depth - 0.5f);
  14914. const int x = (int)nfx, y = (int)nfy, z = (int)nfz;
  14915. const float dx = nfx - x, dy = nfy - y, dz = nfz - z;
  14916. const int
  14917. px = cimg::mod(x - 1,width()), nx = cimg::mod(x + 1,width()), ax = cimg::mod(x + 2,width()),
  14918. py = cimg::mod(y - 1,height()), ny = cimg::mod(y + 1,height()), ay = cimg::mod(y + 2,height()),
  14919. pz = cimg::mod(z - 1,depth()), nz = cimg::mod(z + 1,depth()), az = cimg::mod(z + 2,depth());
  14920. const Tfloat
  14921. Ippp = (Tfloat)(*this)(px,py,pz,c), Icpp = (Tfloat)(*this)(x,py,pz,c),
  14922. Inpp = (Tfloat)(*this)(nx,py,pz,c), Iapp = (Tfloat)(*this)(ax,py,pz,c),
  14923. Ipp = Icpp + 0.5f*(dx*(-Ippp + Inpp) + dx*dx*(2*Ippp - 5*Icpp + 4*Inpp - Iapp) +
  14924. dx*dx*dx*(-Ippp + 3*Icpp - 3*Inpp + Iapp)),
  14925. Ipcp = (Tfloat)(*this)(px,y,pz,c), Iccp = (Tfloat)(*this)(x, y,pz,c),
  14926. Incp = (Tfloat)(*this)(nx,y,pz,c), Iacp = (Tfloat)(*this)(ax,y,pz,c),
  14927. Icp = Iccp + 0.5f*(dx*(-Ipcp + Incp) + dx*dx*(2*Ipcp - 5*Iccp + 4*Incp - Iacp) +
  14928. dx*dx*dx*(-Ipcp + 3*Iccp - 3*Incp + Iacp)),
  14929. Ipnp = (Tfloat)(*this)(px,ny,pz,c), Icnp = (Tfloat)(*this)(x,ny,pz,c),
  14930. Innp = (Tfloat)(*this)(nx,ny,pz,c), Ianp = (Tfloat)(*this)(ax,ny,pz,c),
  14931. Inp = Icnp + 0.5f*(dx*(-Ipnp + Innp) + dx*dx*(2*Ipnp - 5*Icnp + 4*Innp - Ianp) +
  14932. dx*dx*dx*(-Ipnp + 3*Icnp - 3*Innp + Ianp)),
  14933. Ipap = (Tfloat)(*this)(px,ay,pz,c), Icap = (Tfloat)(*this)(x,ay,pz,c),
  14934. Inap = (Tfloat)(*this)(nx,ay,pz,c), Iaap = (Tfloat)(*this)(ax,ay,pz,c),
  14935. Iap = Icap + 0.5f*(dx*(-Ipap + Inap) + dx*dx*(2*Ipap - 5*Icap + 4*Inap - Iaap) +
  14936. dx*dx*dx*(-Ipap + 3*Icap - 3*Inap + Iaap)),
  14937. Ip = Icp + 0.5f*(dy*(-Ipp + Inp) + dy*dy*(2*Ipp - 5*Icp + 4*Inp - Iap) +
  14938. dy*dy*dy*(-Ipp + 3*Icp - 3*Inp + Iap)),
  14939. Ippc = (Tfloat)(*this)(px,py,z,c), Icpc = (Tfloat)(*this)(x,py,z,c),
  14940. Inpc = (Tfloat)(*this)(nx,py,z,c), Iapc = (Tfloat)(*this)(ax,py,z,c),
  14941. Ipc = Icpc + 0.5f*(dx*(-Ippc + Inpc) + dx*dx*(2*Ippc - 5*Icpc + 4*Inpc - Iapc) +
  14942. dx*dx*dx*(-Ippc + 3*Icpc - 3*Inpc + Iapc)),
  14943. Ipcc = (Tfloat)(*this)(px,y,z,c), Iccc = (Tfloat)(*this)(x, y,z,c),
  14944. Incc = (Tfloat)(*this)(nx,y,z,c), Iacc = (Tfloat)(*this)(ax,y,z,c),
  14945. Icc = Iccc + 0.5f*(dx*(-Ipcc + Incc) + dx*dx*(2*Ipcc - 5*Iccc + 4*Incc - Iacc) +
  14946. dx*dx*dx*(-Ipcc + 3*Iccc - 3*Incc + Iacc)),
  14947. Ipnc = (Tfloat)(*this)(px,ny,z,c), Icnc = (Tfloat)(*this)(x,ny,z,c),
  14948. Innc = (Tfloat)(*this)(nx,ny,z,c), Ianc = (Tfloat)(*this)(ax,ny,z,c),
  14949. Inc = Icnc + 0.5f*(dx*(-Ipnc + Innc) + dx*dx*(2*Ipnc - 5*Icnc + 4*Innc - Ianc) +
  14950. dx*dx*dx*(-Ipnc + 3*Icnc - 3*Innc + Ianc)),
  14951. Ipac = (Tfloat)(*this)(px,ay,z,c), Icac = (Tfloat)(*this)(x,ay,z,c),
  14952. Inac = (Tfloat)(*this)(nx,ay,z,c), Iaac = (Tfloat)(*this)(ax,ay,z,c),
  14953. Iac = Icac + 0.5f*(dx*(-Ipac + Inac) + dx*dx*(2*Ipac - 5*Icac + 4*Inac - Iaac) +
  14954. dx*dx*dx*(-Ipac + 3*Icac - 3*Inac + Iaac)),
  14955. Ic = Icc + 0.5f*(dy*(-Ipc + Inc) + dy*dy*(2*Ipc - 5*Icc + 4*Inc - Iac) +
  14956. dy*dy*dy*(-Ipc + 3*Icc - 3*Inc + Iac)),
  14957. Ippn = (Tfloat)(*this)(px,py,nz,c), Icpn = (Tfloat)(*this)(x,py,nz,c),
  14958. Inpn = (Tfloat)(*this)(nx,py,nz,c), Iapn = (Tfloat)(*this)(ax,py,nz,c),
  14959. Ipn = Icpn + 0.5f*(dx*(-Ippn + Inpn) + dx*dx*(2*Ippn - 5*Icpn + 4*Inpn - Iapn) +
  14960. dx*dx*dx*(-Ippn + 3*Icpn - 3*Inpn + Iapn)),
  14961. Ipcn = (Tfloat)(*this)(px,y,nz,c), Iccn = (Tfloat)(*this)(x, y,nz,c),
  14962. Incn = (Tfloat)(*this)(nx,y,nz,c), Iacn = (Tfloat)(*this)(ax,y,nz,c),
  14963. Icn = Iccn + 0.5f*(dx*(-Ipcn + Incn) + dx*dx*(2*Ipcn - 5*Iccn + 4*Incn - Iacn) +
  14964. dx*dx*dx*(-Ipcn + 3*Iccn - 3*Incn + Iacn)),
  14965. Ipnn = (Tfloat)(*this)(px,ny,nz,c), Icnn = (Tfloat)(*this)(x,ny,nz,c),
  14966. Innn = (Tfloat)(*this)(nx,ny,nz,c), Iann = (Tfloat)(*this)(ax,ny,nz,c),
  14967. Inn = Icnn + 0.5f*(dx*(-Ipnn + Innn) + dx*dx*(2*Ipnn - 5*Icnn + 4*Innn - Iann) +
  14968. dx*dx*dx*(-Ipnn + 3*Icnn - 3*Innn + Iann)),
  14969. Ipan = (Tfloat)(*this)(px,ay,nz,c), Ican = (Tfloat)(*this)(x,ay,nz,c),
  14970. Inan = (Tfloat)(*this)(nx,ay,nz,c), Iaan = (Tfloat)(*this)(ax,ay,nz,c),
  14971. Ian = Ican + 0.5f*(dx*(-Ipan + Inan) + dx*dx*(2*Ipan - 5*Ican + 4*Inan - Iaan) +
  14972. dx*dx*dx*(-Ipan + 3*Ican - 3*Inan + Iaan)),
  14973. In = Icn + 0.5f*(dy*(-Ipn + Inn) + dy*dy*(2*Ipn - 5*Icn + 4*Inn - Ian) +
  14974. dy*dy*dy*(-Ipn + 3*Icn - 3*Inn + Ian)),
  14975. Ippa = (Tfloat)(*this)(px,py,az,c), Icpa = (Tfloat)(*this)(x,py,az,c),
  14976. Inpa = (Tfloat)(*this)(nx,py,az,c), Iapa = (Tfloat)(*this)(ax,py,az,c),
  14977. Ipa = Icpa + 0.5f*(dx*(-Ippa + Inpa) + dx*dx*(2*Ippa - 5*Icpa + 4*Inpa - Iapa) +
  14978. dx*dx*dx*(-Ippa + 3*Icpa - 3*Inpa + Iapa)),
  14979. Ipca = (Tfloat)(*this)(px,y,az,c), Icca = (Tfloat)(*this)(x, y,az,c),
  14980. Inca = (Tfloat)(*this)(nx,y,az,c), Iaca = (Tfloat)(*this)(ax,y,az,c),
  14981. Ica = Icca + 0.5f*(dx*(-Ipca + Inca) + dx*dx*(2*Ipca - 5*Icca + 4*Inca - Iaca) +
  14982. dx*dx*dx*(-Ipca + 3*Icca - 3*Inca + Iaca)),
  14983. Ipna = (Tfloat)(*this)(px,ny,az,c), Icna = (Tfloat)(*this)(x,ny,az,c),
  14984. Inna = (Tfloat)(*this)(nx,ny,az,c), Iana = (Tfloat)(*this)(ax,ny,az,c),
  14985. Ina = Icna + 0.5f*(dx*(-Ipna + Inna) + dx*dx*(2*Ipna - 5*Icna + 4*Inna - Iana) +
  14986. dx*dx*dx*(-Ipna + 3*Icna - 3*Inna + Iana)),
  14987. Ipaa = (Tfloat)(*this)(px,ay,az,c), Icaa = (Tfloat)(*this)(x,ay,az,c),
  14988. Inaa = (Tfloat)(*this)(nx,ay,az,c), Iaaa = (Tfloat)(*this)(ax,ay,az,c),
  14989. Iaa = Icaa + 0.5f*(dx*(-Ipaa + Inaa) + dx*dx*(2*Ipaa - 5*Icaa + 4*Inaa - Iaaa) +
  14990. dx*dx*dx*(-Ipaa + 3*Icaa - 3*Inaa + Iaaa)),
  14991. Ia = Ica + 0.5f*(dy*(-Ipa + Ina) + dy*dy*(2*Ipa - 5*Ica + 4*Ina - Iaa) +
  14992. dy*dy*dy*(-Ipa + 3*Ica - 3*Ina + Iaa));
  14993. 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));
  14994. }
  14995. T cubic_atXYZ_pc(const float fx, const float fy, const float fz, const int c) const {
  14996. return cimg::type<T>::cut(cubic_atXYZ_p(fx,fy,fz,c));
  14997. }
  14998. T _cubic_atXYZ_pc(const float fx, const float fy, const float fz, const int c) const {
  14999. return cimg::type<T>::cut(_cubic_atXYZ_p(fx,fy,fz,c));
  15000. }
  15001. //! Set pixel value, using linear interpolation for the X-coordinates.
  15002. /**
  15003. Set pixel value at specified coordinates (\c fx,\c y,\c z,\c c) in the image instance, in a way that
  15004. the value is spread amongst several neighbors if the pixel coordinates are float-valued.
  15005. \param value Pixel value to set.
  15006. \param fx X-coordinate of the pixel value (float-valued).
  15007. \param y Y-coordinate of the pixel value.
  15008. \param z Z-coordinate of the pixel value.
  15009. \param c C-coordinate of the pixel value.
  15010. \param is_added Tells if the pixel value is added to (\c true), or simply replace (\c false) the current image
  15011. pixel(s).
  15012. \return A reference to the current image instance.
  15013. \note
  15014. - Calling this method with out-of-bounds coordinates does nothing.
  15015. **/
  15016. CImg<T>& set_linear_atX(const T& value, const float fx, const int y=0, const int z=0, const int c=0,
  15017. const bool is_added=false) {
  15018. const int
  15019. x = (int)fx - (fx>=0?0:1), nx = x + 1;
  15020. const float
  15021. dx = fx - x;
  15022. if (y>=0 && y<height() && z>=0 && z<depth() && c>=0 && c<spectrum()) {
  15023. if (x>=0 && x<width()) {
  15024. const float w1 = 1 - dx, w2 = is_added?1:(1 - w1);
  15025. (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
  15026. }
  15027. if (nx>=0 && nx<width()) {
  15028. const float w1 = dx, w2 = is_added?1:(1 - w1);
  15029. (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
  15030. }
  15031. }
  15032. return *this;
  15033. }
  15034. //! Set pixel value, using linear interpolation for the X and Y-coordinates.
  15035. /**
  15036. Similar to set_linear_atX(const T&,float,int,int,int,bool), except that the linear interpolation
  15037. is achieved both for X and Y-coordinates.
  15038. **/
  15039. CImg<T>& set_linear_atXY(const T& value, const float fx, const float fy=0, const int z=0, const int c=0,
  15040. const bool is_added=false) {
  15041. const int
  15042. x = (int)fx - (fx>=0?0:1), nx = x + 1,
  15043. y = (int)fy - (fy>=0?0:1), ny = y + 1;
  15044. const float
  15045. dx = fx - x,
  15046. dy = fy - y;
  15047. if (z>=0 && z<depth() && c>=0 && c<spectrum()) {
  15048. if (y>=0 && y<height()) {
  15049. if (x>=0 && x<width()) {
  15050. const float w1 = (1 - dx)*(1 - dy), w2 = is_added?1:(1 - w1);
  15051. (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
  15052. }
  15053. if (nx>=0 && nx<width()) {
  15054. const float w1 = dx*(1 - dy), w2 = is_added?1:(1 - w1);
  15055. (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
  15056. }
  15057. }
  15058. if (ny>=0 && ny<height()) {
  15059. if (x>=0 && x<width()) {
  15060. const float w1 = (1 - dx)*dy, w2 = is_added?1:(1 - w1);
  15061. (*this)(x,ny,z,c) = (T)(w1*value + w2*(*this)(x,ny,z,c));
  15062. }
  15063. if (nx>=0 && nx<width()) {
  15064. const float w1 = dx*dy, w2 = is_added?1:(1 - w1);
  15065. (*this)(nx,ny,z,c) = (T)(w1*value + w2*(*this)(nx,ny,z,c));
  15066. }
  15067. }
  15068. }
  15069. return *this;
  15070. }
  15071. //! Set pixel value, using linear interpolation for the X,Y and Z-coordinates.
  15072. /**
  15073. Similar to set_linear_atXY(const T&,float,float,int,int,bool), except that the linear interpolation
  15074. is achieved both for X,Y and Z-coordinates.
  15075. **/
  15076. CImg<T>& set_linear_atXYZ(const T& value, const float fx, const float fy=0, const float fz=0, const int c=0,
  15077. const bool is_added=false) {
  15078. const int
  15079. x = (int)fx - (fx>=0?0:1), nx = x + 1,
  15080. y = (int)fy - (fy>=0?0:1), ny = y + 1,
  15081. z = (int)fz - (fz>=0?0:1), nz = z + 1;
  15082. const float
  15083. dx = fx - x,
  15084. dy = fy - y,
  15085. dz = fz - z;
  15086. if (c>=0 && c<spectrum()) {
  15087. if (z>=0 && z<depth()) {
  15088. if (y>=0 && y<height()) {
  15089. if (x>=0 && x<width()) {
  15090. const float w1 = (1 - dx)*(1 - dy)*(1 - dz), w2 = is_added?1:(1 - w1);
  15091. (*this)(x,y,z,c) = (T)(w1*value + w2*(*this)(x,y,z,c));
  15092. }
  15093. if (nx>=0 && nx<width()) {
  15094. const float w1 = dx*(1 - dy)*(1 - dz), w2 = is_added?1:(1 - w1);
  15095. (*this)(nx,y,z,c) = (T)(w1*value + w2*(*this)(nx,y,z,c));
  15096. }
  15097. }
  15098. if (ny>=0 && ny<height()) {
  15099. if (x>=0 && x<width()) {
  15100. const float w1 = (1 - dx)*dy*(1 - dz), w2 = is_added?1:(1 - w1);
  15101. (*this)(x,ny,z,c) = (T)(w1*value + w2*(*this)(x,ny,z,c));
  15102. }
  15103. if (nx>=0 && nx<width()) {
  15104. const float w1 = dx*dy*(1 - dz), w2 = is_added?1:(1 - w1);
  15105. (*this)(nx,ny,z,c) = (T)(w1*value + w2*(*this)(nx,ny,z,c));
  15106. }
  15107. }
  15108. }
  15109. if (nz>=0 && nz<depth()) {
  15110. if (y>=0 && y<height()) {
  15111. if (x>=0 && x<width()) {
  15112. const float w1 = (1 - dx)*(1 - dy)*dz, w2 = is_added?1:(1 - w1);
  15113. (*this)(x,y,nz,c) = (T)(w1*value + w2*(*this)(x,y,nz,c));
  15114. }
  15115. if (nx>=0 && nx<width()) {
  15116. const float w1 = dx*(1 - dy)*dz, w2 = is_added?1:(1 - w1);
  15117. (*this)(nx,y,nz,c) = (T)(w1*value + w2*(*this)(nx,y,nz,c));
  15118. }
  15119. }
  15120. if (ny>=0 && ny<height()) {
  15121. if (x>=0 && x<width()) {
  15122. const float w1 = (1 - dx)*dy*dz, w2 = is_added?1:(1 - w1);
  15123. (*this)(x,ny,nz,c) = (T)(w1*value + w2*(*this)(x,ny,nz,c));
  15124. }
  15125. if (nx>=0 && nx<width()) {
  15126. const float w1 = dx*dy*dz, w2 = is_added?1:(1 - w1);
  15127. (*this)(nx,ny,nz,c) = (T)(w1*value + w2*(*this)(nx,ny,nz,c));
  15128. }
  15129. }
  15130. }
  15131. }
  15132. return *this;
  15133. }
  15134. //! Return a C-string containing a list of all values of the image instance.
  15135. /**
  15136. Return a new \c CImg<char> image whose buffer data() is a \c char* string describing the list of all pixel values
  15137. of the image instance (written in base 10), separated by specified \c separator character.
  15138. \param separator A \c char character which specifies the separator between values in the returned C-string.
  15139. \param max_size Maximum size of the returned image (or \c 0 if no limits are set).
  15140. \param format For float/double-values, tell the printf format used to generate the text representation
  15141. of the numbers (or \c 0 for default representation).
  15142. \note
  15143. - The returned image is never empty.
  15144. - For an empty image instance, the returned string is <tt>""</tt>.
  15145. - If \c max_size is equal to \c 0, there are no limits on the size of the returned string.
  15146. - Otherwise, if the maximum number of string characters is exceeded, the value string is cut off
  15147. and terminated by character \c '\0'. In that case, the returned image size is <tt>max_size + 1</tt>.
  15148. **/
  15149. CImg<charT> value_string(const char separator=',', const unsigned int max_size=0,
  15150. const char *const format=0) const {
  15151. if (is_empty() || max_size==1) return CImg<charT>(1,1,1,1,0);
  15152. CImgList<charT> items;
  15153. CImg<charT> s_item(256); *s_item = 0;
  15154. const T *ptrs = _data;
  15155. unsigned int string_size = 0;
  15156. const char *const _format = format?format:cimg::type<T>::format();
  15157. for (ulongT off = 0, siz = size(); off<siz && (!max_size || string_size<max_size); ++off) {
  15158. const unsigned int printed_size = 1U + cimg_snprintf(s_item,s_item._width,_format,
  15159. cimg::type<T>::format(*(ptrs++)));
  15160. CImg<charT> item(s_item._data,printed_size);
  15161. item[printed_size - 1] = separator;
  15162. item.move_to(items);
  15163. if (max_size) string_size+=printed_size;
  15164. }
  15165. CImg<charT> res;
  15166. (items>'x').move_to(res);
  15167. if (max_size && res._width>=max_size) res.crop(0,max_size - 1);
  15168. res.back() = 0;
  15169. return res;
  15170. }
  15171. //@}
  15172. //-------------------------------------
  15173. //
  15174. //! \name Instance Checking
  15175. //@{
  15176. //-------------------------------------
  15177. //! Test shared state of the pixel buffer.
  15178. /**
  15179. Return \c true if image instance has a shared memory buffer, and \c false otherwise.
  15180. \note
  15181. - A shared image do not own his pixel buffer data() and will not deallocate it on destruction.
  15182. - Most of the time, a \c CImg<T> image instance will \e not be shared.
  15183. - A shared image can only be obtained by a limited set of constructors and methods (see list below).
  15184. **/
  15185. bool is_shared() const {
  15186. return _is_shared;
  15187. }
  15188. //! Test if image instance is empty.
  15189. /**
  15190. Return \c true, if image instance is empty, i.e. does \e not contain any pixel values, has dimensions
  15191. \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.
  15192. **/
  15193. bool is_empty() const {
  15194. return !(_data && _width && _height && _depth && _spectrum);
  15195. }
  15196. //! Test if image instance contains a 'inf' value.
  15197. /**
  15198. Return \c true, if image instance contains a 'inf' value, and \c false otherwise.
  15199. **/
  15200. bool is_inf() const {
  15201. if (cimg::type<T>::is_float()) cimg_for(*this,p,T) if (cimg::type<T>::is_inf((float)*p)) return true;
  15202. return false;
  15203. }
  15204. //! Test if image instance contains a NaN value.
  15205. /**
  15206. Return \c true, if image instance contains a NaN value, and \c false otherwise.
  15207. **/
  15208. bool is_nan() const {
  15209. if (cimg::type<T>::is_float()) cimg_for(*this,p,T) if (cimg::type<T>::is_nan((float)*p)) return true;
  15210. return false;
  15211. }
  15212. //! Test if image width is equal to specified value.
  15213. bool is_sameX(const unsigned int size_x) const {
  15214. return _width==size_x;
  15215. }
  15216. //! Test if image width is equal to specified value.
  15217. template<typename t>
  15218. bool is_sameX(const CImg<t>& img) const {
  15219. return is_sameX(img._width);
  15220. }
  15221. //! Test if image width is equal to specified value.
  15222. bool is_sameX(const CImgDisplay& disp) const {
  15223. return is_sameX(disp._width);
  15224. }
  15225. //! Test if image height is equal to specified value.
  15226. bool is_sameY(const unsigned int size_y) const {
  15227. return _height==size_y;
  15228. }
  15229. //! Test if image height is equal to specified value.
  15230. template<typename t>
  15231. bool is_sameY(const CImg<t>& img) const {
  15232. return is_sameY(img._height);
  15233. }
  15234. //! Test if image height is equal to specified value.
  15235. bool is_sameY(const CImgDisplay& disp) const {
  15236. return is_sameY(disp._height);
  15237. }
  15238. //! Test if image depth is equal to specified value.
  15239. bool is_sameZ(const unsigned int size_z) const {
  15240. return _depth==size_z;
  15241. }
  15242. //! Test if image depth is equal to specified value.
  15243. template<typename t>
  15244. bool is_sameZ(const CImg<t>& img) const {
  15245. return is_sameZ(img._depth);
  15246. }
  15247. //! Test if image spectrum is equal to specified value.
  15248. bool is_sameC(const unsigned int size_c) const {
  15249. return _spectrum==size_c;
  15250. }
  15251. //! Test if image spectrum is equal to specified value.
  15252. template<typename t>
  15253. bool is_sameC(const CImg<t>& img) const {
  15254. return is_sameC(img._spectrum);
  15255. }
  15256. //! Test if image width and height are equal to specified values.
  15257. /**
  15258. Test if is_sameX(unsigned int) const and is_sameY(unsigned int) const are both verified.
  15259. **/
  15260. bool is_sameXY(const unsigned int size_x, const unsigned int size_y) const {
  15261. return _width==size_x && _height==size_y;
  15262. }
  15263. //! Test if image width and height are the same as that of another image.
  15264. /**
  15265. Test if is_sameX(const CImg<t>&) const and is_sameY(const CImg<t>&) const are both verified.
  15266. **/
  15267. template<typename t>
  15268. bool is_sameXY(const CImg<t>& img) const {
  15269. return is_sameXY(img._width,img._height);
  15270. }
  15271. //! Test if image width and height are the same as that of an existing display window.
  15272. /**
  15273. Test if is_sameX(const CImgDisplay&) const and is_sameY(const CImgDisplay&) const are both verified.
  15274. **/
  15275. bool is_sameXY(const CImgDisplay& disp) const {
  15276. return is_sameXY(disp._width,disp._height);
  15277. }
  15278. //! Test if image width and depth are equal to specified values.
  15279. /**
  15280. Test if is_sameX(unsigned int) const and is_sameZ(unsigned int) const are both verified.
  15281. **/
  15282. bool is_sameXZ(const unsigned int size_x, const unsigned int size_z) const {
  15283. return _width==size_x && _depth==size_z;
  15284. }
  15285. //! Test if image width and depth are the same as that of another image.
  15286. /**
  15287. Test if is_sameX(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
  15288. **/
  15289. template<typename t>
  15290. bool is_sameXZ(const CImg<t>& img) const {
  15291. return is_sameXZ(img._width,img._depth);
  15292. }
  15293. //! Test if image width and spectrum are equal to specified values.
  15294. /**
  15295. Test if is_sameX(unsigned int) const and is_sameC(unsigned int) const are both verified.
  15296. **/
  15297. bool is_sameXC(const unsigned int size_x, const unsigned int size_c) const {
  15298. return _width==size_x && _spectrum==size_c;
  15299. }
  15300. //! Test if image width and spectrum are the same as that of another image.
  15301. /**
  15302. Test if is_sameX(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
  15303. **/
  15304. template<typename t>
  15305. bool is_sameXC(const CImg<t>& img) const {
  15306. return is_sameXC(img._width,img._spectrum);
  15307. }
  15308. //! Test if image height and depth are equal to specified values.
  15309. /**
  15310. Test if is_sameY(unsigned int) const and is_sameZ(unsigned int) const are both verified.
  15311. **/
  15312. bool is_sameYZ(const unsigned int size_y, const unsigned int size_z) const {
  15313. return _height==size_y && _depth==size_z;
  15314. }
  15315. //! Test if image height and depth are the same as that of another image.
  15316. /**
  15317. Test if is_sameY(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
  15318. **/
  15319. template<typename t>
  15320. bool is_sameYZ(const CImg<t>& img) const {
  15321. return is_sameYZ(img._height,img._depth);
  15322. }
  15323. //! Test if image height and spectrum are equal to specified values.
  15324. /**
  15325. Test if is_sameY(unsigned int) const and is_sameC(unsigned int) const are both verified.
  15326. **/
  15327. bool is_sameYC(const unsigned int size_y, const unsigned int size_c) const {
  15328. return _height==size_y && _spectrum==size_c;
  15329. }
  15330. //! Test if image height and spectrum are the same as that of another image.
  15331. /**
  15332. Test if is_sameY(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
  15333. **/
  15334. template<typename t>
  15335. bool is_sameYC(const CImg<t>& img) const {
  15336. return is_sameYC(img._height,img._spectrum);
  15337. }
  15338. //! Test if image depth and spectrum are equal to specified values.
  15339. /**
  15340. Test if is_sameZ(unsigned int) const and is_sameC(unsigned int) const are both verified.
  15341. **/
  15342. bool is_sameZC(const unsigned int size_z, const unsigned int size_c) const {
  15343. return _depth==size_z && _spectrum==size_c;
  15344. }
  15345. //! Test if image depth and spectrum are the same as that of another image.
  15346. /**
  15347. Test if is_sameZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
  15348. **/
  15349. template<typename t>
  15350. bool is_sameZC(const CImg<t>& img) const {
  15351. return is_sameZC(img._depth,img._spectrum);
  15352. }
  15353. //! Test if image width, height and depth are equal to specified values.
  15354. /**
  15355. Test if is_sameXY(unsigned int,unsigned int) const and is_sameZ(unsigned int) const are both verified.
  15356. **/
  15357. bool is_sameXYZ(const unsigned int size_x, const unsigned int size_y, const unsigned int size_z) const {
  15358. return is_sameXY(size_x,size_y) && _depth==size_z;
  15359. }
  15360. //! Test if image width, height and depth are the same as that of another image.
  15361. /**
  15362. Test if is_sameXY(const CImg<t>&) const and is_sameZ(const CImg<t>&) const are both verified.
  15363. **/
  15364. template<typename t>
  15365. bool is_sameXYZ(const CImg<t>& img) const {
  15366. return is_sameXYZ(img._width,img._height,img._depth);
  15367. }
  15368. //! Test if image width, height and spectrum are equal to specified values.
  15369. /**
  15370. Test if is_sameXY(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
  15371. **/
  15372. bool is_sameXYC(const unsigned int size_x, const unsigned int size_y, const unsigned int size_c) const {
  15373. return is_sameXY(size_x,size_y) && _spectrum==size_c;
  15374. }
  15375. //! Test if image width, height and spectrum are the same as that of another image.
  15376. /**
  15377. Test if is_sameXY(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
  15378. **/
  15379. template<typename t>
  15380. bool is_sameXYC(const CImg<t>& img) const {
  15381. return is_sameXYC(img._width,img._height,img._spectrum);
  15382. }
  15383. //! Test if image width, depth and spectrum are equal to specified values.
  15384. /**
  15385. Test if is_sameXZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
  15386. **/
  15387. bool is_sameXZC(const unsigned int size_x, const unsigned int size_z, const unsigned int size_c) const {
  15388. return is_sameXZ(size_x,size_z) && _spectrum==size_c;
  15389. }
  15390. //! Test if image width, depth and spectrum are the same as that of another image.
  15391. /**
  15392. Test if is_sameXZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
  15393. **/
  15394. template<typename t>
  15395. bool is_sameXZC(const CImg<t>& img) const {
  15396. return is_sameXZC(img._width,img._depth,img._spectrum);
  15397. }
  15398. //! Test if image height, depth and spectrum are equal to specified values.
  15399. /**
  15400. Test if is_sameYZ(unsigned int,unsigned int) const and is_sameC(unsigned int) const are both verified.
  15401. **/
  15402. bool is_sameYZC(const unsigned int size_y, const unsigned int size_z, const unsigned int size_c) const {
  15403. return is_sameYZ(size_y,size_z) && _spectrum==size_c;
  15404. }
  15405. //! Test if image height, depth and spectrum are the same as that of another image.
  15406. /**
  15407. Test if is_sameYZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
  15408. **/
  15409. template<typename t>
  15410. bool is_sameYZC(const CImg<t>& img) const {
  15411. return is_sameYZC(img._height,img._depth,img._spectrum);
  15412. }
  15413. //! Test if image width, height, depth and spectrum are equal to specified values.
  15414. /**
  15415. Test if is_sameXYZ(unsigned int,unsigned int,unsigned int) const and is_sameC(unsigned int) const are both
  15416. verified.
  15417. **/
  15418. bool is_sameXYZC(const unsigned int size_x, const unsigned int size_y,
  15419. const unsigned int size_z, const unsigned int size_c) const {
  15420. return is_sameXYZ(size_x,size_y,size_z) && _spectrum==size_c;
  15421. }
  15422. //! Test if image width, height, depth and spectrum are the same as that of another image.
  15423. /**
  15424. Test if is_sameXYZ(const CImg<t>&) const and is_sameC(const CImg<t>&) const are both verified.
  15425. **/
  15426. template<typename t>
  15427. bool is_sameXYZC(const CImg<t>& img) const {
  15428. return is_sameXYZC(img._width,img._height,img._depth,img._spectrum);
  15429. }
  15430. //! Test if specified coordinates are inside image bounds.
  15431. /**
  15432. Return \c true if pixel located at (\c x,\c y,\c z,\c c) is inside bounds of the image instance,
  15433. and \c false otherwise.
  15434. \param x X-coordinate of the pixel value.
  15435. \param y Y-coordinate of the pixel value.
  15436. \param z Z-coordinate of the pixel value.
  15437. \param c C-coordinate of the pixel value.
  15438. \note
  15439. - Return \c true only if all these conditions are verified:
  15440. - The image instance is \e not empty.
  15441. - <tt>0<=x<=\ref width() - 1</tt>.
  15442. - <tt>0<=y<=\ref height() - 1</tt>.
  15443. - <tt>0<=z<=\ref depth() - 1</tt>.
  15444. - <tt>0<=c<=\ref spectrum() - 1</tt>.
  15445. **/
  15446. bool containsXYZC(const int x, const int y=0, const int z=0, const int c=0) const {
  15447. return !is_empty() && x>=0 && x<width() && y>=0 && y<height() && z>=0 && z<depth() && c>=0 && c<spectrum();
  15448. }
  15449. //! Test if pixel value is inside image bounds and get its X,Y,Z and C-coordinates.
  15450. /**
  15451. Return \c true, if specified reference refers to a pixel value inside bounds of the image instance,
  15452. and \c false otherwise.
  15453. \param pixel Reference to pixel value to test.
  15454. \param[out] x X-coordinate of the pixel value, if test succeeds.
  15455. \param[out] y Y-coordinate of the pixel value, if test succeeds.
  15456. \param[out] z Z-coordinate of the pixel value, if test succeeds.
  15457. \param[out] c C-coordinate of the pixel value, if test succeeds.
  15458. \note
  15459. - Useful to convert an offset to a buffer value into pixel value coordinates:
  15460. \code
  15461. const CImg<float> img(100,100,1,3); // Construct a 100x100 RGB color image
  15462. const unsigned long offset = 1249; // Offset to the pixel (49,12,0,0)
  15463. unsigned int x,y,z,c;
  15464. if (img.contains(img[offset],x,y,z,c)) { // Convert offset to (x,y,z,c) coordinates
  15465. std::printf("Offset %u refers to pixel located at (%u,%u,%u,%u).\n",
  15466. offset,x,y,z,c);
  15467. }
  15468. \endcode
  15469. **/
  15470. template<typename t>
  15471. bool contains(const T& pixel, t& x, t& y, t& z, t& c) const {
  15472. const ulongT wh = (ulongT)_width*_height, whd = wh*_depth, siz = whd*_spectrum;
  15473. const T *const ppixel = &pixel;
  15474. if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false;
  15475. ulongT off = (ulongT)(ppixel - _data);
  15476. const ulongT nc = off/whd;
  15477. off%=whd;
  15478. const ulongT nz = off/wh;
  15479. off%=wh;
  15480. const ulongT ny = off/_width, nx = off%_width;
  15481. x = (t)nx; y = (t)ny; z = (t)nz; c = (t)nc;
  15482. return true;
  15483. }
  15484. //! Test if pixel value is inside image bounds and get its X,Y and Z-coordinates.
  15485. /**
  15486. Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X,Y and Z-coordinates are set.
  15487. **/
  15488. template<typename t>
  15489. bool contains(const T& pixel, t& x, t& y, t& z) const {
  15490. const ulongT wh = (ulongT)_width*_height, whd = wh*_depth, siz = whd*_spectrum;
  15491. const T *const ppixel = &pixel;
  15492. if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false;
  15493. ulongT off = ((ulongT)(ppixel - _data))%whd;
  15494. const ulongT nz = off/wh;
  15495. off%=wh;
  15496. const ulongT ny = off/_width, nx = off%_width;
  15497. x = (t)nx; y = (t)ny; z = (t)nz;
  15498. return true;
  15499. }
  15500. //! Test if pixel value is inside image bounds and get its X and Y-coordinates.
  15501. /**
  15502. Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X and Y-coordinates are set.
  15503. **/
  15504. template<typename t>
  15505. bool contains(const T& pixel, t& x, t& y) const {
  15506. const ulongT wh = (ulongT)_width*_height, siz = wh*_depth*_spectrum;
  15507. const T *const ppixel = &pixel;
  15508. if (is_empty() || ppixel<_data || ppixel>=_data + siz) return false;
  15509. ulongT off = ((unsigned int)(ppixel - _data))%wh;
  15510. const ulongT ny = off/_width, nx = off%_width;
  15511. x = (t)nx; y = (t)ny;
  15512. return true;
  15513. }
  15514. //! Test if pixel value is inside image bounds and get its X-coordinate.
  15515. /**
  15516. Similar to contains(const T&,t&,t&,t&,t&) const, except that only the X-coordinate is set.
  15517. **/
  15518. template<typename t>
  15519. bool contains(const T& pixel, t& x) const {
  15520. const T *const ppixel = &pixel;
  15521. if (is_empty() || ppixel<_data || ppixel>=_data + size()) return false;
  15522. x = (t)(((ulongT)(ppixel - _data))%_width);
  15523. return true;
  15524. }
  15525. //! Test if pixel value is inside image bounds.
  15526. /**
  15527. Similar to contains(const T&,t&,t&,t&,t&) const, except that no pixel coordinates are set.
  15528. **/
  15529. bool contains(const T& pixel) const {
  15530. const T *const ppixel = &pixel;
  15531. return !is_empty() && ppixel>=_data && ppixel<_data + size();
  15532. }
  15533. //! Test if pixel buffers of instance and input images overlap.
  15534. /**
  15535. Return \c true, if pixel buffers attached to image instance and input image \c img overlap,
  15536. and \c false otherwise.
  15537. \param img Input image to compare with.
  15538. \note
  15539. - Buffer overlapping may happen when manipulating \e shared images.
  15540. - If two image buffers overlap, operating on one of the image will probably modify the other one.
  15541. - Most of the time, \c CImg<T> instances are \e non-shared and do not overlap between each others.
  15542. \par Example
  15543. \code
  15544. const CImg<float>
  15545. img1("reference.jpg"), // Load RGB-color image
  15546. img2 = img1.get_shared_channel(1); // Get shared version of the green channel
  15547. if (img1.is_overlapped(img2)) { // Test succeeds, 'img1' and 'img2' overlaps
  15548. std::printf("Buffers overlap!\n");
  15549. }
  15550. \endcode
  15551. **/
  15552. template<typename t>
  15553. bool is_overlapped(const CImg<t>& img) const {
  15554. const ulongT csiz = size(), isiz = img.size();
  15555. return !((void*)(_data + csiz)<=(void*)img._data || (void*)_data>=(void*)(img._data + isiz));
  15556. }
  15557. //! Test if the set {\c *this,\c primitives,\c colors,\c opacities} defines a valid 3D object.
  15558. /**
  15559. Return \c true is the 3D object represented by the set {\c *this,\c primitives,\c colors,\c opacities} defines a
  15560. valid 3D object, and \c false otherwise. The vertex coordinates are defined by the instance image.
  15561. \param primitives List of primitives of the 3D object.
  15562. \param colors List of colors of the 3D object.
  15563. \param opacities List (or image) of opacities of the 3D object.
  15564. \param full_check Tells if full checking of the 3D object must be performed.
  15565. \param[out] error_message C-string to contain the error message, if the test does not succeed
  15566. (at least 256 bytes).
  15567. \note
  15568. - Set \c full_checking to \c false to speed-up the 3D object checking. In this case, only the size of
  15569. each 3D object component is checked.
  15570. - Size of the string \c error_message should be at least 128-bytes long, to be able to contain the error message.
  15571. **/
  15572. template<typename tp, typename tc, typename to>
  15573. bool is_object3d(const CImgList<tp>& primitives,
  15574. const CImgList<tc>& colors,
  15575. const to& opacities,
  15576. const bool full_check=true,
  15577. char *const error_message=0) const {
  15578. if (error_message) *error_message = 0;
  15579. // Check consistency for the particular case of an empty 3D object.
  15580. if (is_empty()) {
  15581. if (primitives || colors || opacities) {
  15582. if (error_message) cimg_snprintf(error_message,256,
  15583. "3D object (%u,%u) defines no vertices but %u primitives, "
  15584. "%u colors and %lu opacities",
  15585. _width,primitives._width,primitives._width,
  15586. colors._width,(unsigned long)opacities.size());
  15587. return false;
  15588. }
  15589. return true;
  15590. }
  15591. // Check consistency of vertices.
  15592. if (_height!=3 || _depth>1 || _spectrum>1) { // Check vertices dimensions
  15593. if (error_message) cimg_snprintf(error_message,256,
  15594. "3D object (%u,%u) has invalid vertex dimensions (%u,%u,%u,%u)",
  15595. _width,primitives._width,_width,_height,_depth,_spectrum);
  15596. return false;
  15597. }
  15598. if (colors._width>primitives._width + 1) {
  15599. if (error_message) cimg_snprintf(error_message,256,
  15600. "3D object (%u,%u) defines %u colors",
  15601. _width,primitives._width,colors._width);
  15602. return false;
  15603. }
  15604. if (opacities.size()>primitives._width) {
  15605. if (error_message) cimg_snprintf(error_message,256,
  15606. "3D object (%u,%u) defines %lu opacities",
  15607. _width,primitives._width,(unsigned long)opacities.size());
  15608. return false;
  15609. }
  15610. if (!full_check) return true;
  15611. // Check consistency of primitives.
  15612. cimglist_for(primitives,l) {
  15613. const CImg<tp>& primitive = primitives[l];
  15614. const unsigned int psiz = (unsigned int)primitive.size();
  15615. switch (psiz) {
  15616. case 1 : { // Point
  15617. const unsigned int i0 = (unsigned int)primitive(0);
  15618. if (i0>=_width) {
  15619. if (error_message) cimg_snprintf(error_message,256,
  15620. "3D object (%u,%u) refers to invalid vertex index %u in "
  15621. "point primitive [%u]",
  15622. _width,primitives._width,i0,l);
  15623. return false;
  15624. }
  15625. } break;
  15626. case 5 : { // Sphere
  15627. const unsigned int
  15628. i0 = (unsigned int)primitive(0),
  15629. i1 = (unsigned int)primitive(1);
  15630. if (i0>=_width || i1>=_width) {
  15631. if (error_message) cimg_snprintf(error_message,256,
  15632. "3D object (%u,%u) refers to invalid vertex indices (%u,%u) in "
  15633. "sphere primitive [%u]",
  15634. _width,primitives._width,i0,i1,l);
  15635. return false;
  15636. }
  15637. } break;
  15638. case 2 : case 6 : { // Segment
  15639. const unsigned int
  15640. i0 = (unsigned int)primitive(0),
  15641. i1 = (unsigned int)primitive(1);
  15642. if (i0>=_width || i1>=_width) {
  15643. if (error_message) cimg_snprintf(error_message,256,
  15644. "3D object (%u,%u) refers to invalid vertex indices (%u,%u) in "
  15645. "segment primitive [%u]",
  15646. _width,primitives._width,i0,i1,l);
  15647. return false;
  15648. }
  15649. } break;
  15650. case 3 : case 9 : { // Triangle
  15651. const unsigned int
  15652. i0 = (unsigned int)primitive(0),
  15653. i1 = (unsigned int)primitive(1),
  15654. i2 = (unsigned int)primitive(2);
  15655. if (i0>=_width || i1>=_width || i2>=_width) {
  15656. if (error_message) cimg_snprintf(error_message,256,
  15657. "3D object (%u,%u) refers to invalid vertex indices (%u,%u,%u) in "
  15658. "triangle primitive [%u]",
  15659. _width,primitives._width,i0,i1,i2,l);
  15660. return false;
  15661. }
  15662. } break;
  15663. case 4 : case 12 : { // Quadrangle
  15664. const unsigned int
  15665. i0 = (unsigned int)primitive(0),
  15666. i1 = (unsigned int)primitive(1),
  15667. i2 = (unsigned int)primitive(2),
  15668. i3 = (unsigned int)primitive(3);
  15669. if (i0>=_width || i1>=_width || i2>=_width || i3>=_width) {
  15670. if (error_message) cimg_snprintf(error_message,256,
  15671. "3D object (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in "
  15672. "quadrangle primitive [%u]",
  15673. _width,primitives._width,i0,i1,i2,i3,l);
  15674. return false;
  15675. }
  15676. } break;
  15677. default :
  15678. if (error_message) cimg_snprintf(error_message,256,
  15679. "3D object (%u,%u) defines an invalid primitive [%u] of size %u",
  15680. _width,primitives._width,l,(unsigned int)psiz);
  15681. return false;
  15682. }
  15683. }
  15684. // Check consistency of colors.
  15685. cimglist_for(colors,c) {
  15686. const CImg<tc>& color = colors[c];
  15687. if (!color) {
  15688. if (error_message) cimg_snprintf(error_message,256,
  15689. "3D object (%u,%u) defines no color for primitive [%u]",
  15690. _width,primitives._width,c);
  15691. return false;
  15692. }
  15693. }
  15694. // Check consistency of light texture.
  15695. if (colors._width>primitives._width) {
  15696. const CImg<tc> &light = colors.back();
  15697. if (!light || light._depth>1) {
  15698. if (error_message) cimg_snprintf(error_message,256,
  15699. "3D object (%u,%u) defines an invalid light texture (%u,%u,%u,%u)",
  15700. _width,primitives._width,light._width,
  15701. light._height,light._depth,light._spectrum);
  15702. return false;
  15703. }
  15704. }
  15705. return true;
  15706. }
  15707. //! Test if image instance represents a valid serialization of a 3D object.
  15708. /**
  15709. Return \c true if the image instance represents a valid serialization of a 3D object, and \c false otherwise.
  15710. \param full_check Tells if full checking of the instance must be performed.
  15711. \param[out] error_message C-string to contain the error message, if the test does not succeed.
  15712. \note
  15713. - Set \c full_check to \c false to speed-up the 3D object checking. In this case, only the size of
  15714. each 3D object component is checked.
  15715. - Size of the string \c error_message should be at least 256-bytes long, to be able to contain the error message.
  15716. **/
  15717. bool is_CImg3d(const bool full_check=true, char *const error_message=0) const {
  15718. if (error_message) *error_message = 0;
  15719. // Check instance dimension and header.
  15720. if (_width!=1 || _height<8 || _depth!=1 || _spectrum!=1) {
  15721. if (error_message) cimg_snprintf(error_message,256,
  15722. "CImg3d has invalid dimensions (%u,%u,%u,%u)",
  15723. _width,_height,_depth,_spectrum);
  15724. return false;
  15725. }
  15726. const T *ptrs = _data, *const ptre = end();
  15727. if (!_is_CImg3d(*(ptrs++),'C') || !_is_CImg3d(*(ptrs++),'I') || !_is_CImg3d(*(ptrs++),'m') ||
  15728. !_is_CImg3d(*(ptrs++),'g') || !_is_CImg3d(*(ptrs++),'3') || !_is_CImg3d(*(ptrs++),'d')) {
  15729. if (error_message) cimg_snprintf(error_message,256,
  15730. "CImg3d header not found");
  15731. return false;
  15732. }
  15733. if (!cimg::type<T>::is_finite(*ptrs) || !cimg::type<T>::is_finite(ptrs[1])) {
  15734. if (error_message) cimg_snprintf(error_message,256,
  15735. "Specified numbers of vertices/primitives (%g/%g) are invalid",
  15736. (double)*ptrs,(double)ptrs[1]);
  15737. return false;
  15738. }
  15739. const unsigned int
  15740. nb_points = cimg::float2uint((float)*(ptrs++)),
  15741. nb_primitives = cimg::float2uint((float)*(ptrs++));
  15742. // Check consistency of number of vertices / primitives.
  15743. if (!full_check) {
  15744. const ulongT minimal_size = 8UL + 3*nb_points + 6*nb_primitives;
  15745. if (_data + minimal_size>ptre) {
  15746. if (error_message) cimg_snprintf(error_message,256,
  15747. "CImg3d (%u,%u) has only %lu values, "
  15748. "while at least %lu values were expected",
  15749. nb_points,nb_primitives,(unsigned long)size(),(unsigned long)minimal_size);
  15750. return false;
  15751. }
  15752. }
  15753. // Check consistency of vertex data.
  15754. if (!nb_points) {
  15755. if (nb_primitives) {
  15756. if (error_message) cimg_snprintf(error_message,256,
  15757. "CImg3d (%u,%u) defines no vertices but %u primitives",
  15758. nb_points,nb_primitives,nb_primitives);
  15759. return false;
  15760. }
  15761. if (ptrs!=ptre) {
  15762. if (error_message) cimg_snprintf(error_message,256,
  15763. "CImg3d (%u,%u) is an empty object but contains %u value%s "
  15764. "more than expected",
  15765. nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":"");
  15766. return false;
  15767. }
  15768. return true;
  15769. }
  15770. if (ptrs + 3*nb_points>ptre) {
  15771. if (error_message) cimg_snprintf(error_message,256,
  15772. "CImg3d (%u,%u) defines only %u vertices data",
  15773. nb_points,nb_primitives,(unsigned int)(ptre - ptrs)/3);
  15774. return false;
  15775. }
  15776. ptrs+=3*nb_points;
  15777. // Check consistency of primitive data.
  15778. if (ptrs==ptre) {
  15779. if (error_message) cimg_snprintf(error_message,256,
  15780. "CImg3d (%u,%u) defines %u vertices but no primitive",
  15781. nb_points,nb_primitives,nb_points);
  15782. return false;
  15783. }
  15784. if (!full_check) return true;
  15785. for (unsigned int p = 0; p<nb_primitives; ++p) {
  15786. const unsigned int nb_inds = (unsigned int)*(ptrs++);
  15787. switch (nb_inds) {
  15788. case 1 : { // Point
  15789. const unsigned int i0 = cimg::float2uint((float)*(ptrs++));
  15790. if (i0>=nb_points) {
  15791. if (error_message) cimg_snprintf(error_message,256,
  15792. "CImg3d (%u,%u) refers to invalid vertex index %u in point primitive [%u]",
  15793. nb_points,nb_primitives,i0,p);
  15794. return false;
  15795. }
  15796. } break;
  15797. case 5 : { // Sphere
  15798. const unsigned int
  15799. i0 = cimg::float2uint((float)*(ptrs++)),
  15800. i1 = cimg::float2uint((float)*(ptrs++));
  15801. ptrs+=3;
  15802. if (i0>=nb_points || i1>=nb_points) {
  15803. if (error_message) cimg_snprintf(error_message,256,
  15804. "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in "
  15805. "sphere primitive [%u]",
  15806. nb_points,nb_primitives,i0,i1,p);
  15807. return false;
  15808. }
  15809. } break;
  15810. case 2 : case 6 : { // Segment
  15811. const unsigned int
  15812. i0 = cimg::float2uint((float)*(ptrs++)),
  15813. i1 = cimg::float2uint((float)*(ptrs++));
  15814. if (nb_inds==6) ptrs+=4;
  15815. if (i0>=nb_points || i1>=nb_points) {
  15816. if (error_message) cimg_snprintf(error_message,256,
  15817. "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u) in "
  15818. "segment primitive [%u]",
  15819. nb_points,nb_primitives,i0,i1,p);
  15820. return false;
  15821. }
  15822. } break;
  15823. case 3 : case 9 : { // Triangle
  15824. const unsigned int
  15825. i0 = cimg::float2uint((float)*(ptrs++)),
  15826. i1 = cimg::float2uint((float)*(ptrs++)),
  15827. i2 = cimg::float2uint((float)*(ptrs++));
  15828. if (nb_inds==9) ptrs+=6;
  15829. if (i0>=nb_points || i1>=nb_points || i2>=nb_points) {
  15830. if (error_message) cimg_snprintf(error_message,256,
  15831. "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u) in "
  15832. "triangle primitive [%u]",
  15833. nb_points,nb_primitives,i0,i1,i2,p);
  15834. return false;
  15835. }
  15836. } break;
  15837. case 4 : case 12 : { // Quadrangle
  15838. const unsigned int
  15839. i0 = cimg::float2uint((float)*(ptrs++)),
  15840. i1 = cimg::float2uint((float)*(ptrs++)),
  15841. i2 = cimg::float2uint((float)*(ptrs++)),
  15842. i3 = cimg::float2uint((float)*(ptrs++));
  15843. if (nb_inds==12) ptrs+=8;
  15844. if (i0>=nb_points || i1>=nb_points || i2>=nb_points || i3>=nb_points) {
  15845. if (error_message) cimg_snprintf(error_message,256,
  15846. "CImg3d (%u,%u) refers to invalid vertex indices (%u,%u,%u,%u) in "
  15847. "quadrangle primitive [%u]",
  15848. nb_points,nb_primitives,i0,i1,i2,i3,p);
  15849. return false;
  15850. }
  15851. } break;
  15852. default :
  15853. if (error_message) cimg_snprintf(error_message,256,
  15854. "CImg3d (%u,%u) defines an invalid primitive [%u] of size %u",
  15855. nb_points,nb_primitives,p,nb_inds);
  15856. return false;
  15857. }
  15858. if (ptrs>ptre) {
  15859. if (error_message) cimg_snprintf(error_message,256,
  15860. "CImg3d (%u,%u) has incomplete primitive data for primitive [%u], "
  15861. "%u values missing",
  15862. nb_points,nb_primitives,p,(unsigned int)(ptrs - ptre));
  15863. return false;
  15864. }
  15865. }
  15866. // Check consistency of color data.
  15867. if (ptrs==ptre) {
  15868. if (error_message) cimg_snprintf(error_message,256,
  15869. "CImg3d (%u,%u) defines no color/texture data",
  15870. nb_points,nb_primitives);
  15871. return false;
  15872. }
  15873. for (unsigned int c = 0; c<nb_primitives; ++c) {
  15874. if (*(ptrs++)!=(T)-128) ptrs+=2;
  15875. else if ((ptrs+=3)<ptre) {
  15876. const unsigned int
  15877. w = (unsigned int)cimg::float2uint((float)*(ptrs - 3)),
  15878. h = (unsigned int)*(ptrs - 2),
  15879. s = (unsigned int)*(ptrs - 1);
  15880. if (!h && !s) {
  15881. if (w>=c) {
  15882. if (error_message) cimg_snprintf(error_message,256,
  15883. "CImg3d (%u,%u) refers to invalid shared sprite/texture index %u "
  15884. "for primitive [%u]",
  15885. nb_points,nb_primitives,w,c);
  15886. return false;
  15887. }
  15888. } else ptrs+=w*h*s;
  15889. }
  15890. if (ptrs>ptre) {
  15891. if (error_message) cimg_snprintf(error_message,256,
  15892. "CImg3d (%u,%u) has incomplete color/texture data for primitive [%u], "
  15893. "%u values missing",
  15894. nb_points,nb_primitives,c,(unsigned int)(ptrs - ptre));
  15895. return false;
  15896. }
  15897. }
  15898. // Check consistency of opacity data.
  15899. if (ptrs==ptre) {
  15900. if (error_message) cimg_snprintf(error_message,256,
  15901. "CImg3d (%u,%u) defines no opacity data",
  15902. nb_points,nb_primitives);
  15903. return false;
  15904. }
  15905. for (unsigned int o = 0; o<nb_primitives; ++o) {
  15906. if (*(ptrs++)==(T)-128 && (ptrs+=3)<ptre) {
  15907. const unsigned int
  15908. w = (unsigned int)cimg::float2uint((float)*(ptrs - 3)),
  15909. h = (unsigned int)*(ptrs - 2),
  15910. s = (unsigned int)*(ptrs - 1);
  15911. if (!h && !s) {
  15912. if (w>=o) {
  15913. if (error_message) cimg_snprintf(error_message,256,
  15914. "CImg3d (%u,%u) refers to invalid shared opacity index %u "
  15915. "for primitive [%u]",
  15916. nb_points,nb_primitives,w,o);
  15917. return false;
  15918. }
  15919. } else ptrs+=w*h*s;
  15920. }
  15921. if (ptrs>ptre) {
  15922. if (error_message) cimg_snprintf(error_message,256,
  15923. "CImg3d (%u,%u) has incomplete opacity data for primitive [%u]",
  15924. nb_points,nb_primitives,o);
  15925. return false;
  15926. }
  15927. }
  15928. // Check end of data.
  15929. if (ptrs<ptre) {
  15930. if (error_message) cimg_snprintf(error_message,256,
  15931. "CImg3d (%u,%u) contains %u value%s more than expected",
  15932. nb_points,nb_primitives,(unsigned int)(ptre - ptrs),(ptre - ptrs)>1?"s":"");
  15933. return false;
  15934. }
  15935. return true;
  15936. }
  15937. static bool _is_CImg3d(const T val, const char c) {
  15938. return val>=(T)c && val<(T)(c + 1);
  15939. }
  15940. //@}
  15941. //-------------------------------------
  15942. //
  15943. //! \name Mathematical Functions
  15944. //@{
  15945. //-------------------------------------
  15946. // Define the math formula parser/compiler and expression evaluator.
  15947. struct _cimg_math_parser {
  15948. CImg<doubleT> mem;
  15949. CImg<intT> memtype, memmerge;
  15950. CImgList<ulongT> _code, &code, code_begin, code_end,
  15951. _code_begin_t, &code_begin_t, _code_end_t, &code_end_t;
  15952. CImg<ulongT> opcode;
  15953. const CImg<ulongT> *p_code_end, *p_code;
  15954. const CImg<ulongT> *const p_break;
  15955. CImg<charT> expr, pexpr;
  15956. const CImg<T>& imgin;
  15957. CImg<T> &imgout;
  15958. CImgList<T>& imglist;
  15959. CImg<doubleT> _img_stats, &img_stats, constcache_vals;
  15960. CImgList<doubleT> _list_stats, &list_stats, _list_median, &list_median, _list_norm, &list_norm;
  15961. CImg<uintT> mem_img_stats, constcache_inds;
  15962. CImg<uintT> level, variable_pos, reserved_label;
  15963. CImgList<charT> variable_def, macro_def, macro_body;
  15964. char *user_macro;
  15965. unsigned int mempos, mem_img_median, mem_img_norm, mem_img_index, debug_indent,
  15966. result_dim, result_end_dim, break_type, constcache_size;
  15967. bool is_parallelizable, is_noncritical_run, is_end_code, is_fill, need_input_copy, return_new_comp;
  15968. double *result, *result_end;
  15969. cimg_uint64 rng;
  15970. const char *const calling_function, *s_op, *ss_op;
  15971. typedef double (*mp_func)(_cimg_math_parser&);
  15972. #define _cimg_mp_is_scalar(arg) (memtype[arg]<2) // Is scalar value?
  15973. #define _cimg_mp_is_const_scalar(arg) (memtype[arg]==1) // Is const scalar?
  15974. #define _cimg_mp_is_vector(arg) (memtype[arg]>1) // Is vector?
  15975. #define _cimg_mp_is_comp(arg) (!memtype[arg]) // Is computation value?
  15976. #define _cimg_mp_is_reserved(arg) (memtype[arg]==-1) // Is scalar and reserved (e.g. variable)?
  15977. #define _cimg_mp_size(arg) (_cimg_mp_is_scalar(arg)?0U:(unsigned int)memtype[arg] - 1) // Size (0=scalar, N>0=vectorN)
  15978. #define _cimg_mp_calling_function s_calling_function()._data
  15979. #define _cimg_mp_op(s) s_op = s; ss_op = ss
  15980. #define _cimg_mp_check_const_scalar(arg,n_arg,mode) check_const_scalar(arg,n_arg,mode,ss,se,saved_char)
  15981. #define _cimg_mp_check_const_index(arg) check_const_index(arg,ss,se,saved_char)
  15982. #define _cimg_mp_check_notnan_index(arg) check_notnan_index(arg,ss,se,saved_char)
  15983. #define _cimg_mp_check_list() check_list(ss,se,saved_char)
  15984. #define _cimg_mp_check_matrix_square(arg,n_arg) check_matrix_square(arg,n_arg,ss,se,saved_char)
  15985. #define _cimg_mp_check_type(arg,n_arg,mode,N) check_type(arg,n_arg,mode,N,ss,se,saved_char)
  15986. #define _cimg_mp_defunc(mp) (*(mp_func)(*(mp).opcode))(mp)
  15987. #define _cimg_mp_return(x) { *se = saved_char; s_op = previous_s_op; ss_op = previous_ss_op; return x; }
  15988. #define _cimg_mp_return_nan() _cimg_mp_return(_cimg_mp_slot_nan)
  15989. #define _cimg_mp_const_scalar(val) _cimg_mp_return(const_scalar((double)(val)))
  15990. #define _cimg_mp_scalar0(op) _cimg_mp_return(scalar0(op))
  15991. #define _cimg_mp_scalar1(op,i1) _cimg_mp_return(scalar1(op,i1))
  15992. #define _cimg_mp_scalar2(op,i1,i2) _cimg_mp_return(scalar2(op,i1,i2))
  15993. #define _cimg_mp_scalar3(op,i1,i2,i3) _cimg_mp_return(scalar3(op,i1,i2,i3))
  15994. #define _cimg_mp_scalar4(op,i1,i2,i3,i4) _cimg_mp_return(scalar4(op,i1,i2,i3,i4))
  15995. #define _cimg_mp_scalar5(op,i1,i2,i3,i4,i5) _cimg_mp_return(scalar5(op,i1,i2,i3,i4,i5))
  15996. #define _cimg_mp_scalar6(op,i1,i2,i3,i4,i5,i6) _cimg_mp_return(scalar6(op,i1,i2,i3,i4,i5,i6))
  15997. #define _cimg_mp_scalar7(op,i1,i2,i3,i4,i5,i6,i7) _cimg_mp_return(scalar7(op,i1,i2,i3,i4,i5,i6,i7))
  15998. #define _cimg_mp_vector1_v(op,i1) _cimg_mp_return(vector1_v(op,i1))
  15999. #define _cimg_mp_vector2_sv(op,i1,i2) _cimg_mp_return(vector2_sv(op,i1,i2))
  16000. #define _cimg_mp_vector2_vs(op,i1,i2) _cimg_mp_return(vector2_vs(op,i1,i2))
  16001. #define _cimg_mp_vector2_vv(op,i1,i2) _cimg_mp_return(vector2_vv(op,i1,i2))
  16002. #define _cimg_mp_vector3_vss(op,i1,i2,i3) _cimg_mp_return(vector3_vss(op,i1,i2,i3))
  16003. #define _cimg_mp_vector4_vvss(op,i1,i2,i3,i4) _cimg_mp_return(vector4_vvss(op,i1,i2,i3,i4))
  16004. #define _cimg_mp_vector4_vsss(op,i1,i2,i3,i4) _cimg_mp_return(vector4_vsss(op,i1,i2,i3,i4))
  16005. #define _cimg_mp_vector4_svss(op,i1,i2,i3,i4) _cimg_mp_return(vector4_svss(op,i1,i2,i3,i4))
  16006. #define _cimg_mp_strerr \
  16007. *se = saved_char; \
  16008. for (s0 = ss; s0>expr._data && *s0!=';'; --s0) {} \
  16009. if (*s0==';') ++s0; \
  16010. while (cimg::is_blank(*s0)) ++s0; \
  16011. cimg::strellipsize(s0,64)
  16012. // Constructors / Destructors.
  16013. ~_cimg_math_parser() {
  16014. cimg::srand(rng);
  16015. }
  16016. _cimg_math_parser(const char *const expression, const char *const funcname=0,
  16017. const CImg<T>& img_input=CImg<T>::const_empty(), CImg<T> *const img_output=0,
  16018. CImgList<T> *const list_images=0, const bool _is_fill=false):
  16019. code(_code),code_begin_t(_code_begin_t),code_end_t(_code_end_t),
  16020. p_break((CImg<ulongT>*)(cimg_ulong)-2),imgin(img_input),
  16021. imgout(img_output?*img_output:CImg<T>::empty()),imglist(list_images?*list_images:CImgList<T>::empty()),
  16022. img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),list_norm(_list_norm),user_macro(0),
  16023. mem_img_median(~0U),mem_img_norm(~0U),mem_img_index(~0U),debug_indent(0),result_dim(0),result_end_dim(0),
  16024. break_type(0),constcache_size(0),is_parallelizable(true),is_noncritical_run(false),is_fill(_is_fill),
  16025. need_input_copy(false),result_end(0),rng((cimg::_rand(),cimg::rng())),
  16026. calling_function(funcname?funcname:"cimg_math_parser") {
  16027. #if cimg_use_openmp!=0
  16028. rng+=omp_get_thread_num();
  16029. #endif
  16030. if (!expression || !*expression)
  16031. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  16032. "CImg<%s>::%s: Empty expression.",
  16033. pixel_type(),_cimg_mp_calling_function);
  16034. const char *_expression = expression;
  16035. while (*_expression && (cimg::is_blank(*_expression) || *_expression==';')) ++_expression;
  16036. CImg<charT>::string(_expression).move_to(expr);
  16037. char *ps = &expr.back() - 1;
  16038. while (ps>expr._data && (cimg::is_blank(*ps) || *ps==';')) --ps;
  16039. *(++ps) = 0; expr._width = (unsigned int)(ps - expr._data + 1);
  16040. // Ease the retrieval of previous non-space characters afterwards.
  16041. pexpr.assign(expr._width);
  16042. char c, *pe = pexpr._data;
  16043. for (ps = expr._data, c = ' '; *ps; ++ps) {
  16044. if (!cimg::is_blank(*ps)) c = *ps; else *ps = ' ';
  16045. *(pe++) = c;
  16046. }
  16047. *pe = 0;
  16048. level = get_level(expr);
  16049. // Init constant values.
  16050. #define _cimg_mp_interpolation (reserved_label[31]!=~0U?reserved_label[31]:0)
  16051. #define _cimg_mp_boundary (reserved_label[32]!=~0U?reserved_label[32]:0)
  16052. #define _cimg_mp_slot_t 17
  16053. #define _cimg_mp_slot_nan 29
  16054. #define _cimg_mp_slot_x 30
  16055. #define _cimg_mp_slot_y 31
  16056. #define _cimg_mp_slot_z 32
  16057. #define _cimg_mp_slot_c 33
  16058. mem.assign(96);
  16059. for (unsigned int i = 0; i<=10; ++i) mem[i] = (double)i; // mem[0-10] = 0...10
  16060. for (unsigned int i = 1; i<=5; ++i) mem[i + 10] = -(double)i; // mem[11-15] = -1...-5
  16061. mem[16] = 0.5;
  16062. mem[_cimg_mp_slot_t] = 0; // thread_id
  16063. mem[18] = (double)imgin._width; // w
  16064. mem[19] = (double)imgin._height; // h
  16065. mem[20] = (double)imgin._depth; // d
  16066. mem[21] = (double)imgin._spectrum; // s
  16067. mem[22] = (double)imgin._is_shared; // r
  16068. mem[23] = (double)imgin._width*imgin._height; // wh
  16069. mem[24] = (double)imgin._width*imgin._height*imgin._depth; // whd
  16070. mem[25] = (double)imgin._width*imgin._height*imgin._depth*imgin._spectrum; // whds
  16071. mem[26] = (double)imglist._width; // l
  16072. mem[27] = std::exp(1.); // e
  16073. mem[28] = cimg::PI; // pi
  16074. mem[_cimg_mp_slot_nan] = cimg::type<double>::nan(); // nan
  16075. // Set value property :
  16076. // { -1 = reserved (e.g. variable) | 0 = computation value |
  16077. // 1 = compile-time constant | N>1 = constant ptr to vector[N-1] }.
  16078. memtype.assign(mem._width,1,1,1,0);
  16079. for (unsigned int i = 0; i<_cimg_mp_slot_x; ++i) memtype[i] = 1;
  16080. memtype[_cimg_mp_slot_t] = memtype[_cimg_mp_slot_x] = memtype[_cimg_mp_slot_y] =
  16081. memtype[_cimg_mp_slot_z] = memtype[_cimg_mp_slot_c] = -1;
  16082. mempos = _cimg_mp_slot_c + 1;
  16083. variable_pos.assign(8);
  16084. reserved_label.assign(128,1,1,1,~0U);
  16085. // reserved_label[0-32] are used to store the memory index of these variables:
  16086. // [0] = wh, [1] = whd, [2] = whds, [3] = pi, [4] = im, [5] = iM, [6] = ia, [7] = iv, [8] = id,
  16087. // [9] = is, [10] = ip, [11] = ic, [12] = in, [13] = xm, [14] = ym, [15] = zm, [16] = cm, [17] = xM,
  16088. // [18] = yM, [19] = zM, [20] = cM, [21] = i0...[30] = i9, [31] = interpolation, [32] = boundary
  16089. // Compile expression into a sequence of opcodes.
  16090. s_op = ""; ss_op = expr._data;
  16091. const unsigned int ind_result = compile(expr._data,expr._data + expr._width - 1,0,0,0);
  16092. if (!_cimg_mp_is_const_scalar(ind_result)) {
  16093. if (_cimg_mp_is_vector(ind_result))
  16094. CImg<doubleT>(&mem[ind_result] + 1,_cimg_mp_size(ind_result),1,1,1,true).
  16095. fill(cimg::type<double>::nan());
  16096. else if (ind_result!=_cimg_mp_slot_t) mem[ind_result] = cimg::type<double>::nan();
  16097. }
  16098. if (mem._width>=256 && mem._width - mempos>=mem._width/2) mem.resize(mempos,1,1,1,-1);
  16099. result_dim = _cimg_mp_size(ind_result);
  16100. result = mem._data + ind_result;
  16101. // Free resources used for compiling expression and prepare evaluation.
  16102. memtype.assign();
  16103. constcache_vals.assign();
  16104. constcache_inds.assign();
  16105. level.assign();
  16106. variable_pos.assign();
  16107. reserved_label.assign();
  16108. expr.assign();
  16109. pexpr.assign();
  16110. opcode.assign();
  16111. opcode._is_shared = true;
  16112. // Execute begin() block if any specified.
  16113. if (code_begin) {
  16114. mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
  16115. p_code_end = code_begin.end();
  16116. for (p_code = code_begin; p_code<p_code_end; ++p_code) {
  16117. opcode._data = p_code->_data;
  16118. const ulongT target = opcode[1];
  16119. mem[target] = _cimg_mp_defunc(*this);
  16120. }
  16121. }
  16122. p_code_end = code.end();
  16123. }
  16124. _cimg_math_parser():
  16125. code(_code),code_begin_t(_code_begin_t),code_end_t(_code_end_t),
  16126. p_code_end(0),p_break((CImg<ulongT>*)(cimg_ulong)-2),
  16127. imgin(CImg<T>::const_empty()),imgout(CImg<T>::empty()),imglist(CImgList<T>::empty()),
  16128. img_stats(_img_stats),list_stats(_list_stats),list_median(_list_median),list_norm(_list_norm),debug_indent(0),
  16129. result_dim(0),result_end_dim(0),break_type(0),constcache_size(0),is_parallelizable(true),
  16130. is_noncritical_run(false),is_fill(false),need_input_copy(false),
  16131. result_end(0),rng(0),calling_function(0) {
  16132. mem.assign(1 + _cimg_mp_slot_c,1,1,1,0); // Allow to skip 'is_empty?' test in operator()()
  16133. result = mem._data;
  16134. }
  16135. _cimg_math_parser(const _cimg_math_parser& mp):
  16136. mem(mp.mem),code(mp.code),code_begin_t(mp.code_begin_t),code_end_t(mp.code_end_t),
  16137. p_code_end(mp.p_code_end),p_break(mp.p_break),
  16138. imgin(mp.imgin),imgout(mp.imgout),imglist(mp.imglist),
  16139. img_stats(mp.img_stats),list_stats(mp.list_stats),list_median(mp.list_median),list_norm(mp.list_norm),
  16140. debug_indent(0),result_dim(mp.result_dim),result_end_dim(mp.result_end_dim),break_type(0),constcache_size(0),
  16141. is_parallelizable(mp.is_parallelizable),is_noncritical_run(mp.is_noncritical_run),is_fill(mp.is_fill),
  16142. need_input_copy(mp.need_input_copy),
  16143. result(mem._data + (mp.result - mp.mem._data)),
  16144. result_end(mp.result_end?mem._data + (mp.result_end - mp.mem._data):0),
  16145. rng((cimg::_rand(),cimg::rng())),calling_function(0) {
  16146. #if cimg_use_openmp!=0
  16147. mem[_cimg_mp_slot_t] = (double)omp_get_thread_num();
  16148. rng+=omp_get_thread_num();
  16149. #endif
  16150. opcode.assign();
  16151. opcode._is_shared = true;
  16152. }
  16153. // Compilation procedure.
  16154. unsigned int compile(char *ss, char *se, const unsigned int depth, unsigned int *const p_ref,
  16155. const unsigned char block_flags) {
  16156. if (depth>256) {
  16157. cimg::strellipsize(expr,64);
  16158. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  16159. "CImg<%s>::%s: Call stack overflow (infinite recursion?), "
  16160. "in expression '%s'.",
  16161. pixel_type(),_cimg_mp_calling_function,
  16162. (ss - 4)>expr._data?ss - 4:expr._data);
  16163. }
  16164. char c1, c2;
  16165. // Simplify expression when possible.
  16166. do {
  16167. c2 = 0;
  16168. if (ss<se) {
  16169. while (*ss && (cimg::is_blank(*ss) || *ss==';')) ++ss; // Remove leading blanks and ';'
  16170. while (se>ss && (cimg::is_blank(c1 = *(se - 1)) || c1==';')) --se; // Remove trailing blanks and ';'
  16171. }
  16172. while (*ss=='(' && *(se - 1)==')' && std::strchr(ss,')')==se - 1) { // Remove useless start/end parentheses
  16173. ++ss; --se; c2 = 1;
  16174. }
  16175. if (*ss=='_' && ss + 1<se && ss[1]=='(') { // Remove leading '_(something)' comment.
  16176. const unsigned int clevel = level[ss - expr._data];
  16177. ss+=2;
  16178. while (ss<se && (*ss!=')' || level[ss - expr._data]!=clevel)) ++ss;
  16179. if (ss<se) ++ss;
  16180. if (ss>=se) return _cimg_mp_slot_nan;
  16181. c2 = 1;
  16182. }
  16183. } while (c2 && ss<se);
  16184. if (se<=ss || !*ss) {
  16185. cimg::strellipsize(expr,64);
  16186. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  16187. "CImg<%s>::%s: %s%s Missing %s, in expression '%s'.",
  16188. pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
  16189. *s_op=='F'?"argument":"item",
  16190. ss_op);
  16191. }
  16192. static const size_t siz_ref = 7*sizeof(unsigned int);
  16193. const char *const previous_s_op = s_op, *const previous_ss_op = ss_op;
  16194. const unsigned int depth1 = depth + 1;
  16195. unsigned int pos, p1, p2, p3, arg1, arg2, arg3, arg4, arg5, arg6;
  16196. char
  16197. *const se1 = se - 1, *const se2 = se - 2, *const se3 = se - 3,
  16198. *const ss1 = ss + 1, *const ss2 = ss + 2, *const ss3 = ss + 3, *const ss4 = ss + 4,
  16199. *const ss5 = ss + 5, *const ss6 = ss + 6, *const ss7 = ss + 7, *const ss8 = ss + 8,
  16200. *s, *ps, *ns, *s0, *s1, *s2, *s3, sep = 0, end = 0;
  16201. double val = 0, val1, val2;
  16202. mp_func op;
  16203. return_new_comp = false;
  16204. // Bits of 'block_flags' tell about in which code block we currently are:
  16205. // 0: critical(), 1: begin(), 2: begin_t(), 3: end(), 4: end_t().
  16206. const bool
  16207. is_inside_critical = (bool)(block_flags&1),
  16208. is_inside_begin = (bool)(block_flags&2),
  16209. is_inside_begin_t = (bool)(block_flags&4),
  16210. is_inside_end = (bool)(block_flags&8),
  16211. is_inside_end_t = (bool)(block_flags&16);
  16212. // 'p_ref' is a 'unsigned int[7]' used to return a reference to an image or vector value
  16213. // linked to the returned memory slot (reference that cannot be determined at compile time).
  16214. // p_ref[0] can be { 0 = scalar (unlinked) | 1 = vector value | 2 = image value (offset) |
  16215. // 3 = image value (coordinates) | 4 = image value as a vector (offset) |
  16216. // 5 = image value as a vector (coordinates) }.
  16217. // Depending on p_ref[0], the remaining p_ref[k] have the following meaning:
  16218. // When p_ref[0]==0, p_ref is actually unlinked.
  16219. // When p_ref[0]==1, p_ref = [ 1, vector_ind, offset ].
  16220. // When p_ref[0]==2, p_ref = [ 2, image_ind (or ~0U), is_relative, offset ].
  16221. // When p_ref[0]==3, p_ref = [ 3, image_ind (or ~0U), is_relative, x, y, z, c ].
  16222. // When p_ref[0]==4, p_ref = [ 4, image_ind (or ~0U), is_relative, offset ].
  16223. // When p_ref[0]==5, p_ref = [ 5, image_ind (or ~0U), is_relative, x, y, z ].
  16224. 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; }
  16225. const char saved_char = *se; *se = 0;
  16226. const unsigned int clevel = level[ss - expr._data], clevel1 = clevel + 1;
  16227. bool is_sth, is_relative;
  16228. CImg<uintT> ref;
  16229. CImg<charT> variable_name;
  16230. CImgList<ulongT> l_opcode;
  16231. // Look for a single value or a pre-defined variable.
  16232. int nb = 0;
  16233. s = ss + (*ss=='+' || *ss=='-'?1:0);
  16234. if (*s=='i' || *s=='I' || *s=='n' || *s=='N') { // Particular cases : +/-NaN and +/-Inf
  16235. is_sth = *ss=='-';
  16236. if (!cimg::strcasecmp(s,"inf")) { val = cimg::type<double>::inf(); nb = 1; }
  16237. else if (!cimg::strcasecmp(s,"nan")) { val = cimg::type<double>::nan(); nb = 1; }
  16238. if (nb==1 && is_sth) val = -val;
  16239. } else if (*s=='0' && (s[1]=='x' || s[1]=='X')) { // Hexadecimal number
  16240. is_sth = *ss=='-';
  16241. if (cimg_sscanf(s + 2,"%x%c",&arg1,&sep)==1) {
  16242. nb = 1;
  16243. val = (double)arg1;
  16244. if (is_sth) val = -val;
  16245. }
  16246. }
  16247. if (!nb) nb = cimg_sscanf(ss,"%lf%c%c",&val,&(sep=0),&(end=0));
  16248. if (nb==1) _cimg_mp_const_scalar(val);
  16249. if (nb==2 && sep=='%') _cimg_mp_const_scalar(val/100);
  16250. if (ss1==se) switch (*ss) { // One-char reserved variable
  16251. case 'c' : _cimg_mp_return(reserved_label[(int)'c']!=~0U?reserved_label[(int)'c']:_cimg_mp_slot_c);
  16252. case 'd' : _cimg_mp_return(reserved_label[(int)'d']!=~0U?reserved_label[(int)'d']:20);
  16253. case 'e' : _cimg_mp_return(reserved_label[(int)'e']!=~0U?reserved_label[(int)'e']:27);
  16254. case 'h' : _cimg_mp_return(reserved_label[(int)'h']!=~0U?reserved_label[(int)'h']:19);
  16255. case 'k' :
  16256. if (reserved_label[(int)'k']!=~0U) _cimg_mp_return(reserved_label[(int)'k']);
  16257. pos = get_mem_img_index();
  16258. if (pos!=~0U) _cimg_mp_return(pos);
  16259. _cimg_mp_return_nan();
  16260. case 'l' : _cimg_mp_return(reserved_label[(int)'l']!=~0U?reserved_label[(int)'l']:26);
  16261. case 'r' : _cimg_mp_return(reserved_label[(int)'r']!=~0U?reserved_label[(int)'r']:22);
  16262. case 's' : _cimg_mp_return(reserved_label[(int)'s']!=~0U?reserved_label[(int)'s']:21);
  16263. case 't' : _cimg_mp_return(reserved_label[(int)'t']!=~0U?reserved_label[(int)'t']:_cimg_mp_slot_t);
  16264. case 'n' :
  16265. if (reserved_label[(int)'n']!=~0U) _cimg_mp_return(reserved_label[(int)'n']);
  16266. #if cimg_use_openmp!=0
  16267. _cimg_mp_const_scalar((double)omp_get_max_threads());
  16268. #else
  16269. _cimg_mp_return(1);
  16270. #endif
  16271. case 'w' : _cimg_mp_return(reserved_label[(int)'w']!=~0U?reserved_label[(int)'w']:18);
  16272. case 'x' : _cimg_mp_return(reserved_label[(int)'x']!=~0U?reserved_label[(int)'x']:_cimg_mp_slot_x);
  16273. case 'y' : _cimg_mp_return(reserved_label[(int)'y']!=~0U?reserved_label[(int)'y']:_cimg_mp_slot_y);
  16274. case 'z' : _cimg_mp_return(reserved_label[(int)'z']!=~0U?reserved_label[(int)'z']:_cimg_mp_slot_z);
  16275. case 'u' :
  16276. if (reserved_label[(int)'u']!=~0U) _cimg_mp_return(reserved_label[(int)'u']);
  16277. _cimg_mp_scalar2(mp_u,0,1);
  16278. case 'v' :
  16279. if (reserved_label[(int)'v']!=~0U) _cimg_mp_return(reserved_label[(int)'v']);
  16280. _cimg_mp_scalar2(mp_u,11,1);
  16281. case 'g' :
  16282. if (reserved_label[(int)'g']!=~0U) _cimg_mp_return(reserved_label[(int)'g']);
  16283. _cimg_mp_scalar0(mp_g);
  16284. case 'i' :
  16285. if (reserved_label[(int)'i']!=~0U) _cimg_mp_return(reserved_label[(int)'i']);
  16286. _cimg_mp_scalar0(mp_i);
  16287. case 'I' :
  16288. _cimg_mp_op("Variable 'I'");
  16289. if (reserved_label[(int)'I']!=~0U) _cimg_mp_return(reserved_label[(int)'I']);
  16290. if (!imgin._spectrum) _cimg_mp_return(0);
  16291. need_input_copy = true;
  16292. pos = vector(imgin._spectrum);
  16293. CImg<ulongT>::vector((ulongT)mp_Joff,pos,0,0,imgin._spectrum).move_to(code);
  16294. return_new_comp = true;
  16295. _cimg_mp_return(pos);
  16296. case 'R' :
  16297. if (reserved_label[(int)'R']!=~0U) _cimg_mp_return(reserved_label[(int)'R']);
  16298. need_input_copy = true;
  16299. _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0,0,0);
  16300. case 'G' :
  16301. if (reserved_label[(int)'G']!=~0U) _cimg_mp_return(reserved_label[(int)'G']);
  16302. need_input_copy = true;
  16303. _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1,0,0);
  16304. case 'B' :
  16305. if (reserved_label[(int)'B']!=~0U) _cimg_mp_return(reserved_label[(int)'B']);
  16306. need_input_copy = true;
  16307. _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2,0,0);
  16308. case 'A' :
  16309. if (reserved_label[(int)'A']!=~0U) _cimg_mp_return(reserved_label[(int)'A']);
  16310. need_input_copy = true;
  16311. _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3,0,0);
  16312. }
  16313. else if (ss2==se) { // Two-chars reserved variable
  16314. arg1 = arg2 = ~0U;
  16315. if (*ss=='w' && *ss1=='h') // wh
  16316. _cimg_mp_return(reserved_label[0]!=~0U?reserved_label[0]:23);
  16317. if (*ss=='p' && *ss1=='i') // pi
  16318. _cimg_mp_return(reserved_label[3]!=~0U?reserved_label[3]:28);
  16319. if (*ss=='i') {
  16320. if (*ss1>='0' && *ss1<='9') { // i0...i9
  16321. pos = 21 + *ss1 - '0';
  16322. if (reserved_label[pos]!=~0U) _cimg_mp_return(reserved_label[pos]);
  16323. need_input_copy = true;
  16324. _cimg_mp_scalar6(mp_ixyzc,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,pos - 21,0,0);
  16325. }
  16326. switch (*ss1) {
  16327. case 'a' : arg1 = 6; arg2 = 2; break; // ia
  16328. case 'c' : // ic
  16329. if (reserved_label[11]!=~0U) _cimg_mp_return(reserved_label[11]);
  16330. if (mem_img_median==~0U) mem_img_median = imgin?const_scalar(imgin.median()):0;
  16331. _cimg_mp_return(mem_img_median);
  16332. break;
  16333. case 'd' : arg1 = 8; arg2 = 3; break; // id
  16334. case 'm' : arg1 = 4; arg2 = 0; break; // im
  16335. case 'M' : arg1 = 5; arg2 = 1; break; // iM
  16336. case 'n' : // in
  16337. if (reserved_label[12]!=~0U) _cimg_mp_return(reserved_label[12]);
  16338. if (mem_img_norm==~0U) mem_img_norm = imgin?const_scalar(imgin.magnitude(2)):0;
  16339. _cimg_mp_return(mem_img_norm);
  16340. break;
  16341. case 'p' : arg1 = 10; arg2 = 13; break; // ip
  16342. case 's' : arg1 = 9; arg2 = 12; break; // is
  16343. case 'v' : arg1 = 7; arg2 = 3; break; // iv
  16344. }
  16345. }
  16346. else if (*ss1=='m') switch (*ss) {
  16347. case 'x' : arg1 = 13; arg2 = 4; break; // xm
  16348. case 'y' : arg1 = 14; arg2 = 5; break; // ym
  16349. case 'z' : arg1 = 15; arg2 = 6; break; // zm
  16350. case 'c' : arg1 = 16; arg2 = 7; break; // cm
  16351. }
  16352. else if (*ss1=='M') switch (*ss) {
  16353. case 'x' : arg1 = 17; arg2 = 8; break; // xM
  16354. case 'y' : arg1 = 18; arg2 = 9; break; // yM
  16355. case 'z' : arg1 = 19; arg2 = 10; break; // zM
  16356. case 'c' : arg1 = 20; arg2 = 11; break; // cM
  16357. }
  16358. if (arg1!=~0U) {
  16359. if (reserved_label[arg1]!=~0U) _cimg_mp_return(reserved_label[arg1]);
  16360. if (!img_stats) {
  16361. img_stats.assign(1,14,1,1,0).fill(imgin.get_stats(),false);
  16362. mem_img_stats.assign(1,14,1,1,~0U);
  16363. }
  16364. if (mem_img_stats[arg2]==~0U) mem_img_stats[arg2] = const_scalar(img_stats[arg2]);
  16365. if (arg1==8) _cimg_mp_const_scalar(std::sqrt(img_stats[arg2])); // id: std variation
  16366. _cimg_mp_return(mem_img_stats[arg2]);
  16367. }
  16368. } else if (ss3==se) { // Three-chars reserved variable
  16369. if (*ss=='w' && *ss1=='h' && *ss2=='d') // whd
  16370. _cimg_mp_return(reserved_label[1]!=~0U?reserved_label[1]:24);
  16371. } else if (ss4==se) { // Four-chars reserved variable
  16372. if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s') // whds
  16373. _cimg_mp_return(reserved_label[2]!=~0U?reserved_label[2]:25);
  16374. }
  16375. pos = ~0U;
  16376. is_sth = false;
  16377. for (s0 = ss, s = ss1; s<se1; ++s)
  16378. if (*s==';' && level[s - expr._data]==clevel) { // Separator ';'
  16379. is_end_code = false;
  16380. arg1 = compile(s0,s++,depth,0,block_flags);
  16381. if (!is_end_code) pos = arg1; // 'end()' and 'end_t()' return void
  16382. is_sth = true;
  16383. while (*s && (cimg::is_blank(*s) || *s==';')) ++s;
  16384. s0 = s;
  16385. }
  16386. if (is_sth) {
  16387. is_end_code = false;
  16388. arg1 = compile(s0,se,depth,p_ref,block_flags);
  16389. if (!is_end_code) pos = arg1; // 'end()' and 'end_t()' return void
  16390. _cimg_mp_return(pos!=~0U?pos:_cimg_mp_slot_nan);
  16391. }
  16392. // Declare / assign variable, vector value or image value.
  16393. for (s = ss1, ps = ss, ns = ss2; s<se1; ++s, ++ps, ++ns)
  16394. if (*s=='=' && *ns!='=' && *ps!='=' && *ps!='>' && *ps!='<' && *ps!='!' &&
  16395. *ps!='+' && *ps!='-' && *ps!='*' && *ps!='/' && *ps!='%' &&
  16396. *ps!='>' && *ps!='<' && *ps!='&' && *ps!='|' && *ps!='^' &&
  16397. level[s - expr._data]==clevel) {
  16398. variable_name.assign(ss,(unsigned int)(s + 1 - ss)).back() = 0;
  16399. cimg::strpare(variable_name,false,true);
  16400. const unsigned int l_variable_name = (unsigned int)std::strlen(variable_name);
  16401. char *const ve1 = ss + l_variable_name - 1;
  16402. _cimg_mp_op("Operator '='");
  16403. // Assign image value (direct).
  16404. if (l_variable_name>2 && (*ss=='i' || *ss=='j' || *ss=='I' || *ss=='J') && (*ss1=='(' || *ss1=='[') &&
  16405. (reserved_label[(int)*ss]==~0U || *ss1=='(' || !_cimg_mp_is_vector(reserved_label[(int)*ss]))) {
  16406. is_relative = *ss=='j' || *ss=='J';
  16407. if (*ss1=='[' && *ve1==']') { // i/j/I/J[_#ind,offset] = value
  16408. if (!is_inside_critical) is_parallelizable = false;
  16409. if (*ss2=='#') { // Index specified
  16410. s0 = ss3; while (s0<ve1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  16411. p1 = compile(ss3,s0++,depth1,0,block_flags);
  16412. _cimg_mp_check_notnan_index(p1);
  16413. _cimg_mp_check_list();
  16414. } else { p1 = ~0U; s0 = ss2; }
  16415. arg1 = compile(s0,ve1,depth1,0,block_flags); // Offset
  16416. _cimg_mp_check_type(arg1,0,1,0);
  16417. arg2 = compile(s + 1,se,depth1,0,block_flags); // Value to assign
  16418. _cimg_mp_check_type(arg2,2,*ss>='i'?1:3,0);
  16419. if (_cimg_mp_is_vector(arg2)) {
  16420. if (p1!=~0U) {
  16421. _cimg_mp_check_const_index(p1);
  16422. p3 = (unsigned int)cimg::mod((int)mem[p1],imglist.width());
  16423. p2 = imglist[p3]._spectrum;
  16424. } else p2 = imgin._spectrum;
  16425. if (!p2) _cimg_mp_return(0);
  16426. _cimg_mp_check_type(arg2,2,2,p2);
  16427. } else p2 = 0;
  16428. if (p_ref) {
  16429. *p_ref = *ss=='I' || *ss=='J'?4:2;
  16430. p_ref[1] = p1;
  16431. p_ref[2] = (unsigned int)is_relative;
  16432. p_ref[3] = arg1;
  16433. if (_cimg_mp_is_vector(arg2))
  16434. set_reserved_vector(arg2); // Prevent from being used in further optimization
  16435. else if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1;
  16436. if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1;
  16437. }
  16438. if (p1!=~0U) {
  16439. if (!imglist) _cimg_mp_return(arg2);
  16440. if (*ss>='i')
  16441. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
  16442. arg2,p1,arg1).move_to(code);
  16443. else if (_cimg_mp_is_scalar(arg2))
  16444. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s),
  16445. arg2,p1,arg1).move_to(code);
  16446. else
  16447. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
  16448. arg2,p1,arg1,_cimg_mp_size(arg2)).move_to(code);
  16449. } else {
  16450. if (!imgout) _cimg_mp_return(arg2);
  16451. if (*ss>='i')
  16452. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
  16453. arg2,arg1).move_to(code);
  16454. else if (_cimg_mp_is_scalar(arg2))
  16455. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s),
  16456. arg2,arg1).move_to(code);
  16457. else
  16458. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
  16459. arg2,arg1,_cimg_mp_size(arg2)).move_to(code);
  16460. }
  16461. _cimg_mp_return(arg2);
  16462. }
  16463. if (*ss1=='(' && *ve1==')') { // i/j/I/J(_#ind,_x,_y,_z,_c) = value
  16464. if (!is_inside_critical) is_parallelizable = false;
  16465. if (*ss2=='#') { // Index specified
  16466. s0 = ss3; while (s0<ve1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  16467. p1 = compile(ss3,s0++,depth1,0,block_flags);
  16468. _cimg_mp_check_notnan_index(p1);
  16469. _cimg_mp_check_list();
  16470. } else { p1 = ~0U; s0 = ss2; }
  16471. arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
  16472. arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
  16473. arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
  16474. arg4 = is_relative?0U:(unsigned int)_cimg_mp_slot_c;
  16475. arg5 = compile(s + 1,se,depth1,0,block_flags); // Value to assign
  16476. _cimg_mp_check_type(arg5,2,*ss>='i'?1:3,0);
  16477. if (s0<ve1) { // X or [ X,_Y,_Z,_C ]
  16478. s1 = s0; while (s1<ve1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  16479. arg1 = compile(s0,s1,depth1,0,block_flags);
  16480. if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
  16481. p2 = _cimg_mp_size(arg1); // Vector size
  16482. ++arg1;
  16483. if (p2>1) {
  16484. arg2 = arg1 + 1;
  16485. if (p2>2) {
  16486. arg3 = arg2 + 1;
  16487. if (p2>3) arg4 = arg3 + 1;
  16488. }
  16489. }
  16490. } else if (s1<ve1) { // Y
  16491. s2 = ++s1; while (s2<ve1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  16492. arg2 = compile(s1,s2,depth1,0,block_flags);
  16493. if (s2<ve1) { // Z
  16494. s3 = ++s2; while (s3<ve1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
  16495. arg3 = compile(s2,s3,depth1,0,block_flags);
  16496. if (s3<ve1) arg4 = compile(++s3,ve1,depth1,0,block_flags); // C
  16497. }
  16498. }
  16499. }
  16500. if (_cimg_mp_is_vector(arg5)) {
  16501. if (p1!=~0U) {
  16502. _cimg_mp_check_const_index(p1);
  16503. p3 = (unsigned int)cimg::mod((int)mem[p1],imglist.width());
  16504. p2 = imglist[p3]._spectrum;
  16505. } else p2 = imgin._spectrum;
  16506. if (!p2) _cimg_mp_return(0);
  16507. _cimg_mp_check_type(arg5,2,2,p2);
  16508. } else p2 = 0;
  16509. if (p_ref) {
  16510. *p_ref = *ss=='I' || *ss=='J'?5:3;
  16511. p_ref[1] = p1;
  16512. p_ref[2] = (unsigned int)is_relative;
  16513. p_ref[3] = arg1;
  16514. p_ref[4] = arg2;
  16515. p_ref[5] = arg3;
  16516. p_ref[6] = arg4;
  16517. if (_cimg_mp_is_vector(arg5))
  16518. set_reserved_vector(arg5); // Prevent from being used in further optimization
  16519. else if (_cimg_mp_is_comp(arg5)) memtype[arg5] = -1;
  16520. if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -1;
  16521. if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1;
  16522. if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1;
  16523. if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1;
  16524. if (_cimg_mp_is_comp(arg4)) memtype[arg4] = -1;
  16525. }
  16526. if (p1!=~0U) {
  16527. if (!imglist) _cimg_mp_return(arg5);
  16528. if (*ss>='i')
  16529. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
  16530. arg5,p1,arg1,arg2,arg3,arg4).move_to(code);
  16531. else if (_cimg_mp_is_scalar(arg5))
  16532. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s),
  16533. arg5,p1,arg1,arg2,arg3).move_to(code);
  16534. else
  16535. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
  16536. arg5,p1,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code);
  16537. } else {
  16538. if (!imgout) _cimg_mp_return(arg5);
  16539. if (*ss>='i')
  16540. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
  16541. arg5,arg1,arg2,arg3,arg4).move_to(code);
  16542. else if (_cimg_mp_is_scalar(arg5))
  16543. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s),
  16544. arg5,arg1,arg2,arg3).move_to(code);
  16545. else
  16546. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
  16547. arg5,arg1,arg2,arg3,_cimg_mp_size(arg5)).move_to(code);
  16548. }
  16549. _cimg_mp_return(arg5);
  16550. }
  16551. }
  16552. // Assign vector value (direct).
  16553. if (l_variable_name>3 && *ve1==']' && *ss!='[') {
  16554. s0 = ve1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0;
  16555. if (s0>ss && cimg::is_varname(ss,s0 - ss)) {
  16556. variable_name[s0 - ss] = 0; // Remove brackets in variable name
  16557. get_variable_pos(variable_name,arg1,arg2);
  16558. arg1 = arg2!=~0U?reserved_label[arg2]:arg1!=~0U?variable_pos[arg1]:~0U; // Vector slot
  16559. if (arg1==~0U || _cimg_mp_is_scalar(arg1))
  16560. compile(ss,s0,depth1,0,block_flags); // Variable does not exist or is not a vector -> error
  16561. arg2 = compile(++s0,ve1,depth1,0,block_flags); // Index
  16562. arg3 = compile(s + 1,se,depth1,0,block_flags); // Value to assign
  16563. _cimg_mp_check_type(arg3,2,1,0);
  16564. if (_cimg_mp_is_const_scalar(arg2)) { // Constant index -> return corresponding variable slot directly
  16565. nb = (int)mem[arg2];
  16566. if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) {
  16567. arg1+=nb + 1;
  16568. CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg3).move_to(code);
  16569. _cimg_mp_return(arg1);
  16570. }
  16571. compile(ss,s,depth1,0,block_flags); // Out-of-bounds reference -> error
  16572. }
  16573. // Case of non-constant index -> return assigned value + linked reference
  16574. if (p_ref) {
  16575. *p_ref = 1;
  16576. p_ref[1] = arg1;
  16577. p_ref[2] = arg2;
  16578. if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1; // Prevent from being used in further optimization
  16579. if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1;
  16580. }
  16581. CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg3,arg1,(ulongT)_cimg_mp_size(arg1),arg2).
  16582. move_to(code);
  16583. _cimg_mp_return(arg3);
  16584. }
  16585. }
  16586. // Assign user-defined macro.
  16587. if (l_variable_name>2 && *ve1==')' && *ss!='(') {
  16588. s0 = ve1; while (s0>ss && *s0!='(') --s0;
  16589. if (cimg::is_varname(ss,s0 - ss) && std::strncmp(variable_name,"debug(",6) &&
  16590. std::strncmp(variable_name,"print(",6)) { // Valid macro name
  16591. s0 = variable_name._data + (s0 - ss);
  16592. *s0 = 0;
  16593. s1 = variable_name._data + l_variable_name - 1; // Pointer to closing parenthesis
  16594. CImg<charT>(variable_name._data,(unsigned int)(s0 - variable_name._data + 1)).move_to(macro_def,0);
  16595. ++s; while (*s && cimg::is_blank(*s)) ++s;
  16596. CImg<charT>(s,(unsigned int)(se - s + 1)).move_to(macro_body,0);
  16597. bool is_variadic = false;
  16598. p1 = 1; // Index of current parsed argument
  16599. for (s = s0 + 1; s<=s1; ++p1, s = ns + 1) { // Parse function arguments
  16600. if (is_variadic && p1>1) {
  16601. _cimg_mp_strerr;
  16602. cimg::strellipsize(variable_name,64);
  16603. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  16604. "CImg<%s>::%s: %s: Multiple arguments not allowed when first one is "
  16605. "variadic, in macro definition '%s()', in expression '%s'.",
  16606. pixel_type(),_cimg_mp_calling_function,s_op,
  16607. variable_name._data,s0);
  16608. }
  16609. if (p1>24) {
  16610. _cimg_mp_strerr;
  16611. cimg::strellipsize(variable_name,64);
  16612. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  16613. "CImg<%s>::%s: %s: Too much specified arguments (>24), in macro "
  16614. "definition '%s()', in expression '%s'.",
  16615. pixel_type(),_cimg_mp_calling_function,s_op,
  16616. variable_name._data,s0);
  16617. }
  16618. while (*s && cimg::is_blank(*s)) ++s;
  16619. if (*s==')' && p1==1) break; // Function has no arguments
  16620. s2 = s; // Start of the argument name
  16621. is_sth = true; // is_valid_argument_name?
  16622. if (*s2>='0' && *s2<='9') is_sth = false;
  16623. else for (ns = s2; ns<s1 && *ns!=',' && *ns!='.' && !cimg::is_blank(*ns); ++ns)
  16624. if (!cimg::is_varchar(*ns)) { is_sth = false; break; }
  16625. s3 = ns; // End of the argument name
  16626. if (is_sth && ns>s2 && *ns=='.' && ns[1]=='.' && ns[2]=='.') { is_variadic = true; ns+=3; }
  16627. else if (*ns=='.') is_sth = false;
  16628. while (*ns && cimg::is_blank(*ns)) ++ns;
  16629. if (!is_sth || s2==s3 || (*ns!=',' && ns!=s1)) {
  16630. _cimg_mp_strerr;
  16631. cimg::strellipsize(variable_name,64);
  16632. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  16633. "CImg<%s>::%s: %s: %s name specified for argument %u when defining "
  16634. "macro '%s()', in expression '%s'.",
  16635. pixel_type(),_cimg_mp_calling_function,s_op,
  16636. is_sth?"Empty":"Invalid",p1,
  16637. variable_name._data,s0);
  16638. }
  16639. if (ns==s1 || *ns==',' || (is_variadic && *ns=='.')) { // New argument found
  16640. *s3 = 0;
  16641. p2 = (unsigned int)(s3 - s2); // Argument length
  16642. for (ps = std::strstr(macro_body[0],s2); ps; ps = std::strstr(ps,s2)) { // Replace by arg number
  16643. if (!((ps>macro_body[0]._data && cimg::is_varchar(*(ps - 1))) ||
  16644. (ps + p2<macro_body[0].end() && cimg::is_varchar(*(ps + p2))))) {
  16645. if (ps>macro_body[0]._data && *(ps - 1)=='#') { // Remove pre-number sign
  16646. *(ps - 1) = (char)p1;
  16647. if (ps + p2<macro_body[0].end() && *(ps + p2)=='#') { // Has pre & post number signs
  16648. std::memmove(ps,ps + p2 + 1,macro_body[0].end() - ps - p2 - 1);
  16649. macro_body[0]._width-=p2 + 1;
  16650. } else { // Has pre number sign only
  16651. std::memmove(ps,ps + p2,macro_body[0].end() - ps - p2);
  16652. macro_body[0]._width-=p2;
  16653. }
  16654. } else if (ps + p2<macro_body[0].end() && *(ps + p2)=='#') { // Remove post-number sign
  16655. *(ps++) = (char)p1;
  16656. std::memmove(ps,ps + p2,macro_body[0].end() - ps - p2);
  16657. macro_body[0]._width-=p2;
  16658. } else if (is_variadic) { // Replace variadic argument
  16659. *(ps++) = (char)p1;
  16660. std::memmove(ps,ps + p2 - 1,macro_body[0].end() - ps - p2 + 1);
  16661. macro_body[0]._width-=p2 - 1;
  16662. } else { // Not near a number sign
  16663. if (p2<3) {
  16664. ps-=(ulongT)macro_body[0]._data;
  16665. macro_body[0].resize(macro_body[0]._width - p2 + 3,1,1,1,0);
  16666. ps+=(ulongT)macro_body[0]._data;
  16667. } else macro_body[0]._width-=p2 - 3;
  16668. std::memmove(ps + 3,ps + p2,macro_body[0].end() - ps - 3);
  16669. *(ps++) = '(';
  16670. *(ps++) = (char)p1;
  16671. *(ps++) = ')';
  16672. }
  16673. } else ++ps;
  16674. }
  16675. }
  16676. }
  16677. // Store number of arguments.
  16678. macro_def[0].resize(macro_def[0]._width + 1,1,1,1,0).back() = is_variadic?(char)-1:(char)(p1 - 1);
  16679. _cimg_mp_return_nan();
  16680. }
  16681. }
  16682. // Check if the variable name could be valid. If not, this is probably an lvalue assignment.
  16683. const bool is_const = l_variable_name>6 && !std::strncmp(variable_name,"const ",6);
  16684. s0 = variable_name._data;
  16685. if (is_const) {
  16686. s0+=6; while (cimg::is_blank(*s0)) ++s0;
  16687. variable_name.resize(variable_name.end() - s0,1,1,1,0,0,1);
  16688. }
  16689. if (cimg::is_varname(variable_name)) { // Valid variable name
  16690. // Assign variable (direct).
  16691. get_variable_pos(variable_name,arg1,arg2);
  16692. arg3 = compile(s + 1,se,depth1,0,block_flags);
  16693. is_sth = return_new_comp; // is arg3 a new blank object?
  16694. if (is_const) _cimg_mp_check_const_scalar(arg3,2,0);
  16695. arg1 = arg2!=~0U?reserved_label[arg2]:arg1!=~0U?variable_pos[arg1]:~0U;
  16696. if (arg1==~0U) { // Create new variable
  16697. if (_cimg_mp_is_vector(arg3)) { // Vector variable
  16698. arg1 = is_sth || is_comp_vector(arg3)?arg3:vector_copy(arg3);
  16699. set_reserved_vector(arg1); // Prevent from being used in further optimization
  16700. } else { // Scalar variable
  16701. if (is_const) arg1 = arg3;
  16702. else {
  16703. arg1 = is_sth || _cimg_mp_is_comp(arg3)?arg3:scalar1(mp_copy,arg3);
  16704. memtype[arg1] = -1;
  16705. }
  16706. }
  16707. if (arg2!=~0U) reserved_label[arg2] = arg1;
  16708. else {
  16709. if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0);
  16710. variable_pos[variable_def._width] = arg1;
  16711. variable_name.move_to(variable_def);
  16712. }
  16713. } else { // Variable already exists -> assign a new value
  16714. if (is_const || _cimg_mp_is_const_scalar(arg1)) {
  16715. _cimg_mp_strerr;
  16716. cimg::strellipsize(variable_name,64);
  16717. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  16718. "CImg<%s>::%s: %s: Invalid assignment of %sconst variable '%s'%s, "
  16719. "in expression '%s'.",
  16720. pixel_type(),_cimg_mp_calling_function,s_op,
  16721. _cimg_mp_is_const_scalar(arg1)?"":"non-",
  16722. variable_name._data,
  16723. !_cimg_mp_is_const_scalar(arg1) && is_const?" as a const variable":"",
  16724. s0);
  16725. }
  16726. _cimg_mp_check_type(arg3,2,_cimg_mp_is_vector(arg1)?3:1,_cimg_mp_size(arg1));
  16727. if (_cimg_mp_is_vector(arg1)) { // Vector
  16728. if (_cimg_mp_is_vector(arg3)) // From vector
  16729. CImg<ulongT>::vector((ulongT)mp_vector_copy,arg1,arg3,(ulongT)_cimg_mp_size(arg1)).
  16730. move_to(code);
  16731. else // From scalar
  16732. CImg<ulongT>::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg3).
  16733. move_to(code);
  16734. } else // Scalar
  16735. CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg3).move_to(code);
  16736. }
  16737. return_new_comp = false;
  16738. _cimg_mp_return(arg1);
  16739. }
  16740. // Assign lvalue (variable name was not valid for a direct assignment).
  16741. arg1 = ~0U;
  16742. is_sth = (bool)std::strchr(variable_name,'?'); // Contains_ternary_operator?
  16743. if (is_sth) break; // Do nothing and make ternary operator priority over assignment
  16744. if (l_variable_name>2 && (std::strchr(variable_name,'(') || std::strchr(variable_name,'['))) {
  16745. ref.assign(7);
  16746. arg1 = compile(ss,s,depth1,ref,block_flags); // Lvalue slot
  16747. arg2 = compile(s + 1,se,depth1,0,block_flags); // Value to assign
  16748. if (*ref==1) { // Vector value (scalar): V[k] = scalar
  16749. _cimg_mp_check_type(arg2,2,1,0);
  16750. arg3 = ref[1]; // Vector slot
  16751. arg4 = ref[2]; // Index
  16752. if (p_ref) std::memcpy(p_ref,ref,siz_ref);
  16753. CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg2,arg3,(ulongT)_cimg_mp_size(arg3),arg4).
  16754. move_to(code);
  16755. _cimg_mp_return(arg2);
  16756. }
  16757. if (*ref==2) { // Image value (scalar): i/j[_#ind,off] = scalar
  16758. if (!is_inside_critical) is_parallelizable = false;
  16759. _cimg_mp_check_type(arg2,2,1,0);
  16760. p1 = ref[1]; // Index
  16761. is_relative = (bool)ref[2];
  16762. arg3 = ref[3]; // Offset
  16763. if (p_ref) std::memcpy(p_ref,ref,siz_ref);
  16764. if (p1!=~0U) {
  16765. if (!imglist) _cimg_mp_return(arg2);
  16766. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
  16767. arg2,p1,arg3).move_to(code);
  16768. } else {
  16769. if (!imgout) _cimg_mp_return(arg2);
  16770. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
  16771. arg2,arg3).move_to(code);
  16772. }
  16773. _cimg_mp_return(arg2);
  16774. }
  16775. if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) = scalar
  16776. if (!is_inside_critical) is_parallelizable = false;
  16777. _cimg_mp_check_type(arg2,2,1,0);
  16778. p1 = ref[1]; // Index
  16779. is_relative = (bool)ref[2];
  16780. arg3 = ref[3]; // X
  16781. arg4 = ref[4]; // Y
  16782. arg5 = ref[5]; // Z
  16783. arg6 = ref[6]; // C
  16784. if (p_ref) std::memcpy(p_ref,ref,siz_ref);
  16785. if (p1!=~0U) {
  16786. if (!imglist) _cimg_mp_return(arg2);
  16787. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
  16788. arg2,p1,arg3,arg4,arg5,arg6).move_to(code);
  16789. } else {
  16790. if (!imgout) _cimg_mp_return(arg2);
  16791. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
  16792. arg2,arg3,arg4,arg5,arg6).move_to(code);
  16793. }
  16794. _cimg_mp_return(arg2);
  16795. }
  16796. if (*ref==4) { // Image value (vector): I/J[_#ind,off] = value
  16797. if (!is_inside_critical) is_parallelizable = false;
  16798. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  16799. p1 = ref[1]; // Index
  16800. is_relative = (bool)ref[2];
  16801. arg3 = ref[3]; // Offset
  16802. if (p_ref) std::memcpy(p_ref,ref,siz_ref);
  16803. if (p1!=~0U) {
  16804. if (!imglist) _cimg_mp_return(arg2);
  16805. if (_cimg_mp_is_scalar(arg2))
  16806. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s),
  16807. arg2,p1,arg3).move_to(code);
  16808. else {
  16809. _cimg_mp_check_const_index(p1);
  16810. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
  16811. arg2,p1,arg3,_cimg_mp_size(arg2)).move_to(code);
  16812. }
  16813. } else {
  16814. if (!imgout) _cimg_mp_return(arg2);
  16815. if (_cimg_mp_is_scalar(arg2))
  16816. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s),
  16817. arg2,arg3).move_to(code);
  16818. else
  16819. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
  16820. arg2,arg3,_cimg_mp_size(arg2)).move_to(code);
  16821. }
  16822. _cimg_mp_return(arg2);
  16823. }
  16824. if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) = value
  16825. if (!is_inside_critical) is_parallelizable = false;
  16826. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  16827. p1 = ref[1]; // Index
  16828. is_relative = (bool)ref[2];
  16829. arg3 = ref[3]; // X
  16830. arg4 = ref[4]; // Y
  16831. arg5 = ref[5]; // Z
  16832. if (p_ref) std::memcpy(p_ref,ref,siz_ref);
  16833. if (p1!=~0U) {
  16834. if (!imglist) _cimg_mp_return(arg2);
  16835. if (_cimg_mp_is_scalar(arg2))
  16836. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s),
  16837. arg2,p1,arg3,arg4,arg5).move_to(code);
  16838. else {
  16839. _cimg_mp_check_const_index(p1);
  16840. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
  16841. arg2,p1,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code);
  16842. }
  16843. } else {
  16844. if (!imgout) _cimg_mp_return(arg2);
  16845. if (_cimg_mp_is_scalar(arg2))
  16846. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s),
  16847. arg2,arg3,arg4,arg5).move_to(code);
  16848. else
  16849. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
  16850. arg2,arg3,arg4,arg5,_cimg_mp_size(arg2)).move_to(code);
  16851. }
  16852. _cimg_mp_return(arg2);
  16853. }
  16854. if (_cimg_mp_is_vector(arg1)) { // Vector variable: V = value
  16855. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  16856. if (_cimg_mp_is_vector(arg2)) // From vector
  16857. CImg<ulongT>::vector((ulongT)mp_vector_copy,arg1,arg2,(ulongT)_cimg_mp_size(arg1)).
  16858. move_to(code);
  16859. else // From scalar
  16860. CImg<ulongT>::vector((ulongT)mp_vector_init,arg1,1,(ulongT)_cimg_mp_size(arg1),arg2).
  16861. move_to(code);
  16862. _cimg_mp_return(arg1);
  16863. }
  16864. if (_cimg_mp_is_reserved(arg1)) { // Scalar variable: s = scalar
  16865. _cimg_mp_check_type(arg2,2,1,0);
  16866. CImg<ulongT>::vector((ulongT)mp_copy,arg1,arg2).move_to(code);
  16867. _cimg_mp_return(arg1);
  16868. }
  16869. }
  16870. // No assignment expressions match -> error
  16871. _cimg_mp_strerr;
  16872. cimg::strellipsize(variable_name,64);
  16873. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  16874. "CImg<%s>::%s: %s: Invalid %slvalue '%s', "
  16875. "in expression '%s'.",
  16876. pixel_type(),_cimg_mp_calling_function,s_op,
  16877. arg1!=~0U && _cimg_mp_is_const_scalar(arg1)?"const ":"",
  16878. variable_name._data,s0);
  16879. }
  16880. // Apply unary/binary/ternary operators. The operator precedences should be the same as in C++.
  16881. for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1
  16882. if (*s=='=' && (*ps=='*' || *ps=='/' || *ps=='^') && *ns==*ps &&
  16883. level[s - expr._data]==clevel) { // Self-operators for complex numbers only (**=,//=,^^=)
  16884. _cimg_mp_op(*ps=='*'?"Operator '**='":*ps=='/'?"Operator '//='":"Operator '^^='");
  16885. ref.assign(7);
  16886. arg1 = compile(ss,ns,depth1,ref,block_flags); // Vector slot
  16887. arg2 = compile(s + 1,se,depth1,0,block_flags); // Right operand
  16888. _cimg_mp_check_type(arg1,1,2,2);
  16889. _cimg_mp_check_type(arg2,2,3,2);
  16890. if (_cimg_mp_is_vector(arg2)) { // Complex **= complex
  16891. if (*ps=='*')
  16892. CImg<ulongT>::vector((ulongT)mp_complex_mul,arg1,arg1,arg2).move_to(code);
  16893. else if (*ps=='/')
  16894. CImg<ulongT>::vector((ulongT)mp_complex_div_vv,arg1,arg1,arg2).move_to(code);
  16895. else
  16896. CImg<ulongT>::vector((ulongT)mp_complex_pow_vv,arg1,arg1,arg2).move_to(code);
  16897. } else { // Complex **= scalar
  16898. if (*ps=='*') {
  16899. if (arg2==1) _cimg_mp_return(arg1);
  16900. self_vector_s(arg1,mp_self_mul,arg2);
  16901. } else if (*ps=='/') {
  16902. if (arg2==1) _cimg_mp_return(arg1);
  16903. self_vector_s(arg1,mp_self_div,arg2);
  16904. } else {
  16905. if (arg2==1) _cimg_mp_return(arg1);
  16906. CImg<ulongT>::vector((ulongT)mp_complex_pow_vs,arg1,arg1,arg2).move_to(code);
  16907. }
  16908. }
  16909. if (*ref==4) { // Image value (vector): I/J[_#ind,off] **= value
  16910. if (!is_inside_critical) is_parallelizable = false;
  16911. p1 = ref[1]; // Index
  16912. is_relative = (bool)ref[2];
  16913. arg3 = ref[3]; // Offset
  16914. if (p_ref) std::memcpy(p_ref,ref,siz_ref);
  16915. if (p1!=~0U) {
  16916. if (!imglist) _cimg_mp_return(arg1);
  16917. _cimg_mp_check_const_index(p1);
  16918. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
  16919. arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
  16920. } else {
  16921. if (!imgout) _cimg_mp_return(arg1);
  16922. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
  16923. arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
  16924. }
  16925. } else if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) **= value
  16926. if (!is_inside_critical) is_parallelizable = false;
  16927. p1 = ref[1]; // Index
  16928. is_relative = (bool)ref[2];
  16929. arg3 = ref[3]; // X
  16930. arg4 = ref[4]; // Y
  16931. arg5 = ref[5]; // Z
  16932. if (p_ref) std::memcpy(p_ref,ref,siz_ref);
  16933. if (p1!=~0U) {
  16934. if (!imglist) _cimg_mp_return(arg1);
  16935. _cimg_mp_check_const_index(p1);
  16936. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
  16937. arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
  16938. } else {
  16939. if (!imgout) _cimg_mp_return(arg1);
  16940. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
  16941. arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
  16942. }
  16943. }
  16944. _cimg_mp_return(arg1);
  16945. }
  16946. for (s = se2, ps = se3, ns = ps - 1; s>ss1; --s, --ps, --ns) // Here, ns = ps - 1
  16947. if (*s=='=' && (*ps=='+' || *ps=='-' || *ps=='*' || *ps=='/' || (*ps=='%' && s[1]!='=') ||
  16948. *ps=='&' || *ps=='^' || *ps=='|' ||
  16949. (*ps=='>' && *ns=='>') || (*ps=='<' && *ns=='<')) &&
  16950. level[s - expr._data]==clevel) { // Self-operators (+=,-=,*=,/=,%=,>>=,<<=,&=,^=,|=)
  16951. switch (*ps) {
  16952. case '+' : op = mp_self_add; _cimg_mp_op("Operator '+='"); break;
  16953. case '-' : op = mp_self_sub; _cimg_mp_op("Operator '-='"); break;
  16954. case '*' : op = mp_self_mul; _cimg_mp_op("Operator '*='"); break;
  16955. case '/' : op = mp_self_div; _cimg_mp_op("Operator '/='"); break;
  16956. case '%' : op = mp_self_modulo; _cimg_mp_op("Operator '%='"); break;
  16957. case '<' : op = mp_self_bitwise_left_shift; _cimg_mp_op("Operator '<<='"); break;
  16958. case '>' : op = mp_self_bitwise_right_shift; _cimg_mp_op("Operator '>>='"); break;
  16959. case '&' : op = mp_self_bitwise_and; _cimg_mp_op("Operator '&='"); break;
  16960. case '|' : op = mp_self_bitwise_or; _cimg_mp_op("Operator '|='"); break;
  16961. default : op = mp_self_pow; _cimg_mp_op("Operator '^='"); break;
  16962. }
  16963. s1 = *ps=='>' || *ps=='<'?ns:ps;
  16964. ref.assign(7);
  16965. arg1 = compile(ss,s1,depth1,ref,block_flags); // Variable slot
  16966. arg2 = compile(s + 1,se,depth1,0,block_flags); // Value to apply
  16967. // Check for particular case to be simplified.
  16968. if ((op==mp_self_add || op==mp_self_sub) && !arg2) _cimg_mp_return(arg1);
  16969. if ((op==mp_self_mul || op==mp_self_div) && arg2==1) _cimg_mp_return(arg1);
  16970. // Apply operator on a copy to prevent modifying a constant or a variable.
  16971. if (*ref && (_cimg_mp_is_const_scalar(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_reserved(arg1))) {
  16972. if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1);
  16973. else arg1 = scalar1(mp_copy,arg1);
  16974. }
  16975. if (*ref==1) { // Vector value (scalar): V[k] += scalar
  16976. _cimg_mp_check_type(arg2,2,1,0);
  16977. arg3 = ref[1]; // Vector slot
  16978. arg4 = ref[2]; // Index
  16979. if (p_ref) std::memcpy(p_ref,ref,siz_ref);
  16980. CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
  16981. CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4).
  16982. move_to(code);
  16983. _cimg_mp_return(arg1);
  16984. }
  16985. if (*ref==2) { // Image value (scalar): i/j[_#ind,off] += scalar
  16986. if (!is_inside_critical) is_parallelizable = false;
  16987. _cimg_mp_check_type(arg2,2,1,0);
  16988. p1 = ref[1]; // Index
  16989. is_relative = (bool)ref[2];
  16990. arg3 = ref[3]; // Offset
  16991. if (p_ref) std::memcpy(p_ref,ref,siz_ref);
  16992. CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
  16993. if (p1!=~0U) {
  16994. if (!imglist) _cimg_mp_return(arg1);
  16995. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
  16996. arg1,p1,arg3).move_to(code);
  16997. } else {
  16998. if (!imgout) _cimg_mp_return(arg1);
  16999. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
  17000. arg1,arg3).move_to(code);
  17001. }
  17002. _cimg_mp_return(arg1);
  17003. }
  17004. if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c) += scalar
  17005. if (!is_inside_critical) is_parallelizable = false;
  17006. _cimg_mp_check_type(arg2,2,1,0);
  17007. p1 = ref[1]; // Index
  17008. is_relative = (bool)ref[2];
  17009. arg3 = ref[3]; // X
  17010. arg4 = ref[4]; // Y
  17011. arg5 = ref[5]; // Z
  17012. arg6 = ref[6]; // C
  17013. if (p_ref) std::memcpy(p_ref,ref,siz_ref);
  17014. CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
  17015. if (p1!=~0U) {
  17016. if (!imglist) _cimg_mp_return(arg1);
  17017. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
  17018. arg1,p1,arg3,arg4,arg5,arg6).move_to(code);
  17019. } else {
  17020. if (!imgout) _cimg_mp_return(arg1);
  17021. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
  17022. arg1,arg3,arg4,arg5,arg6).move_to(code);
  17023. }
  17024. _cimg_mp_return(arg1);
  17025. }
  17026. if (*ref==4) { // Image value (vector): I/J[_#ind,off] += value
  17027. if (!is_inside_critical) is_parallelizable = false;
  17028. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  17029. p1 = ref[1]; // Index
  17030. is_relative = (bool)ref[2];
  17031. arg3 = ref[3]; // Offset
  17032. if (p_ref) std::memcpy(p_ref,ref,siz_ref);
  17033. if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2);
  17034. if (p1!=~0U) {
  17035. if (!imglist) _cimg_mp_return(arg1);
  17036. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
  17037. arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
  17038. } else {
  17039. if (!imgout) _cimg_mp_return(arg1);
  17040. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
  17041. arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
  17042. }
  17043. _cimg_mp_return(arg1);
  17044. }
  17045. if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c) += value
  17046. if (!is_inside_critical) is_parallelizable = false;
  17047. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  17048. p1 = ref[1]; // Index
  17049. is_relative = (bool)ref[2];
  17050. arg3 = ref[3]; // X
  17051. arg4 = ref[4]; // Y
  17052. arg5 = ref[5]; // Z
  17053. if (p_ref) std::memcpy(p_ref,ref,siz_ref);
  17054. if (_cimg_mp_is_scalar(arg2)) self_vector_s(arg1,op,arg2); else self_vector_v(arg1,op,arg2);
  17055. if (p1!=~0U) {
  17056. if (!imglist) _cimg_mp_return(arg1);
  17057. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
  17058. arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
  17059. } else {
  17060. if (!imgout) _cimg_mp_return(arg1);
  17061. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
  17062. arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
  17063. }
  17064. _cimg_mp_return(arg1);
  17065. }
  17066. if (_cimg_mp_is_vector(arg1)) { // Vector variable: V += value
  17067. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  17068. if (_cimg_mp_is_vector(arg2)) self_vector_v(arg1,op,arg2); // Vector += vector
  17069. else self_vector_s(arg1,op,arg2); // Vector += scalar
  17070. _cimg_mp_return(arg1);
  17071. }
  17072. if (_cimg_mp_is_reserved(arg1)) { // Scalar variable: s += scalar
  17073. _cimg_mp_check_type(arg2,2,1,0);
  17074. CImg<ulongT>::vector((ulongT)op,arg1,arg2).move_to(code);
  17075. _cimg_mp_return(arg1);
  17076. }
  17077. variable_name.assign(ss,(unsigned int)(s - ss)).back() = 0;
  17078. cimg::strpare(variable_name,false,true);
  17079. _cimg_mp_strerr;
  17080. cimg::strellipsize(variable_name,64);
  17081. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  17082. "CImg<%s>::%s: %s: Invalid %slvalue '%s', "
  17083. "in expression '%s'.",
  17084. pixel_type(),_cimg_mp_calling_function,s_op,
  17085. _cimg_mp_is_const_scalar(arg1)?"const ":"",
  17086. variable_name._data,s0);
  17087. }
  17088. for (s = ss1; s<se1; ++s)
  17089. if (*s=='?' && level[s - expr._data]==clevel) { // Ternary operator 'cond?expr1:expr2'
  17090. _cimg_mp_op("Operator '?:'");
  17091. s1 = s + 1; while (s1<se1 && (*s1!=':' || level[s1 - expr._data]!=clevel)) ++s1;
  17092. arg1 = compile(ss,s,depth1,0,block_flags);
  17093. _cimg_mp_check_type(arg1,1,1,0);
  17094. if (_cimg_mp_is_const_scalar(arg1)) {
  17095. if ((bool)mem[arg1]) return compile(s + 1,*s1!=':'?se:s1,depth1,0,block_flags);
  17096. else return *s1!=':'?0:compile(++s1,se,depth1,0,block_flags);
  17097. }
  17098. p2 = code._width;
  17099. arg2 = compile(s + 1,*s1!=':'?se:s1,depth1,0,block_flags);
  17100. p3 = code._width;
  17101. arg3 = *s1==':'?compile(++s1,se,depth1,0,block_flags):
  17102. _cimg_mp_is_vector(arg2)?vector(_cimg_mp_size(arg2),0):0;
  17103. _cimg_mp_check_type(arg3,3,_cimg_mp_is_vector(arg2)?2:1,_cimg_mp_size(arg2));
  17104. arg4 = _cimg_mp_size(arg2);
  17105. if (arg4) pos = vector(arg4); else pos = scalar();
  17106. CImg<ulongT>::vector((ulongT)mp_if,pos,arg1,arg2,arg3,
  17107. p3 - p2,code._width - p3,arg4).move_to(code,p2);
  17108. return_new_comp = true;
  17109. _cimg_mp_return(pos);
  17110. }
  17111. for (s = se3, ns = se2; s>ss; --s, --ns)
  17112. if (*s=='|' && *ns=='|' && level[s - expr._data]==clevel) { // Logical or ('||')
  17113. _cimg_mp_op("Operator '||'");
  17114. arg1 = compile(ss,s,depth1,0,block_flags);
  17115. _cimg_mp_check_type(arg1,1,1,0);
  17116. if (arg1>0 && arg1<=16) _cimg_mp_return(1);
  17117. p2 = code._width;
  17118. arg2 = compile(s + 2,se,depth1,0,block_flags);
  17119. _cimg_mp_check_type(arg2,2,1,0);
  17120. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
  17121. _cimg_mp_const_scalar(mem[arg1] || mem[arg2]);
  17122. if (!arg1) _cimg_mp_return(arg2);
  17123. pos = scalar();
  17124. CImg<ulongT>::vector((ulongT)mp_logical_or,pos,arg1,arg2,code._width - p2).
  17125. move_to(code,p2);
  17126. return_new_comp = true;
  17127. _cimg_mp_return(pos);
  17128. }
  17129. for (s = se3, ns = se2; s>ss; --s, --ns)
  17130. if (*s=='&' && *ns=='&' && level[s - expr._data]==clevel) { // Logical and ('&&')
  17131. _cimg_mp_op("Operator '&&'");
  17132. arg1 = compile(ss,s,depth1,0,block_flags);
  17133. _cimg_mp_check_type(arg1,1,1,0);
  17134. if (!arg1) _cimg_mp_return(0);
  17135. p2 = code._width;
  17136. arg2 = compile(s + 2,se,depth1,0,block_flags);
  17137. _cimg_mp_check_type(arg2,2,1,0);
  17138. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
  17139. _cimg_mp_const_scalar(mem[arg1] && mem[arg2]);
  17140. if (arg1>0 && arg1<=16) _cimg_mp_return(arg2);
  17141. pos = scalar();
  17142. CImg<ulongT>::vector((ulongT)mp_logical_and,pos,arg1,arg2,code._width - p2).
  17143. move_to(code,p2);
  17144. return_new_comp = true;
  17145. _cimg_mp_return(pos);
  17146. }
  17147. for (s = se2; s>ss; --s)
  17148. if (*s=='|' && level[s - expr._data]==clevel) { // Bitwise or ('|')
  17149. _cimg_mp_op("Operator '|'");
  17150. arg1 = compile(ss,s,depth1,0,block_flags);
  17151. arg2 = compile(s + 1,se,depth1,0,block_flags);
  17152. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  17153. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_or,arg1,arg2);
  17154. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
  17155. if (!arg2) _cimg_mp_return(arg1);
  17156. _cimg_mp_vector2_vs(mp_bitwise_or,arg1,arg2);
  17157. }
  17158. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
  17159. if (!arg1) _cimg_mp_return(arg2);
  17160. _cimg_mp_vector2_sv(mp_bitwise_or,arg1,arg2);
  17161. }
  17162. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
  17163. _cimg_mp_const_scalar((longT)mem[arg1] | (longT)mem[arg2]);
  17164. if (!arg2) _cimg_mp_return(arg1);
  17165. if (!arg1) _cimg_mp_return(arg2);
  17166. _cimg_mp_scalar2(mp_bitwise_or,arg1,arg2);
  17167. }
  17168. for (s = se2; s>ss; --s)
  17169. if (*s=='&' && level[s - expr._data]==clevel) { // Bitwise and ('&')
  17170. _cimg_mp_op("Operator '&'");
  17171. arg1 = compile(ss,s,depth1,0,block_flags);
  17172. arg2 = compile(s + 1,se,depth1,0,block_flags);
  17173. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  17174. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_and,arg1,arg2);
  17175. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_and,arg1,arg2);
  17176. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_and,arg1,arg2);
  17177. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
  17178. _cimg_mp_const_scalar((longT)mem[arg1] & (longT)mem[arg2]);
  17179. if (!arg1 || !arg2) _cimg_mp_return(0);
  17180. _cimg_mp_scalar2(mp_bitwise_and,arg1,arg2);
  17181. }
  17182. for (s = se3, ns = se2; s>ss; --s, --ns)
  17183. if (*s=='!' && *ns=='=' && level[s - expr._data]==clevel) { // Not equal to ('!=')
  17184. _cimg_mp_op("Operator '!='");
  17185. arg1 = compile(ss,s,depth1,0,block_flags);
  17186. arg2 = compile(s + 2,se,depth1,0,block_flags);
  17187. if (arg1==arg2) _cimg_mp_return(0);
  17188. p1 = _cimg_mp_size(arg1);
  17189. p2 = _cimg_mp_size(arg2);
  17190. if (p1 || p2) {
  17191. if (p1 && p2 && p1!=p2) _cimg_mp_return(1);
  17192. pos = scalar();
  17193. CImg<ulongT>::vector((ulongT)mp_vector_neq,pos,arg1,p1,arg2,p2,11,1).move_to(code);
  17194. return_new_comp = true;
  17195. _cimg_mp_return(pos);
  17196. }
  17197. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
  17198. _cimg_mp_const_scalar(mem[arg1]!=mem[arg2]);
  17199. _cimg_mp_scalar2(mp_neq,arg1,arg2);
  17200. }
  17201. for (s = se3, ns = se2; s>ss; --s, --ns)
  17202. if (*s=='=' && *ns=='=' && level[s - expr._data]==clevel) { // Equal to ('==')
  17203. _cimg_mp_op("Operator '=='");
  17204. arg1 = compile(ss,s,depth1,0,block_flags);
  17205. arg2 = compile(s + 2,se,depth1,0,block_flags);
  17206. if (arg1==arg2) _cimg_mp_return(1);
  17207. p1 = _cimg_mp_size(arg1);
  17208. p2 = _cimg_mp_size(arg2);
  17209. if (p1 || p2) {
  17210. if (p1 && p2 && p1!=p2) _cimg_mp_return(0);
  17211. pos = scalar();
  17212. CImg<ulongT>::vector((ulongT)mp_vector_eq,pos,arg1,p1,arg2,p2,11,1).move_to(code);
  17213. return_new_comp = true;
  17214. _cimg_mp_return(pos);
  17215. }
  17216. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
  17217. _cimg_mp_const_scalar(mem[arg1]==mem[arg2]);
  17218. _cimg_mp_scalar2(mp_eq,arg1,arg2);
  17219. }
  17220. for (s = se3, ns = se2; s>ss; --s, --ns)
  17221. if (*s=='<' && *ns=='=' && level[s - expr._data]==clevel) { // Less or equal than ('<=')
  17222. _cimg_mp_op("Operator '<='");
  17223. arg1 = compile(ss,s,depth1,0,block_flags);
  17224. arg2 = compile(s + 2,se,depth1,0,block_flags);
  17225. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  17226. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lte,arg1,arg2);
  17227. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lte,arg1,arg2);
  17228. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lte,arg1,arg2);
  17229. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
  17230. _cimg_mp_const_scalar(mem[arg1]<=mem[arg2]);
  17231. if (arg1==arg2) _cimg_mp_return(1);
  17232. _cimg_mp_scalar2(mp_lte,arg1,arg2);
  17233. }
  17234. for (s = se3, ns = se2; s>ss; --s, --ns)
  17235. if (*s=='>' && *ns=='=' && level[s - expr._data]==clevel) { // Greater or equal than ('>=')
  17236. _cimg_mp_op("Operator '>='");
  17237. arg1 = compile(ss,s,depth1,0,block_flags);
  17238. arg2 = compile(s + 2,se,depth1,0,block_flags);
  17239. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  17240. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gte,arg1,arg2);
  17241. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gte,arg1,arg2);
  17242. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gte,arg1,arg2);
  17243. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
  17244. _cimg_mp_const_scalar(mem[arg1]>=mem[arg2]);
  17245. if (arg1==arg2) _cimg_mp_return(1);
  17246. _cimg_mp_scalar2(mp_gte,arg1,arg2);
  17247. }
  17248. for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps)
  17249. if (*s=='<' && *ns!='<' && *ps!='<' && level[s - expr._data]==clevel) { // Less than ('<')
  17250. _cimg_mp_op("Operator '<'");
  17251. arg1 = compile(ss,s,depth1,0,block_flags);
  17252. arg2 = compile(s + 1,se,depth1,0,block_flags);
  17253. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  17254. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_lt,arg1,arg2);
  17255. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_lt,arg1,arg2);
  17256. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_lt,arg1,arg2);
  17257. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
  17258. _cimg_mp_const_scalar(mem[arg1]<mem[arg2]);
  17259. if (arg1==arg2) _cimg_mp_return(0);
  17260. _cimg_mp_scalar2(mp_lt,arg1,arg2);
  17261. }
  17262. for (s = se2, ns = se1, ps = se3; s>ss; --s, --ns, --ps)
  17263. if (*s=='>' && *ns!='>' && *ps!='>' && level[s - expr._data]==clevel) { // Greater than ('>')
  17264. _cimg_mp_op("Operator '>'");
  17265. arg1 = compile(ss,s,depth1,0,block_flags);
  17266. arg2 = compile(s + 1,se,depth1,0,block_flags);
  17267. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  17268. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_gt,arg1,arg2);
  17269. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_gt,arg1,arg2);
  17270. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_gt,arg1,arg2);
  17271. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
  17272. _cimg_mp_const_scalar(mem[arg1]>mem[arg2]);
  17273. if (arg1==arg2) _cimg_mp_return(0);
  17274. _cimg_mp_scalar2(mp_gt,arg1,arg2);
  17275. }
  17276. for (s = se3, ns = se2; s>ss; --s, --ns)
  17277. if (*s=='<' && *ns=='<' && level[s - expr._data]==clevel) { // Left bit shift ('<<')
  17278. _cimg_mp_op("Operator '<<'");
  17279. arg1 = compile(ss,s,depth1,0,block_flags);
  17280. arg2 = compile(s + 2,se,depth1,0,block_flags);
  17281. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  17282. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2))
  17283. _cimg_mp_vector2_vv(mp_bitwise_left_shift,arg1,arg2);
  17284. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
  17285. if (!arg2) _cimg_mp_return(arg1);
  17286. _cimg_mp_vector2_vs(mp_bitwise_left_shift,arg1,arg2);
  17287. }
  17288. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2))
  17289. _cimg_mp_vector2_sv(mp_bitwise_left_shift,arg1,arg2);
  17290. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
  17291. _cimg_mp_const_scalar((longT)mem[arg1]<<(unsigned int)mem[arg2]);
  17292. if (!arg1) _cimg_mp_return(0);
  17293. if (!arg2) _cimg_mp_return(arg1);
  17294. _cimg_mp_scalar2(mp_bitwise_left_shift,arg1,arg2);
  17295. }
  17296. for (s = se3, ns = se2; s>ss; --s, --ns)
  17297. if (*s=='>' && *ns=='>' && level[s - expr._data]==clevel) { // Right bit shift ('>>')
  17298. _cimg_mp_op("Operator '>>'");
  17299. arg1 = compile(ss,s,depth1,0,block_flags);
  17300. arg2 = compile(s + 2,se,depth1,0,block_flags);
  17301. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  17302. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2))
  17303. _cimg_mp_vector2_vv(mp_bitwise_right_shift,arg1,arg2);
  17304. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) {
  17305. if (!arg2) _cimg_mp_return(arg1);
  17306. _cimg_mp_vector2_vs(mp_bitwise_right_shift,arg1,arg2);
  17307. }
  17308. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2))
  17309. _cimg_mp_vector2_sv(mp_bitwise_right_shift,arg1,arg2);
  17310. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
  17311. _cimg_mp_const_scalar((longT)mem[arg1]>>(unsigned int)mem[arg2]);
  17312. if (!arg1) _cimg_mp_return(0);
  17313. if (!arg2) _cimg_mp_return(arg1);
  17314. _cimg_mp_scalar2(mp_bitwise_right_shift,arg1,arg2);
  17315. }
  17316. for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps)
  17317. if (*s=='+' && (*ns!='+' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' &&
  17318. *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' &&
  17319. (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' &&
  17320. *(ps - 1)<='9')))) &&
  17321. level[s - expr._data]==clevel) { // Addition ('+')
  17322. _cimg_mp_op("Operator '+'");
  17323. arg1 = compile(ss,s,depth1,0,block_flags);
  17324. arg2 = compile(s + 1,se,depth1,0,block_flags);
  17325. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  17326. if (!arg2) _cimg_mp_return(arg1);
  17327. if (!arg1) _cimg_mp_return(arg2);
  17328. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_add,arg1,arg2);
  17329. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_add,arg1,arg2);
  17330. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_add,arg1,arg2);
  17331. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
  17332. _cimg_mp_const_scalar(mem[arg1] + mem[arg2]);
  17333. if (code) { // Try to spot linear case 'a*b + c'
  17334. CImg<ulongT> &pop = code.back();
  17335. if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) {
  17336. arg3 = (unsigned int)pop[1];
  17337. arg4 = (unsigned int)pop[2];
  17338. arg5 = (unsigned int)pop[3];
  17339. code.remove();
  17340. CImg<ulongT>::vector((ulongT)mp_linear_add,arg3,arg4,arg5,arg3==arg2?arg1:arg2).move_to(code);
  17341. _cimg_mp_return(arg3);
  17342. }
  17343. }
  17344. if (arg2==1) _cimg_mp_scalar1(mp_increment,arg1);
  17345. if (arg1==1) _cimg_mp_scalar1(mp_increment,arg2);
  17346. _cimg_mp_scalar2(mp_add,arg1,arg2);
  17347. }
  17348. for (ns = se1, s = se2, ps = pexpr._data + (se3 - expr._data); s>ss; --ns, --s, --ps)
  17349. if (*s=='-' && (*ns!='-' || ns!=se1) && *ps!='-' && *ps!='+' && *ps!='*' && *ps!='/' && *ps!='%' &&
  17350. *ps!='&' && *ps!='|' && *ps!='^' && *ps!='!' && *ps!='~' && *ps!='#' &&
  17351. (*ps!='e' || !(ps - pexpr._data>ss - expr._data && (*(ps - 1)=='.' || (*(ps - 1)>='0' &&
  17352. *(ps - 1)<='9')))) &&
  17353. level[s - expr._data]==clevel) { // Subtraction ('-')
  17354. _cimg_mp_op("Operator '-'");
  17355. arg1 = compile(ss,s,depth1,0,block_flags);
  17356. arg2 = compile(s + 1,se,depth1,0,block_flags);
  17357. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  17358. if (!arg2) _cimg_mp_return(arg1);
  17359. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_sub,arg1,arg2);
  17360. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_sub,arg1,arg2);
  17361. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
  17362. if (!arg1) _cimg_mp_vector1_v(mp_minus,arg2);
  17363. _cimg_mp_vector2_sv(mp_sub,arg1,arg2);
  17364. }
  17365. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
  17366. _cimg_mp_const_scalar(mem[arg1] - mem[arg2]);
  17367. if (!arg1) _cimg_mp_scalar1(mp_minus,arg2);
  17368. if (code) { // Try to spot linear cases 'a*b - c' and 'c - a*b'
  17369. CImg<ulongT> &pop = code.back();
  17370. if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) {
  17371. arg3 = (unsigned int)pop[1];
  17372. arg4 = (unsigned int)pop[2];
  17373. arg5 = (unsigned int)pop[3];
  17374. code.remove();
  17375. CImg<ulongT>::vector((ulongT)(arg3==arg1?mp_linear_sub_left:mp_linear_sub_right),
  17376. arg3,arg4,arg5,arg3==arg1?arg2:arg1).move_to(code);
  17377. _cimg_mp_return(arg3);
  17378. }
  17379. }
  17380. if (arg2==1) _cimg_mp_scalar1(mp_decrement,arg1);
  17381. _cimg_mp_scalar2(mp_sub,arg1,arg2);
  17382. }
  17383. for (s = se3, ns = se2; s>ss; --s, --ns)
  17384. if (*s=='*' && *ns=='*' && level[s - expr._data]==clevel) { // Complex multiplication ('**')
  17385. _cimg_mp_op("Operator '**'");
  17386. arg1 = compile(ss,s,depth1,0,block_flags);
  17387. arg2 = compile(s + 2,se,depth1,0,block_flags);
  17388. _cimg_mp_check_type(arg1,1,3,2);
  17389. _cimg_mp_check_type(arg2,2,3,2);
  17390. if (arg2==1) _cimg_mp_return(arg1);
  17391. if (arg1==1) _cimg_mp_return(arg2);
  17392. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) {
  17393. pos = vector(2);
  17394. CImg<ulongT>::vector((ulongT)mp_complex_mul,pos,arg1,arg2).move_to(code);
  17395. return_new_comp = true;
  17396. _cimg_mp_return(pos);
  17397. }
  17398. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2);
  17399. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2);
  17400. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
  17401. _cimg_mp_const_scalar(mem[arg1]*mem[arg2]);
  17402. if (!arg1 || !arg2) _cimg_mp_return(0);
  17403. _cimg_mp_scalar2(mp_mul,arg1,arg2);
  17404. }
  17405. for (s = se3, ns = se2; s>ss; --s, --ns)
  17406. if (*s=='/' && *ns=='/' && level[s - expr._data]==clevel) { // Complex division ('//')
  17407. _cimg_mp_op("Operator '//'");
  17408. arg1 = compile(ss,s,depth1,0,block_flags);
  17409. arg2 = compile(s + 2,se,depth1,0,block_flags);
  17410. _cimg_mp_check_type(arg1,1,3,2);
  17411. _cimg_mp_check_type(arg2,2,3,2);
  17412. if (arg2==1) _cimg_mp_return(arg1);
  17413. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) {
  17414. pos = vector(2);
  17415. CImg<ulongT>::vector((ulongT)mp_complex_div_vv,pos,arg1,arg2).move_to(code);
  17416. return_new_comp = true;
  17417. _cimg_mp_return(pos);
  17418. }
  17419. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2);
  17420. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) {
  17421. pos = vector(2);
  17422. CImg<ulongT>::vector((ulongT)mp_complex_div_sv,pos,arg1,arg2).move_to(code);
  17423. return_new_comp = true;
  17424. _cimg_mp_return(pos);
  17425. }
  17426. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
  17427. _cimg_mp_const_scalar(mem[arg1]/mem[arg2]);
  17428. if (!arg1) _cimg_mp_return(0);
  17429. _cimg_mp_scalar2(mp_div,arg1,arg2);
  17430. }
  17431. for (s = se2; s>ss; --s) if (*s=='*' && level[s - expr._data]==clevel) { // Multiplication ('*')
  17432. _cimg_mp_op("Operator '*'");
  17433. arg1 = compile(ss,s,depth1,0,block_flags);
  17434. arg2 = compile(s + 1,se,depth1,0,block_flags);
  17435. p2 = _cimg_mp_size(arg2);
  17436. if (p2>0 && (ulongT)_cimg_mp_size(arg1)==(ulongT)p2*p2) { // Particular case of matrix multiplication
  17437. pos = vector(p2);
  17438. CImg<ulongT>::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,p2,p2,1).move_to(code);
  17439. return_new_comp = true;
  17440. _cimg_mp_return(pos);
  17441. }
  17442. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  17443. if (arg2==1) _cimg_mp_return(arg1);
  17444. if (arg1==1) _cimg_mp_return(arg2);
  17445. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_mul,arg1,arg2);
  17446. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_mul,arg1,arg2);
  17447. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_mul,arg1,arg2);
  17448. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
  17449. _cimg_mp_const_scalar(mem[arg1]*mem[arg2]);
  17450. if (code) { // Try to spot double multiplication 'a*b*c'
  17451. CImg<ulongT> &pop = code.back();
  17452. if (pop[0]==(ulongT)mp_mul && _cimg_mp_is_comp(pop[1]) && (pop[1]==arg1 || pop[1]==arg2)) {
  17453. arg3 = (unsigned int)pop[1];
  17454. arg4 = (unsigned int)pop[2];
  17455. arg5 = (unsigned int)pop[3];
  17456. code.remove();
  17457. CImg<ulongT>::vector((ulongT)mp_mul2,arg3,arg4,arg5,arg3==arg2?arg1:arg2).move_to(code);
  17458. _cimg_mp_return(arg3);
  17459. }
  17460. }
  17461. if (!arg1 || !arg2) _cimg_mp_return(0);
  17462. _cimg_mp_scalar2(mp_mul,arg1,arg2);
  17463. }
  17464. for (s = se2; s>ss; --s) if (*s=='/' && level[s - expr._data]==clevel) { // Division ('/')
  17465. _cimg_mp_op("Operator '/'");
  17466. arg1 = compile(ss,s,depth1,0,block_flags);
  17467. arg2 = compile(s + 1,se,depth1,0,block_flags);
  17468. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  17469. if (arg2==1) _cimg_mp_return(arg1);
  17470. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_div,arg1,arg2);
  17471. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_div,arg1,arg2);
  17472. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_div,arg1,arg2);
  17473. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
  17474. _cimg_mp_const_scalar(mem[arg1]/mem[arg2]);
  17475. if (!arg1) _cimg_mp_return(0);
  17476. _cimg_mp_scalar2(mp_div,arg1,arg2);
  17477. }
  17478. for (s = se2, ns = se1; s>ss; --s, --ns)
  17479. if (*s=='%' && *ns!='^' && level[s - expr._data]==clevel) { // Modulo ('%')
  17480. _cimg_mp_op("Operator '%'");
  17481. arg1 = compile(ss,s,depth1,0,block_flags);
  17482. arg2 = compile(s + 1,se,depth1,0,block_flags);
  17483. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  17484. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_modulo,arg1,arg2);
  17485. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_modulo,arg1,arg2);
  17486. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_modulo,arg1,arg2);
  17487. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
  17488. _cimg_mp_const_scalar(cimg::mod(mem[arg1],mem[arg2]));
  17489. _cimg_mp_scalar2(mp_modulo,arg1,arg2);
  17490. }
  17491. if (se1>ss) {
  17492. if (*ss=='+' && (*ss1!='+' || (ss2<se && *ss2>='0' && *ss2<='9'))) { // Unary plus ('+')
  17493. _cimg_mp_op("Operator '+'");
  17494. _cimg_mp_return(compile(ss1,se,depth1,0,block_flags));
  17495. }
  17496. if (*ss=='-' && (*ss1!='-' || (ss2<se && *ss2>='0' && *ss2<='9'))) { // Unary minus ('-')
  17497. _cimg_mp_op("Operator '-'");
  17498. arg1 = compile(ss1,se,depth1,0,block_flags);
  17499. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_minus,arg1);
  17500. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(-mem[arg1]);
  17501. _cimg_mp_scalar1(mp_minus,arg1);
  17502. }
  17503. if (*ss=='!') { // Logical not ('!')
  17504. _cimg_mp_op("Operator '!'");
  17505. if (*ss1=='!') { // '!!expr' optimized as 'bool(expr)'
  17506. arg1 = compile(ss2,se,depth1,0,block_flags);
  17507. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1);
  17508. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar((bool)mem[arg1]);
  17509. _cimg_mp_scalar1(mp_bool,arg1);
  17510. }
  17511. arg1 = compile(ss1,se,depth1,0,block_flags);
  17512. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_logical_not,arg1);
  17513. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(!mem[arg1]);
  17514. _cimg_mp_scalar1(mp_logical_not,arg1);
  17515. }
  17516. if (*ss=='~') { // Bitwise not ('~')
  17517. _cimg_mp_op("Operator '~'");
  17518. arg1 = compile(ss1,se,depth1,0,block_flags);
  17519. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bitwise_not,arg1);
  17520. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(~(unsigned int)mem[arg1]);
  17521. _cimg_mp_scalar1(mp_bitwise_not,arg1);
  17522. }
  17523. }
  17524. for (s = se3, ns = se2; s>ss; --s, --ns)
  17525. if (*s=='^' && *ns=='^' && level[s - expr._data]==clevel) { // Complex power ('^^')
  17526. _cimg_mp_op("Operator '^^'");
  17527. arg1 = compile(ss,s,depth1,0,block_flags);
  17528. arg2 = compile(s + 2,se,depth1,0,block_flags);
  17529. _cimg_mp_check_type(arg1,1,3,2);
  17530. _cimg_mp_check_type(arg2,2,3,2);
  17531. if (arg2==1) _cimg_mp_return(arg1);
  17532. pos = vector(2);
  17533. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2))
  17534. CImg<ulongT>::vector((ulongT)mp_complex_pow_vv,pos,arg1,arg2).move_to(code);
  17535. else if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2))
  17536. CImg<ulongT>::vector((ulongT)mp_complex_pow_vs,pos,arg1,arg2).move_to(code);
  17537. else if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2))
  17538. CImg<ulongT>::vector((ulongT)mp_complex_pow_sv,pos,arg1,arg2).move_to(code);
  17539. else
  17540. CImg<ulongT>::vector((ulongT)mp_complex_pow_ss,pos,arg1,arg2).move_to(code);
  17541. return_new_comp = true;
  17542. _cimg_mp_return(pos);
  17543. }
  17544. for (s = se2; s>ss; --s)
  17545. if (*s=='^' && level[s - expr._data]==clevel) { // Power ('^')
  17546. _cimg_mp_op("Operator '^'");
  17547. arg1 = compile(ss,s,depth1,0,block_flags);
  17548. arg2 = compile(s + 1,se,depth1,0,block_flags);
  17549. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  17550. if (arg2==1) _cimg_mp_return(arg1);
  17551. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_pow,arg1,arg2);
  17552. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_pow,arg1,arg2);
  17553. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_pow,arg1,arg2);
  17554. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
  17555. _cimg_mp_const_scalar(std::pow(mem[arg1],mem[arg2]));
  17556. switch (arg2) {
  17557. case 0 : _cimg_mp_return(1);
  17558. case 2 : _cimg_mp_scalar1(mp_sqr,arg1);
  17559. case 3 : _cimg_mp_scalar1(mp_pow3,arg1);
  17560. case 4 : _cimg_mp_scalar1(mp_pow4,arg1);
  17561. default :
  17562. if (_cimg_mp_is_const_scalar(arg2)) {
  17563. if (mem[arg2]==0.5) { _cimg_mp_scalar1(mp_sqrt,arg1); }
  17564. else if (mem[arg2]==0.25) { _cimg_mp_scalar1(mp_pow0_25,arg1); }
  17565. }
  17566. _cimg_mp_scalar2(mp_pow,arg1,arg2);
  17567. }
  17568. }
  17569. // Percentage computation.
  17570. if (*se1=='%') {
  17571. arg1 = compile(ss,se1,depth1,0,block_flags);
  17572. arg2 = _cimg_mp_is_const_scalar(arg1)?0:const_scalar(100);
  17573. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector2_vs(mp_div,arg1,arg2);
  17574. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(mem[arg1]/100);
  17575. _cimg_mp_scalar2(mp_div,arg1,arg2);
  17576. }
  17577. // Degree to radian postfix operator ('°' in UTF-8).
  17578. if (se2>ss && (unsigned char)*se2==0xC2 && (unsigned char)*se1==0xB0) {
  17579. arg1 = compile(ss,se2,depth1,0,block_flags);
  17580. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_deg2rad,arg1);
  17581. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(mem[arg1]*cimg::PI/180);
  17582. _cimg_mp_scalar1(mp_deg2rad,arg1);
  17583. }
  17584. // Pre/post-decrement and increment.
  17585. is_sth = ss1<se1 && (*ss=='+' || *ss=='-') && *ss1==*ss; // is pre-?
  17586. if (is_sth || (se2>ss && (*se1=='+' || *se1=='-') && *se2==*se1)) {
  17587. if ((is_sth && *ss=='+') || (!is_sth && *se1=='+')) {
  17588. _cimg_mp_op("Operator '++'");
  17589. op = mp_self_increment;
  17590. } else {
  17591. _cimg_mp_op("Operator '--'");
  17592. op = mp_self_decrement;
  17593. }
  17594. ref.assign(7);
  17595. arg1 = is_sth?compile(ss2,se,depth1,ref,block_flags):
  17596. compile(ss,se2,depth1,ref,block_flags); // Variable slot
  17597. // Apply operator on a copy to prevent modifying a constant or a variable.
  17598. if (*ref && (_cimg_mp_is_const_scalar(arg1) || _cimg_mp_is_vector(arg1) || _cimg_mp_is_reserved(arg1))) {
  17599. if (_cimg_mp_is_vector(arg1)) arg1 = vector_copy(arg1);
  17600. else arg1 = scalar1(mp_copy,arg1);
  17601. }
  17602. if (is_sth) pos = arg1; // Determine return index, depending on pre/post action
  17603. else {
  17604. if (_cimg_mp_is_vector(arg1)) pos = vector_copy(arg1);
  17605. else pos = scalar1(mp_copy,arg1);
  17606. }
  17607. if (*ref==1) { // Vector value (scalar): V[k]++
  17608. arg3 = ref[1]; // Vector slot
  17609. arg4 = ref[2]; // Index
  17610. if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
  17611. CImg<ulongT>::vector((ulongT)op,arg1,1).move_to(code);
  17612. CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4).
  17613. move_to(code);
  17614. _cimg_mp_return(pos);
  17615. }
  17616. if (*ref==2) { // Image value (scalar): i/j[_#ind,off]++
  17617. if (!is_inside_critical) is_parallelizable = false;
  17618. p1 = ref[1]; // Index
  17619. is_relative = (bool)ref[2];
  17620. arg3 = ref[3]; // Offset
  17621. if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
  17622. CImg<ulongT>::vector((ulongT)op,arg1).move_to(code);
  17623. if (p1!=~0U) {
  17624. if (!imglist) _cimg_mp_return(pos);
  17625. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
  17626. arg1,p1,arg3).move_to(code);
  17627. } else {
  17628. if (!imgout) _cimg_mp_return(pos);
  17629. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
  17630. arg1,arg3).move_to(code);
  17631. }
  17632. _cimg_mp_return(pos);
  17633. }
  17634. if (*ref==3) { // Image value (scalar): i/j(_#ind,_x,_y,_z,_c)++
  17635. if (!is_inside_critical) is_parallelizable = false;
  17636. p1 = ref[1]; // Index
  17637. is_relative = (bool)ref[2];
  17638. arg3 = ref[3]; // X
  17639. arg4 = ref[4]; // Y
  17640. arg5 = ref[5]; // Z
  17641. arg6 = ref[6]; // C
  17642. if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
  17643. CImg<ulongT>::vector((ulongT)op,arg1).move_to(code);
  17644. if (p1!=~0U) {
  17645. if (!imglist) _cimg_mp_return(pos);
  17646. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
  17647. arg1,p1,arg3,arg4,arg5,arg6).move_to(code);
  17648. } else {
  17649. if (!imgout) _cimg_mp_return(pos);
  17650. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
  17651. arg1,arg3,arg4,arg5,arg6).move_to(code);
  17652. }
  17653. _cimg_mp_return(pos);
  17654. }
  17655. if (*ref==4) { // Image value (vector): I/J[_#ind,off]++
  17656. if (!is_inside_critical) is_parallelizable = false;
  17657. p1 = ref[1]; // Index
  17658. is_relative = (bool)ref[2];
  17659. arg3 = ref[3]; // Offset
  17660. if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
  17661. self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1);
  17662. if (p1!=~0U) {
  17663. if (!imglist) _cimg_mp_return(pos);
  17664. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
  17665. arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
  17666. } else {
  17667. if (!imgout) _cimg_mp_return(pos);
  17668. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
  17669. arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
  17670. }
  17671. _cimg_mp_return(pos);
  17672. }
  17673. if (*ref==5) { // Image value (vector): I/J(_#ind,_x,_y,_z,_c)++
  17674. if (!is_inside_critical) is_parallelizable = false;
  17675. p1 = ref[1]; // Index
  17676. is_relative = (bool)ref[2];
  17677. arg3 = ref[3]; // X
  17678. arg4 = ref[4]; // Y
  17679. arg5 = ref[5]; // Z
  17680. if (is_sth && p_ref) std::memcpy(p_ref,ref,ref._width*sizeof(unsigned int));
  17681. self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1);
  17682. if (p1!=~0U) {
  17683. if (!imglist) _cimg_mp_return(pos);
  17684. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
  17685. arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
  17686. } else {
  17687. if (!imgout) _cimg_mp_return(pos);
  17688. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
  17689. arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
  17690. }
  17691. _cimg_mp_return(pos);
  17692. }
  17693. if (_cimg_mp_is_vector(arg1)) { // Vector variable: V++
  17694. self_vector_s(arg1,op==mp_self_increment?mp_self_add:mp_self_sub,1);
  17695. _cimg_mp_return(pos);
  17696. }
  17697. if (_cimg_mp_is_reserved(arg1)) { // Scalar variable: s++
  17698. CImg<ulongT>::vector((ulongT)op,arg1).move_to(code);
  17699. _cimg_mp_return(pos);
  17700. }
  17701. if (is_sth) variable_name.assign(ss2,(unsigned int)(se - ss1));
  17702. else variable_name.assign(ss,(unsigned int)(se1 - ss));
  17703. variable_name.back() = 0;
  17704. cimg::strpare(variable_name,false,true);
  17705. _cimg_mp_strerr;
  17706. cimg::strellipsize(variable_name,64);
  17707. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  17708. "CImg<%s>::%s: %s: Invalid %slvalue '%s', "
  17709. "in expression '%s'.",
  17710. pixel_type(),_cimg_mp_calling_function,s_op,
  17711. _cimg_mp_is_const_scalar(arg1)?"const ":"",
  17712. variable_name._data,s0);
  17713. }
  17714. // Array-like access to vectors and image values 'i/j/I/J[_#ind,offset,_boundary]' and 'vector[offset]'.
  17715. if (*se1==']') {
  17716. _cimg_mp_op("Value accessor '[]'");
  17717. // Find opening bracket for the offset.
  17718. s0 = se1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0;
  17719. if (s0>ss) { s1 = s0; do { --s1; } while (cimg::is_blank(*s1)); cimg::swap(*s0,*++s1); }
  17720. is_sth=s0>ss && *(s0-1)==']'; // Particular case s.a. '..[..][..]' ?
  17721. is_relative = *ss=='j' || *ss=='J';
  17722. if (!is_sth && (*ss=='I' || *ss=='J') && *ss1=='[' &&
  17723. (reserved_label[(int)*ss]==~0U ||
  17724. !_cimg_mp_is_vector(reserved_label[(int)*ss]))) { // Image value as a vector
  17725. if (*ss2=='#') { // Index specified
  17726. s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  17727. p1 = compile(ss3,s0++,depth1,0,block_flags);
  17728. _cimg_mp_check_const_index(p1);
  17729. _cimg_mp_check_notnan_index(p1);
  17730. _cimg_mp_check_list();
  17731. } else { p1 = ~0U; s0 = ss2; }
  17732. s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17733. p2 = 1 + (p1!=~0U);
  17734. arg1 = compile(s0,s1,depth1,0,block_flags); // Offset
  17735. _cimg_mp_check_type(arg1,p2,1,0);
  17736. arg2 = ~0U;
  17737. if (s1<se1) {
  17738. arg2 = compile(++s1,se1,depth1,0,block_flags); // Boundary
  17739. _cimg_mp_check_type(arg2,p2 + 1,1,0);
  17740. }
  17741. if (p_ref && arg2==~0U) {
  17742. *p_ref = 4;
  17743. p_ref[1] = p1;
  17744. p_ref[2] = (unsigned int)is_relative;
  17745. p_ref[3] = arg1;
  17746. if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1;
  17747. }
  17748. p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any
  17749. if (p1==~0U) p2 = imgin._spectrum;
  17750. else {
  17751. p3 = (unsigned int)cimg::mod((int)mem[p1],imglist.width());
  17752. p2 = imglist[p3]._spectrum;
  17753. }
  17754. if (!p2) _cimg_mp_return(0);
  17755. pos = vector(p2);
  17756. if (p1!=~0U) {
  17757. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_Joff:mp_list_Ioff),
  17758. pos,p1,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code);
  17759. } else {
  17760. need_input_copy = true;
  17761. CImg<ulongT>::vector((ulongT)(is_relative?mp_Joff:mp_Ioff),
  17762. pos,arg1,arg2==~0U?_cimg_mp_boundary:arg2,p2).move_to(code);
  17763. }
  17764. return_new_comp = true;
  17765. _cimg_mp_return(pos);
  17766. }
  17767. if (!is_sth && (*ss=='i' || *ss=='j') && *ss1=='[' &&
  17768. (reserved_label[(int)*ss]==~0U ||
  17769. !_cimg_mp_is_vector(reserved_label[(int)*ss]))) { // Image value as a scalar
  17770. if (*ss2=='#') { // Index specified
  17771. s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  17772. p1 = compile(ss3,s0++,depth1,0,block_flags);
  17773. _cimg_mp_check_notnan_index(p1);
  17774. } else { p1 = ~0U; s0 = ss2; }
  17775. s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17776. arg1 = compile(s0,s1,depth1,0,block_flags); // Offset
  17777. arg2 = s1<se1?compile(++s1,se1,depth1,0,block_flags):~0U; // Boundary
  17778. if (p_ref && arg2==~0U) {
  17779. *p_ref = 2;
  17780. p_ref[1] = p1;
  17781. p_ref[2] = (unsigned int)is_relative;
  17782. p_ref[3] = arg1;
  17783. if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -1; // Prevent from being used in further optimization
  17784. if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1;
  17785. }
  17786. if (p1!=~0U) {
  17787. if (!imglist) _cimg_mp_return(0);
  17788. pos = scalar3(is_relative?mp_list_joff:mp_list_ioff,p1,arg1,arg2==~0U?_cimg_mp_boundary:arg2);
  17789. } else {
  17790. if (!imgin) _cimg_mp_return(0);
  17791. need_input_copy = true;
  17792. pos = scalar2(is_relative?mp_joff:mp_ioff,arg1,arg2==~0U?_cimg_mp_boundary:arg2);
  17793. }
  17794. memtype[pos] = -1; // Prevent from being used in further optimization
  17795. _cimg_mp_return(pos);
  17796. }
  17797. s0 = se1; while (s0>ss && (*s0!='[' || level[s0 - expr._data]!=clevel)) --s0;
  17798. if (s0>ss) { // Vector element
  17799. arg1 = compile(ss,s0,depth1,0,block_flags);
  17800. if (_cimg_mp_is_scalar(arg1)) {
  17801. variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0;
  17802. _cimg_mp_strerr;
  17803. cimg::strellipsize(variable_name,64);
  17804. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  17805. "CImg<%s>::%s: %s: Array brackets used on non-vector variable '%s', "
  17806. "in expression '%s'.",
  17807. pixel_type(),_cimg_mp_calling_function,s_op,
  17808. variable_name._data,s0);
  17809. }
  17810. s1 = s0 + 1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17811. if (s1<se1) { // Two or three arguments -> sub-vector extraction
  17812. p1 = _cimg_mp_size(arg1);
  17813. arg2 = compile(++s0,s1,depth1,0,block_flags); // Starting index
  17814. s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  17815. arg3 = compile(s1,s0,depth1,0,block_flags); // Length
  17816. arg4 = s0<se1?compile(++s0,se1,depth1,0,block_flags):1; // Step
  17817. _cimg_mp_check_const_scalar(arg3,2,3);
  17818. arg3 = (unsigned int)mem[arg3];
  17819. pos = vector(arg3);
  17820. CImg<ulongT>::vector((ulongT)mp_vector_crop,pos,arg1,p1,arg2,arg3,arg4).move_to(code);
  17821. return_new_comp = true;
  17822. _cimg_mp_return(pos);
  17823. }
  17824. // One argument -> vector value reference
  17825. arg2 = compile(++s0,se1,depth1,0,block_flags);
  17826. if (_cimg_mp_is_const_scalar(arg2)) { // Constant index
  17827. nb = (int)mem[arg2];
  17828. if (nb>=0 && nb<(int)_cimg_mp_size(arg1)) _cimg_mp_return(arg1 + 1 + nb);
  17829. variable_name.assign(ss,(unsigned int)(s0 - ss)).back() = 0;
  17830. _cimg_mp_strerr;
  17831. cimg::strellipsize(variable_name,64);
  17832. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  17833. "CImg<%s>::%s: Out-of-bounds reference '%s[%d]' "
  17834. "(vector '%s' has dimension %u), "
  17835. "in expression '%s'.",
  17836. pixel_type(),_cimg_mp_calling_function,
  17837. variable_name._data,nb,
  17838. variable_name._data,_cimg_mp_size(arg1),s0);
  17839. }
  17840. if (p_ref) {
  17841. *p_ref = 1;
  17842. p_ref[1] = arg1;
  17843. p_ref[2] = arg2;
  17844. if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1; // Prevent from being used in further optimization
  17845. }
  17846. pos = scalar3(mp_vector_off,arg1,_cimg_mp_size(arg1),arg2);
  17847. memtype[pos] = -1; // Prevent from being used in further optimization
  17848. _cimg_mp_return(pos);
  17849. }
  17850. }
  17851. // Look for a function call, an access to image value, or a parenthesis.
  17852. if (*se1==')') {
  17853. if (*ss=='(') _cimg_mp_return(compile(ss1,se1,depth1,p_ref,block_flags)); // Simple parentheses
  17854. _cimg_mp_op("Value accessor '()'");
  17855. is_relative = *ss=='j' || *ss=='J';
  17856. s0 = s1 = std::strchr(ss,'('); if (s0) { do { --s1; } while (cimg::is_blank(*s1)); cimg::swap(*s0,*++s1); }
  17857. // I/J(_#ind,_x,_y,_z,_interpolation,_boundary_conditions)
  17858. if ((*ss=='I' || *ss=='J') && *ss1=='(') { // Image value as scalar
  17859. if (*ss2=='#') { // Index specified
  17860. s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  17861. p1 = compile(ss3,s0++,depth1,0,block_flags);
  17862. _cimg_mp_check_const_index(p1);
  17863. _cimg_mp_check_notnan_index(p1);
  17864. _cimg_mp_check_list();
  17865. } else { p1 = ~0U; s0 = ss2; }
  17866. arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
  17867. arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
  17868. arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
  17869. arg4 = arg5 = ~0U;
  17870. if (s0<se1) {
  17871. s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17872. arg1 = compile(s0,s1,depth1,0,block_flags);
  17873. if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
  17874. p2 = _cimg_mp_size(arg1);
  17875. ++arg1;
  17876. if (p2>1) {
  17877. arg2 = arg1 + 1;
  17878. if (p2>2) arg3 = arg2 + 1;
  17879. }
  17880. if (s1<se1) {
  17881. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  17882. arg4 = compile(s1,s2,depth1,0,block_flags);
  17883. arg5 = s2<se1?compile(++s2,se1,depth1,0,block_flags):~0U;
  17884. }
  17885. } else if (s1<se1) {
  17886. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  17887. arg2 = compile(s1,s2,depth1,0,block_flags);
  17888. if (s2<se1) {
  17889. s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
  17890. arg3 = compile(s2,s3,depth1,0,block_flags);
  17891. if (s3<se1) {
  17892. s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  17893. arg4 = compile(s3,s2,depth1,0,block_flags);
  17894. arg5 = s2<se1?compile(++s2,se1,depth1,0,block_flags):~0U;
  17895. }
  17896. }
  17897. }
  17898. }
  17899. if (p_ref && arg4==~0U && arg5==~0U) {
  17900. *p_ref = 5;
  17901. p_ref[1] = p1;
  17902. p_ref[2] = (unsigned int)is_relative;
  17903. p_ref[3] = arg1;
  17904. p_ref[4] = arg2;
  17905. p_ref[5] = arg3;
  17906. if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -1; // Prevent from being used in further optimization
  17907. if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1;
  17908. if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1;
  17909. if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1;
  17910. }
  17911. p2 = ~0U; // 'p2' must be the dimension of the vector-valued operand if any
  17912. if (p1==~0U) p2 = imgin._spectrum;
  17913. else if (_cimg_mp_is_const_scalar(p1)) {
  17914. p3 = (unsigned int)cimg::mod((int)mem[p1],imglist.width());
  17915. p2 = imglist[p3]._spectrum;
  17916. }
  17917. if (!p2) _cimg_mp_return(0);
  17918. pos = vector(p2);
  17919. if (p1!=~0U)
  17920. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_Jxyz:mp_list_Ixyz),
  17921. pos,p1,arg1,arg2,arg3,
  17922. arg4==~0U?_cimg_mp_interpolation:arg4,
  17923. arg5==~0U?_cimg_mp_boundary:arg5,p2).move_to(code);
  17924. else {
  17925. need_input_copy = true;
  17926. CImg<ulongT>::vector((ulongT)(is_relative?mp_Jxyz:mp_Ixyz),
  17927. pos,arg1,arg2,arg3,
  17928. arg4==~0U?_cimg_mp_interpolation:arg4,
  17929. arg5==~0U?_cimg_mp_boundary:arg5,p2).move_to(code);
  17930. }
  17931. return_new_comp = true;
  17932. _cimg_mp_return(pos);
  17933. }
  17934. // i/j(_#ind,_x,_y,_z,_c,_interpolation,_boundary_conditions)
  17935. if ((*ss=='i' || *ss=='j') && *ss1=='(') { // Image value as scalar
  17936. if (*ss2=='#') { // Index specified
  17937. s0 = ss3; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  17938. p1 = compile(ss3,s0++,depth1,0,block_flags);
  17939. _cimg_mp_check_notnan_index(p1);
  17940. } else { p1 = ~0U; s0 = ss2; }
  17941. arg1 = is_relative?0U:(unsigned int)_cimg_mp_slot_x;
  17942. arg2 = is_relative?0U:(unsigned int)_cimg_mp_slot_y;
  17943. arg3 = is_relative?0U:(unsigned int)_cimg_mp_slot_z;
  17944. arg4 = is_relative?0U:(unsigned int)_cimg_mp_slot_c;
  17945. arg5 = arg6 = ~0U;
  17946. if (s0<se1) {
  17947. s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  17948. arg1 = compile(s0,s1,depth1,0,block_flags);
  17949. if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
  17950. p2 = _cimg_mp_size(arg1);
  17951. ++arg1;
  17952. if (p2>1) {
  17953. arg2 = arg1 + 1;
  17954. if (p2>2) {
  17955. arg3 = arg2 + 1;
  17956. if (p2>3) arg4 = arg3 + 1;
  17957. }
  17958. }
  17959. if (s1<se1) {
  17960. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  17961. arg5 = compile(s1,s2,depth1,0,block_flags);
  17962. arg6 = s2<se1?compile(++s2,se1,depth1,0,block_flags):~0U;
  17963. }
  17964. } else if (s1<se1) {
  17965. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  17966. arg2 = compile(s1,s2,depth1,0,block_flags);
  17967. if (s2<se1) {
  17968. s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
  17969. arg3 = compile(s2,s3,depth1,0,block_flags);
  17970. if (s3<se1) {
  17971. s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  17972. arg4 = compile(s3,s2,depth1,0,block_flags);
  17973. if (s2<se1) {
  17974. s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
  17975. arg5 = compile(s2,s3,depth1,0,block_flags);
  17976. arg6 = s3<se1?compile(++s3,se1,depth1,0,block_flags):~0U;
  17977. }
  17978. }
  17979. }
  17980. }
  17981. }
  17982. if (p_ref && arg5==~0U && arg6==~0U) {
  17983. *p_ref = 3;
  17984. p_ref[1] = p1;
  17985. p_ref[2] = (unsigned int)is_relative;
  17986. p_ref[3] = arg1;
  17987. p_ref[4] = arg2;
  17988. p_ref[5] = arg3;
  17989. p_ref[6] = arg4;
  17990. if (p1!=~0U && _cimg_mp_is_comp(p1)) memtype[p1] = -1; // Prevent from being used in further optimization
  17991. if (_cimg_mp_is_comp(arg1)) memtype[arg1] = -1;
  17992. if (_cimg_mp_is_comp(arg2)) memtype[arg2] = -1;
  17993. if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1;
  17994. if (_cimg_mp_is_comp(arg4)) memtype[arg4] = -1;
  17995. }
  17996. if (p1!=~0U) {
  17997. if (!imglist) _cimg_mp_return(0);
  17998. pos = scalar7(is_relative?mp_list_jxyzc:mp_list_ixyzc,
  17999. p1,arg1,arg2,arg3,arg4,
  18000. arg5==~0U?_cimg_mp_interpolation:arg5,
  18001. arg6==~0U?_cimg_mp_boundary:arg6);
  18002. } else {
  18003. if (!imgin) _cimg_mp_return(0);
  18004. need_input_copy = true;
  18005. pos = scalar6(is_relative?mp_jxyzc:mp_ixyzc,
  18006. arg1,arg2,arg3,arg4,
  18007. arg5==~0U?_cimg_mp_interpolation:arg5,
  18008. arg6==~0U?_cimg_mp_boundary:arg6);
  18009. }
  18010. memtype[pos] = -1; // Prevent from being used in further optimization
  18011. _cimg_mp_return(pos);
  18012. }
  18013. // Mathematical functions.
  18014. switch (*ss) {
  18015. case 'a' :
  18016. #ifdef cimg_mp_func_abort
  18017. if (!std::strncmp(ss,"abort(",6)) { // Abort
  18018. _cimg_mp_op("Function 'abort()'");
  18019. if (pexpr[se2 - expr._data]=='(') { // no arguments?
  18020. CImg<ulongT>::vector((ulongT)mp_abort,_cimg_mp_slot_nan).move_to(code);
  18021. _cimg_mp_return_nan();
  18022. }
  18023. }
  18024. #endif
  18025. if (!std::strncmp(ss,"abs(",4)) { // Absolute value
  18026. _cimg_mp_op("Function 'abs()'");
  18027. arg1 = compile(ss4,se1,depth1,0,block_flags);
  18028. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_abs,arg1);
  18029. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::abs(mem[arg1]));
  18030. _cimg_mp_scalar1(mp_abs,arg1);
  18031. }
  18032. if (!std::strncmp(ss,"addr(",5)) { // Pointer address
  18033. _cimg_mp_op("Function 'addr()'");
  18034. arg1 = compile(ss5,se1,depth1,0,block_flags);
  18035. _cimg_mp_const_scalar((double)arg1);
  18036. }
  18037. if (!std::strncmp(ss,"acos(",5)) { // Arccos
  18038. _cimg_mp_op("Function 'acos()'");
  18039. arg1 = compile(ss5,se1,depth1,0,block_flags);
  18040. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_acos,arg1);
  18041. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::acos(mem[arg1]));
  18042. _cimg_mp_scalar1(mp_acos,arg1);
  18043. }
  18044. if (!std::strncmp(ss,"acosh(",6)) { // Hyperbolic arccosine
  18045. _cimg_mp_op("Function 'acosh()'");
  18046. arg1 = compile(ss6,se1,depth1,0,block_flags);
  18047. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_acosh,arg1);
  18048. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::acosh(mem[arg1]));
  18049. _cimg_mp_scalar1(mp_acosh,arg1);
  18050. }
  18051. if (!std::strncmp(ss,"asinh(",6)) { // Hyperbolic arcsine
  18052. _cimg_mp_op("Function 'asinh()'");
  18053. arg1 = compile(ss6,se1,depth1,0,block_flags);
  18054. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_asinh,arg1);
  18055. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::asinh(mem[arg1]));
  18056. _cimg_mp_scalar1(mp_asinh,arg1);
  18057. }
  18058. if (!std::strncmp(ss,"atanh(",6)) { // Hyperbolic arctangent
  18059. _cimg_mp_op("Function 'atanh()'");
  18060. arg1 = compile(ss6,se1,depth1,0,block_flags);
  18061. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_atanh,arg1);
  18062. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::atanh(mem[arg1]));
  18063. _cimg_mp_scalar1(mp_atanh,arg1);
  18064. }
  18065. if (!std::strncmp(ss,"arg(",4) ||
  18066. !std::strncmp(ss,"arg0(",5) ||
  18067. !std::strncmp(ss,"arg1(",5)) { // Nth argument
  18068. _cimg_mp_op(*ss3=='('?"Function 'arg()'":*ss3=='0'?"Function 'arg0()'":"Function 'arg1()'");
  18069. s0 = ss4 + (*ss3!='('?1:0);
  18070. s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  18071. arg1 = compile(s0,s1,depth1,0,block_flags);
  18072. _cimg_mp_check_type(arg1,1,1,0);
  18073. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  18074. arg2 = compile(s1,s2,depth1,0,block_flags);
  18075. p2 = _cimg_mp_size(arg2);
  18076. p3 = 3;
  18077. CImg<ulongT>::vector((ulongT)(*ss3=='0'?mp_arg0:mp_arg),0,0,p2,arg1,arg2).move_to(l_opcode);
  18078. for (s = ++s2; s<se; ++s) {
  18079. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  18080. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  18081. arg3 = compile(s,ns,depth1,0,block_flags);
  18082. _cimg_mp_check_type(arg3,p3,p2?2:1,p2);
  18083. CImg<ulongT>::vector(arg3).move_to(l_opcode);
  18084. ++p3;
  18085. s = ns;
  18086. }
  18087. (l_opcode>'y').move_to(opcode);
  18088. opcode[2] = opcode._height;
  18089. if (_cimg_mp_is_const_scalar(arg1)) {
  18090. p3-=1; // Number of args
  18091. if (*ss3=='0') arg1 = (unsigned int)(mem[arg1]<0?mem[arg1] + p3:mem[arg1] + 1);
  18092. else arg1 = (unsigned int)(mem[arg1]<0?mem[arg1] + p3:mem[arg1]);
  18093. if (arg1<p3) _cimg_mp_return(opcode[4 + arg1]);
  18094. if (p2) {
  18095. pos = vector(p2);
  18096. std::memset(&mem[pos] + 1,0,p2*sizeof(double));
  18097. return_new_comp = true;
  18098. _cimg_mp_return(pos);
  18099. } else _cimg_mp_return(0);
  18100. }
  18101. pos = opcode[1] = p2?vector(p2):scalar();
  18102. opcode.move_to(code);
  18103. return_new_comp = true;
  18104. _cimg_mp_return(pos);
  18105. }
  18106. if (!std::strncmp(ss,"asin(",5)) { // Arcsin
  18107. _cimg_mp_op("Function 'asin()'");
  18108. arg1 = compile(ss5,se1,depth1,0,block_flags);
  18109. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_asin,arg1);
  18110. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::asin(mem[arg1]));
  18111. _cimg_mp_scalar1(mp_asin,arg1);
  18112. }
  18113. if (!std::strncmp(ss,"atan(",5)) { // Arctan
  18114. _cimg_mp_op("Function 'atan()'");
  18115. arg1 = compile(ss5,se1,depth1,0,block_flags);
  18116. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_atan,arg1);
  18117. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::atan(mem[arg1]));
  18118. _cimg_mp_scalar1(mp_atan,arg1);
  18119. }
  18120. if (!std::strncmp(ss,"atan2(",6)) { // Arctan2
  18121. _cimg_mp_op("Function 'atan2()'");
  18122. s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  18123. arg1 = compile(ss6,s1,depth1,0,block_flags);
  18124. arg2 = compile(++s1,se1,depth1,0,block_flags);
  18125. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  18126. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_atan2,arg1,arg2);
  18127. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_atan2,arg1,arg2);
  18128. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_atan2,arg1,arg2);
  18129. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
  18130. _cimg_mp_const_scalar(std::atan2(mem[arg1],mem[arg2]));
  18131. _cimg_mp_scalar2(mp_atan2,arg1,arg2);
  18132. }
  18133. break;
  18134. case 'b' :
  18135. if (!std::strncmp(ss,"break(",6)) { // Break current block
  18136. if (pexpr[se2 - expr._data]=='(') { // no arguments?
  18137. CImg<ulongT>::vector((ulongT)mp_break,_cimg_mp_slot_nan).move_to(code);
  18138. _cimg_mp_return_nan();
  18139. }
  18140. }
  18141. if (!std::strncmp(ss,"breakpoint(",11)) { // Break point (for abort test)
  18142. _cimg_mp_op("Function 'breakpoint()'");
  18143. if (pexpr[se2 - expr._data]=='(') { // no arguments?
  18144. CImg<ulongT>::vector((ulongT)mp_breakpoint,_cimg_mp_slot_nan).move_to(code);
  18145. _cimg_mp_return_nan();
  18146. }
  18147. }
  18148. if (!std::strncmp(ss,"bool(",5)) { // Boolean cast
  18149. _cimg_mp_op("Function 'bool()'");
  18150. arg1 = compile(ss5,se1,depth1,0,block_flags);
  18151. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_bool,arg1);
  18152. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar((bool)mem[arg1]);
  18153. _cimg_mp_scalar1(mp_bool,arg1);
  18154. }
  18155. if (!std::strncmp(ss,"begin(",6)) { // Begin
  18156. _cimg_mp_op("Function 'begin()'");
  18157. s1 = ss6; while (s1<se1 && cimg::is_blank(*s1)) ++s1;
  18158. if (s1!=se1) {
  18159. if (!is_inside_begin) code.swap(code_begin);
  18160. arg1 = compile(s1,se1,depth1,p_ref,2);
  18161. if (!is_inside_begin) code.swap(code_begin);
  18162. _cimg_mp_return(arg1);
  18163. } else _cimg_mp_return_nan();
  18164. }
  18165. if (!std::strncmp(ss,"begin_t(",8)) { // Begin thread
  18166. _cimg_mp_op("Function 'begin_t()'");
  18167. s1 = ss8; while (s1<se1 && cimg::is_blank(*s1)) ++s1;
  18168. if (s1!=se1) {
  18169. if (!is_inside_begin_t) code.swap(code_begin_t);
  18170. arg1 = compile(s1,se1,depth1,p_ref,4);
  18171. if (!is_inside_begin_t) code.swap(code_begin_t);
  18172. _cimg_mp_return(arg1);
  18173. } else _cimg_mp_return_nan();
  18174. }
  18175. break;
  18176. case 'c' :
  18177. if (!std::strncmp(ss,"c2o(",4)) { // Coordinates to offset
  18178. _cimg_mp_op("Function 'c2o()'");
  18179. if (*ss4=='#') { // Index specified
  18180. s0 = ss5; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  18181. p1 = compile(ss5,s0++,depth1,0,block_flags);
  18182. _cimg_mp_check_notnan_index(p1);
  18183. _cimg_mp_check_list();
  18184. } else { p1 = ~0U; s0 = ss4; }
  18185. s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  18186. pos = compile(s0,s1,depth1,0,block_flags);
  18187. p2 = _cimg_mp_size(pos);
  18188. if (p2) { // Coordinates specified as a vector
  18189. if (s1!=se1) compile(s0,se1,depth1,0,block_flags); // -> Error too much arguments
  18190. if (p2>4) {
  18191. *s1 = 0; s1 = s0; _cimg_mp_strerr;
  18192. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  18193. "CImg<%s>::%s: %s: Argument '%s' is a vector of size %u (should be <=4), "
  18194. "in expression '%s'.",
  18195. pixel_type(),_cimg_mp_calling_function,s_op,s1,p2,s0);
  18196. }
  18197. arg1 = pos + 1;
  18198. arg2 = p2>1?pos + 2:0;
  18199. arg3 = p2>2?pos + 3:0;
  18200. arg4 = p2>3?pos + 4:0;
  18201. } else { // Coordinates specified as scalars
  18202. arg1 = pos; arg2 = arg3 = arg4 = 0;
  18203. if (s1<se1) {
  18204. s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  18205. arg2 = compile(s1,s0,depth1,0,block_flags);
  18206. if (s0<se1) {
  18207. s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  18208. arg3 = compile(s0,s1,depth1,0,block_flags);
  18209. arg4 = s1<se1?compile(++s1,se1,depth1,0,block_flags):0;
  18210. } else arg3 = 0;
  18211. } else arg2 = 0;
  18212. }
  18213. _cimg_mp_scalar5(mp_c2o,p1,arg1,arg2,arg3,arg4);
  18214. }
  18215. if (!std::strncmp(ss,"cabs(",5)) { // Complex absolute value
  18216. _cimg_mp_op("Function 'cabs()'");
  18217. arg1 = compile(ss5,se1,depth1,0,block_flags);
  18218. _cimg_mp_check_type(arg1,0,3,2);
  18219. if (_cimg_mp_is_scalar(arg1)) _cimg_mp_scalar2(mp_complex_abs,arg1,0);
  18220. _cimg_mp_scalar2(mp_complex_abs,arg1 + 1,arg1 + 2);
  18221. }
  18222. if (!std::strncmp(ss,"carg(",5)) { // Complex argument
  18223. _cimg_mp_op("Function 'carg()'");
  18224. arg1 = compile(ss5,se1,depth1,0,block_flags);
  18225. _cimg_mp_check_type(arg1,0,3,2);
  18226. if (_cimg_mp_is_scalar(arg1)) _cimg_mp_scalar2(mp_atan2,0,arg1);
  18227. _cimg_mp_scalar2(mp_atan2,arg1 + 2,arg1 + 1);
  18228. }
  18229. if (!std::strncmp(ss,"cbrt(",5)) { // Cubic root
  18230. _cimg_mp_op("Function 'cbrt()'");
  18231. arg1 = compile(ss5,se1,depth1,0,block_flags);
  18232. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cbrt,arg1);
  18233. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::cbrt(mem[arg1]));
  18234. _cimg_mp_scalar1(mp_cbrt,arg1);
  18235. }
  18236. if (!std::strncmp(ss,"cconj(",6)) { // Complex conjugate
  18237. _cimg_mp_op("Function 'cconj()'");
  18238. arg1 = compile(ss6,se1,depth1,0,block_flags);
  18239. _cimg_mp_check_type(arg1,0,3,2);
  18240. pos = vector(2);
  18241. if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_conj,pos,arg1,0).move_to(code);
  18242. else CImg<ulongT>::vector((ulongT)mp_complex_conj,pos,arg1 + 1,arg1 + 2).move_to(code);
  18243. return_new_comp = true;
  18244. _cimg_mp_return(pos);
  18245. }
  18246. if (!std::strncmp(ss,"ceil(",5)) { // Ceil
  18247. _cimg_mp_op("Function 'ceil()'");
  18248. arg1 = compile(ss5,se1,depth1,0,block_flags);
  18249. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_ceil,arg1);
  18250. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::ceil(mem[arg1]));
  18251. _cimg_mp_scalar1(mp_ceil,arg1);
  18252. }
  18253. if (!std::strncmp(ss,"cexp(",5)) { // Complex exponential
  18254. _cimg_mp_op("Function 'cexp()'");
  18255. arg1 = compile(ss5,se1,depth1,0,block_flags);
  18256. _cimg_mp_check_type(arg1,0,3,2);
  18257. pos = vector(2);
  18258. if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_exp,pos,arg1,0).move_to(code);
  18259. else CImg<ulongT>::vector((ulongT)mp_complex_exp,pos,arg1 + 1,arg1 + 2).move_to(code);
  18260. return_new_comp = true;
  18261. _cimg_mp_return(pos);
  18262. }
  18263. if (!std::strncmp(ss,"clog(",5)) { // Complex logarithm
  18264. _cimg_mp_op("Function 'clog()'");
  18265. arg1 = compile(ss5,se1,depth1,0,block_flags);
  18266. _cimg_mp_check_type(arg1,0,3,2);
  18267. pos = vector(2);
  18268. if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_log,pos,arg1,0).move_to(code);
  18269. else CImg<ulongT>::vector((ulongT)mp_complex_log,pos,arg1 + 1,arg1 + 2).move_to(code);
  18270. return_new_comp = true;
  18271. _cimg_mp_return(pos);
  18272. }
  18273. if (!std::strncmp(ss,"ccos(",5)) { // Complex cosine
  18274. _cimg_mp_op("Function 'ccos()'");
  18275. arg1 = compile(ss5,se1,depth1,0,block_flags);
  18276. _cimg_mp_check_type(arg1,0,3,2);
  18277. pos = vector(2);
  18278. if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_cos,pos,arg1,0).move_to(code);
  18279. else CImg<ulongT>::vector((ulongT)mp_complex_cos,pos,arg1 + 1,arg1 + 2).move_to(code);
  18280. return_new_comp = true;
  18281. _cimg_mp_return(pos);
  18282. }
  18283. if (!std::strncmp(ss,"csin(",5)) { // Complex sine
  18284. _cimg_mp_op("Function 'csin()'");
  18285. arg1 = compile(ss5,se1,depth1,0,block_flags);
  18286. _cimg_mp_check_type(arg1,0,3,2);
  18287. pos = vector(2);
  18288. if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_sin,pos,arg1,0).move_to(code);
  18289. else CImg<ulongT>::vector((ulongT)mp_complex_sin,pos,arg1 + 1,arg1 + 2).move_to(code);
  18290. return_new_comp = true;
  18291. _cimg_mp_return(pos);
  18292. }
  18293. if (!std::strncmp(ss,"csqrt(",6)) { // Complex square root
  18294. _cimg_mp_op("Function 'csqrt()'");
  18295. arg1 = compile(ss6,se1,depth1,0,block_flags);
  18296. _cimg_mp_check_type(arg1,0,3,2);
  18297. pos = vector(2);
  18298. if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_sqrt,pos,arg1,0).move_to(code);
  18299. else CImg<ulongT>::vector((ulongT)mp_complex_sqrt,pos,arg1 + 1,arg1 + 2).move_to(code);
  18300. return_new_comp = true;
  18301. _cimg_mp_return(pos);
  18302. }
  18303. if (!std::strncmp(ss,"ctan(",5)) { // Complex tangent
  18304. _cimg_mp_op("Function 'ctan()'");
  18305. arg1 = compile(ss5,se1,depth1,0,block_flags);
  18306. _cimg_mp_check_type(arg1,0,3,2);
  18307. pos = vector(2);
  18308. if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_tan,pos,arg1,0).move_to(code);
  18309. else CImg<ulongT>::vector((ulongT)mp_complex_tan,pos,arg1 + 1,arg1 + 2).move_to(code);
  18310. return_new_comp = true;
  18311. _cimg_mp_return(pos);
  18312. }
  18313. if (!std::strncmp(ss,"ccosh(",6)) { // Complex hyperbolic cosine
  18314. _cimg_mp_op("Function 'ccosh()'");
  18315. arg1 = compile(ss6,se1,depth1,0,block_flags);
  18316. _cimg_mp_check_type(arg1,0,3,2);
  18317. pos = vector(2);
  18318. if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_cosh,pos,arg1,0).move_to(code);
  18319. else CImg<ulongT>::vector((ulongT)mp_complex_cosh,pos,arg1 + 1,arg1 + 2).move_to(code);
  18320. return_new_comp = true;
  18321. _cimg_mp_return(pos);
  18322. }
  18323. if (!std::strncmp(ss,"csinh(",6)) { // Complex hyperbolic sine
  18324. _cimg_mp_op("Function 'csinh()'");
  18325. arg1 = compile(ss6,se1,depth1,0,block_flags);
  18326. _cimg_mp_check_type(arg1,0,3,2);
  18327. pos = vector(2);
  18328. if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_sinh,pos,arg1,0).move_to(code);
  18329. else CImg<ulongT>::vector((ulongT)mp_complex_sinh,pos,arg1 + 1,arg1 + 2).move_to(code);
  18330. return_new_comp = true;
  18331. _cimg_mp_return(pos);
  18332. }
  18333. if (!std::strncmp(ss,"ctanh(",6)) { // Complex hyperbolic tangent
  18334. _cimg_mp_op("Function 'ctanh()'");
  18335. arg1 = compile(ss6,se1,depth1,0,block_flags);
  18336. _cimg_mp_check_type(arg1,0,3,2);
  18337. pos = vector(2);
  18338. if (_cimg_mp_is_scalar(arg1)) CImg<ulongT>::vector((ulongT)mp_complex_tanh,pos,arg1,0).move_to(code);
  18339. else CImg<ulongT>::vector((ulongT)mp_complex_tanh,pos,arg1 + 1,arg1 + 2).move_to(code);
  18340. return_new_comp = true;
  18341. _cimg_mp_return(pos);
  18342. }
  18343. if (!std::strncmp(ss,"continue(",9)) { // Continue loop
  18344. if (pexpr[se2 - expr._data]=='(') { // no arguments?
  18345. CImg<ulongT>::vector((ulongT)mp_continue,_cimg_mp_slot_nan).move_to(code);
  18346. _cimg_mp_return_nan();
  18347. }
  18348. }
  18349. if (!std::strncmp(ss,"copy(",5)) { // Memory copy
  18350. _cimg_mp_op("Function 'copy()'");
  18351. ref.assign(14);
  18352. s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  18353. arg1 = p1 = compile(ss5,s1,depth1,ref,block_flags);
  18354. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  18355. arg2 = compile(s1,s2,depth1,ref._data + 7,block_flags);
  18356. arg3 = arg4 = arg5 = ~0U; arg6 = 1;
  18357. if (s2<se1) {
  18358. s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
  18359. arg3 = compile(s2,s3,depth1,0,block_flags);
  18360. if (s3<se1) {
  18361. s1 = ++s3; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  18362. arg4 = compile(s3,s1,depth1,0,block_flags);
  18363. if (s1<se1) {
  18364. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  18365. arg5 = compile(s1,s2,depth1,0,block_flags);
  18366. arg6 = s2<se1?compile(++s2,se1,depth1,0,block_flags):1;
  18367. }
  18368. }
  18369. }
  18370. if (_cimg_mp_is_vector(arg1)) {
  18371. if (!ref[0]) ++arg1;
  18372. else if (ref[0]>=4 && arg4==~0U) arg4 = scalar1(mp_image_whd,ref[1]);
  18373. }
  18374. if (_cimg_mp_is_vector(arg2)) {
  18375. if (arg3==~0U) arg3 = const_scalar(_cimg_mp_size(arg2));
  18376. if (!ref[7]) ++arg2;
  18377. if (ref[7]>=4 && arg5==~0U) arg5 = scalar1(mp_image_whd,ref[8]);
  18378. }
  18379. if (arg3==~0U) arg3 = 1;
  18380. if (arg4==~0U) arg4 = 1;
  18381. if (arg5==~0U) arg5 = 1;
  18382. _cimg_mp_check_type(arg3,3,1,0);
  18383. _cimg_mp_check_type(arg4,4,1,0);
  18384. _cimg_mp_check_type(arg5,5,1,0);
  18385. _cimg_mp_check_type(arg6,5,1,0);
  18386. CImg<ulongT>(1,22).move_to(code);
  18387. code.back().get_shared_rows(0,7).fill((ulongT)mp_memcopy,p1,arg1,arg2,arg3,arg4,arg5,arg6);
  18388. code.back().get_shared_rows(8,21).fill(ref);
  18389. _cimg_mp_return_nan();
  18390. }
  18391. if (!std::strncmp(ss,"cos(",4)) { // Cosine
  18392. _cimg_mp_op("Function 'cos()'");
  18393. arg1 = compile(ss4,se1,depth1,0,block_flags);
  18394. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cos,arg1);
  18395. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::cos(mem[arg1]));
  18396. _cimg_mp_scalar1(mp_cos,arg1);
  18397. }
  18398. if (!std::strncmp(ss,"cosh(",5)) { // Hyperbolic cosine
  18399. _cimg_mp_op("Function 'cosh()'");
  18400. arg1 = compile(ss5,se1,depth1,0,block_flags);
  18401. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_cosh,arg1);
  18402. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::cosh(mem[arg1]));
  18403. _cimg_mp_scalar1(mp_cosh,arg1);
  18404. }
  18405. if (!std::strncmp(ss,"cov(",4)) { // Covariance
  18406. _cimg_mp_op("Function 'cov()'");
  18407. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  18408. arg1 = compile(ss4,s1,depth1,0,block_flags);
  18409. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  18410. arg2 = compile(s1,s2,depth1,0,block_flags);
  18411. arg3 = arg4 = ~0U;
  18412. if (s2<se1) {
  18413. s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  18414. arg3 = compile(s2,s1,depth1,0,block_flags);
  18415. arg4 = s1<se1?compile(++s1,se1,depth1,0,block_flags):~0U;
  18416. }
  18417. _cimg_mp_check_type(arg2,2,_cimg_mp_is_scalar(arg1)?1:2,_cimg_mp_size(arg1));
  18418. if (arg3!=~0U) _cimg_mp_check_type(arg3,3,1,0);
  18419. if (arg4!=~0U) _cimg_mp_check_type(arg4,4,1,0);
  18420. if (arg3==~0U && arg4==~0U && _cimg_mp_size(arg1)<2) _cimg_mp_const_scalar(0);
  18421. _cimg_mp_scalar5(mp_cov,arg1,arg2,_cimg_mp_size(arg1),arg3,arg4);
  18422. }
  18423. if (!std::strncmp(ss,"critical(",9)) { // Critical section (single thread at a time)
  18424. _cimg_mp_op("Function 'critical()'");
  18425. p1 = code._width;
  18426. arg1 = compile(ss + 9,se1,depth1,p_ref,block_flags | 1);
  18427. CImg<ulongT>::vector((ulongT)mp_critical,arg1,code._width - p1).move_to(code,p1);
  18428. _cimg_mp_return(arg1);
  18429. }
  18430. if (!std::strncmp(ss,"crop(",5)) { // Image or vector crop
  18431. _cimg_mp_op("Function 'crop()'");
  18432. is_sth = false; // is image crop ?
  18433. arg1 = 0;
  18434. if (*ss5=='#') { // Index specified
  18435. s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  18436. p1 = compile(ss6,s0++,depth1,0,block_flags);
  18437. _cimg_mp_check_notnan_index(p1);
  18438. _cimg_mp_check_list();
  18439. pos = 2;
  18440. is_sth = true;
  18441. } else { p1 = ~0U; s0 = ss5; need_input_copy = true; pos = 1; }
  18442. if (s0<se1) for (s = s0; s<se; ++s, ++pos) {
  18443. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  18444. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  18445. arg1 = compile(s,ns,depth1,0,block_flags);
  18446. if (pos==1 && _cimg_mp_is_scalar(arg1)) is_sth = true;
  18447. else if (pos>1) _cimg_mp_check_type(arg1,pos,1,0);
  18448. CImg<ulongT>::vector(arg1).move_to(l_opcode);
  18449. s = ns;
  18450. }
  18451. (l_opcode>'y').move_to(opcode);
  18452. if (!opcode || is_sth) { // Image crop
  18453. arg1 = 0; arg2 = (p1!=~0U);
  18454. switch (opcode._height) {
  18455. case 0 : case 1 :
  18456. CImg<ulongT>::vector(0,0,0,0,~0U,~0U,~0U,~0U,0).move_to(opcode);
  18457. break;
  18458. case 2 :
  18459. CImg<ulongT>::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,_cimg_mp_boundary).move_to(opcode);
  18460. arg1 = 2 + arg2; break;
  18461. case 3 :
  18462. CImg<ulongT>::vector(*opcode,0,0,0,opcode[1],~0U,~0U,~0U,opcode[2]).move_to(opcode);
  18463. arg1 = 2 + arg2; break;
  18464. case 4 :
  18465. CImg<ulongT>::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,_cimg_mp_boundary).
  18466. move_to(opcode);
  18467. arg1 = 3 + arg2; break;
  18468. case 5 :
  18469. CImg<ulongT>::vector(*opcode,opcode[1],0,0,opcode[2],opcode[3],~0U,~0U,opcode[4]).
  18470. move_to(opcode);
  18471. arg1 = 3 + arg2; break;
  18472. case 6 :
  18473. CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U,
  18474. _cimg_mp_boundary).move_to(opcode);
  18475. arg1 = 4 + arg2; break;
  18476. case 7 :
  18477. CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],0,opcode[3],opcode[4],opcode[5],~0U,
  18478. opcode[6]).move_to(opcode);
  18479. arg1 = 4 + arg2; break;
  18480. case 8 :
  18481. CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],opcode[5],opcode[6],
  18482. opcode[7],_cimg_mp_boundary).move_to(opcode);
  18483. arg1 = 5 + arg2; break;
  18484. case 9 :
  18485. arg1 = 5 + arg2; break;
  18486. default : // Error -> too much arguments
  18487. _cimg_mp_strerr;
  18488. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  18489. "CImg<%s>::%s: %s: Too much arguments specified, "
  18490. "in expression '%s'.",
  18491. pixel_type(),_cimg_mp_calling_function,s_op,s0);
  18492. }
  18493. if (opcode[4]!=(ulongT)~0U) {
  18494. _cimg_mp_check_const_scalar((unsigned int)opcode[4],arg1,3);
  18495. opcode[4] = (ulongT)mem[opcode[4]];
  18496. }
  18497. if (opcode[5]!=(ulongT)~0U) {
  18498. _cimg_mp_check_const_scalar((unsigned int)opcode[5],arg1 + 1,3);
  18499. opcode[5] = (ulongT)mem[opcode[5]];
  18500. }
  18501. if (opcode[6]!=(ulongT)~0U) {
  18502. _cimg_mp_check_const_scalar((unsigned int)opcode[6],arg1 + 2,3);
  18503. opcode[6] = (ulongT)mem[opcode[6]];
  18504. }
  18505. if (opcode[7]!=(ulongT)~0U) {
  18506. _cimg_mp_check_const_scalar((unsigned int)opcode[7],arg1 + 3,3);
  18507. opcode[7] = (ulongT)mem[opcode[7]];
  18508. }
  18509. _cimg_mp_check_type((unsigned int)opcode[8],arg1 + 4,1,0);
  18510. if (opcode[4]==(ulongT)~0U || opcode[5]==(ulongT)~0U ||
  18511. opcode[6]==(ulongT)~0U || opcode[7]==(ulongT)~0U) {
  18512. p2 = 0;
  18513. if (p1!=~0U) {
  18514. _cimg_mp_check_const_scalar(p1,1,1);
  18515. p2 = (unsigned int)cimg::mod((int)mem[p1],imglist.width());
  18516. }
  18517. const CImg<T> &img = p1!=~0U?imglist[p2]:imgin;
  18518. if (!img) {
  18519. _cimg_mp_strerr;
  18520. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  18521. "CImg<%s>::%s: %s: Cannot crop empty image when "
  18522. "some xyzc-coordinates are unspecified, in expression '%s'.",
  18523. pixel_type(),_cimg_mp_calling_function,s_op,s0);
  18524. }
  18525. if (opcode[4]==(ulongT)~0U) opcode[4] = (ulongT)img._width;
  18526. if (opcode[5]==(ulongT)~0U) opcode[5] = (ulongT)img._height;
  18527. if (opcode[6]==(ulongT)~0U) opcode[6] = (ulongT)img._depth;
  18528. if (opcode[7]==(ulongT)~0U) opcode[7] = (ulongT)img._spectrum;
  18529. }
  18530. pos = vector((unsigned int)(opcode[4]*opcode[5]*opcode[6]*opcode[7]));
  18531. CImg<ulongT>::vector((ulongT)mp_image_crop,
  18532. pos,p1, // 1-2: res,#ind
  18533. *opcode,opcode[1],opcode[2],opcode[3], // 3-6: x,y,z,c
  18534. opcode[4],opcode[5],opcode[6],opcode[7], // 7-10: dx,dy,dz,dc
  18535. opcode[8]).move_to(code); // 11: boundary conditions
  18536. } else { // Vector crop
  18537. switch (opcode._height) {
  18538. case 5 : case 6 :
  18539. CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],
  18540. 0,0,0,0,~0U,~0U,~0U,~0U,0).move_to(opcode);
  18541. break;
  18542. case 7 :
  18543. CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],
  18544. opcode[5],0,0,0,opcode[6],~0U,~0U,~0U,_cimg_mp_boundary).move_to(opcode);
  18545. arg1 = 7; break;
  18546. case 8 :
  18547. CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],
  18548. opcode[5],0,0,0,opcode[6],~0U,~0U,~0U,opcode[7]).move_to(opcode);
  18549. arg1 = 7; break;
  18550. case 9 :
  18551. CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],
  18552. opcode[5],opcode[6],0,0,opcode[7],opcode[8],~0U,~0U,_cimg_mp_boundary).
  18553. move_to(opcode);
  18554. arg1 = 8; break;
  18555. case 10 :
  18556. CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],
  18557. opcode[5],opcode[6],0,0,opcode[7],opcode[8],~0U,~0U,opcode[9]).
  18558. move_to(opcode);
  18559. arg1 = 8; break;
  18560. case 11 :
  18561. CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],
  18562. opcode[5],opcode[6],opcode[7],0,opcode[8],opcode[9],opcode[10],~0U,
  18563. _cimg_mp_boundary).move_to(opcode);
  18564. arg1 = 9; break;
  18565. case 12 :
  18566. CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],
  18567. opcode[5],opcode[6],opcode[7],0,opcode[8],opcode[9],opcode[10],~0U,
  18568. opcode[11]).move_to(opcode);
  18569. arg1 = 9; break;
  18570. case 13 :
  18571. CImg<ulongT>::vector(*opcode,opcode[1],opcode[2],opcode[3],opcode[4],
  18572. opcode[5],opcode[6],opcode[7],opcode[8],opcode[9],opcode[10],opcode[11],
  18573. opcode[12],_cimg_mp_boundary).move_to(opcode);
  18574. arg1 = 10; break;
  18575. case 14 :
  18576. arg1 = 10; break;
  18577. default : // Error -> too few or too much arguments
  18578. _cimg_mp_strerr;
  18579. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  18580. "CImg<%s>::%s: %s: Too %s arguments specified, "
  18581. "in expression '%s'.",
  18582. pixel_type(),_cimg_mp_calling_function,s_op,
  18583. opcode._height<5?"few":"much",s0);
  18584. }
  18585. _cimg_mp_check_const_scalar((unsigned int)opcode[1],2,3); // w
  18586. opcode[1] = (ulongT)mem[opcode[1]];
  18587. _cimg_mp_check_const_scalar((unsigned int)opcode[2],3,3); // h
  18588. opcode[2] = (ulongT)mem[opcode[2]];
  18589. _cimg_mp_check_const_scalar((unsigned int)opcode[3],4,3); // d
  18590. opcode[3] = (ulongT)mem[opcode[3]];
  18591. _cimg_mp_check_const_scalar((unsigned int)opcode[4],5,3); // s
  18592. opcode[4] = (ulongT)mem[opcode[4]];
  18593. p1 = _cimg_mp_size((unsigned int)opcode[0]);
  18594. arg2 = (unsigned int)opcode[1];
  18595. arg3 = (unsigned int)opcode[2];
  18596. arg4 = (unsigned int)opcode[3];
  18597. arg5 = (unsigned int)opcode[4];
  18598. if (arg2*arg3*arg4*arg5!=p1)
  18599. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  18600. "CImg<%s>::%s: %s: Input vector size (%lu values) and its specified "
  18601. "geometry (%u,%u,%u,%u) (%lu values) do not match.",
  18602. pixel_type(),_cimg_mp_calling_function,s_op,
  18603. p1,arg2,arg3,arg4,arg5,(ulongT)arg2*arg3*arg4*arg5);
  18604. if (opcode[9]!=(ulongT)~0U) {
  18605. _cimg_mp_check_const_scalar((unsigned int)opcode[9],arg1,3);
  18606. opcode[9] = (ulongT)mem[opcode[9]];
  18607. } else opcode[9] = opcode[1];
  18608. if (opcode[10]!=(ulongT)~0U) {
  18609. _cimg_mp_check_const_scalar((unsigned int)opcode[10],arg1 + 1,3);
  18610. opcode[10] = (ulongT)mem[opcode[10]];
  18611. } else opcode[10] = opcode[2];
  18612. if (opcode[11]!=(ulongT)~0U) {
  18613. _cimg_mp_check_const_scalar((unsigned int)opcode[11],arg1 + 2,3);
  18614. opcode[11] = (ulongT)mem[opcode[11]];
  18615. } else opcode[11] = opcode[3];
  18616. if (opcode[12]!=(ulongT)~0U) {
  18617. _cimg_mp_check_const_scalar((unsigned int)opcode[12],arg1 + 3,3);
  18618. opcode[12] = (ulongT)mem[opcode[12]];
  18619. } else opcode[12] = opcode[4];
  18620. _cimg_mp_check_type((unsigned int)opcode[13],arg1 + 4,1,0);
  18621. pos = vector((unsigned int)(opcode[9]*opcode[10]*opcode[11]*opcode[12]));
  18622. CImg<ulongT>::vector((ulongT)mp_vector_crop_ext,
  18623. pos,*opcode, // 1-2: res,S
  18624. opcode[1],opcode[2],opcode[3],opcode[4], // 3-6: w,h,d,s
  18625. opcode[5],opcode[6],opcode[7],opcode[8], // 7-10: x,y,z,c
  18626. opcode[9],opcode[10],opcode[11],opcode[12], // 11-14: dx,dy,dz,dc
  18627. opcode[13]).move_to(code); // 15: boundary conditions
  18628. }
  18629. return_new_comp = true;
  18630. _cimg_mp_return(pos);
  18631. }
  18632. if (!std::strncmp(ss,"cross(",6)) { // Cross product
  18633. _cimg_mp_op("Function 'cross()'");
  18634. s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  18635. arg1 = compile(ss6,s1,depth1,0,block_flags);
  18636. arg2 = compile(++s1,se1,depth1,0,block_flags);
  18637. _cimg_mp_check_type(arg1,1,2,3);
  18638. _cimg_mp_check_type(arg2,2,2,3);
  18639. pos = vector(3);
  18640. CImg<ulongT>::vector((ulongT)mp_cross,pos,arg1,arg2).move_to(code);
  18641. return_new_comp = true;
  18642. _cimg_mp_return(pos);
  18643. }
  18644. if (!std::strncmp(ss,"cut(",4)) { // Cut
  18645. _cimg_mp_op("Function 'cut()'");
  18646. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  18647. arg1 = compile(ss4,s1,depth1,0,block_flags);
  18648. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  18649. arg2 = compile(s1,s2,depth1,0,block_flags);
  18650. arg3 = compile(++s2,se1,depth1,0,block_flags);
  18651. _cimg_mp_check_type(arg2,2,1,0);
  18652. _cimg_mp_check_type(arg3,3,1,0);
  18653. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_cut,arg1,arg2,arg3);
  18654. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2) && _cimg_mp_is_const_scalar(arg3)) {
  18655. val = mem[arg1];
  18656. val1 = mem[arg2];
  18657. val2 = mem[arg3];
  18658. _cimg_mp_const_scalar(val<val1?val1:val>val2?val2:val);
  18659. }
  18660. _cimg_mp_scalar3(mp_cut,arg1,arg2,arg3);
  18661. }
  18662. if (!std::strncmp(ss,"convolve(",9) || !std::strncmp(ss,"correlate(",10)) { // Convolve & Correlate
  18663. is_sth = *ss2=='n'; // is_convolve?
  18664. _cimg_mp_op(is_sth?"Function 'convolve()'":"Function 'correlate()'");
  18665. op = is_sth?mp_convolve:mp_correlate;
  18666. const ulongT default_params[] = { (ulongT)op,0, // [0]=function, [1]=result vector
  18667. 0,0,0,0,0, // [2]=A, [3]=wA, [4]=hA, [5]=dA, [6]=sA
  18668. 0,0,0,0,0, // [7]=M, [8]=wM, [9]=hM, [10]=dM, [11]=sM
  18669. 1,0,1, // [12]=boundary_conditions, [13]=is_normalized, [14]=chan._mode
  18670. ~0U,~0U,~0U, // [15]=xcenter, [16]=ycenter, [17]=zcenter
  18671. 0,0,0, // [18]=xstart, [19]=ystart, [20]=zstart
  18672. ~0U,~0U,~0U, // [21]=xend, [22]=yend, [23]=zend
  18673. 1,1,1, // [24]=xstride, [25]=ystride, [26]=zstride
  18674. 1,1,1, // [27]=xdilation, [28]=ydilation, [29]=zdilation,
  18675. 0 }; // [30]=interpolation_type
  18676. l_opcode.assign(); // Don't use 'opcode': it could be modified by further calls to 'compile()'!
  18677. CImg<ulongT>(default_params,1,sizeof(default_params)/sizeof(ulongT)).move_to(l_opcode);
  18678. arg1 = 2;
  18679. for (s = std::strchr(ss,'(') + 1; s<se && arg1<l_opcode[0]._height; ++s) {
  18680. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  18681. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  18682. l_opcode(0,arg1++) = compile(s,ns,depth1,0,block_flags);
  18683. s = ns;
  18684. }
  18685. l_opcode[0].move_to(opcode);
  18686. if (arg1<12 || arg1>opcode._height) {
  18687. _cimg_mp_strerr;
  18688. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  18689. "CImg<%s>::%s: %s: %s arguments provided, in expression '%s'.",
  18690. pixel_type(),_cimg_mp_calling_function,s_op,
  18691. arg1<12?"Not enough":"Too much",s0);
  18692. }
  18693. _cimg_mp_check_type(opcode[2],1,2,0); // A
  18694. _cimg_mp_check_const_scalar(opcode[3],2,3); // wA
  18695. _cimg_mp_check_const_scalar(opcode[4],3,3); // hA
  18696. _cimg_mp_check_const_scalar(opcode[5],4,3); // dA
  18697. _cimg_mp_check_const_scalar(opcode[6],5,3); // sA
  18698. _cimg_mp_check_type(opcode[7],6,2,0); // M
  18699. _cimg_mp_check_const_scalar(opcode[8],7,3); // wM
  18700. _cimg_mp_check_const_scalar(opcode[9],8,3); // hM
  18701. _cimg_mp_check_const_scalar(opcode[10],9,3); // dM
  18702. _cimg_mp_check_const_scalar(opcode[11],10,3); // sM
  18703. _cimg_mp_check_type(opcode[12],11,1,0); // boundary_conditions
  18704. _cimg_mp_check_type(opcode[13],12,1,0); // is_normalized
  18705. _cimg_mp_check_const_scalar(opcode[14],13,1); // channel_mode
  18706. if (opcode[15]!=~0U) _cimg_mp_check_type(opcode[15],14,1,0); // xcenter
  18707. if (opcode[16]!=~0U) _cimg_mp_check_type(opcode[16],15,1,0); // ycenter
  18708. if (opcode[17]!=~0U) _cimg_mp_check_type(opcode[17],16,1,0); // zcenter
  18709. _cimg_mp_check_const_scalar(opcode[18],17,1); // xstart
  18710. _cimg_mp_check_const_scalar(opcode[19],18,1); // ystart
  18711. _cimg_mp_check_const_scalar(opcode[20],19,1); // zstart
  18712. if (opcode[21]!=~0U) _cimg_mp_check_const_scalar(opcode[21],20,1); // xend
  18713. if (opcode[22]!=~0U) _cimg_mp_check_const_scalar(opcode[22],21,1); // yend
  18714. if (opcode[23]!=~0U) _cimg_mp_check_const_scalar(opcode[23],22,1); // zend
  18715. _cimg_mp_check_const_scalar(opcode[24],23,0); // xstride
  18716. _cimg_mp_check_const_scalar(opcode[25],24,0); // ystride
  18717. _cimg_mp_check_const_scalar(opcode[26],25,0); // zstride
  18718. _cimg_mp_check_type(opcode[27],26,1,0); // xdilation
  18719. _cimg_mp_check_type(opcode[28],27,1,0); // ydilation
  18720. _cimg_mp_check_type(opcode[29],28,1,0); // zdilation
  18721. _cimg_mp_check_type(opcode[30],29,1,0); // interpolation_type
  18722. const unsigned int
  18723. wA = (unsigned int)mem[opcode[3]],
  18724. hA = (unsigned int)mem[opcode[4]],
  18725. dA = (unsigned int)mem[opcode[5]],
  18726. sA = (unsigned int)mem[opcode[6]],
  18727. wM = (unsigned int)mem[opcode[8]],
  18728. hM = (unsigned int)mem[opcode[9]],
  18729. dM = (unsigned int)mem[opcode[10]],
  18730. sM = (unsigned int)mem[opcode[11]],
  18731. channel_mode = (unsigned int)mem[opcode[14]];
  18732. const int
  18733. xstart = (int)mem[opcode[18]],
  18734. ystart = (int)mem[opcode[19]],
  18735. zstart = (int)mem[opcode[20]],
  18736. xend = opcode[21]!=~0U?(int)mem[opcode[21]]:wA - 1,
  18737. yend = opcode[22]!=~0U?(int)mem[opcode[22]]:hA - 1,
  18738. zend = opcode[23]!=~0U?(int)mem[opcode[23]]:dA - 1;
  18739. if (xstart>xend || ystart>yend || zstart>zend) {
  18740. _cimg_mp_strerr;
  18741. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  18742. "CImg<%s>::%s: %s: Invalid xyz-start/end arguments "
  18743. "(start = (%d,%d,%d), end = (%d,%d,%d)), in expression '%s'.",
  18744. pixel_type(),_cimg_mp_calling_function,s_op,
  18745. xstart,ystart,zstart,xend,yend,zend,s0);
  18746. }
  18747. const float
  18748. xstride = (float)mem[opcode[24]],
  18749. ystride = (float)mem[opcode[25]],
  18750. zstride = (float)mem[opcode[26]];
  18751. if (xstride<=0 || ystride<=0 || zstride<=0) {
  18752. _cimg_mp_strerr;
  18753. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  18754. "CImg<%s>::%s: %s: Invalid stride arguments (%g,%g,%g), "
  18755. "in expression '%s'.",
  18756. pixel_type(),_cimg_mp_calling_function,s_op,
  18757. xstride,ystride,zstride,s0);
  18758. }
  18759. arg2 = xend - xstart + 1;
  18760. arg3 = yend - ystart + 1;
  18761. arg4 = zend - zstart + 1;
  18762. arg5 = !channel_mode?sA*sM:channel_mode==1?std::max(sA,sM):
  18763. channel_mode==2?std::max(sA,sM)/std::min(sA,sM):1U;
  18764. opcode[1] = pos = vector(arg2*arg3*arg4*arg5);
  18765. opcode[3] = (ulongT)wA;
  18766. opcode[4] = (ulongT)hA;
  18767. opcode[5] = (ulongT)dA;
  18768. opcode[6] = (ulongT)sA;
  18769. opcode[8] = (ulongT)wM;
  18770. opcode[9] = (ulongT)hM;
  18771. opcode[10] = (ulongT)dM;
  18772. opcode[11] = (ulongT)sM;
  18773. opcode[14] = (ulongT)channel_mode;
  18774. opcode[18] = (ulongT)xstart;
  18775. opcode[19] = (ulongT)ystart;
  18776. opcode[20] = (ulongT)zstart;
  18777. opcode[21] = (ulongT)xend;
  18778. opcode[22] = (ulongT)yend;
  18779. opcode[23] = (ulongT)zend;
  18780. opcode.move_to(code);
  18781. return_new_comp = true;
  18782. _cimg_mp_return(pos);
  18783. }
  18784. break;
  18785. case 'd' :
  18786. if (*ss1=='(') { // Image depth
  18787. _cimg_mp_op("Function 'd()'");
  18788. if (*ss2=='#') { // Index specified
  18789. p1 = compile(ss3,se1,depth1,0,block_flags);
  18790. _cimg_mp_check_notnan_index(p1);
  18791. _cimg_mp_check_list();
  18792. } else { if (ss2!=se1) break; p1 = ~0U; }
  18793. _cimg_mp_scalar1(mp_image_d,p1);
  18794. }
  18795. if (!std::strncmp(ss,"da_back(",8) ||
  18796. !std::strncmp(ss,"da_pop(",7) ||
  18797. !std::strncmp(ss,"da_pop_heap(",12)) { // Get latest element in a dynamic array
  18798. if (!is_inside_critical) is_parallelizable = false;
  18799. const bool is_pop = *ss3=='p', is_pop_heap = *ss6=='_';
  18800. _cimg_mp_op(is_pop_heap?"Function 'da_pop_heap()":
  18801. is_pop?"Function 'da_pop()'":"Function 'da_back()'");
  18802. s0 = ss + (is_pop_heap?12:is_pop?7:8);
  18803. if (*s0=='#') { // Index specified
  18804. s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  18805. p1 = compile(s0,s1++,depth1,0,block_flags);
  18806. _cimg_mp_check_notnan_index(p1);
  18807. } else { p1 = 11; s1 = s0; }
  18808. _cimg_mp_check_list();
  18809. _cimg_mp_check_const_scalar(p1,1,1);
  18810. p3 = (unsigned int)cimg::mod((int)mem[p1],imglist.width());
  18811. p2 = imglist[p3]._spectrum;
  18812. if (p2>1) pos = vector(p2); else pos = scalar(); // Return vector or scalar result
  18813. CImg<ulongT>::vector((ulongT)mp_da_back_or_pop,pos,p2,p1,is_pop_heap?2:is_pop?1:0).move_to(code);
  18814. return_new_comp = true;
  18815. _cimg_mp_return(pos);
  18816. }
  18817. if (!std::strncmp(ss,"da_insert(",10) ||
  18818. !std::strncmp(ss,"da_push(",8) ||
  18819. !std::strncmp(ss,"da_push_heap(",13)) { // Insert element(s) in a dynamic array
  18820. if (!is_inside_critical) is_parallelizable = false;
  18821. const bool is_push = *ss3=='p', is_push_heap = *ss7=='_';
  18822. _cimg_mp_op(is_push_heap?"Function 'da_push_heap()'":
  18823. is_push?"Function 'da_push()'":"Function 'da_insert()'");
  18824. s0 = ss + (is_push_heap?13:is_push?8:10);
  18825. if (*s0=='#') { // Index specified
  18826. s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  18827. p1 = compile(s0,s1++,depth1,0,block_flags);
  18828. _cimg_mp_check_notnan_index(p1);
  18829. } else { p1 = 11; s1 = s0; }
  18830. _cimg_mp_check_list();
  18831. if (!is_push) {
  18832. s0 = s1; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  18833. arg1 = compile(s0,s1++,depth1,0,block_flags); // Position
  18834. } else if (is_push_heap) arg1 = ~0U - 1;
  18835. else arg1 = ~0U;
  18836. CImg<ulongT>::vector((ulongT)mp_da_insert_or_push,_cimg_mp_slot_nan,p1,arg1,0,0).move_to(l_opcode);
  18837. p3 = p1==~0U?2:3;
  18838. p1 = ~0U;
  18839. for (s = s1; s<se; ++s) {
  18840. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  18841. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  18842. arg2 = compile(s,ns,depth1,0,block_flags); // Element
  18843. p2 = _cimg_mp_size(arg2);
  18844. if (p1==~0U) p1 = p2;
  18845. else {
  18846. if (!p1) _cimg_mp_check_type(arg2,p3,1,0);
  18847. else _cimg_mp_check_type(arg2,p3,2,p1);
  18848. }
  18849. CImg<ulongT>::vector(arg2).move_to(l_opcode);
  18850. s = ns;
  18851. ++p3;
  18852. }
  18853. if (p1==~0U) compile(++s1,se1,depth1,0,block_flags); // Missing element -> error
  18854. (l_opcode>'y').move_to(opcode);
  18855. opcode[4] = p1;
  18856. opcode[5] = opcode._height;
  18857. opcode.move_to(code);
  18858. _cimg_mp_return_nan();
  18859. }
  18860. if (!std::strncmp(ss,"da_freeze(",10)) { // Freeze dynamic array
  18861. if (!is_inside_critical) is_parallelizable = false;
  18862. _cimg_mp_op("Function 'da_freeze()'");
  18863. s0 = ss + 10;
  18864. if (*s0=='#') { // Index specified
  18865. s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  18866. p1 = compile(s0,s1++,depth1,0,block_flags);
  18867. _cimg_mp_check_notnan_index(p1);
  18868. } else { p1 = 11; s1 = s0; }
  18869. _cimg_mp_check_list();
  18870. CImg<ulongT>::vector((ulongT)mp_da_freeze,_cimg_mp_slot_nan,p1).move_to(code);
  18871. _cimg_mp_return_nan();
  18872. }
  18873. if (!std::strncmp(ss,"da_remove(",10)) { // Remove element(s) in a dynamic array
  18874. if (!is_inside_critical) is_parallelizable = false;
  18875. _cimg_mp_op("Function 'da_remove()'");
  18876. if (ss[10]=='#') { // Index specified
  18877. s0 = ss + 11; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  18878. p1 = compile(ss + 11,s0++,depth1,0,block_flags);
  18879. _cimg_mp_check_notnan_index(p1);
  18880. } else { p1 = 11; s0 = ss + 10; }
  18881. _cimg_mp_check_list();
  18882. arg1 = arg2 = ~0U;
  18883. if (s0<se1) {
  18884. s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  18885. arg1 = compile(s0,s1,depth1,0,block_flags); // Starting position
  18886. arg2 = s1<se1?compile(++s1,se1,depth1,0,block_flags):~0U; // Ending position
  18887. }
  18888. CImg<ulongT>::vector((ulongT)mp_da_remove,_cimg_mp_slot_nan,p1,arg1,arg2).move_to(code);
  18889. _cimg_mp_return_nan();
  18890. }
  18891. if (!std::strncmp(ss,"da_size(",8)) { // Size of a dynamic array
  18892. if (!is_inside_critical) is_parallelizable = false;
  18893. _cimg_mp_op("Function 'da_size()'");
  18894. if (ss[8]=='#') { // Index specified
  18895. s0 = ss + 9; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  18896. p1 = compile(ss + 9,s0++,depth1,0,block_flags);
  18897. _cimg_mp_check_notnan_index(p1);
  18898. } else { p1 = 11; s0 = ss + 8; }
  18899. _cimg_mp_check_list();
  18900. _cimg_mp_scalar1(mp_da_size,p1);
  18901. }
  18902. if (!std::strncmp(ss,"date(",5)) { // Current date or file date
  18903. _cimg_mp_op("Function 'date()'");
  18904. s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  18905. arg1 = ss5!=se1?compile(ss5,s1,depth1,0,block_flags):~0U;
  18906. arg2 = s1<se1?compile(++s1,se1,depth1,0,block_flags):~0U;
  18907. if (arg2!=~0U) _cimg_mp_check_type(arg2,1,2,0);
  18908. pos = arg1==~0U || _cimg_mp_is_vector(arg1)?vector(arg1==~0U?7:_cimg_mp_size(arg1)):scalar();
  18909. CImg<ulongT>::vector((ulongT)mp_date,pos,_cimg_mp_size(pos),
  18910. arg1,arg1==~0U?~0U:_cimg_mp_size(arg1),
  18911. arg2,arg2==~0U?~0U:_cimg_mp_size(arg2)).move_to(code);
  18912. return_new_comp = true;
  18913. _cimg_mp_return(pos);
  18914. }
  18915. if (!std::strncmp(ss,"debug(",6)) { // Print debug info
  18916. _cimg_mp_op("Function 'debug()'");
  18917. p1 = code._width;
  18918. arg1 = compile(ss6,se1,depth1,p_ref,block_flags);
  18919. *se1 = 0;
  18920. variable_name.assign(CImg<charT>::string(ss6,true,true).unroll('y'),true);
  18921. cimg::strpare(variable_name,false,true);
  18922. ((CImg<ulongT>::vector((ulongT)mp_debug,arg1,0,code._width - p1),
  18923. variable_name)>'y').move_to(opcode);
  18924. opcode[2] = opcode._height;
  18925. opcode.move_to(code,p1);
  18926. *se1 = ')';
  18927. _cimg_mp_return(arg1);
  18928. }
  18929. if (!std::strncmp(ss,"deg2rad(",8)) { // Degrees to radians
  18930. _cimg_mp_op("Function 'deg2rad()'");
  18931. arg1 = compile(ss8,se1,depth1,0,block_flags);
  18932. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_deg2rad,arg1);
  18933. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(mem[arg1]*cimg::PI/180);
  18934. _cimg_mp_scalar1(mp_deg2rad,arg1);
  18935. }
  18936. if (!std::strncmp(ss,"display(",8)) { // Display memory, vector or image
  18937. _cimg_mp_op("Function 'display()'");
  18938. if (pexpr[se2 - expr._data]=='(') { // no arguments?
  18939. CImg<ulongT>::vector((ulongT)mp_display_memory,_cimg_mp_slot_nan).move_to(code);
  18940. _cimg_mp_return_nan();
  18941. }
  18942. if (*ss8!='#') { // Vector
  18943. s1 = ss8; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  18944. arg1 = compile(ss8,s1,depth1,0,block_flags);
  18945. arg2 = 0; arg3 = arg4 = arg5 = 1;
  18946. if (s1<se1) {
  18947. s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  18948. arg2 = compile(s1 + 1,s2,depth1,0,block_flags);
  18949. if (s2<se1) {
  18950. s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
  18951. arg3 = compile(s2,s3,depth1,0,block_flags);
  18952. if (s3<se1) {
  18953. s2 = ++s3; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  18954. arg4 = compile(s3,s2,depth1,0,block_flags);
  18955. arg5 = s2<se1?compile(++s2,se1,depth1,0,block_flags):0;
  18956. }
  18957. }
  18958. }
  18959. _cimg_mp_check_type(arg2,2,1,0);
  18960. _cimg_mp_check_type(arg3,3,1,0);
  18961. _cimg_mp_check_type(arg4,4,1,0);
  18962. _cimg_mp_check_type(arg5,5,1,0);
  18963. c1 = *s1; *s1 = 0;
  18964. variable_name.assign(CImg<charT>::string(ss8,true,true).unroll('y'),true);
  18965. cimg::strpare(variable_name,false,true);
  18966. if (_cimg_mp_is_vector(arg1))
  18967. ((CImg<ulongT>::vector((ulongT)mp_vector_print,arg1,0,(ulongT)_cimg_mp_size(arg1),0),
  18968. variable_name)>'y').move_to(opcode);
  18969. else
  18970. ((CImg<ulongT>::vector((ulongT)mp_print,arg1,0,0),
  18971. variable_name)>'y').move_to(opcode);
  18972. opcode[2] = opcode._height;
  18973. opcode.move_to(code);
  18974. ((CImg<ulongT>::vector((ulongT)mp_display,arg1,0,(ulongT)_cimg_mp_size(arg1),
  18975. arg2,arg3,arg4,arg5),
  18976. variable_name)>'y').move_to(opcode);
  18977. opcode[2] = opcode._height;
  18978. opcode.move_to(code);
  18979. *s1 = c1;
  18980. _cimg_mp_return(arg1);
  18981. } else { // Image
  18982. p1 = compile(ss8 + 1,se1,depth1,0,block_flags);
  18983. _cimg_mp_check_list();
  18984. CImg<ulongT>::vector((ulongT)mp_image_display,_cimg_mp_slot_nan,p1).move_to(code);
  18985. _cimg_mp_return_nan();
  18986. }
  18987. }
  18988. if (!std::strncmp(ss,"det(",4)) { // Matrix determinant
  18989. _cimg_mp_op("Function 'det()'");
  18990. arg1 = compile(ss4,se1,depth1,0,block_flags);
  18991. _cimg_mp_check_matrix_square(arg1,1);
  18992. p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
  18993. _cimg_mp_scalar2(mp_det,arg1,p1);
  18994. }
  18995. if (!std::strncmp(ss,"diag(",5)) { // Diagonal matrix
  18996. _cimg_mp_op("Function 'diag()'");
  18997. CImg<ulongT>::vector((ulongT)mp_diag,0,0).move_to(l_opcode);
  18998. for (s = ss5; s<se; ++s) {
  18999. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  19000. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  19001. arg2 = compile(s,ns,depth1,0,block_flags);
  19002. if (_cimg_mp_is_vector(arg2))
  19003. CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
  19004. arg2 + (ulongT)_cimg_mp_size(arg2)).
  19005. move_to(l_opcode);
  19006. else CImg<ulongT>::vector(arg2).move_to(l_opcode);
  19007. s = ns;
  19008. }
  19009. (l_opcode>'y').move_to(opcode);
  19010. arg1 = opcode._height - 3;
  19011. pos = vector(arg1*arg1);
  19012. opcode[1] = pos;
  19013. opcode[2] = opcode._height;
  19014. opcode.move_to(code);
  19015. return_new_comp = true;
  19016. _cimg_mp_return(pos);
  19017. }
  19018. if (!std::strncmp(ss,"dot(",4)) { // Dot product
  19019. _cimg_mp_op("Function 'dot()'");
  19020. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  19021. arg1 = compile(ss4,s1,depth1,0,block_flags);
  19022. arg2 = compile(++s1,se1,depth1,0,block_flags);
  19023. if (_cimg_mp_is_vector(arg1)) {
  19024. _cimg_mp_check_type(arg2,2,2,_cimg_mp_size(arg1));
  19025. _cimg_mp_scalar3(mp_dot,arg1,arg2,_cimg_mp_size(arg1));
  19026. }
  19027. _cimg_mp_check_type(arg2,2,1,0);
  19028. _cimg_mp_scalar2(mp_mul,arg1,arg2);
  19029. }
  19030. if (!std::strncmp(ss,"do(",3)) { // Do..while
  19031. _cimg_mp_op("Function 'do()'");
  19032. s0 = *ss2=='('?ss3:ss8;
  19033. s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  19034. arg1 = code._width;
  19035. arg6 = mempos;
  19036. p1 = compile(s0,s1,depth1,0,block_flags); // Body
  19037. arg2 = code._width;
  19038. p2 = s1<se1?compile(++s1,se1,depth1,0,block_flags):p1; // Condition
  19039. _cimg_mp_check_type(p2,2,1,0);
  19040. CImg<ulongT>::vector((ulongT)mp_do,p1,p2,arg2 - arg1,code._width - arg2,_cimg_mp_size(p1),
  19041. p1>=arg6 && !_cimg_mp_is_const_scalar(p1),
  19042. p2>=arg6 && !_cimg_mp_is_const_scalar(p2)).move_to(code,arg1);
  19043. _cimg_mp_return(p1);
  19044. }
  19045. if (!std::strncmp(ss,"draw(",5)) { // Draw image
  19046. _cimg_mp_op("Function 'draw()'");
  19047. if (*ss5=='#') { // Index specified
  19048. s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  19049. p1 = compile(ss6,s0++,depth1,0,block_flags);
  19050. _cimg_mp_check_notnan_index(p1);
  19051. _cimg_mp_check_list();
  19052. } else { p1 = ~0U; s0 = ss5; }
  19053. for (s = s0; s<se; ++s, ++pos) {
  19054. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  19055. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  19056. arg1 = compile(s,ns,depth1,0,block_flags);
  19057. CImg<ulongT>::vector(arg1).move_to(l_opcode);
  19058. s = ns;
  19059. }
  19060. (l_opcode>'y').move_to(opcode);
  19061. is_sth = p1==~0U && opcode._height>5 && _cimg_mp_is_vector((unsigned int)opcode[5]); // Is vector drawing?
  19062. if ((is_sth && (opcode._height<6 || opcode._height>17)) ||
  19063. (!is_sth && (opcode._height<1 || opcode._height>12))) {
  19064. _cimg_mp_strerr;
  19065. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  19066. "CImg<%s>::%s: %s: Too %s arguments specified, "
  19067. "in expression '%s'.",
  19068. pixel_type(),_cimg_mp_calling_function,s_op,
  19069. opcode._height>12?"much":"few",s0);
  19070. }
  19071. if (is_sth) { // Drawing in a vector
  19072. _cimg_mp_check_type((unsigned int)*opcode,1,2,0); // D
  19073. _cimg_mp_check_type((unsigned int)opcode[1],2,1,0); // w
  19074. _cimg_mp_check_type((unsigned int)opcode[2],3,1,0); // h
  19075. _cimg_mp_check_type((unsigned int)opcode[3],4,1,0); // d
  19076. _cimg_mp_check_type((unsigned int)opcode[4],5,1,0); // s
  19077. if (opcode._height<8 || (opcode._height<10 && _cimg_mp_is_vector((unsigned int)opcode[7]))) {
  19078. // D,w,h,d,s,S[,opac,M,maxM]
  19079. if (opcode._height>6) _cimg_mp_check_type((unsigned int)opcode[6],7,1,0); // opac
  19080. if (opcode._height>8) _cimg_mp_check_type((unsigned int)opcode[8],9,1,0); // maxM
  19081. CImg<ulongT>::vector((ulongT)mp_vector_draw,
  19082. *opcode,_cimg_mp_size((unsigned int)*opcode), // 1-2: D,sizD
  19083. opcode[1],opcode[2],opcode[3],opcode[4], // 3-6: w,h,d,s
  19084. opcode[5],_cimg_mp_size((unsigned int)opcode[5]), // 7-8: S,sizS
  19085. 0,0,0,0, // 9-12: x,y,z,c
  19086. ~0U,~0U,~0U,~0U, // 13-16: dx,dy,dz,dc
  19087. opcode._height<7?1:opcode[6], // 17: opac
  19088. opcode._height<8?~0U:opcode[7], // 18: M
  19089. opcode._height<8?0:_cimg_mp_size((unsigned int)opcode[7]), // 19: sizM
  19090. opcode._height<9?1:opcode[8]).move_to(code); // 20: maxM
  19091. } else if (opcode._height<10 || (opcode._height<12 && _cimg_mp_is_vector((unsigned int)opcode[9]))) {
  19092. // D,w,h,d,s,S,x,dx[,opac,M,maxM]
  19093. _cimg_mp_check_type((unsigned int)opcode[6],7,1,0); // x
  19094. _cimg_mp_check_type((unsigned int)opcode[7],8,1,0); // dx
  19095. if (opcode._height>8) _cimg_mp_check_type((unsigned int)opcode[8],9,1,0); // opac
  19096. if (opcode._height>10) _cimg_mp_check_type((unsigned int)opcode[10],11,1,0); // maxM
  19097. CImg<ulongT>::vector((ulongT)mp_vector_draw,
  19098. *opcode,_cimg_mp_size((unsigned int)*opcode), // 1-2: D,sizD
  19099. opcode[1],opcode[2],opcode[3],opcode[4], // 3-6: w,h,d,s
  19100. opcode[5],_cimg_mp_size((unsigned int)opcode[5]), // 7-8: S,sizS
  19101. opcode[6],0,0,0, // 9-12: x,y,z,c
  19102. opcode[7],~0U,~0U,~0U, // 13-16: dx,dy,dz,dc
  19103. opcode._height<9?1:opcode[8], // 17: opac
  19104. opcode._height<10?~0U:opcode[9], // 18: M
  19105. opcode._height<10?0:_cimg_mp_size((unsigned int)opcode[9]), // 19: sizM
  19106. opcode._height<11?1:opcode[10]).move_to(code); // 20: maxM
  19107. } else if (opcode._height<12 || (opcode._height<14 && _cimg_mp_is_vector((unsigned int)opcode[11]))) {
  19108. // D,w,h,d,s,S,x,y,dx,dy[,opac,M,maxM]
  19109. _cimg_mp_check_type((unsigned int)opcode[6],7,1,0); // x
  19110. _cimg_mp_check_type((unsigned int)opcode[7],8,1,0); // y
  19111. _cimg_mp_check_type((unsigned int)opcode[8],9,1,0); // dx
  19112. _cimg_mp_check_type((unsigned int)opcode[9],10,1,0); // dy
  19113. if (opcode._height>10) _cimg_mp_check_type((unsigned int)opcode[10],11,1,0); // opac
  19114. if (opcode._height>12) _cimg_mp_check_type((unsigned int)opcode[12],13,1,0); // maxM
  19115. CImg<ulongT>::vector((ulongT)mp_vector_draw,
  19116. *opcode,_cimg_mp_size((unsigned int)*opcode), // 1-2: D,sizD
  19117. opcode[1],opcode[2],opcode[3],opcode[4], // 3-6: w,h,d,s
  19118. opcode[5],_cimg_mp_size((unsigned int)opcode[5]), // 7-8: S,sizS
  19119. opcode[6],opcode[7],0,0, // 9-12: x,y,z,c
  19120. opcode[8],opcode[9],~0U,~0U, // 13-16: dx,dy,dz,dc
  19121. opcode._height<11?1:opcode[10], // 17: opac
  19122. opcode._height<12?~0U:opcode[11], // 18: M
  19123. opcode._height<12?0:_cimg_mp_size((unsigned int)opcode[11]), // 19: sizM
  19124. opcode._height<13?1:opcode[12]).move_to(code); // 20: maxM
  19125. } else if (opcode._height<14 || (opcode._height<16 && _cimg_mp_is_vector((unsigned int)opcode[13]))) {
  19126. // D,w,h,d,s,S,x,y,z,dx,dy,dz[,opac,M,maxM]
  19127. _cimg_mp_check_type((unsigned int)opcode[6],7,1,0); // x
  19128. _cimg_mp_check_type((unsigned int)opcode[7],8,1,0); // y
  19129. _cimg_mp_check_type((unsigned int)opcode[8],9,1,0); // z
  19130. _cimg_mp_check_type((unsigned int)opcode[9],10,1,0); // dx
  19131. _cimg_mp_check_type((unsigned int)opcode[10],11,1,0); // dy
  19132. _cimg_mp_check_type((unsigned int)opcode[11],12,1,0); // dz
  19133. if (opcode._height>12) _cimg_mp_check_type((unsigned int)opcode[12],13,1,0); // opac
  19134. if (opcode._height>14) _cimg_mp_check_type((unsigned int)opcode[14],15,1,0); // maxM
  19135. CImg<ulongT>::vector((ulongT)mp_vector_draw,
  19136. *opcode,_cimg_mp_size((unsigned int)*opcode), // 1-2: D,sizD
  19137. opcode[1],opcode[2],opcode[3],opcode[4], // 3-6: w,h,d,s
  19138. opcode[5],_cimg_mp_size((unsigned int)opcode[5]), // 7-8: S,sizS
  19139. opcode[6],opcode[7],opcode[8],0, // 9-12: x,y,z,c
  19140. opcode[9],opcode[10],opcode[11],~0U, // 13-16: dx,dy,dz,dc
  19141. opcode._height<13?1:opcode[12], // 17: opac
  19142. opcode._height<14?~0U:opcode[13], // 18: M
  19143. opcode._height<14?0:_cimg_mp_size((unsigned int)opcode[13]), // 19: sizM
  19144. opcode._height<15?1:opcode[14]).move_to(code); // 20: maxM
  19145. } else if (opcode._height<16 || (opcode._height<18 && _cimg_mp_is_vector((unsigned int)opcode[15]))) {
  19146. // D,w,h,d,s,S,x,y,z,c,dx,dy,dz,dc[,opac,M,maxM]
  19147. _cimg_mp_check_type((unsigned int)opcode[6],7,1,0); // x
  19148. _cimg_mp_check_type((unsigned int)opcode[7],8,1,0); // y
  19149. _cimg_mp_check_type((unsigned int)opcode[8],9,1,0); // z
  19150. _cimg_mp_check_type((unsigned int)opcode[9],10,1,0); // c
  19151. _cimg_mp_check_type((unsigned int)opcode[10],11,1,0); // dx
  19152. _cimg_mp_check_type((unsigned int)opcode[11],12,1,0); // dy
  19153. _cimg_mp_check_type((unsigned int)opcode[12],13,1,0); // dz
  19154. _cimg_mp_check_type((unsigned int)opcode[13],14,1,0); // dc
  19155. if (opcode._height>14) _cimg_mp_check_type((unsigned int)opcode[14],15,1,0); // opac
  19156. if (opcode._height>16) _cimg_mp_check_type((unsigned int)opcode[16],17,1,0); // maxM
  19157. CImg<ulongT>::vector((ulongT)mp_vector_draw,
  19158. *opcode,_cimg_mp_size((unsigned int)*opcode), // 1-2: D,sizD
  19159. opcode[1],opcode[2],opcode[3],opcode[4], // 3-6: w,h,d,s
  19160. opcode[5],_cimg_mp_size((unsigned int)opcode[5]), // 7-8: S,sizS
  19161. opcode[6],opcode[7],opcode[8],opcode[9], // 9-12: x,y,z,c
  19162. opcode[10],opcode[11],opcode[12],opcode[13], // 13-16: dx,dy,dz,dc
  19163. opcode._height<15?1:opcode[14], // 17: opac
  19164. opcode._height<16?~0U:opcode[15], // 18: M
  19165. opcode._height<16?0:_cimg_mp_size((unsigned int)opcode[15]), // 19: sizM
  19166. opcode._height<17?1:opcode[16]).move_to(code); // 20: maxM
  19167. } else {
  19168. _cimg_mp_strerr;
  19169. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  19170. "CImg<%s>::%s: %s: Invalid types in specified arguments, "
  19171. "in expression '%s'.",
  19172. pixel_type(),_cimg_mp_calling_function,s_op,s0);
  19173. }
  19174. } else { // Drawing in an image
  19175. if (!is_inside_critical) is_parallelizable = false;
  19176. arg1 = p1!=~0U;
  19177. _cimg_mp_check_type((unsigned int)*opcode,1 + arg1,2,0); // S
  19178. if (opcode._height<3 || (opcode._height<5 && _cimg_mp_is_vector((unsigned int)opcode[2]))) {
  19179. // S[,opac,M,maxM]
  19180. if (opcode._height>1) _cimg_mp_check_type((unsigned int)opcode[1],2 + arg1,1,0); // opac
  19181. if (opcode._height>3) _cimg_mp_check_type((unsigned int)opcode[3],4 + arg1,1,0); // maxM
  19182. CImg<ulongT>::vector((ulongT)mp_image_draw,
  19183. *opcode,_cimg_mp_size((unsigned int)*opcode),p1, // 1-3: S,sizS,#ind
  19184. 0,0,0,0, // 4-7: x,y,z,c
  19185. ~0U,~0U,~0U,~0U, // 8-11: dx,dy,dz,dc
  19186. opcode._height<2?1:opcode[1], // 12: opac
  19187. opcode._height<3?~0U:opcode[2], // 13: M
  19188. opcode._height<3?0:_cimg_mp_size((unsigned int)opcode[2]), // 14: sizM
  19189. opcode._height<4?1:opcode[3]).move_to(code); // 15: maxM
  19190. } else if (opcode._height<5 || (opcode._height<7 && _cimg_mp_is_vector((unsigned int)opcode[4]))) {
  19191. // x,dx,S[,opac,M,maxM]
  19192. _cimg_mp_check_type((unsigned int)opcode[1],2 + arg1,1,0); // x
  19193. _cimg_mp_check_type((unsigned int)opcode[2],3 + arg1,1,0); // dx
  19194. if (opcode._height>3) _cimg_mp_check_type((unsigned int)opcode[3],4 + arg1,1,0); // opac
  19195. if (opcode._height>5) _cimg_mp_check_type((unsigned int)opcode[5],6 + arg1,1,0); // maxM
  19196. CImg<ulongT>::vector((ulongT)mp_image_draw,
  19197. *opcode,_cimg_mp_size((unsigned int)*opcode),p1, // 1-3: S,sizS,#ind
  19198. opcode[1],0,0,0, // 4-7: x,y,z,c
  19199. opcode[2],~0U,~0U,~0U, // 8-11: dx,dy,dz,dc
  19200. opcode._height<4?1:opcode[3], // 12: opac
  19201. opcode._height<5?~0U:opcode[4], // 13: M
  19202. opcode._height<5?0:_cimg_mp_size((unsigned int)opcode[4]), // 14: sizM
  19203. opcode._height<6?1:opcode[5]).move_to(code); // 15: maxM
  19204. } else if (opcode._height<7 || (opcode._height<9 && _cimg_mp_is_vector((unsigned int)opcode[6]))) {
  19205. // x,y,dx,dy,S[,opac,M,maxM]
  19206. _cimg_mp_check_type((unsigned int)opcode[1],2 + arg1,1,0); // x
  19207. _cimg_mp_check_type((unsigned int)opcode[2],3 + arg1,1,0); // y
  19208. _cimg_mp_check_type((unsigned int)opcode[3],4 + arg1,1,0); // dx
  19209. _cimg_mp_check_type((unsigned int)opcode[4],5 + arg1,1,0); // dy
  19210. if (opcode._height>5) _cimg_mp_check_type((unsigned int)opcode[5],6 + arg1,1,0); // opac
  19211. if (opcode._height>7) _cimg_mp_check_type((unsigned int)opcode[7],8 + arg1,1,0); // maxM
  19212. CImg<ulongT>::vector((ulongT)mp_image_draw,
  19213. *opcode,_cimg_mp_size((unsigned int)*opcode),p1, // 1-3: S,sizS,#ind
  19214. opcode[1],opcode[2],0,0, // 4-7: x,y,z,c
  19215. opcode[3],opcode[4],~0U,~0U, // 8-11: dx,dy,dz,dc
  19216. opcode._height<6?1:opcode[5], // 12: opac
  19217. opcode._height<7?~0U:opcode[6], // 13: M
  19218. opcode._height<7?0:_cimg_mp_size((unsigned int)opcode[6]), // 14: sizM
  19219. opcode._height<8?1:opcode[7]).move_to(code); // 15: maxM
  19220. } else if (opcode._height<9 || (opcode._height<11 && _cimg_mp_is_vector((unsigned int)opcode[8]))) {
  19221. // x,y,z,dx,dy,dz,S[,opac,M,maxM]
  19222. _cimg_mp_check_type((unsigned int)opcode[1],2 + arg1,1,0); // x
  19223. _cimg_mp_check_type((unsigned int)opcode[2],3 + arg1,1,0); // y
  19224. _cimg_mp_check_type((unsigned int)opcode[3],4 + arg1,1,0); // z
  19225. _cimg_mp_check_type((unsigned int)opcode[4],5 + arg1,1,0); // dx
  19226. _cimg_mp_check_type((unsigned int)opcode[5],6 + arg1,1,0); // dy
  19227. _cimg_mp_check_type((unsigned int)opcode[6],7 + arg1,1,0); // dz
  19228. if (opcode._height>7) _cimg_mp_check_type((unsigned int)opcode[7],8 + arg1,1,0); // opac
  19229. if (opcode._height>9) _cimg_mp_check_type((unsigned int)opcode[9],10 + arg1,1,0); // maxM
  19230. CImg<ulongT>::vector((ulongT)mp_image_draw,
  19231. *opcode,_cimg_mp_size((unsigned int)*opcode),p1, // 1-3: S,sizS,#ind
  19232. opcode[1],opcode[2],opcode[3],0, // 4-7: x,y,z,c
  19233. opcode[4],opcode[5],opcode[6],~0U, // 8-11: dx,dy,dz,dc
  19234. opcode._height<8?1:opcode[7], // 12: opac
  19235. opcode._height<9?~0U:opcode[8], // 13: M
  19236. opcode._height<9?0:_cimg_mp_size((unsigned int)opcode[8]), // 14: sizM
  19237. opcode._height<10?1:opcode[9]).move_to(code); // 15: maxM
  19238. } else if (opcode._height<11 || (opcode._height<13 && _cimg_mp_is_vector((unsigned int)opcode[10]))) {
  19239. // x,y,z,c,dx,dy,dz,dc,S[,opac,M,maxM]
  19240. _cimg_mp_check_type((unsigned int)opcode[1],2 + arg1,1,0); // x
  19241. _cimg_mp_check_type((unsigned int)opcode[2],3 + arg1,1,0); // y
  19242. _cimg_mp_check_type((unsigned int)opcode[3],4 + arg1,1,0); // z
  19243. _cimg_mp_check_type((unsigned int)opcode[4],5 + arg1,1,0); // c
  19244. _cimg_mp_check_type((unsigned int)opcode[5],6 + arg1,1,0); // dx
  19245. _cimg_mp_check_type((unsigned int)opcode[6],7 + arg1,1,0); // dy
  19246. _cimg_mp_check_type((unsigned int)opcode[7],8 + arg1,1,0); // dz
  19247. _cimg_mp_check_type((unsigned int)opcode[8],9 + arg1,1,0); // dc
  19248. if (opcode._height>9) _cimg_mp_check_type((unsigned int)opcode[9],10 + arg1,1,0); // opac
  19249. if (opcode._height>11) _cimg_mp_check_type((unsigned int)opcode[11],12 + arg1,1,0); // maxM
  19250. CImg<ulongT>::vector((ulongT)mp_image_draw,
  19251. *opcode,_cimg_mp_size((unsigned int)*opcode),p1, // 1-3: S,sizS,#ind
  19252. opcode[1],opcode[2],opcode[3],opcode[4], // 4-7: x,y,z,c
  19253. opcode[5],opcode[6],opcode[7],opcode[8], // 8-11: dx,dy,dz,dc
  19254. opcode._height<10?1:opcode[9], // 12: opac
  19255. opcode._height<11?~0U:opcode[10], // 13: M
  19256. opcode._height<11?0:_cimg_mp_size((unsigned int)opcode[10]), // 14: sizM
  19257. opcode._height<12?1:opcode[11]).move_to(code); // 15: maxM
  19258. } else {
  19259. _cimg_mp_strerr;
  19260. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  19261. "CImg<%s>::%s: %s: Invalid types in specified arguments, "
  19262. "in expression '%s'.",
  19263. pixel_type(),_cimg_mp_calling_function,s_op,s0);
  19264. }
  19265. }
  19266. _cimg_mp_return_nan();
  19267. }
  19268. break;
  19269. case 'e' :
  19270. if (!std::strncmp(ss,"echo(",5)) { // Echo
  19271. _cimg_mp_op("Function 'echo()'");
  19272. CImg<ulongT>::vector((ulongT)mp_echo,_cimg_mp_slot_nan,0).move_to(l_opcode);
  19273. for (s = ss5; s<se1; ++s) {
  19274. ns = s; while (ns<se1 && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  19275. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  19276. arg1 = compile(s,ns,depth1,0,block_flags);
  19277. CImg<ulongT>::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode);
  19278. s = ns;
  19279. }
  19280. (l_opcode>'y').move_to(opcode);
  19281. opcode[2] = opcode._height;
  19282. opcode.move_to(code);
  19283. _cimg_mp_return_nan();
  19284. }
  19285. if (!std::strncmp(ss,"eig(",4)) { // Matrix eigenvalues/eigenvector
  19286. _cimg_mp_op("Function 'eig()'");
  19287. arg1 = compile(ss4,se1,depth1,0,block_flags);
  19288. _cimg_mp_check_matrix_square(arg1,1);
  19289. p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
  19290. pos = vector((p1 + 1)*p1);
  19291. CImg<ulongT>::vector((ulongT)mp_matrix_eig,pos,arg1,p1).move_to(code);
  19292. return_new_comp = true;
  19293. _cimg_mp_return(pos);
  19294. }
  19295. if (!std::strncmp(ss,"ellipse(",8)) { // Ellipse/circle drawing
  19296. if (!is_inside_critical) is_parallelizable = false;
  19297. _cimg_mp_op("Function 'ellipse()'");
  19298. if (*ss8=='#') { // Index specified
  19299. s0 = ss + 9; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  19300. p1 = compile(ss + 9,s0++,depth1,0,block_flags);
  19301. _cimg_mp_check_notnan_index(p1);
  19302. _cimg_mp_check_list();
  19303. } else { p1 = ~0U; s0 = ss8; }
  19304. if (s0==se1) compile(s0,se1,depth1,0,block_flags); // 'missing' argument error
  19305. CImg<ulongT>::vector((ulongT)mp_ellipse,_cimg_mp_slot_nan,0,p1).move_to(l_opcode);
  19306. for (s = s0; s<se; ++s) {
  19307. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  19308. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  19309. arg2 = compile(s,ns,depth1,0,block_flags);
  19310. if (_cimg_mp_is_vector(arg2)) // Vector argument allowed to specify cordinates and color
  19311. CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
  19312. arg2 + (ulongT)_cimg_mp_size(arg2)).
  19313. move_to(l_opcode);
  19314. else
  19315. CImg<ulongT>::vector(arg2).move_to(l_opcode);
  19316. s = ns;
  19317. }
  19318. (l_opcode>'y').move_to(opcode);
  19319. opcode[2] = opcode._height;
  19320. opcode.move_to(code);
  19321. _cimg_mp_return_nan();
  19322. }
  19323. #if cimg_use_cpp11==1
  19324. if (!std::strncmp(ss,"erf(",4)) { // Error function
  19325. _cimg_mp_op("Function 'erf()'");
  19326. arg1 = compile(ss4,se1,depth1,0,block_flags);
  19327. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_erf,arg1);
  19328. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::erf(mem[arg1]));
  19329. _cimg_mp_scalar1(mp_erf,arg1);
  19330. }
  19331. #endif
  19332. if (!std::strncmp(ss,"erfinv(",7)) { // Inverse of error function
  19333. _cimg_mp_op("Function 'erfinv()'");
  19334. arg1 = compile(ss7,se1,depth1,0,block_flags);
  19335. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_erfinv,arg1);
  19336. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::erfinv(mem[arg1]));
  19337. _cimg_mp_scalar1(mp_erfinv,arg1);
  19338. }
  19339. if (!std::strncmp(ss,"exp(",4)) { // Exponential
  19340. _cimg_mp_op("Function 'exp()'");
  19341. arg1 = compile(ss4,se1,depth1,0,block_flags);
  19342. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_exp,arg1);
  19343. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::exp(mem[arg1]));
  19344. _cimg_mp_scalar1(mp_exp,arg1);
  19345. }
  19346. if (!std::strncmp(ss,"expr(",5)) { // Vector from expression
  19347. _cimg_mp_op("Function 'expr()'");
  19348. s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  19349. arg1 = compile(ss5,s1,depth1,0,block_flags);
  19350. _cimg_mp_check_type(arg1,1,2,0);
  19351. p1 = _cimg_mp_size(arg1);
  19352. arg2 = arg3 = arg4 = arg5 = 0;
  19353. if (s1<se1) {
  19354. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  19355. arg2 = compile(s1,s2,depth1,0,block_flags);
  19356. _cimg_mp_check_const_scalar(arg2,2,2);
  19357. arg2 = (unsigned int)mem[arg2];
  19358. if (arg2) arg3 = arg4 = arg5 = 1;
  19359. if (s2<se1) {
  19360. s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  19361. arg3 = compile(s2,s1,depth1,0,block_flags);
  19362. _cimg_mp_check_const_scalar(arg3,3,3);
  19363. arg3 = (unsigned int)mem[arg3];
  19364. if (s1<se1) {
  19365. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  19366. arg4 = compile(s1,s2,depth1,0,block_flags);
  19367. _cimg_mp_check_const_scalar(arg4,4,3);
  19368. arg4 = (unsigned int)mem[arg4];
  19369. arg5 = s2<se1?compile(++s2,se1,depth1,0,block_flags):0;
  19370. _cimg_mp_check_const_scalar(arg5,5,3);
  19371. arg5 = (unsigned int)mem[arg5];
  19372. }
  19373. }
  19374. }
  19375. p2 = arg2*arg3*arg4*arg5;
  19376. if (p2) pos = vector(p2); else pos = scalar();
  19377. CImg<ulongT>::vector((ulongT)mp_expr,pos,arg1,p1,arg2,arg3,arg4,arg5).move_to(code);
  19378. return_new_comp = true;
  19379. _cimg_mp_return(pos);
  19380. }
  19381. if (!std::strncmp(ss,"eye(",4)) { // Identity matrix
  19382. _cimg_mp_op("Function 'eye()'");
  19383. arg1 = compile(ss4,se1,depth1,0,block_flags);
  19384. _cimg_mp_check_const_scalar(arg1,1,3);
  19385. p1 = (unsigned int)mem[arg1];
  19386. pos = vector(p1*p1);
  19387. CImg<ulongT>::vector((ulongT)mp_eye,pos,p1).move_to(code);
  19388. return_new_comp = true;
  19389. _cimg_mp_return(pos);
  19390. }
  19391. if (!std::strncmp(ss,"end(",4)) { // End
  19392. _cimg_mp_op("Function 'end()'");
  19393. s1 = ss4; while (s1<se1 && cimg::is_blank(*s1)) ++s1;
  19394. if (s1!=se1) {
  19395. if (!is_inside_end) code.swap(code_end);
  19396. pos = compile(s1,se1,depth1,p_ref,8);
  19397. if (!is_inside_end) code.swap(code_end);
  19398. result_end_dim = _cimg_mp_size(pos);
  19399. result_end = mem._data + pos;
  19400. is_end_code = true;
  19401. }
  19402. _cimg_mp_return_nan();
  19403. }
  19404. if (!std::strncmp(ss,"end_t(",6)) { // End thread
  19405. _cimg_mp_op("Function 'end_t()'");
  19406. s1 = ss6; while (s1<se1 && cimg::is_blank(*s1)) ++s1;
  19407. if (s1!=se1) {
  19408. if (!is_inside_end_t) code.swap(code_end_t);
  19409. compile(s1,se1,depth1,p_ref,16);
  19410. if (!is_inside_end_t) code.swap(code_end_t);
  19411. is_end_code = true;
  19412. }
  19413. _cimg_mp_return_nan();
  19414. }
  19415. break;
  19416. case 'f' :
  19417. if (!std::strncmp(ss,"f2ui(",5)) { // Special float->uint conversion
  19418. _cimg_mp_op("Function 'f2ui()'");
  19419. arg1 = compile(ss5,se1,depth1,0,block_flags);
  19420. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_f2ui,arg1);
  19421. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar((double)cimg::float2uint((float)mem[arg1]));
  19422. _cimg_mp_scalar1(mp_f2ui,arg1);
  19423. }
  19424. if (!std::strncmp(ss,"fact(",5)) { // Factorial
  19425. _cimg_mp_op("Function 'fact()'");
  19426. arg1 = compile(ss5,se1,depth1,0,block_flags);
  19427. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_factorial,arg1);
  19428. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::factorial((int)mem[arg1]));
  19429. _cimg_mp_scalar1(mp_factorial,arg1);
  19430. }
  19431. if (!std::strncmp(ss,"fibo(",5)) { // Fibonacci
  19432. _cimg_mp_op("Function 'fibo()'");
  19433. arg1 = compile(ss5,se1,depth1,0,block_flags);
  19434. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_fibonacci,arg1);
  19435. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::fibonacci((int)mem[arg1]));
  19436. _cimg_mp_scalar1(mp_fibonacci,arg1);
  19437. }
  19438. if (!std::strncmp(ss,"fill(",5)) { // Fill
  19439. _cimg_mp_op("Function 'fill()'");
  19440. s0 = ss5; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  19441. arg1 = compile(ss5,s0,depth1,0,block_flags); // Object to fill
  19442. if (_cimg_mp_is_const_scalar(arg1))
  19443. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  19444. "CImg<%s>::%s: %s: Target scalar is constant, "
  19445. "in expression '%s'.",
  19446. pixel_type(),_cimg_mp_calling_function,s_op,ss);
  19447. s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  19448. p1 = code._width;
  19449. if (s1<se1) { // Version with 3 arguments
  19450. variable_name.assign(s0,(unsigned int)(s1 + 1 - s0)).back() = 0;
  19451. cimg::strpare(variable_name,false,true);
  19452. if (!cimg::is_varname(variable_name)) { // Invalid variable name
  19453. cimg::strellipsize(variable_name,64);
  19454. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  19455. "CImg<%s>::%s: %s: Invalid loop variable name '%s', "
  19456. "in expression '%s'.",
  19457. pixel_type(),_cimg_mp_calling_function,s_op,
  19458. variable_name._data,s0);
  19459. }
  19460. get_variable_pos(variable_name,arg2,arg3);
  19461. arg2 = arg3!=~0U?reserved_label[arg3]:arg2!=~0U?variable_pos[arg2]:~0U; // Variable slot
  19462. if (arg2!=~0U && (!_cimg_mp_is_scalar(arg2) ||
  19463. _cimg_mp_is_const_scalar(arg2))) { // Variable is not a vector or is a constant->error
  19464. cimg::strellipsize(variable_name,64);
  19465. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  19466. "CImg<%s>::%s: %s: Invalid type '%s' for variable '%s' "
  19467. "(expected 'scalar'), in expression '%s'.",
  19468. pixel_type(),_cimg_mp_calling_function,s_op,
  19469. s_type(arg2)._data,variable_name._data,s0);
  19470. } else if (arg2==~0U) { // Variable does not exist -> create it
  19471. arg2 = scalar();
  19472. if (arg3!=~0U) reserved_label[arg3] = arg2;
  19473. else {
  19474. if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0);
  19475. variable_pos[variable_def._width] = arg2;
  19476. variable_name.move_to(variable_def);
  19477. }
  19478. memtype[arg2] = -1;
  19479. }
  19480. arg3 = compile(++s1,se1,depth1,0,block_flags);
  19481. _cimg_mp_check_type(arg3,3,1,0);
  19482. } else { // Version with 2 arguments
  19483. arg2 = ~0U;
  19484. arg3 = compile(s0,se1,depth1,0,block_flags);
  19485. }
  19486. // arg2 = variable slot, arg3 = fill expression.
  19487. _cimg_mp_check_type(arg3,3,1,0);
  19488. CImg<ulongT>::vector((ulongT)mp_fill,arg1,_cimg_mp_size(arg1),arg2,arg3,code._width - p1).
  19489. move_to(code,p1);
  19490. _cimg_mp_return_nan();
  19491. }
  19492. if (!std::strncmp(ss,"find(",5)) { // Find
  19493. _cimg_mp_op("Function 'find()'");
  19494. // First argument: data to look at.
  19495. s0 = ss5; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  19496. if (*ss5=='#') { // Index specified
  19497. p1 = compile(ss6,s0,depth1,0,block_flags);
  19498. _cimg_mp_check_notnan_index(p1);
  19499. _cimg_mp_check_list();
  19500. arg1 = ~0U;
  19501. } else { // Vector specified
  19502. arg1 = compile(ss5,s0,depth1,0,block_flags);
  19503. _cimg_mp_check_type(arg1,1,2,0);
  19504. p1 = ~0U;
  19505. }
  19506. // Second argument: data to find.
  19507. s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  19508. arg2 = compile(s0,s1,depth1,0,block_flags);
  19509. // Third and fourth arguments: starting index and search direction.
  19510. arg3 = _cimg_mp_slot_nan; arg4 = 1;
  19511. if (s1<se1) {
  19512. s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  19513. arg3 = compile(s1,s0,depth1,0,block_flags);
  19514. _cimg_mp_check_type(arg3,3,1,0);
  19515. if (s0<se1) {
  19516. arg4 = compile(++s0,se1,depth1,0,block_flags);
  19517. _cimg_mp_check_type(arg4,4,1,0);
  19518. }
  19519. }
  19520. if (p1!=~0U) {
  19521. if (_cimg_mp_size(arg2)>1)
  19522. _cimg_mp_scalar5(mp_list_find_seq,p1,arg2,_cimg_mp_size(arg2),arg3,arg4);
  19523. _cimg_mp_scalar4(mp_list_find,p1,arg2 + (_cimg_mp_size(arg2)?1:0),arg3,arg4);
  19524. }
  19525. if (_cimg_mp_size(arg2)>1)
  19526. _cimg_mp_scalar6(mp_find_seq,arg1,_cimg_mp_size(arg1),arg2,_cimg_mp_size(arg2),arg3,arg4);
  19527. _cimg_mp_scalar5(mp_find,arg1,_cimg_mp_size(arg1),arg2 + (_cimg_mp_size(arg2)?1:0),arg3,arg4);
  19528. }
  19529. if (*ss1=='o' && *ss2=='r' && *ss3=='(') { // For loop
  19530. _cimg_mp_op("Function 'for()'");
  19531. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  19532. s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  19533. s3 = s2 + 1; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
  19534. arg1 = code._width;
  19535. p1 = compile(ss4,s1,depth1,0,block_flags); // Init
  19536. arg2 = code._width;
  19537. p2 = compile(++s1,s2,depth1,0,block_flags); // Cond
  19538. arg3 = code._width;
  19539. arg6 = mempos;
  19540. if (s3<se1) { // Body + post
  19541. p3 = compile(s3 + 1,se1,depth1,0,block_flags); // Body
  19542. arg4 = code._width;
  19543. pos = compile(++s2,s3,depth1,0,block_flags); // Post
  19544. } else {
  19545. p3 = compile(++s2,se1,depth1,0,block_flags); // Body only
  19546. arg4 = pos = code._width;
  19547. }
  19548. _cimg_mp_check_type(p2,2,1,0);
  19549. arg5 = _cimg_mp_size(pos);
  19550. CImg<ulongT>::vector((ulongT)mp_for,p3,(ulongT)_cimg_mp_size(p3),p2,arg2 - arg1,arg3 - arg2,
  19551. arg4 - arg3,code._width - arg4,
  19552. p3>=arg6 && !_cimg_mp_is_const_scalar(p3),
  19553. p2>=arg6 && !_cimg_mp_is_const_scalar(p2)).move_to(code,arg1);
  19554. _cimg_mp_return(p3);
  19555. }
  19556. if (!std::strncmp(ss,"floor(",6)) { // Floor
  19557. _cimg_mp_op("Function 'floor()'");
  19558. arg1 = compile(ss6,se1,depth1,0,block_flags);
  19559. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_floor,arg1);
  19560. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::floor(mem[arg1]));
  19561. _cimg_mp_scalar1(mp_floor,arg1);
  19562. }
  19563. if (!std::strncmp(ss,"fsize(",6)) { // File size
  19564. _cimg_mp_op("Function 'fsize()'");
  19565. arg1 = compile(ss6,se1,depth1,0,block_flags);
  19566. _cimg_mp_check_type(arg1,1,2,0);
  19567. pos = scalar();
  19568. CImg<ulongT>::vector((ulongT)mp_fsize,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code);
  19569. return_new_comp = true;
  19570. _cimg_mp_return(pos);
  19571. }
  19572. break;
  19573. case 'g' :
  19574. #if cimg_use_cpp11==1
  19575. if (!std::strncmp(ss,"gamma(",6)) { // Gamma
  19576. _cimg_mp_op("Function 'gamma()'");
  19577. arg1 = compile(ss6,se1,depth1,0,block_flags);
  19578. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_gamma,arg1);
  19579. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::tgamma(mem[arg1]));
  19580. _cimg_mp_scalar1(mp_gamma,arg1);
  19581. }
  19582. #endif
  19583. if (!std::strncmp(ss,"gauss(",6)) { // Gaussian function
  19584. _cimg_mp_op("Function 'gauss()'");
  19585. s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  19586. arg1 = compile(ss6,s1,depth1,0,block_flags);
  19587. arg2 = arg3 = 1;
  19588. if (s1<se1) {
  19589. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  19590. arg2 = compile(s1,s2,depth1,0,block_flags);
  19591. arg3 = s2<se1?compile(++s2,se1,depth1,0,block_flags):1;
  19592. }
  19593. _cimg_mp_check_type(arg2,2,1,0);
  19594. _cimg_mp_check_type(arg3,3,1,0);
  19595. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_gauss,arg1,arg2,arg3);
  19596. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2) && _cimg_mp_is_const_scalar(arg3)) {
  19597. val1 = mem[arg1];
  19598. val2 = mem[arg2];
  19599. _cimg_mp_const_scalar(std::exp(-val1*val1/(2*val2*val2))/(mem[arg3]?std::sqrt(2*val2*val2*cimg::PI):1));
  19600. }
  19601. _cimg_mp_scalar3(mp_gauss,arg1,arg2,arg3);
  19602. }
  19603. if (!std::strncmp(ss,"gcd(",4)) { // Gcd
  19604. _cimg_mp_op("Function 'gcd()'");
  19605. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  19606. arg1 = compile(ss4,s1,depth1,0,block_flags);
  19607. arg2 = compile(++s1,se1,depth1,0,block_flags);
  19608. _cimg_mp_check_type(arg1,1,1,0);
  19609. _cimg_mp_check_type(arg2,2,1,0);
  19610. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
  19611. _cimg_mp_const_scalar(cimg::gcd((long)mem[arg1],(long)mem[arg2]));
  19612. _cimg_mp_scalar2(mp_gcd,arg1,arg2);
  19613. }
  19614. #ifdef cimg_mp_func_get
  19615. if (!std::strncmp(ss,"get(",4)) { // Get value/vector from external variable
  19616. _cimg_mp_op("Function 'get()'");
  19617. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  19618. arg1 = compile(ss4,s1,depth1,0,block_flags);
  19619. arg2 = arg3 = 0;
  19620. if (s1<se1) {
  19621. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  19622. arg2 = compile(s1,s2,depth1,0,block_flags);
  19623. arg3 = s2<se1?compile(++s2,se1,depth1,0,block_flags):0;
  19624. }
  19625. _cimg_mp_check_type(arg1,1,2,0);
  19626. _cimg_mp_check_const_scalar(arg2,2,2);
  19627. _cimg_mp_check_type(arg3,3,1,0);
  19628. p1 = _cimg_mp_size(arg1);
  19629. const int siz_max = cimg::type<int>::max();
  19630. if (mem[arg2]>=siz_max) {
  19631. _cimg_mp_strerr;
  19632. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  19633. "CImg<%s>::%s: %s: Specified variable size %g is larger than %d.",
  19634. pixel_type(),_cimg_mp_calling_function,s_op,
  19635. mem[arg2],siz_max);
  19636. }
  19637. arg2 = (unsigned int)mem[arg2];
  19638. if (arg2) pos = vector(arg2); else pos = scalar();
  19639. CImg<ulongT>::vector((ulongT)mp_get,pos,arg1,p1,arg2,arg3).move_to(code);
  19640. return_new_comp = true;
  19641. _cimg_mp_return(pos);
  19642. }
  19643. #endif
  19644. break;
  19645. case 'h' :
  19646. if (*ss1=='(') { // Image height
  19647. _cimg_mp_op("Function 'h()'");
  19648. if (*ss2=='#') { // Index specified
  19649. p1 = compile(ss3,se1,depth1,0,block_flags);
  19650. _cimg_mp_check_notnan_index(p1);
  19651. _cimg_mp_check_list();
  19652. } else { if (ss2!=se1) break; p1 = ~0U; }
  19653. _cimg_mp_scalar1(mp_image_h,p1);
  19654. }
  19655. break;
  19656. case 'i' :
  19657. if (*ss1=='c' && *ss2=='(') { // Image median
  19658. _cimg_mp_op("Function 'ic()'");
  19659. if (*ss3=='#') { // Index specified
  19660. p1 = compile(ss4,se1,depth1,0,block_flags);
  19661. _cimg_mp_check_notnan_index(p1);
  19662. _cimg_mp_check_list();
  19663. } else { if (ss3!=se1) break; p1 = ~0U; }
  19664. pos = scalar();
  19665. CImg<ulongT>::vector((ulongT)mp_image_median,pos,p1).move_to(code);
  19666. return_new_comp = true;
  19667. _cimg_mp_return(pos);
  19668. }
  19669. if (*ss1=='n' && *ss2=='(') { // Image norm
  19670. _cimg_mp_op("Function 'in()'");
  19671. if (*ss3=='#') { // Index specified
  19672. p1 = compile(ss4,se1,depth1,0,block_flags);
  19673. _cimg_mp_check_notnan_index(p1);
  19674. _cimg_mp_check_list();
  19675. } else { if (ss3!=se1) break; p1 = ~0U; }
  19676. pos = scalar();
  19677. CImg<ulongT>::vector((ulongT)mp_image_norm,pos,p1).move_to(code);
  19678. return_new_comp = true;
  19679. _cimg_mp_return(pos);
  19680. }
  19681. if (*ss1=='f' && *ss2=='(') { // If..then[..else.]
  19682. _cimg_mp_op("Function 'if()'");
  19683. s1 = ss3; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  19684. s2 = s1 + 1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  19685. arg1 = compile(ss3,s1,depth1,0,block_flags);
  19686. _cimg_mp_check_type(arg1,1,1,0);
  19687. if (_cimg_mp_is_const_scalar(arg1)) {
  19688. if ((bool)mem[arg1]) return compile(++s1,s2,depth1,0,block_flags);
  19689. else return s2<se1?compile(++s2,se1,depth1,0,block_flags):0;
  19690. }
  19691. p2 = code._width;
  19692. arg2 = compile(++s1,s2,depth1,0,block_flags);
  19693. p3 = code._width;
  19694. arg3 = s2<se1?compile(++s2,se1,depth1,0,block_flags):
  19695. _cimg_mp_is_vector(arg2)?vector(_cimg_mp_size(arg2),0):0;
  19696. _cimg_mp_check_type(arg3,3,_cimg_mp_is_vector(arg2)?2:1,_cimg_mp_size(arg2));
  19697. arg4 = _cimg_mp_size(arg2);
  19698. if (arg4) pos = vector(arg4); else pos = scalar();
  19699. CImg<ulongT>::vector((ulongT)mp_if,pos,arg1,arg2,arg3,
  19700. p3 - p2,code._width - p3,arg4).move_to(code,p2);
  19701. return_new_comp = true;
  19702. _cimg_mp_return(pos);
  19703. }
  19704. if (!std::strncmp(ss,"inrange(",8)) { // Check value range
  19705. _cimg_mp_op("Function 'inrange()'");
  19706. s1 = ss8; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  19707. arg1 = compile(ss8,s1,depth1,0,block_flags);
  19708. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  19709. arg2 = compile(s1,s2,depth1,0,block_flags);
  19710. s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  19711. arg3 = compile(s2,s1,depth1,0,block_flags);
  19712. arg4 = arg5 = 1;
  19713. if (s1<se1) {
  19714. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  19715. arg4 = compile(s1,s2,depth1,0,block_flags);
  19716. arg5 = s2<se1?compile(++s2,se1,depth1,0,block_flags):arg4;
  19717. _cimg_mp_check_type(arg4,4,1,0);
  19718. _cimg_mp_check_type(arg5,5,1,0);
  19719. }
  19720. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2) &&
  19721. _cimg_mp_is_const_scalar(arg3) && _cimg_mp_is_const_scalar(arg4) &&
  19722. _cimg_mp_is_const_scalar(arg5)) { // Optimize constant case
  19723. val = mem[arg1]; val1 = mem[arg2]; val2 = mem[arg3];
  19724. if (val2>=val1)
  19725. is_sth = (mem[arg4]?(val>=val1):(val>val1)) && (mem[arg5]?(val<=val2):(val<val2));
  19726. else
  19727. is_sth = (mem[arg5]?(val>=val2):(val>val2)) && (mem[arg4]?(val<=val1):(val<val1));
  19728. _cimg_mp_return(is_sth?1:0);
  19729. }
  19730. p1 = _cimg_mp_size(arg1);
  19731. p2 = _cimg_mp_size(arg2);
  19732. p3 = _cimg_mp_size(arg3);
  19733. arg6 = ~0U; // Size of return value
  19734. if (!p1) {
  19735. arg6 = p2?p2:p3;
  19736. if (arg6) _cimg_mp_check_type(arg3,3,3,arg6);
  19737. } else {
  19738. arg6 = p1;
  19739. _cimg_mp_check_type(arg2,2,3,arg6);
  19740. _cimg_mp_check_type(arg3,3,3,arg6);
  19741. }
  19742. pos = arg6?vector(arg6):scalar();
  19743. CImg<ulongT>::vector((ulongT)mp_inrange,pos,arg6,arg1,p1,arg2,p2,arg3,p3,arg4,arg5).move_to(code);
  19744. return_new_comp = true;
  19745. _cimg_mp_return(pos);
  19746. }
  19747. if (!std::strncmp(ss,"int(",4)) { // Integer cast
  19748. _cimg_mp_op("Function 'int()'");
  19749. arg1 = compile(ss4,se1,depth1,0,block_flags);
  19750. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_int,arg1);
  19751. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar((longT)mem[arg1]);
  19752. _cimg_mp_scalar1(mp_int,arg1);
  19753. }
  19754. if (!std::strncmp(ss,"invert(",7)) { // Matrix/scalar inverse (or pseudoinverse)
  19755. _cimg_mp_op("Function 'invert()'");
  19756. s1 = ss7; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  19757. arg1 = compile(ss7,s1,depth1,0,block_flags); // A
  19758. arg2 = ~0U; // nb_colsA
  19759. arg3 = 0; // use_LU
  19760. arg4 = 0; // lambda
  19761. if (s1<se1) {
  19762. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  19763. arg2 = compile(s1,s2,depth1,0,block_flags);
  19764. if (s2<se1) {
  19765. s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  19766. arg3 = compile(s2,s1,depth1,0,block_flags);
  19767. arg4 = s1<se1?compile(++s1,se1,depth1,0,block_flags):0;
  19768. }
  19769. }
  19770. if (arg2!=~0U) {
  19771. _cimg_mp_check_const_scalar(arg2,2,3);
  19772. arg2 = (unsigned int)mem[arg2];
  19773. }
  19774. _cimg_mp_check_type(arg3,3,1,0);
  19775. _cimg_mp_check_type(arg4,4,1,0);
  19776. if (_cimg_mp_is_vector(arg1)) {
  19777. p1 = _cimg_mp_size(arg1);
  19778. if (arg2==~0U) { // nb_colsA not specified: assuming square matrix
  19779. _cimg_mp_check_matrix_square(arg1,1);
  19780. p2 = p3 = (unsigned int)cimg::round(std::sqrt((float)p1));
  19781. } else {
  19782. p2 = arg2;
  19783. p3 = p1/p2;
  19784. if (p3*p2!=p1) {
  19785. _cimg_mp_strerr;
  19786. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  19787. "CImg<%s>::%s: %s: Type of first argument ('%s') "
  19788. "does not match with second argument 'nb_colsA=%u', "
  19789. "in expression '%s'.",
  19790. pixel_type(),_cimg_mp_calling_function,s_op,
  19791. s_type(arg1)._data,p2,s0);
  19792. }
  19793. }
  19794. pos = vector(p1);
  19795. CImg<ulongT>::vector((ulongT)mp_matrix_invert,pos,arg1,p2,p3,arg3,arg4).move_to(code);
  19796. return_new_comp = true;
  19797. _cimg_mp_return(pos);
  19798. }
  19799. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(1/mem[arg1]);
  19800. _cimg_mp_scalar2(mp_div,1,arg1);
  19801. }
  19802. if (*ss1=='s') { // Family of 'is_?()' functions
  19803. if (!std::strncmp(ss,"isbool(",7)) { // Is boolean?
  19804. _cimg_mp_op("Function 'isbool()'");
  19805. if (ss7==se1) _cimg_mp_return(0);
  19806. try { arg1 = compile(ss7,se1,depth1,0,block_flags); }
  19807. catch(CImgException&) { _cimg_mp_return(0); }
  19808. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isbool,arg1);
  19809. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_return(mem[arg1]==0. || mem[arg1]==1.);
  19810. _cimg_mp_scalar1(mp_isbool,arg1);
  19811. }
  19812. if (!std::strncmp(ss,"isconst(",8)) { // Is constant?
  19813. _cimg_mp_op("Function 'isconst()'");
  19814. if (ss8==se1) _cimg_mp_return(0);
  19815. try { arg1 = compile(ss8,se1,depth1,0,block_flags); }
  19816. catch(CImgException&) { _cimg_mp_return(0); }
  19817. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_return(1);
  19818. _cimg_mp_return(0);
  19819. }
  19820. if (!std::strncmp(ss,"isdir(",6)) { // Is directory?
  19821. _cimg_mp_op("Function 'isdir()'");
  19822. arg1 = compile(ss6,se1,depth1,0,block_flags);
  19823. pos = scalar();
  19824. CImg<ulongT>::vector((ulongT)mp_isdir,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code);
  19825. return_new_comp = true;
  19826. _cimg_mp_return(pos);
  19827. }
  19828. if (!std::strncmp(ss,"isfile(",7)) { // Is file?
  19829. _cimg_mp_op("Function 'isfile()'");
  19830. arg1 = compile(ss7,se1,depth1,0,block_flags);
  19831. pos = scalar();
  19832. CImg<ulongT>::vector((ulongT)mp_isfile,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code);
  19833. return_new_comp = true;
  19834. _cimg_mp_return(pos);
  19835. }
  19836. if (!std::strncmp(ss,"isin(",5)) { // Is in sequence/vector?
  19837. if (ss5>=se1) _cimg_mp_return(0);
  19838. _cimg_mp_op("Function 'isin()'");
  19839. pos = scalar();
  19840. CImg<ulongT>::vector((ulongT)mp_isin,pos,0).move_to(l_opcode);
  19841. for (s = ss5; s<se; ++s) {
  19842. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  19843. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  19844. arg1 = compile(s,ns,depth1,0,block_flags);
  19845. CImg<ulongT>::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode);
  19846. s = ns;
  19847. }
  19848. (l_opcode>'y').move_to(opcode);
  19849. opcode[2] = opcode._height;
  19850. opcode.move_to(code);
  19851. return_new_comp = true;
  19852. _cimg_mp_return(pos);
  19853. }
  19854. if (!std::strncmp(ss,"isinf(",6)) { // Is infinite?
  19855. _cimg_mp_op("Function 'isinf()'");
  19856. if (ss6==se1) _cimg_mp_return(0);
  19857. arg1 = compile(ss6,se1,depth1,0,block_flags);
  19858. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isinf,arg1);
  19859. if (_cimg_mp_is_const_scalar(arg1))
  19860. _cimg_mp_return((unsigned int)cimg::type<double>::is_inf(mem[arg1]));
  19861. _cimg_mp_scalar1(mp_isinf,arg1);
  19862. }
  19863. if (!std::strncmp(ss,"isint(",6)) { // Is integer?
  19864. _cimg_mp_op("Function 'isint()'");
  19865. if (ss6==se1) _cimg_mp_return(0);
  19866. try { arg1 = compile(ss6,se1,depth1,0,block_flags); }
  19867. catch(CImgException&) { _cimg_mp_return(0); }
  19868. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isint,arg1);
  19869. if (_cimg_mp_is_const_scalar(arg1))
  19870. _cimg_mp_return((unsigned int)((double)(longT)mem[arg1]==mem[arg1]));
  19871. _cimg_mp_scalar1(mp_isint,arg1);
  19872. }
  19873. if (!std::strncmp(ss,"isnan(",6)) { // Is NaN?
  19874. _cimg_mp_op("Function 'isnan()'");
  19875. if (ss6==se1) _cimg_mp_return(0);
  19876. arg1 = compile(ss6,se1,depth1,0,block_flags);
  19877. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_isnan,arg1);
  19878. if (_cimg_mp_is_const_scalar(arg1))
  19879. _cimg_mp_return((unsigned int)cimg::type<double>::is_nan(mem[arg1]));
  19880. _cimg_mp_scalar1(mp_isnan,arg1);
  19881. }
  19882. if (!std::strncmp(ss,"isnum(",6)) { // Is number?
  19883. _cimg_mp_op("Function 'isnum()'");
  19884. val = 0;
  19885. if (cimg_sscanf(ss6,"%lf%c%c",&val,&sep,&end)==2 && sep==')') _cimg_mp_return(1);
  19886. _cimg_mp_return(0);
  19887. }
  19888. if (!std::strncmp(ss,"isexpr(",7)) { // Is valid expression?
  19889. _cimg_mp_op("Function 'isexpr()'");
  19890. if (ss7==se1) _cimg_mp_return(0);
  19891. try { arg1 = compile(ss7,se1,depth1,0,block_flags); }
  19892. catch (CImgException&) { _cimg_mp_return(0); }
  19893. _cimg_mp_return(1);
  19894. }
  19895. if (!std::strncmp(ss,"ispercentage(",13)) { // Does argument ends with '%'?
  19896. _cimg_mp_op("Function 'ispercentage()'");
  19897. _cimg_mp_return((unsigned int)(*se2=='%'));
  19898. }
  19899. if (!std::strncmp(ss,"isvarname(",10)) { // Is variable name?
  19900. _cimg_mp_op("Function 'isvarname()'");
  19901. arg1 = compile(ss + 10,se1,depth1,0,block_flags);
  19902. pos = scalar();
  19903. CImg<ulongT>::vector((ulongT)mp_isvarname,pos,arg1,(ulongT)_cimg_mp_size(arg1)).move_to(code);
  19904. return_new_comp = true;
  19905. _cimg_mp_return(pos);
  19906. }
  19907. }
  19908. break;
  19909. case 'l' :
  19910. if (*ss1=='(') { // Size of image list
  19911. _cimg_mp_op("Function 'l()'");
  19912. if (ss2!=se1) break;
  19913. _cimg_mp_scalar0(mp_list_l);
  19914. }
  19915. if (!std::strncmp(ss,"lerp(",5)) { // Linear interpolation
  19916. _cimg_mp_op("Function 'lerp()'");
  19917. s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  19918. arg1 = compile(ss5,s1,depth1,0,block_flags);
  19919. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  19920. arg2 = compile(s1,s2,depth1,0,block_flags);
  19921. arg3 = s2<se1?compile(++s2,se1,depth1,0,block_flags):16; // Default value is 0.5
  19922. _cimg_mp_check_type(arg3,3,1,0);
  19923. if (_cimg_mp_is_const_scalar(arg3)) { // Optimize constant cases
  19924. if (!arg3) _cimg_mp_return(arg1);
  19925. if (arg3==1) _cimg_mp_return(arg2);
  19926. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) {
  19927. const double t = mem[arg3];
  19928. _cimg_mp_const_scalar(mem[arg1]*(1-t) + mem[arg2]*t);
  19929. }
  19930. }
  19931. if (_cimg_mp_is_scalar(arg1)) {
  19932. _cimg_mp_check_type(arg2,2,1,0);
  19933. _cimg_mp_scalar3(mp_lerp,arg1,arg2,arg3);
  19934. }
  19935. p1 = _cimg_mp_size(arg1);
  19936. _cimg_mp_check_type(arg2,2,2,p1);
  19937. pos = vector(p1);
  19938. CImg<ulongT>::vector((ulongT)mp_vector_lerp,pos,p1,arg1,arg2,arg3).move_to(code);
  19939. return_new_comp = true;
  19940. _cimg_mp_return(pos);
  19941. }
  19942. if (!std::strncmp(ss,"log(",4)) { // Natural logarithm
  19943. _cimg_mp_op("Function 'log()'");
  19944. arg1 = compile(ss4,se1,depth1,0,block_flags);
  19945. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log,arg1);
  19946. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::log(mem[arg1]));
  19947. _cimg_mp_scalar1(mp_log,arg1);
  19948. }
  19949. if (!std::strncmp(ss,"log2(",5)) { // Base-2 logarithm
  19950. _cimg_mp_op("Function 'log2()'");
  19951. arg1 = compile(ss5,se1,depth1,0,block_flags);
  19952. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log2,arg1);
  19953. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::log2(mem[arg1]));
  19954. _cimg_mp_scalar1(mp_log2,arg1);
  19955. }
  19956. if (!std::strncmp(ss,"log10(",6)) { // Base-10 logarithm
  19957. _cimg_mp_op("Function 'log10()'");
  19958. arg1 = compile(ss6,se1,depth1,0,block_flags);
  19959. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_log10,arg1);
  19960. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::log10(mem[arg1]));
  19961. _cimg_mp_scalar1(mp_log10,arg1);
  19962. }
  19963. if (!std::strncmp(ss,"lowercase(",10)) { // Lower case
  19964. _cimg_mp_op("Function 'lowercase()'");
  19965. arg1 = compile(ss + 10,se1,depth1,0,block_flags);
  19966. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_lowercase,arg1);
  19967. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::lowercase(mem[arg1]));
  19968. _cimg_mp_scalar1(mp_lowercase,arg1);
  19969. }
  19970. break;
  19971. case 'm' :
  19972. if (!std::strncmp(ss,"map(",4)) { // Map vector
  19973. _cimg_mp_op("Function 'map()'");
  19974. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  19975. arg1 = compile(ss4,s1,depth1,0,block_flags);
  19976. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  19977. arg2 = compile(s1,s2,depth1,0,block_flags);
  19978. arg3 = arg4 = 1; arg5 = 0;
  19979. if (s2<se1) {
  19980. s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  19981. arg3 = compile(s2,s1,depth1,0,block_flags);
  19982. if (s1<se1) {
  19983. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  19984. arg4 = compile(s1,s2,depth1,0,block_flags);
  19985. arg5 = s2<se1?compile(++s2,se1,depth1,0,block_flags):0;
  19986. }
  19987. }
  19988. _cimg_mp_check_type(arg1,1,2,0);
  19989. _cimg_mp_check_type(arg2,2,2,0);
  19990. _cimg_mp_check_const_scalar(arg3,3,3);
  19991. _cimg_mp_check_const_scalar(arg4,4,3);
  19992. _cimg_mp_check_type(arg5,5,1,0);
  19993. p1 = _cimg_mp_size(arg1);
  19994. p2 = _cimg_mp_size(arg2);
  19995. arg3 = (unsigned int)mem[arg3];
  19996. arg4 = (unsigned int)mem[arg4];
  19997. if (p1%arg3) {
  19998. _cimg_mp_strerr;
  19999. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  20000. "CImg<%s>::%s: %s: Type of first arguments ('%s') "
  20001. "does not match with third argument 'nb_channelsX=%u', "
  20002. "in expression '%s'.",
  20003. pixel_type(),_cimg_mp_calling_function,s_op,
  20004. s_type(arg1)._data,arg3,s0);
  20005. }
  20006. if (p2%arg4) {
  20007. _cimg_mp_strerr;
  20008. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  20009. "CImg<%s>::%s: %s: Type of second arguments ('%s') "
  20010. "does not match with fourth argument 'nb_channelsP=%u', "
  20011. "in expression '%s'.",
  20012. pixel_type(),_cimg_mp_calling_function,s_op,
  20013. s_type(arg2)._data,arg4,s0);
  20014. }
  20015. pos = vector(p1*arg4);
  20016. CImg<ulongT>::vector((ulongT)mp_map,pos,arg1,arg2,p1,p2,arg3,arg4,arg5).move_to(code);
  20017. return_new_comp = true;
  20018. _cimg_mp_return(pos);
  20019. }
  20020. if (!std::strncmp(ss,"mul(",4)) { // Matrix multiplication
  20021. _cimg_mp_op("Function 'mul()'");
  20022. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20023. arg1 = compile(ss4,s1,depth1,0,block_flags);
  20024. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  20025. arg2 = compile(s1,s2,depth1,0,block_flags);
  20026. arg3 = s2<se1?compile(++s2,se1,depth1,0,block_flags):1;
  20027. _cimg_mp_check_type(arg1,1,2,0);
  20028. _cimg_mp_check_type(arg2,2,2,0);
  20029. _cimg_mp_check_const_scalar(arg3,3,3);
  20030. p1 = _cimg_mp_size(arg1);
  20031. p2 = _cimg_mp_size(arg2);
  20032. p3 = (unsigned int)mem[arg3];
  20033. arg5 = p2/p3;
  20034. arg4 = p1/arg5;
  20035. if (arg4*arg5!=p1 || arg5*p3!=p2) {
  20036. _cimg_mp_strerr;
  20037. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  20038. "CImg<%s>::%s: %s: Types of first and second arguments ('%s' and '%s') "
  20039. "do not match with third argument 'nb_colsB=%u', "
  20040. "in expression '%s'.",
  20041. pixel_type(),_cimg_mp_calling_function,s_op,
  20042. s_type(arg1)._data,s_type(arg2)._data,p3,s0);
  20043. }
  20044. pos = vector(arg4*p3);
  20045. CImg<ulongT>::vector((ulongT)mp_matrix_mul,pos,arg1,arg2,arg4,arg5,p3).move_to(code);
  20046. return_new_comp = true;
  20047. _cimg_mp_return(pos);
  20048. }
  20049. if (!std::strncmp(ss,"mproj(",6)) { // Project matrix onto dictionary
  20050. _cimg_mp_op("Function 'mproj()'");
  20051. s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20052. arg1 = compile(ss6,s1,depth1,0,block_flags); // S
  20053. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  20054. arg2 = compile(s1,s2,depth1,0,block_flags); // ncolS
  20055. s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20056. arg3 = compile(s2,s1,depth1,0,block_flags); // D
  20057. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  20058. arg4 = compile(s1,s2,depth1,0,block_flags); // ncolD
  20059. arg5 = arg6 = p3 = 0;
  20060. if (s2<se1) {
  20061. s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20062. arg5 = compile(s2,s1,depth1,0,block_flags); // method
  20063. if (s1<se1) {
  20064. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  20065. arg6 = compile(s1,s2,depth1,0,block_flags); // max_iter
  20066. p3 = s2<se1?compile(++s2,se1,depth1,0,block_flags):0; // method
  20067. }
  20068. }
  20069. _cimg_mp_check_type(arg1,1,2,0);
  20070. _cimg_mp_check_const_scalar(arg2,2,3);
  20071. _cimg_mp_check_type(arg3,3,2,0);
  20072. _cimg_mp_check_const_scalar(arg4,4,3);
  20073. _cimg_mp_check_type(arg5,5,1,0);
  20074. _cimg_mp_check_type(arg6,6,1,0);
  20075. _cimg_mp_check_type(p3,7,1,0);
  20076. p1 = _cimg_mp_size(arg1);
  20077. p2 = _cimg_mp_size(arg3);
  20078. const unsigned int
  20079. wS = (unsigned int)mem[arg2],
  20080. wD = (unsigned int)mem[arg4],
  20081. hS = p1/wS,
  20082. hD = p2/wD;
  20083. if (wS*hS!=p1) {
  20084. _cimg_mp_strerr;
  20085. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  20086. "CImg<%s>::%s: %s: Type of first argument ('%s') "
  20087. "do not match with second argument 'nb_colsS=%u', "
  20088. "in expression '%s'.",
  20089. pixel_type(),_cimg_mp_calling_function,s_op,
  20090. s_type(arg1)._data,wS,s0);
  20091. }
  20092. if (wD*hD!=p2) {
  20093. _cimg_mp_strerr;
  20094. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  20095. "CImg<%s>::%s: %s: Type of third argument ('%s') "
  20096. "do not match with fourth argument 'nb_colsD=%u', "
  20097. "in expression '%s'.",
  20098. pixel_type(),_cimg_mp_calling_function,s_op,
  20099. s_type(arg3)._data,wD,s0);
  20100. }
  20101. if (hS!=hD) {
  20102. _cimg_mp_strerr;
  20103. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  20104. "CImg<%s>::%s: %s: Type of first argument ('%s') "
  20105. "do not match with third argument ('%s'), "
  20106. "in expression '%s'.",
  20107. pixel_type(),_cimg_mp_calling_function,s_op,
  20108. s_type(arg1)._data,s_type(arg3)._data,s0);
  20109. }
  20110. pos = vector(wS*wD);
  20111. CImg<ulongT>::vector((ulongT)mp_mproj,pos,arg1,wS,hS,arg3,wD,arg5,arg6,p3).move_to(code);
  20112. return_new_comp = true;
  20113. _cimg_mp_return(pos);
  20114. }
  20115. if (!std::strncmp(ss,"mse(",4)) { // Mean-squared error
  20116. _cimg_mp_op("Function 'mse()'");
  20117. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20118. arg1 = compile(ss4,s1,depth1,0,block_flags);
  20119. arg2 = compile(++s1,se1,depth1,0,block_flags);
  20120. _cimg_mp_check_type(arg2,2,_cimg_mp_is_scalar(arg1)?1:2,_cimg_mp_size(arg1));
  20121. _cimg_mp_scalar3(mp_mse,arg1,arg2,_cimg_mp_size(arg1));
  20122. }
  20123. if (!std::strncmp(ss,"merge(",6)) { // Merge inter-thread variables
  20124. _cimg_mp_op("Function 'merge()'");
  20125. s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20126. ref.assign(7);
  20127. pos = compile(ss6,s1,depth1,ref,block_flags);
  20128. if (*ref) {
  20129. _cimg_mp_strerr;
  20130. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  20131. "CImg<%s>::%s: %s: First argument cannot be a linked reference, "
  20132. "in expression '%s'.",
  20133. pixel_type(),_cimg_mp_calling_function,s_op,
  20134. s0);
  20135. }
  20136. arg1 = ~0U; // Merge operator
  20137. // (0='=',1='+',2='-',3='*',4='/',5='&',6='|',7='xor',8='&&',9=='||',10='min',11='max')
  20138. if (s1<se1) {
  20139. ++s1;
  20140. char st_op[4] = {};
  20141. is_sth = false; // blank after operator?
  20142. if (cimg_sscanf(s1," %3[=+-*/&|minaxor]%c",st_op,&sep)==2 && (sep==')' ||
  20143. (is_sth=cimg::is_blank(sep)))) {
  20144. if (!is_sth || (is_sth && cimg_sscanf(s1," %*[=+-*/&|minaxor ]%c",&sep)==1 && sep==')')) {
  20145. cimg::strpare(st_op,' ',false,true);
  20146. if (!st_op[1])
  20147. arg1 = *st_op=='='?0:*st_op=='+'?1:*st_op=='-'?2:*st_op=='*'?3:*st_op=='/'?4:
  20148. *st_op=='&'?5:*st_op=='|'?6:~0U;
  20149. else if (*st_op=='x' && st_op[1]=='o' && st_op[2]=='r' && !st_op[3]) arg1 = 7;
  20150. else if (*st_op=='&' && st_op[1]=='&' && !st_op[2]) arg1 = 8;
  20151. else if (*st_op=='|' && st_op[1]=='|' && !st_op[2]) arg1 = 9;
  20152. else if (*st_op=='m' && st_op[1]=='i' && st_op[2]=='n' && !st_op[3]) arg1 = 10;
  20153. else if (*st_op=='m' && st_op[1]=='a' && st_op[2]=='x' && !st_op[3]) arg1 = 11;
  20154. }
  20155. }
  20156. }
  20157. cimg_rofY(memmerge,k) if (memmerge(0,k)==(int)pos) {
  20158. _cimg_mp_strerr;
  20159. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  20160. "CImg<%s>::%s: %s: Merge has already been requested before "
  20161. "for specified variable "
  20162. "in expression '%s'.",
  20163. pixel_type(),_cimg_mp_calling_function,s_op,s0);
  20164. }
  20165. if (arg1==~0U) {
  20166. _cimg_mp_strerr;
  20167. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  20168. "CImg<%s>::%s: %s: Invalid specified operator "
  20169. "(should be one of '=,+,-,*,/,&,|,xor,&&,||,min,max'), "
  20170. "in expression '%s'.",
  20171. pixel_type(),_cimg_mp_calling_function,s_op,s0);
  20172. }
  20173. memmerge.resize(3,memmerge._height + 1,1,1,0,0);
  20174. memmerge(0,memmerge._height - 1) = (int)pos;
  20175. memmerge(1,memmerge._height - 1) = (int)_cimg_mp_size(pos);
  20176. memmerge(2,memmerge._height - 1) = (int)arg1;
  20177. _cimg_mp_return_nan();
  20178. }
  20179. break;
  20180. case 'n' :
  20181. #ifdef cimg_mp_func_name
  20182. if (!std::strncmp(ss,"name(",5)) { // Get image name as a string vector
  20183. _cimg_mp_op("Function 'name()'");
  20184. if (*ss5=='#') { // Index specified
  20185. s0 = ss6; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  20186. p1 = compile(ss6,s0++,depth1,0,block_flags);
  20187. _cimg_mp_check_notnan_index(p1);
  20188. _cimg_mp_check_list();
  20189. is_sth = true; // is_index_specified?
  20190. } else { s0 = ss5; p1 = get_mem_img_index(); is_sth = false; }
  20191. arg1 = s0<se1?compile(s0,se1,depth1,0,block_flags):~0U;
  20192. if (arg1!=~0U) {
  20193. _cimg_mp_check_const_scalar(arg1,is_sth?2:1,3);
  20194. arg1 = (unsigned int)mem[arg1];
  20195. } else arg1 = 1024;
  20196. pos = vector(arg1);
  20197. CImg<ulongT>::vector((ulongT)mp_name,pos,p1,arg1).move_to(code);
  20198. return_new_comp = true;
  20199. _cimg_mp_return(pos);
  20200. }
  20201. #endif
  20202. if (!std::strncmp(ss,"narg(",5)) { // Number of arguments
  20203. _cimg_mp_op("Function 'narg()'");
  20204. if (ss5>=se1) _cimg_mp_return(0);
  20205. arg1 = 0;
  20206. for (s = ss5; s<se; ++s) {
  20207. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  20208. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  20209. ++arg1; s = ns;
  20210. }
  20211. _cimg_mp_const_scalar((double)arg1);
  20212. }
  20213. if (!std::strncmp(ss,"normp(",6)) { // Lp norm, with variable argument p.
  20214. _cimg_mp_op("Function 'normp()'");
  20215. s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20216. arg1 = compile(ss6,s1,depth1,0,block_flags);
  20217. arg2 = s1<se1?compile(++s1,se1,depth1,0,block_flags):2;
  20218. _cimg_mp_check_type(arg2,0,1,0);
  20219. p1 = _cimg_mp_size(arg1);
  20220. _cimg_mp_scalar3(mp_vector_normp,arg1,p1,arg2);
  20221. }
  20222. break;
  20223. case 'o' :
  20224. if (!std::strncmp(ss,"o2c(",4)) { // Offset to coordinates
  20225. _cimg_mp_op("Function 'o2c()'");
  20226. if (*ss4=='#') { // Index specified
  20227. s0 = ss5; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  20228. p1 = compile(ss5,s0++,depth1,0,block_flags);
  20229. _cimg_mp_check_notnan_index(p1);
  20230. _cimg_mp_check_list();
  20231. } else { p1 = ~0U; s0 = ss4; }
  20232. arg1 = compile(s0,se1,depth1,0,block_flags);
  20233. _cimg_mp_check_type(arg1,1,1,0);
  20234. pos = vector(4);
  20235. CImg<ulongT>::vector((ulongT)mp_o2c,pos,p1,arg1).move_to(code);
  20236. return_new_comp = true;
  20237. _cimg_mp_return(pos);
  20238. }
  20239. break;
  20240. case 'p' :
  20241. if (!std::strncmp(ss,"permut(",7)) { // Number of permutations
  20242. _cimg_mp_op("Function 'permut()'");
  20243. s1 = ss7; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20244. arg1 = compile(ss7,s1,depth1,0,block_flags);
  20245. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  20246. arg2 = compile(s1,s2,depth1,0,block_flags);
  20247. arg3 = compile(++s2,se1,depth1,0,block_flags);
  20248. _cimg_mp_check_type(arg1,1,1,0);
  20249. _cimg_mp_check_type(arg2,2,1,0);
  20250. _cimg_mp_check_type(arg3,3,1,0);
  20251. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2) && _cimg_mp_is_const_scalar(arg3))
  20252. _cimg_mp_const_scalar(cimg::permutations((int)mem[arg1],(int)mem[arg2],(bool)mem[arg3]));
  20253. _cimg_mp_scalar3(mp_permutations,arg1,arg2,arg3);
  20254. }
  20255. if (!std::strncmp(ss,"polygon(",8)) { // Polygon/line drawing
  20256. if (!is_inside_critical) is_parallelizable = false;
  20257. _cimg_mp_op("Function 'polygon()'");
  20258. if (*ss8=='#') { // Index specified
  20259. s0 = ss + 9; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  20260. p1 = compile(ss + 9,s0++,depth1,0,block_flags);
  20261. _cimg_mp_check_notnan_index(p1);
  20262. _cimg_mp_check_list();
  20263. pos = 2;
  20264. } else { p1 = ~0U; s0 = ss8; pos = 1; }
  20265. if (s0==se1) compile(s0,se1,depth1,0,block_flags); // 'missing' argument error
  20266. CImg<ulongT>::vector((ulongT)mp_polygon,_cimg_mp_slot_nan,0,p1).move_to(l_opcode);
  20267. for (s = s0; s<se; ++s, ++pos) {
  20268. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  20269. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  20270. arg2 = compile(s,ns,depth1,0,block_flags);
  20271. if (pos>1 && _cimg_mp_is_vector(arg2)) // Vector argument allowed to specify coordinates and color
  20272. CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,
  20273. arg2 + (ulongT)_cimg_mp_size(arg2)).
  20274. move_to(l_opcode);
  20275. else {
  20276. _cimg_mp_check_type(arg2,pos,1,0);
  20277. CImg<ulongT>::vector(arg2).move_to(l_opcode);
  20278. }
  20279. s = ns;
  20280. }
  20281. (l_opcode>'y').move_to(opcode);
  20282. opcode[2] = opcode._height;
  20283. opcode.move_to(code);
  20284. _cimg_mp_return_nan();
  20285. }
  20286. if (!std::strncmp(ss,"print(",6) ||
  20287. !std::strncmp(ss,"prints(",7)) { // Print expressions
  20288. s0 = ss6 + (*ss5=='('?0:1);
  20289. is_sth = *ss5=='s'; // corresponding string must be printed?
  20290. _cimg_mp_op(is_sth?"Function 'prints()'":"Function 'print()'");
  20291. if (!is_sth && *s0=='#') { // Image
  20292. p1 = compile(ss7,se1,depth1,0,block_flags);
  20293. _cimg_mp_check_notnan_index(p1);
  20294. _cimg_mp_check_list();
  20295. CImg<ulongT>::vector((ulongT)mp_image_print,_cimg_mp_slot_nan,p1).move_to(code);
  20296. _cimg_mp_return_nan();
  20297. }
  20298. // Regular expression
  20299. for (s = s0; s<se; ++s) {
  20300. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  20301. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  20302. ref.assign(7);
  20303. pos = compile(s,ns,depth1,ref,block_flags);
  20304. c1 = *ns; *ns = 0;
  20305. variable_name.assign(CImg<charT>::string(s,true,true).unroll('y'),true);
  20306. cimg::strpare(variable_name,false,true);
  20307. if (_cimg_mp_is_const_scalar(pos)) // Const scalar
  20308. std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %.17g "
  20309. "(mem[%u]: %s%s)",
  20310. variable_name._data,mem[pos],pos,s_type(pos)._data,s_ref(ref)._data);
  20311. else // Vector or non-const scalar
  20312. std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = (uninitialized) "
  20313. "(mem[%u]: %s%s)",
  20314. variable_name._data,pos,s_type(pos)._data,s_ref(ref)._data);
  20315. if (p_ref) std::memcpy(p_ref,ref,siz_ref);
  20316. if (_cimg_mp_is_vector(pos)) // Vector
  20317. ((CImg<ulongT>::vector((ulongT)mp_vector_print,pos,0,(ulongT)_cimg_mp_size(pos),is_sth?1:0),
  20318. variable_name)>'y').move_to(opcode);
  20319. else // Scalar
  20320. ((CImg<ulongT>::vector((ulongT)mp_print,pos,0,is_sth?1:0),
  20321. variable_name)>'y').move_to(opcode);
  20322. opcode[2] = opcode._height;
  20323. opcode.move_to(code);
  20324. *ns = c1; s = ns;
  20325. }
  20326. _cimg_mp_return(pos);
  20327. }
  20328. break;
  20329. case 'r' :
  20330. if (!std::strncmp(ss,"rad2deg(",8)) { // Degrees to radians
  20331. _cimg_mp_op("Function 'rad2deg()'");
  20332. arg1 = compile(ss8,se1,depth1,0,block_flags);
  20333. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_rad2deg,arg1);
  20334. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(mem[arg1]*180/cimg::PI);
  20335. _cimg_mp_scalar1(mp_rad2deg,arg1);
  20336. }
  20337. if (!std::strncmp(ss,"ref(",4)) { // Variable declaration
  20338. _cimg_mp_op("Function 'ref()'");
  20339. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20340. if (s1>=se1 || !*s1) compile(s1,s1,depth1,0,block_flags); // Will throw missing argument error
  20341. arg3 = compile(ss4,s1++,depth1,p_ref,block_flags);
  20342. *se1 = 0;
  20343. if (!cimg::is_varname(s1)) { // Invalid variable name
  20344. variable_name.assign(s1,(unsigned int)(se1 + 1 - s1)).back() = 0;
  20345. cimg::strellipsize(variable_name,64);
  20346. *se1 = ')';
  20347. _cimg_mp_strerr;
  20348. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  20349. "CImg<%s>::%s: %s: Invalid specified variable name '%s', "
  20350. "in expression '%s'.",
  20351. pixel_type(),_cimg_mp_calling_function,s_op,
  20352. variable_name._data,s0);
  20353. }
  20354. get_variable_pos(s1,arg1,arg2);
  20355. if (arg2!=~0U) reserved_label[arg2] = arg3;
  20356. else if (arg1!=~0U) variable_pos[arg1] = arg3;
  20357. else { // New variable
  20358. if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0);
  20359. variable_pos[variable_def._width] = arg3;
  20360. CImg<char>::string(s1).move_to(variable_def);
  20361. }
  20362. if (_cimg_mp_is_vector(arg3))
  20363. set_reserved_vector(arg3); // Prevent from being used in further optimization
  20364. else if (_cimg_mp_is_comp(arg3)) memtype[arg3] = -1;
  20365. *se1 = ')';
  20366. _cimg_mp_return(arg3);
  20367. }
  20368. if (!std::strncmp(ss,"repeat(",7)) { // Repeat
  20369. _cimg_mp_op("Function 'repeat()'");
  20370. s0 = ss7; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  20371. arg1 = compile(ss7,s0,depth1,0,block_flags); // Number of iterations
  20372. _cimg_mp_check_type(arg1,1,1,0);
  20373. s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20374. p1 = code._width;
  20375. if (s1<se1) { // Version with 3 arguments
  20376. variable_name.assign(s0,(unsigned int)(s1 + 1 - s0)).back() = 0;
  20377. cimg::strpare(variable_name,false,true);
  20378. if (!cimg::is_varname(variable_name)) { // Invalid variable name
  20379. cimg::strellipsize(variable_name,64);
  20380. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  20381. "CImg<%s>::%s: %s: Invalid loop variable name '%s', "
  20382. "in expression '%s'.",
  20383. pixel_type(),_cimg_mp_calling_function,s_op,
  20384. variable_name._data,s0);
  20385. }
  20386. get_variable_pos(variable_name,arg2,arg3);
  20387. arg2 = arg3!=~0U?reserved_label[arg3]:arg2!=~0U?variable_pos[arg2]:~0U; // Variable slot
  20388. if (arg2!=~0U && (!_cimg_mp_is_scalar(arg2) ||
  20389. _cimg_mp_is_const_scalar(arg2))) { // Variable is not a vector or is a constant->error
  20390. cimg::strellipsize(variable_name,64);
  20391. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  20392. "CImg<%s>::%s: %s: Invalid type '%s' for variable '%s' "
  20393. "(expected 'scalar'), in expression '%s'.",
  20394. pixel_type(),_cimg_mp_calling_function,s_op,
  20395. s_type(arg2)._data,variable_name._data,s0);
  20396. } else if (arg2==~0U) { // Variable does not exist -> create it
  20397. arg2 = scalar();
  20398. if (arg3!=~0U) reserved_label[arg3] = arg2;
  20399. else {
  20400. if (variable_def._width>=variable_pos._width) variable_pos.resize(-200,1,1,1,0);
  20401. variable_pos[variable_def._width] = arg2;
  20402. variable_name.move_to(variable_def);
  20403. }
  20404. memtype[arg2] = -1;
  20405. }
  20406. arg3 = compile(++s1,se1,depth1,0,block_flags);
  20407. } else { // Version with 2 arguments
  20408. arg2 = ~0U;
  20409. arg3 = compile(s0,se1,depth1,0,block_flags);
  20410. }
  20411. // arg2 = variable slot, arg3 = fill expression.
  20412. CImg<ulongT>::vector((ulongT)mp_repeat,arg3,arg1,arg2,code._width - p1).move_to(code,p1);
  20413. _cimg_mp_return_nan();
  20414. }
  20415. if (!std::strncmp(ss,"resize(",7)) { // Vector or image resize
  20416. _cimg_mp_op("Function 'resize()'");
  20417. if (*ss7!='#') { // Vector
  20418. pos = 1;
  20419. for (s = ss7; s<se; ++s, ++pos) {
  20420. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  20421. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  20422. arg2 = compile(s,ns,depth1,0,block_flags);
  20423. if (s!=ss7) _cimg_mp_check_type(arg2,pos,1,0);
  20424. CImg<ulongT>::vector(arg2).move_to(l_opcode);
  20425. s = ns;
  20426. }
  20427. (l_opcode>'y').move_to(opcode);
  20428. if (opcode.height()<2) compile(s,se1,depth1,0,block_flags); // Not enough arguments -> throw exception
  20429. arg1 = (unsigned int)opcode[0]; // Vector to resize
  20430. p1 = _cimg_mp_size(arg1);
  20431. if (opcode.height()<=4) { // Simple vector resize
  20432. arg2 = (unsigned int)opcode[1];
  20433. _cimg_mp_check_const_scalar(arg2,2,3);
  20434. arg2 = (unsigned int)mem[arg2];
  20435. arg3 = opcode.height()<3?1U:(unsigned int)opcode[2];
  20436. _cimg_mp_check_type(arg3,3,1,0);
  20437. arg4 = opcode.height()<4?0U:(unsigned int)opcode[3];
  20438. _cimg_mp_check_type(arg4,4,1,0);
  20439. pos = vector(arg2);
  20440. CImg<ulongT>::vector((ulongT)mp_vector_resize,pos,arg2,arg1,p1,arg3,arg4).move_to(code);
  20441. } else { // Advanced vector resize (vector viewed as an image)
  20442. // opcode = [ A, ow,oh,od,os, nw,nh,nd,ns, interp, boundary_cond, ax,ay,az,ac ]
  20443. // [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ]
  20444. if (opcode.height()<6) compile(s,se1,depth1,0,block_flags); // Not enough arguments -> throw exception
  20445. p2 = opcode.height();
  20446. opcode.resize(1,15,1,1,0);
  20447. if (p2<7) opcode[6] = opcode[2];
  20448. if (p2<8) opcode[7] = opcode[3];
  20449. if (p2<9) opcode[8] = opcode[4];
  20450. if (p2<10) opcode[9] = 1;
  20451. _cimg_mp_check_const_scalar(opcode[1],2,3);
  20452. _cimg_mp_check_const_scalar(opcode[2],3,3);
  20453. _cimg_mp_check_const_scalar(opcode[3],4,3);
  20454. _cimg_mp_check_const_scalar(opcode[4],5,3);
  20455. _cimg_mp_check_const_scalar(opcode[5],6,3);
  20456. _cimg_mp_check_const_scalar(opcode[6],7,3);
  20457. _cimg_mp_check_const_scalar(opcode[7],8,3);
  20458. _cimg_mp_check_const_scalar(opcode[8],9,3);
  20459. arg2 = (unsigned int)mem[opcode[1]]; opcode[1] = arg2;
  20460. arg3 = (unsigned int)mem[opcode[2]]; opcode[2] = arg3;
  20461. arg4 = (unsigned int)mem[opcode[3]]; opcode[3] = arg4;
  20462. arg5 = (unsigned int)mem[opcode[4]]; opcode[4] = arg5;
  20463. if (arg2*arg3*arg4*arg5!=std::max(1U,p1))
  20464. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  20465. "CImg<%s>::%s: %s: Input vector size (%lu values) and its specified "
  20466. "geometry (%u,%u,%u,%u) (%lu values) do not match.",
  20467. pixel_type(),_cimg_mp_calling_function,s_op,
  20468. std::max(p1,1U),arg2,arg3,arg4,arg5,(ulongT)arg2*arg3*arg4*arg5);
  20469. arg2 = (unsigned int)mem[opcode[5]]; opcode[5] = arg2;
  20470. arg3 = (unsigned int)mem[opcode[6]]; opcode[6] = arg3;
  20471. arg4 = (unsigned int)mem[opcode[7]]; opcode[7] = arg4;
  20472. arg5 = (unsigned int)mem[opcode[8]]; opcode[8] = arg5;
  20473. pos = vector(arg2*arg3*arg4*arg5);
  20474. opcode.resize(1,18,1,1,0,0,0,1);
  20475. opcode[0] = (ulongT)mp_vector_resize_ext;
  20476. opcode[1] = (ulongT)pos;
  20477. opcode[2] = (ulongT)p1;
  20478. opcode.move_to(code);
  20479. }
  20480. return_new_comp = true;
  20481. _cimg_mp_return(pos);
  20482. } else { // Image
  20483. if (!is_inside_critical) is_parallelizable = false;
  20484. s0 = ss8; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  20485. p1 = compile(ss8,s0++,depth1,0,block_flags);
  20486. _cimg_mp_check_list();
  20487. l_opcode.assign(); // Don't use 'opcode': it can be modified by further calls to 'compile()'!
  20488. CImg<ulongT>::vector((ulongT)mp_image_resize,_cimg_mp_slot_nan,p1,~0U,~0U,~0U,~0U,1,0,0,0,0,0).
  20489. move_to(l_opcode);
  20490. pos = 0;
  20491. for (s = s0; s<se && pos<10; ++s) {
  20492. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  20493. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  20494. arg1 = compile(s,ns,depth1,0,block_flags);
  20495. _cimg_mp_check_type(arg1,pos + 2,1,0);
  20496. l_opcode(0,pos + 3) = arg1;
  20497. s = ns;
  20498. ++pos;
  20499. }
  20500. if (pos<1 || pos>10) {
  20501. _cimg_mp_strerr;
  20502. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  20503. "CImg<%s>::%s: %s: %s arguments, in expression '%s'.",
  20504. pixel_type(),_cimg_mp_calling_function,s_op,
  20505. pos<1?"Missing":"Too much",s0);
  20506. }
  20507. l_opcode[0].move_to(code);
  20508. _cimg_mp_return_nan();
  20509. }
  20510. }
  20511. if (!std::strncmp(ss,"reverse(",8)) { // Vector reverse
  20512. _cimg_mp_op("Function 'reverse()'");
  20513. arg1 = compile(ss8,se1,depth1,0,block_flags);
  20514. if (!_cimg_mp_is_vector(arg1)) _cimg_mp_return(arg1);
  20515. p1 = _cimg_mp_size(arg1);
  20516. pos = vector(p1);
  20517. CImg<ulongT>::vector((ulongT)mp_vector_reverse,pos,arg1,p1).move_to(code);
  20518. return_new_comp = true;
  20519. _cimg_mp_return(pos);
  20520. }
  20521. if (!std::strncmp(ss,"rol(",4) || !std::strncmp(ss,"ror(",4)) { // Bitwise rotation
  20522. _cimg_mp_op(ss[2]=='l'?"Function 'rol()'":"Function 'ror()'");
  20523. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1-expr._data]!=clevel1)) ++s1;
  20524. arg1 = compile(ss4,s1,depth1,0,block_flags);
  20525. arg2 = s1<se1?compile(++s1,se1,depth1,0,block_flags):1;
  20526. _cimg_mp_check_type(arg2,2,1,0);
  20527. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector2_vs(*ss2=='l'?mp_rol:mp_ror,arg1,arg2);
  20528. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
  20529. _cimg_mp_const_scalar(*ss2=='l'?cimg::rol(mem[arg1],(unsigned int)mem[arg2]):
  20530. cimg::ror(mem[arg1],(unsigned int)mem[arg2]));
  20531. _cimg_mp_scalar2(*ss2=='l'?mp_rol:mp_ror,arg1,arg2);
  20532. }
  20533. if (!std::strncmp(ss,"rot(",4)) { // 2D/3D rotation matrix
  20534. _cimg_mp_op("Function 'rot()'");
  20535. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20536. arg1 = compile(ss4,s1,depth1,0,block_flags);
  20537. if (s1<se1) { // 3D rotation
  20538. _cimg_mp_check_type(arg1,1,3,3);
  20539. is_sth = false; // Is coordinates as vector?
  20540. if (_cimg_mp_is_vector(arg1)) { // Coordinates specified as a vector
  20541. is_sth = true;
  20542. p2 = _cimg_mp_size(arg1);
  20543. ++arg1;
  20544. arg2 = arg3 = 0;
  20545. if (p2>1) {
  20546. arg2 = arg1 + 1;
  20547. if (p2>2) arg3 = arg2 + 1;
  20548. }
  20549. arg4 = compile(++s1,se1,depth1,0,block_flags);
  20550. } else {
  20551. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  20552. arg2 = compile(s1,s2,depth1,0,block_flags);
  20553. s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
  20554. arg3 = compile(s2,s3,depth1,0,block_flags);
  20555. arg4 = compile(++s3,se1,depth1,0,block_flags);
  20556. _cimg_mp_check_type(arg2,2,1,0);
  20557. _cimg_mp_check_type(arg3,3,1,0);
  20558. }
  20559. _cimg_mp_check_type(arg4,is_sth?2:4,1,0);
  20560. pos = vector(9);
  20561. CImg<ulongT>::vector((ulongT)mp_rot3d,pos,arg1,arg2,arg3,arg4).move_to(code);
  20562. } else { // 2D rotation
  20563. _cimg_mp_check_type(arg1,1,1,0);
  20564. pos = vector(4);
  20565. CImg<ulongT>::vector((ulongT)mp_rot2d,pos,arg1).move_to(code);
  20566. }
  20567. return_new_comp = true;
  20568. _cimg_mp_return(pos);
  20569. }
  20570. if (!std::strncmp(ss,"round(",6)) { // Value rounding
  20571. _cimg_mp_op("Function 'round()'");
  20572. s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20573. arg1 = compile(ss6,s1,depth1,0,block_flags);
  20574. arg2 = 1;
  20575. arg3 = 0;
  20576. if (s1<se1) {
  20577. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  20578. arg2 = compile(s1,s2,depth1,0,block_flags);
  20579. arg3 = s2<se1?compile(++s2,se1,depth1,0,block_flags):0;
  20580. }
  20581. _cimg_mp_check_type(arg2,2,1,0);
  20582. _cimg_mp_check_type(arg3,3,1,0);
  20583. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector3_vss(mp_round,arg1,arg2,arg3);
  20584. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2) && _cimg_mp_is_const_scalar(arg3))
  20585. _cimg_mp_const_scalar(cimg::round(mem[arg1],mem[arg2],(int)mem[arg3]));
  20586. _cimg_mp_scalar3(mp_round,arg1,arg2,arg3);
  20587. }
  20588. #ifdef cimg_mp_func_run
  20589. if (!std::strncmp(ss,"run(",4)) { // Run external command
  20590. _cimg_mp_op("Function 'run()'");
  20591. if (!is_inside_critical && !is_inside_begin && !is_inside_end) is_noncritical_run = true;
  20592. CImg<ulongT>::vector((ulongT)mp_run,0,0).move_to(l_opcode);
  20593. pos = 1;
  20594. for (s = ss4; s<se; ++s) {
  20595. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  20596. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  20597. arg1 = compile(s,ns,depth1,0,block_flags);
  20598. CImg<ulongT>::vector(arg1,_cimg_mp_size(arg1)).move_to(l_opcode);
  20599. s = ns;
  20600. }
  20601. (l_opcode>'y').move_to(opcode);
  20602. pos = scalar();
  20603. opcode[1] = pos;
  20604. opcode[2] = opcode._height;
  20605. opcode.move_to(code);
  20606. return_new_comp = true;
  20607. _cimg_mp_return(pos);
  20608. }
  20609. #endif
  20610. break;
  20611. case 's' :
  20612. if (*ss1=='(') { // Image spectrum
  20613. _cimg_mp_op("Function 's()'");
  20614. if (*ss2=='#') { // Index specified
  20615. p1 = compile(ss3,se1,depth1,0,block_flags);
  20616. _cimg_mp_check_notnan_index(p1);
  20617. _cimg_mp_check_list();
  20618. } else { if (ss2!=se1) break; p1 = ~0U; }
  20619. _cimg_mp_scalar1(mp_image_s,p1);
  20620. }
  20621. if (!std::strncmp(ss,"same(",5)) { // Test if operands have the same values
  20622. _cimg_mp_op("Function 'same()'");
  20623. s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20624. arg1 = compile(ss5,s1,depth1,0,block_flags);
  20625. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  20626. arg2 = compile(s1,s2,depth1,0,block_flags);
  20627. arg3 = 11;
  20628. arg4 = 1;
  20629. if (s2<se1) {
  20630. s3 = ++s2; while (s3<se1 && (*s3!=',' || level[s3 - expr._data]!=clevel1)) ++s3;
  20631. arg3 = compile(s2,s3,depth1,0,block_flags);
  20632. _cimg_mp_check_type(arg3,3,1,0);
  20633. arg4 = s3<se1?compile(++s3,se1,depth1,0,block_flags):1;
  20634. }
  20635. p1 = _cimg_mp_size(arg1);
  20636. p2 = _cimg_mp_size(arg2);
  20637. _cimg_mp_scalar6(mp_vector_eq,arg1,p1,arg2,p2,arg3,arg4);
  20638. }
  20639. #ifdef cimg_mp_func_set
  20640. if (!std::strncmp(ss,"set(",4)) { // Set value/vector to external variable
  20641. _cimg_mp_op("Function 'set()'");
  20642. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20643. arg1 = compile(ss4,s1,depth1,0,block_flags);
  20644. arg2 = compile(++s1,se1,depth1,0,block_flags);
  20645. _cimg_mp_check_type(arg1,2,2,0);
  20646. p1 = _cimg_mp_size(arg1);
  20647. p2 = _cimg_mp_size(arg2);
  20648. CImg<ulongT>::vector((ulongT)mp_set,arg2,p2,arg1,p1).move_to(code);
  20649. _cimg_mp_return_nan();
  20650. }
  20651. #endif
  20652. if (!std::strncmp(ss,"shift(",6)) { // Shift vector
  20653. _cimg_mp_op("Function 'shift()'");
  20654. s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20655. arg1 = compile(ss6,s1,depth1,0,block_flags);
  20656. arg2 = 1; arg3 = 0;
  20657. if (s1<se1) {
  20658. s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  20659. arg2 = compile(s1,s0,depth1,0,block_flags);
  20660. arg3 = s0<se1?compile(++s0,se1,depth1,0,block_flags):0;
  20661. }
  20662. _cimg_mp_check_type(arg1,1,2,0);
  20663. _cimg_mp_check_type(arg2,2,1,0);
  20664. _cimg_mp_check_type(arg3,3,1,0);
  20665. p1 = _cimg_mp_size(arg1);
  20666. pos = vector(p1);
  20667. CImg<ulongT>::vector((ulongT)mp_shift,pos,arg1,p1,arg2,arg3).move_to(code);
  20668. return_new_comp = true;
  20669. _cimg_mp_return(pos);
  20670. }
  20671. if (!std::strncmp(ss,"sign(",5)) { // Sign
  20672. _cimg_mp_op("Function 'sign()'");
  20673. arg1 = compile(ss5,se1,depth1,0,block_flags);
  20674. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sign,arg1);
  20675. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::sign(mem[arg1]));
  20676. _cimg_mp_scalar1(mp_sign,arg1);
  20677. }
  20678. if (!std::strncmp(ss,"sin(",4)) { // Sine
  20679. _cimg_mp_op("Function 'sin()'");
  20680. arg1 = compile(ss4,se1,depth1,0,block_flags);
  20681. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sin,arg1);
  20682. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::sin(mem[arg1]));
  20683. _cimg_mp_scalar1(mp_sin,arg1);
  20684. }
  20685. if (!std::strncmp(ss,"sinc(",5)) { // Sine cardinal
  20686. _cimg_mp_op("Function 'sinc()'");
  20687. arg1 = compile(ss5,se1,depth1,0,block_flags);
  20688. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinc,arg1);
  20689. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::sinc(mem[arg1]));
  20690. _cimg_mp_scalar1(mp_sinc,arg1);
  20691. }
  20692. if (!std::strncmp(ss,"sinh(",5)) { // Hyperbolic sine
  20693. _cimg_mp_op("Function 'sinh()'");
  20694. arg1 = compile(ss5,se1,depth1,0,block_flags);
  20695. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sinh,arg1);
  20696. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::sinh(mem[arg1]));
  20697. _cimg_mp_scalar1(mp_sinh,arg1);
  20698. }
  20699. if (!std::strncmp(ss,"size(",5)) { // Vector size
  20700. _cimg_mp_op("Function 'size()'");
  20701. arg1 = compile(ss5,se1,depth1,0,block_flags);
  20702. _cimg_mp_const_scalar(_cimg_mp_is_scalar(arg1)?0:_cimg_mp_size(arg1));
  20703. }
  20704. if (!std::strncmp(ss,"solve(",6)) { // Solve square linear system
  20705. _cimg_mp_op("Function 'solve()'");
  20706. s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20707. arg1 = compile(ss6,s1,depth1,0,block_flags);
  20708. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  20709. arg2 = compile(s1,s2,depth1,0,block_flags);
  20710. arg3 = 1;
  20711. arg4 = 0;
  20712. if (s2<se1) {
  20713. s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20714. arg3 = compile(s2,s1,depth1,0,block_flags);
  20715. arg4 = s1<se1?compile(++s1,se1,depth1,0,block_flags):0;
  20716. }
  20717. _cimg_mp_check_type(arg1,1,2,0);
  20718. _cimg_mp_check_type(arg2,2,2,0);
  20719. _cimg_mp_check_const_scalar(arg3,3,3);
  20720. _cimg_mp_check_type(arg4,4,1,0);
  20721. p1 = _cimg_mp_size(arg1);
  20722. p2 = _cimg_mp_size(arg2);
  20723. p3 = (unsigned int)mem[arg3];
  20724. arg5 = p2/p3;
  20725. arg6 = p1/arg5;
  20726. if (arg6*arg5!=p1 || arg5*p3!=p2) {
  20727. _cimg_mp_strerr;
  20728. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  20729. "CImg<%s>::%s: %s: Types of first and second arguments ('%s' and '%s') "
  20730. "do not match with third argument 'nb_colsB=%u', "
  20731. "in expression '%s'.",
  20732. pixel_type(),_cimg_mp_calling_function,s_op,
  20733. s_type(arg1)._data,s_type(arg2)._data,p3,s0);
  20734. }
  20735. pos = vector(arg6*p3);
  20736. CImg<ulongT>::vector((ulongT)mp_solve,pos,arg1,arg2,arg6,arg5,p3,arg4).move_to(code);
  20737. return_new_comp = true;
  20738. _cimg_mp_return(pos);
  20739. }
  20740. if (!std::strncmp(ss,"sort(",5)) { // Sort vector
  20741. _cimg_mp_op("Function 'sort()'");
  20742. s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20743. arg1 = compile(ss5,s1,depth1,0,block_flags);
  20744. arg2 = arg4 = 1; arg3 = ~0U;
  20745. if (s1<se1) {
  20746. s0 = ++s1; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  20747. arg2 = compile(s1,s0,depth1,0,block_flags);
  20748. if (s0<se1) {
  20749. s1 = ++s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20750. arg3 = compile(s0,s1,depth1,0,block_flags);
  20751. arg4 = s1<se1?compile(++s1,se1,depth1,0,block_flags):1;
  20752. }
  20753. }
  20754. _cimg_mp_check_type(arg1,1,2,0);
  20755. _cimg_mp_check_type(arg2,2,1,0);
  20756. if (arg3!=~0U) _cimg_mp_check_type(arg3,3,1,0);
  20757. _cimg_mp_check_type(arg4,4,1,0);
  20758. p1 = _cimg_mp_size(arg1);
  20759. pos = vector(p1);
  20760. CImg<ulongT>::vector((ulongT)mp_sort,pos,arg1,p1,arg2,arg3,arg4).move_to(code);
  20761. return_new_comp = true;
  20762. _cimg_mp_return(pos);
  20763. }
  20764. if (!std::strncmp(ss,"sqr(",4)) { // Square
  20765. _cimg_mp_op("Function 'sqr()'");
  20766. arg1 = compile(ss4,se1,depth1,0,block_flags);
  20767. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqr,arg1);
  20768. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::sqr(mem[arg1]));
  20769. _cimg_mp_scalar1(mp_sqr,arg1);
  20770. }
  20771. if (!std::strncmp(ss,"sqrt(",5)) { // Square root
  20772. _cimg_mp_op("Function 'sqrt()'");
  20773. arg1 = compile(ss5,se1,depth1,0,block_flags);
  20774. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_sqrt,arg1);
  20775. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::sqrt(mem[arg1]));
  20776. _cimg_mp_scalar1(mp_sqrt,arg1);
  20777. }
  20778. if (!std::strncmp(ss,"srand(",6)) { // Set RNG seed
  20779. _cimg_mp_op("Function 'srand()'");
  20780. arg1 = ss6<se1?compile(ss6,se1,depth1,0,block_flags):~0U;
  20781. if (arg1!=~0U) { _cimg_mp_check_type(arg1,1,1,0); _cimg_mp_scalar1(mp_srand,arg1); }
  20782. _cimg_mp_scalar0(mp_srand0);
  20783. }
  20784. if (!std::strncmp(ss,"stats(",6)) { // Image statistics
  20785. _cimg_mp_op("Function 'stats()'");
  20786. if (*ss6=='#') { // Index specified
  20787. p1 = compile(ss7,se1,depth1,0,block_flags);
  20788. _cimg_mp_check_notnan_index(p1);
  20789. _cimg_mp_check_list();
  20790. } else { if (ss6!=se1) break; p1 = ~0U; }
  20791. pos = vector(14);
  20792. CImg<ulongT>::vector((ulongT)mp_image_stats,pos,p1).move_to(code);
  20793. return_new_comp = true;
  20794. _cimg_mp_return(pos);
  20795. }
  20796. #ifdef cimg_mp_func_store
  20797. if (!std::strncmp(ss,"store(",6)) { // Store vector to variable
  20798. _cimg_mp_op("Function 'store()'");
  20799. s1 = ss6; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20800. arg1 = compile(ss6,s1,depth1,0,block_flags);
  20801. _cimg_mp_check_type(arg1,2,2,0);
  20802. p1 = _cimg_mp_size(arg1);
  20803. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  20804. arg2 = compile(s1,s2,depth1,0,block_flags);
  20805. p2 = _cimg_mp_size(arg2);
  20806. p3 = std::max(1U,p2);
  20807. arg3 = ~0U; arg4 = arg5 = arg6 = 1U; pos = 0;
  20808. if (s2<se1) {
  20809. s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20810. arg3 = compile(s2,s1,depth1,0,block_flags);
  20811. _cimg_mp_check_type(arg3,3,1,0);
  20812. arg4 = arg5 = arg6 = 1U;
  20813. if (s1<se1) {
  20814. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  20815. arg4 = compile(s1,s2,depth1,0,block_flags);
  20816. _cimg_mp_check_type(arg4,4,1,0);
  20817. if (s2<se1) {
  20818. s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20819. arg5 = compile(s2,s1,depth1,0,block_flags);
  20820. _cimg_mp_check_type(arg5,5,1,0);
  20821. if (s1<se1) {
  20822. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  20823. arg6 = compile(s1,s2,depth1,0,block_flags);
  20824. pos = s2<se1?compile(++s2,se1,depth1,0,block_flags):0;
  20825. _cimg_mp_check_type(arg6,6,1,0);
  20826. _cimg_mp_check_type(pos,7,1,0);
  20827. }
  20828. }
  20829. }
  20830. }
  20831. if (arg3==~0U) arg3 = const_scalar(p3);
  20832. CImg<ulongT>::vector((ulongT)mp_store,_cimg_mp_slot_nan,arg2,p2,arg1,p1,
  20833. arg3,arg4,arg5,arg6,pos).move_to(code);
  20834. _cimg_mp_return_nan();
  20835. }
  20836. #endif
  20837. if (!std::strncmp(ss,"s2v(",4)) { // String to double
  20838. _cimg_mp_op("Function 's2v()'");
  20839. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20840. arg1 = compile(ss4,s1,depth1,0,block_flags);
  20841. arg2 = arg3 = 0;
  20842. if (s1<se1) {
  20843. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  20844. arg2 = compile(s1,s2,depth1,0,block_flags);
  20845. arg3 = s2<se1?compile(++s2,se1,depth1,0,block_flags):0;
  20846. }
  20847. _cimg_mp_check_type(arg2,2,1,0);
  20848. _cimg_mp_check_type(arg3,3,1,0);
  20849. p1 = _cimg_mp_size(arg1);
  20850. pos = scalar();
  20851. CImg<ulongT>::vector((ulongT)mp_s2v,pos,arg1,p1,arg2,arg3).move_to(code);
  20852. return_new_comp = true;
  20853. _cimg_mp_return(pos);
  20854. }
  20855. if (!std::strncmp(ss,"string(",7)) { // Construct string from list of arguments
  20856. _cimg_mp_op("Function 'string()'");
  20857. CImg<ulongT>::vector((ulongT)mp_string,0,0,0).move_to(l_opcode);
  20858. if (*ss7=='#') { // Output vector size specified, with '#'
  20859. s0 = ss8; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  20860. arg1 = compile(ss8,s0++,depth1,0,block_flags);
  20861. _cimg_mp_check_const_scalar(arg1,1,3);
  20862. arg1 = (unsigned int)mem[arg1];
  20863. s = s0;
  20864. } else { arg1=~0U; s = ss7; }
  20865. p1 = 0;
  20866. for (; s<se; ++s) {
  20867. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  20868. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  20869. arg2 = compile(s,ns,depth1,0,block_flags);
  20870. p2 = _cimg_mp_size(arg2);
  20871. if (p2) p1+=p2;
  20872. else {
  20873. if (_cimg_mp_is_const_scalar(arg2)) p1+=cimg_snprintf(variable_name.assign(24),24,"%.17g",mem[arg2]);
  20874. else p1+=23;
  20875. }
  20876. CImg<ulongT>::vector(arg2,p2).move_to(l_opcode);
  20877. s = ns;
  20878. }
  20879. if (arg1==~0U) arg1 = p1;
  20880. pos = vector(arg1,0);
  20881. (l_opcode>'y').move_to(opcode);
  20882. opcode[1] = pos;
  20883. opcode[2] = arg1;
  20884. opcode[3] = opcode._height;
  20885. opcode.move_to(code);
  20886. return_new_comp = true;
  20887. _cimg_mp_return(pos);
  20888. }
  20889. if (!std::strncmp(ss,"svd(",4)) { // Matrix SVD
  20890. _cimg_mp_op("Function 'svd()'");
  20891. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20892. arg1 = compile(ss4,s1,depth1,0,block_flags);
  20893. arg2 = s1<se1?compile(++s1,se1,depth1,0,block_flags):1;
  20894. _cimg_mp_check_type(arg1,1,2,0);
  20895. _cimg_mp_check_const_scalar(arg2,2,3);
  20896. p1 = _cimg_mp_size(arg1);
  20897. p2 = (unsigned int)mem[arg2];
  20898. p3 = p1/p2;
  20899. if (p3*p2!=p1) {
  20900. _cimg_mp_strerr;
  20901. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  20902. "CImg<%s>::%s: %s: Type of first argument ('%s') "
  20903. "does not match with second argument 'nb_colsA=%u', "
  20904. "in expression '%s'.",
  20905. pixel_type(),_cimg_mp_calling_function,s_op,
  20906. s_type(arg1)._data,p2,s0);
  20907. }
  20908. pos = vector(p1 + p2 + p2*p2);
  20909. CImg<ulongT>::vector((ulongT)mp_matrix_svd,pos,arg1,p2,p3).move_to(code);
  20910. return_new_comp = true;
  20911. _cimg_mp_return(pos);
  20912. }
  20913. if (!std::strncmp(ss,"swap(",5)) { // Swap values
  20914. _cimg_mp_op("Function 'swap()'");
  20915. s1 = ss5; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  20916. ref.assign(14);
  20917. arg1 = compile(ss5,s1,depth1,ref,block_flags);
  20918. arg2 = compile(++s1,se1,depth1,ref._data + 7,block_flags);
  20919. p1 = _cimg_mp_size(arg1);
  20920. _cimg_mp_check_type(arg2,2,p1?2:1,p1);
  20921. if (_cimg_mp_is_const_scalar(arg1) || _cimg_mp_is_const_scalar(arg2)) {
  20922. _cimg_mp_strerr;
  20923. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  20924. "CImg<%s>::%s: %s: %s argument cannot be a constant, "
  20925. "in expression '%s'.",
  20926. pixel_type(),_cimg_mp_calling_function,s_op,
  20927. _cimg_mp_is_const_scalar(arg1)?"First":"Second",s0);
  20928. }
  20929. CImg<ulongT>::vector((ulongT)mp_swap,arg1,arg2,p1).move_to(code);
  20930. // Write back values of linked arg1 and arg2.
  20931. const unsigned int *_ref = ref;
  20932. is_sth = true; // Is first argument?
  20933. do {
  20934. switch (*_ref) {
  20935. case 1 : // arg1: V[k]
  20936. arg3 = _ref[1]; // Vector slot
  20937. arg4 = _ref[2]; // Index
  20938. CImg<ulongT>::vector((ulongT)mp_vector_set_off,arg1,arg3,(ulongT)_cimg_mp_size(arg3),arg4).
  20939. move_to(code);
  20940. break;
  20941. case 2 : // arg1: i/j[_#ind,off]
  20942. if (!is_inside_critical) is_parallelizable = false;
  20943. p1 = _ref[1]; // Index
  20944. is_relative = (bool)_ref[2];
  20945. arg3 = _ref[3]; // Offset
  20946. if (p1!=~0U) {
  20947. if (imglist)
  20948. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_joff:mp_list_set_ioff),
  20949. arg1,p1,arg3).move_to(code);
  20950. } else {
  20951. if (imgout)
  20952. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_joff:mp_set_ioff),
  20953. arg1,arg3).move_to(code);
  20954. }
  20955. break;
  20956. case 3 : // arg1: i/j(_#ind,_x,_y,_z,_c)
  20957. if (!is_inside_critical) is_parallelizable = false;
  20958. p1 = _ref[1]; // Index
  20959. is_relative = (bool)_ref[2];
  20960. arg3 = _ref[3]; // X
  20961. arg4 = _ref[4]; // Y
  20962. arg5 = _ref[5]; // Z
  20963. arg6 = _ref[6]; // C
  20964. if (p1!=~0U) {
  20965. if (imglist)
  20966. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_jxyzc:mp_list_set_ixyzc),
  20967. arg1,p1,arg3,arg4,arg5,arg6).move_to(code);
  20968. } else {
  20969. if (imgout)
  20970. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_jxyzc:mp_set_ixyzc),
  20971. arg1,arg3,arg4,arg5,arg6).move_to(code);
  20972. }
  20973. break;
  20974. case 4: // arg1: I/J[_#ind,off]
  20975. if (!is_inside_critical) is_parallelizable = false;
  20976. p1 = _ref[1]; // Index
  20977. is_relative = (bool)_ref[2];
  20978. arg3 = _ref[3]; // Offset
  20979. if (p1!=~0U) {
  20980. if (imglist) {
  20981. if (_cimg_mp_is_scalar(arg1))
  20982. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_s:mp_list_set_Ioff_s),
  20983. arg1,p1,arg3).move_to(code);
  20984. else {
  20985. _cimg_mp_check_const_index(p1);
  20986. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Joff_v:mp_list_set_Ioff_v),
  20987. arg1,p1,arg3,_cimg_mp_size(arg1)).move_to(code);
  20988. }
  20989. }
  20990. } else {
  20991. if (imgout) {
  20992. if (_cimg_mp_is_scalar(arg1))
  20993. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_s:mp_set_Ioff_s),
  20994. arg1,arg3).move_to(code);
  20995. else
  20996. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Joff_v:mp_set_Ioff_v),
  20997. arg1,arg3,_cimg_mp_size(arg1)).move_to(code);
  20998. }
  20999. }
  21000. break;
  21001. case 5 : // arg1: I/J(_#ind,_x,_y,_z,_c)
  21002. if (!is_inside_critical) is_parallelizable = false;
  21003. p1 = _ref[1]; // Index
  21004. is_relative = (bool)_ref[2];
  21005. arg3 = _ref[3]; // X
  21006. arg4 = _ref[4]; // Y
  21007. arg5 = _ref[5]; // Z
  21008. if (p1!=~0U) {
  21009. if (imglist) {
  21010. if (_cimg_mp_is_scalar(arg1))
  21011. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_s:mp_list_set_Ixyz_s),
  21012. arg1,p1,arg3,arg4,arg5).move_to(code);
  21013. else {
  21014. _cimg_mp_check_const_index(p1);
  21015. CImg<ulongT>::vector((ulongT)(is_relative?mp_list_set_Jxyz_v:mp_list_set_Ixyz_v),
  21016. arg1,p1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
  21017. }
  21018. }
  21019. } else {
  21020. if (imgout) {
  21021. if (_cimg_mp_is_scalar(arg1))
  21022. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_s:mp_set_Ixyz_s),
  21023. arg1,arg3,arg4,arg5).move_to(code);
  21024. else
  21025. CImg<ulongT>::vector((ulongT)(is_relative?mp_set_Jxyz_v:mp_set_Ixyz_v),
  21026. arg1,arg3,arg4,arg5,_cimg_mp_size(arg1)).move_to(code);
  21027. }
  21028. }
  21029. break;
  21030. }
  21031. _ref+=7;
  21032. arg1 = arg2;
  21033. is_sth = !is_sth;
  21034. } while (!is_sth);
  21035. if (p_ref) std::memcpy(p_ref,ref,siz_ref);
  21036. _cimg_mp_return_nan();
  21037. }
  21038. break;
  21039. case 't' :
  21040. if (!std::strncmp(ss,"tan(",4)) { // Tangent
  21041. _cimg_mp_op("Function 'tan()'");
  21042. arg1 = compile(ss4,se1,depth1,0,block_flags);
  21043. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tan,arg1);
  21044. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::tan(mem[arg1]));
  21045. _cimg_mp_scalar1(mp_tan,arg1);
  21046. }
  21047. if (!std::strncmp(ss,"tanh(",5)) { // Hyperbolic tangent
  21048. _cimg_mp_op("Function 'tanh()'");
  21049. arg1 = compile(ss5,se1,depth1,0,block_flags);
  21050. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_tanh,arg1);
  21051. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(std::tanh(mem[arg1]));
  21052. _cimg_mp_scalar1(mp_tanh,arg1);
  21053. }
  21054. if (!std::strncmp(ss,"trace(",6)) { // Matrix trace
  21055. _cimg_mp_op("Function 'trace()'");
  21056. arg1 = compile(ss6,se1,depth1,0,block_flags);
  21057. _cimg_mp_check_matrix_square(arg1,1);
  21058. p1 = (unsigned int)cimg::round(std::sqrt((float)_cimg_mp_size(arg1)));
  21059. _cimg_mp_scalar2(mp_trace,arg1,p1);
  21060. }
  21061. if (!std::strncmp(ss,"transpose(",10)) { // Matrix transpose
  21062. _cimg_mp_op("Function 'transpose()'");
  21063. s1 = ss + 10; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  21064. arg1 = compile(ss + 10,s1,depth1,0,block_flags);
  21065. arg2 = compile(++s1,se1,depth1,0,block_flags);
  21066. _cimg_mp_check_type(arg1,1,2,0);
  21067. _cimg_mp_check_const_scalar(arg2,2,3);
  21068. p1 = _cimg_mp_size(arg1);
  21069. p2 = (unsigned int)mem[arg2];
  21070. p3 = p1/p2;
  21071. if (p2*p3!=p1) {
  21072. _cimg_mp_strerr;
  21073. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  21074. "CImg<%s>::%s: %s: Size of first argument ('%s') does not match "
  21075. "second argument 'nb_cols=%u', in expression '%s'.",
  21076. pixel_type(),_cimg_mp_calling_function,s_op,
  21077. s_type(arg1)._data,p2,s0);
  21078. }
  21079. pos = vector(p3*p2);
  21080. CImg<ulongT>::vector((ulongT)mp_transpose,pos,arg1,p2,p3).move_to(code);
  21081. return_new_comp = true;
  21082. _cimg_mp_return(pos);
  21083. }
  21084. break;
  21085. case 'u' :
  21086. if (*ss1=='(') { // Random value with uniform distribution in specified range
  21087. _cimg_mp_op("Function 'u()'");
  21088. if (*ss2==')') _cimg_mp_scalar2(mp_u,0,1);
  21089. s1 = ss2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  21090. arg1 = compile(ss2,s1,depth1,0,block_flags);
  21091. arg3 = arg4 = 1;
  21092. if (s1<se1) {
  21093. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  21094. arg2 = compile(s1,s2,depth1,0,block_flags);
  21095. if (s2<se1) {
  21096. s1 = ++s2; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  21097. arg3 = compile(s2,s1,depth1,0,block_flags);
  21098. arg4 = s1<se1?compile(++s1,se1,depth1,0,block_flags):1;
  21099. }
  21100. } else { arg2 = arg1; arg1 = 0; }
  21101. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  21102. _cimg_mp_check_type(arg3,3,1,0);
  21103. _cimg_mp_check_type(arg4,4,1,0);
  21104. if (arg3==1 && arg4==1) { // Fastest version (closed set)
  21105. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2))
  21106. _cimg_mp_vector2_vv(mp_u,arg1,arg2);
  21107. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2))
  21108. _cimg_mp_vector2_vs(mp_u,arg1,arg2);
  21109. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2))
  21110. _cimg_mp_vector2_sv(mp_u,arg1,arg2);
  21111. _cimg_mp_scalar2(mp_u,arg1,arg2);
  21112. } else { // Slower version (open set)
  21113. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2))
  21114. _cimg_mp_vector4_vvss(mp_u_ext,arg1,arg2,arg3,arg4);
  21115. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2))
  21116. _cimg_mp_vector4_vsss(mp_u_ext,arg1,arg2,arg3,arg4);
  21117. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2))
  21118. _cimg_mp_vector4_svss(mp_u_ext,arg1,arg2,arg3,arg4);
  21119. _cimg_mp_scalar4(mp_u_ext,arg1,arg2,arg3,arg4);
  21120. }
  21121. }
  21122. if (!std::strncmp(ss,"ui2f(",5)) { // Special uint->float conversion
  21123. _cimg_mp_op("Function 'ui2f()'");
  21124. arg1 = compile(ss5,se1,depth1,0,block_flags);
  21125. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_ui2f,arg1);
  21126. if (_cimg_mp_is_const_scalar(arg1))
  21127. _cimg_mp_const_scalar((double)cimg::uint2float((unsigned int)mem[arg1]));
  21128. _cimg_mp_scalar1(mp_ui2f,arg1);
  21129. }
  21130. if (!std::strncmp(ss,"unitnorm(",9)) { // Normalize vector to unit norm
  21131. _cimg_mp_op("Function 'unitnorm()'");
  21132. s0 = ss + 9;
  21133. s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  21134. arg1 = compile(s0,s1,depth1,0,block_flags);
  21135. arg2 = s1<se1?compile(++s1,se1,depth1,0,block_flags):2;
  21136. _cimg_mp_check_type(arg2,0,1,0);
  21137. p1 = _cimg_mp_size(arg1);
  21138. if (p1>0) pos = is_comp_vector(arg1)?arg1:((return_new_comp = true), vector(p1));
  21139. else {
  21140. pos = scalar();
  21141. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2)) {
  21142. val = mem[arg1];
  21143. _cimg_mp_const_scalar(val?(mem[arg2]?1:val):0);
  21144. }
  21145. }
  21146. CImg<ulongT>::vector((ulongT)mp_vector_unitnorm,pos,arg1,p1,arg2).move_to(code);
  21147. _cimg_mp_return(pos);
  21148. }
  21149. if (!std::strncmp(ss,"unref(",6)) { // Un-reference variable
  21150. _cimg_mp_op("Function 'unref()'");
  21151. arg1=~0U;
  21152. for (s0 = ss6; s0<se1; s0 = s1) {
  21153. if (s0>ss6 && *s0==',') ++s0;
  21154. s1 = s0; while (s1<se1 && *s1!=',') ++s1;
  21155. c1 = *s1;
  21156. if (s1>s0) {
  21157. *s1 = 0;
  21158. get_variable_pos(s0,arg1,arg2);
  21159. if (arg2!=~0U) reserved_label[arg2] = ~0U;
  21160. else if (arg1!=~0U) {
  21161. variable_def.remove(arg1);
  21162. if (arg1<variable_pos._width - 1)
  21163. std::memmove(variable_pos._data + arg1,variable_pos._data + arg1 + 1,
  21164. sizeof(uintT)*(variable_pos._width - arg1 - 1));
  21165. --variable_pos._width;
  21166. }
  21167. *s1 = c1;
  21168. } else compile(s0,s1,depth1,0,block_flags); // Will throw a 'missing argument' exception
  21169. }
  21170. _cimg_mp_return(arg1!=~0U?arg1:_cimg_mp_slot_nan); // Return value of last specified variable
  21171. }
  21172. if (!std::strncmp(ss,"uppercase(",10)) { // Upper case
  21173. _cimg_mp_op("Function 'uppercase()'");
  21174. arg1 = compile(ss + 10,se1,depth1,0,block_flags);
  21175. if (_cimg_mp_is_vector(arg1)) _cimg_mp_vector1_v(mp_uppercase,arg1);
  21176. if (_cimg_mp_is_const_scalar(arg1)) _cimg_mp_const_scalar(cimg::uppercase(mem[arg1]));
  21177. _cimg_mp_scalar1(mp_uppercase,arg1);
  21178. }
  21179. break;
  21180. case 'v' :
  21181. if ((cimg_sscanf(ss,"vector%u%c",&(arg1=~0U),&sep)==2 && sep=='(' && arg1>0) ||
  21182. !std::strncmp(ss,"vector(",7) ||
  21183. (!std::strncmp(ss,"vector",6) && ss7<se1 && (s=std::strchr(ss7,'('))!=0)) { // Vector
  21184. _cimg_mp_op("Function 'vector()'");
  21185. arg2 = 0; // Number of specified values
  21186. if (arg1==~0U && *ss6!='(') {
  21187. arg1 = compile(ss6,s++,depth1,0,block_flags);
  21188. _cimg_mp_check_const_scalar(arg1,0,3);
  21189. arg1 = (unsigned int)mem[arg1];
  21190. } else s = std::strchr(ss6,'(') + 1;
  21191. if (arg1==~0U && *s=='#') { // Number of elements specified as first argument with '#'
  21192. s0 = ++s; while (s0<se1 && (*s0!=',' || level[s0 - expr._data]!=clevel1)) ++s0;
  21193. arg1 = compile(s,s0++,depth1,0,block_flags);
  21194. _cimg_mp_check_const_scalar(arg1,1,3);
  21195. arg1 = (unsigned int)mem[arg1];
  21196. s = s0;
  21197. }
  21198. if (s<se1 || arg1==~0U) for ( ; s<se; ++s) {
  21199. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  21200. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  21201. arg3 = compile(s,ns,depth1,0,block_flags);
  21202. if (_cimg_mp_is_vector(arg3)) {
  21203. arg4 = _cimg_mp_size(arg3);
  21204. CImg<ulongT>::sequence(arg4,arg3 + 1,arg3 + arg4).move_to(l_opcode);
  21205. arg2+=arg4;
  21206. } else { CImg<ulongT>::vector(arg3).move_to(l_opcode); ++arg2; }
  21207. s = ns;
  21208. }
  21209. if (arg1==~0U) arg1 = arg2;
  21210. if (!arg1) _cimg_mp_return(0);
  21211. pos = vector(arg1);
  21212. l_opcode.insert(CImg<ulongT>::vector((ulongT)mp_vector_init,pos,0,arg1),0);
  21213. (l_opcode>'y').move_to(opcode);
  21214. opcode[2] = opcode._height;
  21215. opcode.move_to(code);
  21216. return_new_comp = true;
  21217. _cimg_mp_return(pos);
  21218. }
  21219. if (!std::strncmp(ss,"vmax(",5) || !std::strncmp(ss,"vmin(",5) ||
  21220. !std::strncmp(ss,"vmaxabs(",8) || !std::strncmp(ss,"vminabs(",8) ||
  21221. !std::strncmp(ss,"vmed(",5) || !std::strncmp(ss,"vkth(",5) ||
  21222. !std::strncmp(ss,"vsum(",5) || !std::strncmp(ss,"vavg(",5) ||
  21223. !std::strncmp(ss,"vstd(",5) || !std::strncmp(ss,"vvar(",5) ||
  21224. !std::strncmp(ss,"vprod(",6) ||
  21225. !std::strncmp(ss,"vargmin(",8) || !std::strncmp(ss,"vargmax(",8) ||
  21226. !std::strncmp(ss,"vargminabs(",11) || !std::strncmp(ss,"vargmaxabs(",11) ||
  21227. !std::strncmp(ss,"vargkth(",8)) { // Multi-argument vector functions
  21228. _cimg_mp_op(ss[1]=='a'?(ss[2]=='v'?"Function 'vavg()'":
  21229. ss[4]=='k'?"Function 'vargkth()'":
  21230. ss[5]=='i' && ss[7]=='('?"Function 'vargmin()'":
  21231. ss[5]=='i'?"Function vargminabs()'":
  21232. ss[7]=='('?"Function 'vargmax()'":
  21233. "Function 'vargmaxabs()'"):
  21234. ss[1]=='s'?(ss[2]=='u'?"Function 'vsum()'":"Function 'vstd()'"):
  21235. ss[1]=='k'?"Function 'vkth()'":
  21236. ss[1]=='p'?"Function 'vprod()'":
  21237. ss[1]=='v'?"Function 'vvar()'":
  21238. ss[2]=='i'?(ss[4]=='('?"Function 'vmin()'":
  21239. "Function 'vminabs()'"):
  21240. ss[2]=='a'?(ss[4]=='('?"Function 'vmax()'":
  21241. "Function 'vmaxabs()'"):
  21242. "Function 'vmed()'");
  21243. op = ss[1]=='a'?(ss[2]=='v'?mp_vavg:
  21244. ss[4]=='k'?mp_vargkth:
  21245. ss[5]=='i' && ss[7]=='('?mp_vargmin:
  21246. ss[5]=='i'?mp_vargminabs:
  21247. ss[7]=='('?mp_vargmax:mp_vargmaxabs):
  21248. ss[1]=='s'?(ss[2]=='u'?mp_vsum:mp_vstd):
  21249. ss[1]=='k'?mp_vkth:
  21250. ss[1]=='p'?mp_vprod:
  21251. ss[1]=='v'?mp_vvar:
  21252. ss[2]=='i'?(ss[4]=='('?mp_vmin:mp_vminabs):
  21253. ss[2]=='a'?(ss[4]=='('?mp_vmax:mp_vmaxabs):
  21254. mp_vmedian;
  21255. CImg<ulongT>::vector((ulongT)op,0,0,0).move_to(l_opcode);
  21256. p1 = ~0U;
  21257. p3 = 1;
  21258. for (s = std::strchr(ss,'(') + 1; s<se; ++s) {
  21259. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  21260. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  21261. arg2 = compile(s,ns,depth1,0,block_flags);
  21262. p2 = _cimg_mp_size(arg2);
  21263. if (p1==~0U) { if (_cimg_mp_is_vector(arg2)) p1 = p2; }
  21264. else _cimg_mp_check_type(arg2,p3,3,p1);
  21265. CImg<ulongT>::vector(arg2,p2).move_to(l_opcode);
  21266. s = ns;
  21267. ++p3;
  21268. }
  21269. (l_opcode>'y').move_to(opcode);
  21270. if (p1==~0U) { pos = scalar(); p1 = 0; } else pos = vector(p1);
  21271. opcode[1] = pos;
  21272. opcode[2] = p1;
  21273. opcode[3] = opcode._height;
  21274. opcode.move_to(code);
  21275. return_new_comp = true;
  21276. _cimg_mp_return(pos);
  21277. }
  21278. if (!std::strncmp(ss,"v2s(",4)) { // Double(s) to string
  21279. _cimg_mp_op("Function 'v2s()'");
  21280. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  21281. arg1 = compile(ss4,s1,depth1,0,block_flags);
  21282. arg2 = 0; arg3 = ~0U;
  21283. if (s1<se1) {
  21284. s2 = ++s1; while (s2<se1 && (*s2!=',' || level[s2 - expr._data]!=clevel1)) ++s2;
  21285. arg2 = compile(s1,s2,depth1,0,block_flags);
  21286. arg3 = s2<se1?compile(++s2,se1,depth1,0,block_flags):~0U;
  21287. }
  21288. _cimg_mp_check_type(arg2,2,1,0);
  21289. if (arg3==~0U) { // Auto-guess best output vector size
  21290. p1 = _cimg_mp_size(arg1);
  21291. p1 = p1?25*p1 - 1:24;
  21292. } else {
  21293. _cimg_mp_check_const_scalar(arg3,3,3);
  21294. p1 = (unsigned int)mem[arg3];
  21295. }
  21296. pos = vector(p1);
  21297. CImg<ulongT>::vector((ulongT)mp_v2s,pos,p1,arg1,_cimg_mp_size(arg1),arg2).move_to(code);
  21298. return_new_comp = true;
  21299. _cimg_mp_return(pos);
  21300. }
  21301. break;
  21302. case 'w' :
  21303. if (*ss1=='(') { // Image width
  21304. _cimg_mp_op("Function 'w()'");
  21305. if (*ss2=='#') { // Index specified
  21306. p1 = compile(ss3,se1,depth1,0,block_flags);
  21307. _cimg_mp_check_notnan_index(p1);
  21308. _cimg_mp_check_list();
  21309. } else { if (ss2!=se1) break; p1 = ~0U; }
  21310. _cimg_mp_scalar1(mp_image_w,p1);
  21311. }
  21312. if (*ss1=='h' && *ss2=='(') { // Image width*height
  21313. _cimg_mp_op("Function 'wh()'");
  21314. if (*ss3=='#') { // Index specified
  21315. p1 = compile(ss4,se1,depth1,0,block_flags);
  21316. _cimg_mp_check_notnan_index(p1);
  21317. _cimg_mp_check_list();
  21318. } else { if (ss3!=se1) break; p1 = ~0U; }
  21319. _cimg_mp_scalar1(mp_image_wh,p1);
  21320. }
  21321. if (*ss1=='h' && *ss2=='d' && *ss3=='(') { // Image width*height*depth
  21322. _cimg_mp_op("Function 'whd()'");
  21323. if (*ss4=='#') { // Index specified
  21324. p1 = compile(ss5,se1,depth1,0,block_flags);
  21325. _cimg_mp_check_notnan_index(p1);
  21326. _cimg_mp_check_list();
  21327. } else { if (ss4!=se1) break; p1 = ~0U; }
  21328. _cimg_mp_scalar1(mp_image_whd,p1);
  21329. }
  21330. if (*ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='(') { // Image width*height*depth*spectrum
  21331. _cimg_mp_op("Function 'whds()'");
  21332. if (*ss5=='#') { // Index specified
  21333. p1 = compile(ss6,se1,depth1,0,block_flags);
  21334. _cimg_mp_check_notnan_index(p1);
  21335. _cimg_mp_check_list();
  21336. } else { if (ss5!=se1) break; p1 = ~0U; }
  21337. _cimg_mp_scalar1(mp_image_whds,p1);
  21338. }
  21339. if (!std::strncmp(ss,"while(",6)) { // While...do
  21340. _cimg_mp_op("Function 'while()'");
  21341. s0 = *ss5=='('?ss6:ss8;
  21342. s1 = s0; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  21343. p1 = code._width;
  21344. arg1 = compile(s0,s1,depth1,0,block_flags);
  21345. p2 = code._width;
  21346. arg6 = mempos;
  21347. pos = compile(++s1,se1,depth1,0,block_flags);
  21348. _cimg_mp_check_type(arg1,1,1,0);
  21349. arg2 = _cimg_mp_size(pos);
  21350. CImg<ulongT>::vector((ulongT)mp_while,pos,arg1,p2 - p1,code._width - p2,arg2,
  21351. pos>=arg6 && !_cimg_mp_is_const_scalar(pos),
  21352. arg1>=arg6 && !_cimg_mp_is_const_scalar(arg1)).move_to(code,p1);
  21353. _cimg_mp_return(pos);
  21354. }
  21355. break;
  21356. case 'x' :
  21357. if (!std::strncmp(ss,"xor(",4)) { // Xor
  21358. _cimg_mp_op("Function 'xor()'");
  21359. s1 = ss4; while (s1<se1 && (*s1!=',' || level[s1 - expr._data]!=clevel1)) ++s1;
  21360. arg1 = compile(ss4,s1,depth1,0,block_flags);
  21361. arg2 = compile(++s1,se1,depth1,0,block_flags);
  21362. _cimg_mp_check_type(arg2,2,3,_cimg_mp_size(arg1));
  21363. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_vv(mp_bitwise_xor,arg1,arg2);
  21364. if (_cimg_mp_is_vector(arg1) && _cimg_mp_is_scalar(arg2)) _cimg_mp_vector2_vs(mp_bitwise_xor,arg1,arg2);
  21365. if (_cimg_mp_is_scalar(arg1) && _cimg_mp_is_vector(arg2)) _cimg_mp_vector2_sv(mp_bitwise_xor,arg1,arg2);
  21366. if (_cimg_mp_is_const_scalar(arg1) && _cimg_mp_is_const_scalar(arg2))
  21367. _cimg_mp_const_scalar((longT)mem[arg1] ^ (longT)mem[arg2]);
  21368. _cimg_mp_scalar2(mp_bitwise_xor,arg1,arg2);
  21369. }
  21370. break;
  21371. }
  21372. if ((!std::strncmp(ss,"norm",4) && ss4<se1 && (s = std::strchr(ss4,'('))!=0) || // Lp norm (constant p)
  21373. (!std::strncmp(ss,"hypot(",6) && (s = ss5))) {
  21374. const bool is_hypot = *ss=='h';
  21375. _cimg_mp_op(is_hypot?"Function 'hypot()'":"Function 'norm()'");
  21376. arg1 = !is_hypot && s!=ss4?compile(ss4,s,depth1,0,block_flags):2;
  21377. _cimg_mp_check_const_scalar(arg1,0,0);
  21378. val = mem[arg1];
  21379. is_sth = true; // Tell if all arguments are constant
  21380. CImg<ulongT>::vector(0,0,0,arg1).move_to(l_opcode);
  21381. for (++s; s<se; ++s) {
  21382. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  21383. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  21384. arg2 = compile(s,ns,depth1,0,block_flags);
  21385. if (_cimg_mp_is_vector(arg2))
  21386. CImg<ulongT>::sequence(_cimg_mp_size(arg2),arg2 + 1,arg2 + (ulongT)_cimg_mp_size(arg2)).
  21387. move_to(l_opcode);
  21388. else CImg<ulongT>::vector(arg2).move_to(l_opcode);
  21389. is_sth&=_cimg_mp_is_const_scalar(arg2);
  21390. s = ns;
  21391. }
  21392. (l_opcode>'y').move_to(opcode);
  21393. op = val==2?(is_hypot && opcode._height<8?_mp_vector_hypot:_mp_vector_norm2):
  21394. val==1?_mp_vector_norm1:!val?_mp_vector_norm0:
  21395. cimg::type<double>::is_inf(val)?_mp_vector_norminf:_mp_vector_normp;
  21396. opcode[0] = (ulongT)op;
  21397. opcode[2] = opcode._height;
  21398. if (is_sth) _cimg_mp_const_scalar(op(*this));
  21399. if (opcode._height==5) { // Single argument
  21400. if (arg1) { _cimg_mp_scalar1(mp_abs,opcode[4]); }
  21401. else { _cimg_mp_scalar2(mp_neq,opcode[4],0); }
  21402. }
  21403. opcode[1] = pos = scalar();
  21404. opcode.move_to(code);
  21405. return_new_comp = true;
  21406. _cimg_mp_return(pos);
  21407. }
  21408. if (!std::strncmp(ss,"max(",4) || !std::strncmp(ss,"min(",4) ||
  21409. !std::strncmp(ss,"maxabs(",7) || !std::strncmp(ss,"minabs(",7) ||
  21410. !std::strncmp(ss,"med(",4) || !std::strncmp(ss,"kth(",4) ||
  21411. !std::strncmp(ss,"sum(",4) || !std::strncmp(ss,"avg(",4) ||
  21412. !std::strncmp(ss,"std(",4) || !std::strncmp(ss,"var(",4) ||
  21413. !std::strncmp(ss,"prod(",5) ||
  21414. !std::strncmp(ss,"argmin(",7) || !std::strncmp(ss,"argmax(",7) ||
  21415. !std::strncmp(ss,"argminabs(",10) || !std::strncmp(ss,"argmaxabs(",10) ||
  21416. !std::strncmp(ss,"argkth(",7)) { // Multi-argument functions
  21417. _cimg_mp_op(*ss=='a'?(ss[1]=='v'?"Function 'avg()'":
  21418. ss[3]=='k'?"Function 'argkth()'":
  21419. ss[4]=='i' && ss[6]=='('?"Function 'argmin()'":
  21420. ss[4]=='i'?"Function argminabs()'":
  21421. ss[6]=='('?"Function 'argmax()'":
  21422. "Function 'argmaxabs()'"):
  21423. *ss=='s'?(ss[1]=='u'?"Function 'sum()'":"Function 'std()'"):
  21424. *ss=='k'?"Function 'kth()'":
  21425. *ss=='p'?"Function 'prod()'":
  21426. *ss=='v'?"Function 'var()'":
  21427. ss[1]=='i'?(ss[3]=='('?"Function 'min()'":
  21428. "Function 'minabs()'"):
  21429. ss[1]=='a'?(ss[3]=='('?"Function 'max()'":
  21430. "Function 'maxabs()'"):
  21431. "Function 'med()'");
  21432. op = *ss=='a'?(ss[1]=='v'?mp_avg:
  21433. ss[3]=='k'?mp_argkth:
  21434. ss[4]=='i' && ss[6]=='('?mp_argmin:
  21435. ss[4]=='i'?mp_argminabs:
  21436. ss[6]=='('?mp_argmax:mp_argmaxabs):
  21437. *ss=='s'?(ss[1]=='u'?mp_sum:mp_std):
  21438. *ss=='k'?mp_kth:
  21439. *ss=='p'?mp_prod:
  21440. *ss=='v'?mp_var:
  21441. ss[1]=='i'?(ss[3]=='('?mp_min:mp_minabs):
  21442. ss[1]=='a'?(ss[3]=='('?mp_max:mp_maxabs):
  21443. mp_med;
  21444. is_sth = true; // Tell if all arguments are constant
  21445. pos = scalar();
  21446. CImg<ulongT>::vector((ulongT)op,pos,0).move_to(l_opcode);
  21447. for (s = std::strchr(ss,'(') + 1; s<se; ++s) {
  21448. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  21449. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  21450. arg2 = compile(s,ns,depth1,0,block_flags);
  21451. if (_cimg_mp_is_vector(arg2)) CImg<ulongT>::vector(arg2 + 1,_cimg_mp_size(arg2)).move_to(l_opcode);
  21452. else CImg<ulongT>::vector(arg2,1).move_to(l_opcode);
  21453. is_sth&=_cimg_mp_is_const_scalar(arg2);
  21454. s = ns;
  21455. }
  21456. (l_opcode>'y').move_to(opcode);
  21457. opcode[2] = opcode._height;
  21458. if (is_sth) _cimg_mp_const_scalar(op(*this));
  21459. opcode.move_to(code);
  21460. return_new_comp = true;
  21461. _cimg_mp_return(pos);
  21462. }
  21463. // No corresponding built-in function -> Look for a user-defined macro call.
  21464. s0 = strchr(ss,'(');
  21465. if (s0) {
  21466. variable_name.assign(ss,(unsigned int)(s0 - ss + 1)).back() = 0;
  21467. // Count number of specified arguments.
  21468. p1 = 0;
  21469. for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) {
  21470. while (*s && cimg::is_blank(*s)) ++s;
  21471. if (*s==')' && !p1) break;
  21472. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  21473. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  21474. }
  21475. char mb = 0;
  21476. arg3 = 0; // Number of possible name matches
  21477. cimglist_for(macro_def,l) if (!std::strcmp(macro_def[l],variable_name) && ++arg3 &&
  21478. ((mb = macro_def[l].back())==(char)p1 || mb==(char)-1)) {
  21479. const bool is_variadic = mb==(char)-1;
  21480. p2 = is_variadic?1U:(unsigned int)mb; // Number of required arguments
  21481. CImg<charT> _expr = macro_body[l]; // Expression to be substituted
  21482. p1 = 1; // Index of current parsed argument
  21483. for (s = s0 + 1; s<=se1; ++p1, s = ns + 1) { // Parse function arguments
  21484. while (*s && cimg::is_blank(*s)) ++s;
  21485. if (!is_variadic && *s==')' && p1==1) break; // Function has no arguments
  21486. if (p1>p2) { ++p1; break; }
  21487. if (is_variadic) ns = se1;
  21488. else {
  21489. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  21490. (*ns!=')' || level[ns - expr._data]!=clevel)) ++ns;
  21491. }
  21492. variable_name.assign(s,(unsigned int)(ns - s + 1)).back() = 0; // Argument to write
  21493. arg2 = 0;
  21494. cimg_forX(_expr,k) {
  21495. if (_expr[k]==(char)p1) { // Perform argument substitution
  21496. arg1 = _expr._width;
  21497. if (variable_name._width>1) {
  21498. _expr.resize(arg1 + variable_name._width - 2,1,1,1,0);
  21499. std::memmove(_expr._data + k + variable_name._width - 1,_expr._data + k + 1,arg1 - k - 1);
  21500. std::memcpy(_expr._data + k,variable_name,variable_name._width - 1);
  21501. k+=variable_name._width - 2;
  21502. } else {
  21503. std::memmove(_expr._data + k,_expr._data + k + 1,arg1 - k - 1);
  21504. --k;
  21505. }
  21506. }
  21507. ++arg2;
  21508. }
  21509. }
  21510. // Recompute 'pexpr' and 'level' for evaluating substituted expression.
  21511. CImg<charT> _pexpr(_expr._width);
  21512. ns = _pexpr._data;
  21513. for (ps = _expr._data, c1 = ' '; *ps; ++ps) {
  21514. if (!cimg::is_blank(*ps)) c1 = *ps;
  21515. *(ns++) = c1;
  21516. }
  21517. *ns = 0;
  21518. CImg<uintT> _level = get_level(_expr);
  21519. expr.swap(_expr);
  21520. pexpr.swap(_pexpr);
  21521. level.swap(_level);
  21522. s0 = user_macro;
  21523. user_macro = macro_def[l];
  21524. pos = compile(expr._data,expr._data + expr._width - 1,depth1,p_ref,block_flags);
  21525. user_macro = s0;
  21526. level.swap(_level);
  21527. pexpr.swap(_pexpr);
  21528. expr.swap(_expr);
  21529. _cimg_mp_return(pos);
  21530. }
  21531. if (arg3) { // Macro name matched but number of arguments does not
  21532. CImg<uintT> sig_nargs(arg3);
  21533. arg1 = 0;
  21534. cimglist_for(macro_def,l) if (!std::strcmp(macro_def[l],variable_name))
  21535. sig_nargs[arg1++] = (unsigned int)macro_def[l].back();
  21536. _cimg_mp_strerr;
  21537. cimg::strellipsize(variable_name,64);
  21538. if (sig_nargs._width>1) {
  21539. sig_nargs.sort();
  21540. arg1 = sig_nargs.back();
  21541. --sig_nargs._width;
  21542. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  21543. "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) "
  21544. "does not match macro declaration (defined for %s or %u arguments), "
  21545. "in expression '%s'.",
  21546. pixel_type(),_cimg_mp_calling_function,variable_name._data,
  21547. p1,sig_nargs.value_string()._data,arg1,s0);
  21548. } else
  21549. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  21550. "CImg<%s>::%s: Function '%s()': Number of specified arguments (%u) "
  21551. "does not match macro declaration (defined for %u argument%s), "
  21552. "in expression '%s'.",
  21553. pixel_type(),_cimg_mp_calling_function,variable_name._data,
  21554. p1,*sig_nargs,*sig_nargs!=1?"s":"",s0);
  21555. }
  21556. }
  21557. } // if (se1==')')
  21558. // Char / string initializer.
  21559. if (*se1=='\'' &&
  21560. ((se1>ss && *ss=='\'') ||
  21561. (se1>ss1 && *ss=='_' && *ss1=='\''))) {
  21562. if (*ss=='_') { _cimg_mp_op("Char initializer"); s1 = ss2; }
  21563. else { _cimg_mp_op("String initializer"); s1 = ss1; }
  21564. arg1 = (unsigned int)(se1 - s1); // Original string length
  21565. if (arg1) {
  21566. CImg<charT>(s1,arg1 + 1).move_to(variable_name).back() = 0;
  21567. cimg::strunescape(variable_name);
  21568. arg1 = (unsigned int)std::strlen(variable_name);
  21569. }
  21570. if (!arg1) _cimg_mp_return(0); // Empty string -> 0
  21571. if (*ss=='_') {
  21572. if (arg1==1) _cimg_mp_const_scalar((unsigned char)*variable_name);
  21573. _cimg_mp_strerr;
  21574. cimg::strellipsize(variable_name,64);
  21575. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  21576. "CImg<%s>::%s: %s: Literal %s contains more than one byte, "
  21577. "in expression '%s'.",
  21578. pixel_type(),_cimg_mp_calling_function,s_op,
  21579. ss1,s0);
  21580. }
  21581. pos = vector(arg1);
  21582. CImg<ulongT>::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode);
  21583. CImg<ulongT>(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode);
  21584. std::memcpy((char*)l_opcode[1]._data,variable_name,arg1);
  21585. (l_opcode>'y').move_to(code);
  21586. return_new_comp = true;
  21587. _cimg_mp_return(pos);
  21588. }
  21589. // Vector initializer [ ... ].
  21590. if (*ss=='[' && *se1==']') {
  21591. _cimg_mp_op("Vector initializer");
  21592. s1 = ss1; while (s1<se2 && cimg::is_blank(*s1)) ++s1;
  21593. s2 = se2; while (s2>s1 && cimg::is_blank(*s2)) --s2;
  21594. if (s2>s1 && *s1=='\'' && *s2=='\'') { // Vector values provided as a string
  21595. arg1 = (unsigned int)(s2 - s1 - 1); // Original string length
  21596. if (arg1) {
  21597. CImg<charT>(s1 + 1,arg1 + 1).move_to(variable_name).back() = 0;
  21598. cimg::strunescape(variable_name);
  21599. arg1 = (unsigned int)std::strlen(variable_name);
  21600. }
  21601. if (!arg1) _cimg_mp_return(0); // Empty string -> 0
  21602. pos = vector(arg1);
  21603. CImg<ulongT>::vector((ulongT)mp_string_init,pos,arg1).move_to(l_opcode);
  21604. CImg<ulongT>(1,arg1/sizeof(ulongT) + (arg1%sizeof(ulongT)?1:0)).move_to(l_opcode);
  21605. std::memcpy((char*)l_opcode[1]._data,variable_name,arg1);
  21606. (l_opcode>'y').move_to(code);
  21607. } else { // Vector values provided as list of items
  21608. arg1 = 0; // Number of specified values
  21609. if (*ss1!=']') for (s = ss1; s<se; ++s) {
  21610. ns = s; while (ns<se && (*ns!=',' || level[ns - expr._data]!=clevel1) &&
  21611. (*ns!=']' || level[ns - expr._data]!=clevel)) ++ns;
  21612. arg2 = compile(s,ns,depth1,0,block_flags);
  21613. if (_cimg_mp_is_vector(arg2)) {
  21614. arg3 = _cimg_mp_size(arg2);
  21615. CImg<ulongT>::sequence(arg3,arg2 + 1,arg2 + arg3).move_to(l_opcode);
  21616. arg1+=arg3;
  21617. } else { CImg<ulongT>::vector(arg2).move_to(l_opcode); ++arg1; }
  21618. s = ns;
  21619. }
  21620. if (!arg1) _cimg_mp_return(0);
  21621. pos = vector(arg1);
  21622. l_opcode.insert(CImg<ulongT>::vector((ulongT)mp_vector_init,pos,0,arg1),0);
  21623. (l_opcode>'y').move_to(opcode);
  21624. opcode[2] = opcode._height;
  21625. opcode.move_to(code);
  21626. }
  21627. return_new_comp = true;
  21628. _cimg_mp_return(pos);
  21629. }
  21630. // Variables related to the input list of images.
  21631. if (*ss1=='#' && ss2<se) {
  21632. arg1 = compile(ss2,se,depth1,0,block_flags);
  21633. _cimg_mp_check_notnan_index(arg1);
  21634. p1 = (unsigned int)(imglist._width && _cimg_mp_is_const_scalar(arg1)?
  21635. cimg::mod((int)mem[arg1],imglist.width()):~0U);
  21636. switch (*ss) {
  21637. case 'w' : // w#ind
  21638. if (!imglist) _cimg_mp_return(0);
  21639. if (p1!=~0U) _cimg_mp_const_scalar(imglist[p1]._width);
  21640. _cimg_mp_scalar1(mp_list_width,arg1);
  21641. case 'h' : // h#ind
  21642. if (!imglist) _cimg_mp_return(0);
  21643. if (p1!=~0U) _cimg_mp_const_scalar(imglist[p1]._height);
  21644. _cimg_mp_scalar1(mp_list_height,arg1);
  21645. case 'd' : // d#ind
  21646. if (!imglist) _cimg_mp_return(0);
  21647. if (p1!=~0U) _cimg_mp_const_scalar(imglist[p1]._depth);
  21648. _cimg_mp_scalar1(mp_list_depth,arg1);
  21649. case 'r' : // r#ind
  21650. if (!imglist) _cimg_mp_return(0);
  21651. if (p1!=~0U) _cimg_mp_const_scalar(imglist[p1]._is_shared);
  21652. _cimg_mp_scalar1(mp_list_is_shared,arg1);
  21653. case 's' : // s#ind
  21654. if (!imglist) _cimg_mp_return(0);
  21655. if (p1!=~0U) _cimg_mp_const_scalar(imglist[p1]._spectrum);
  21656. _cimg_mp_scalar1(mp_list_spectrum,arg1);
  21657. case 'i' : // i#ind
  21658. if (!imglist) _cimg_mp_return(0);
  21659. _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,_cimg_mp_slot_c,
  21660. 0,_cimg_mp_boundary);
  21661. case 'I' : // I#ind
  21662. p2 = p1!=~0U?imglist[p1]._spectrum:imglist._width?~0U:0;
  21663. if (!p2) _cimg_mp_return(0);
  21664. pos = vector(p2);
  21665. CImg<ulongT>::vector((ulongT)mp_list_Joff,pos,p1,0,0,p2).move_to(code);
  21666. return_new_comp = true;
  21667. _cimg_mp_return(pos);
  21668. case 'R' : // R#ind
  21669. if (!imglist) _cimg_mp_return(0);
  21670. _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,0,
  21671. 0,_cimg_mp_boundary);
  21672. case 'G' : // G#ind
  21673. if (!imglist) _cimg_mp_return(0);
  21674. _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,1,
  21675. 0,_cimg_mp_boundary);
  21676. case 'B' : // B#ind
  21677. if (!imglist) _cimg_mp_return(0);
  21678. _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,2,
  21679. 0,_cimg_mp_boundary);
  21680. case 'A' : // A#ind
  21681. if (!imglist) _cimg_mp_return(0);
  21682. _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,3,
  21683. 0,_cimg_mp_boundary);
  21684. }
  21685. }
  21686. if (*ss1 && *ss2=='#' && ss3<se) {
  21687. arg1 = compile(ss3,se,depth1,0,block_flags);
  21688. _cimg_mp_check_notnan_index(arg1);
  21689. p1 = (unsigned int)(imglist._width && _cimg_mp_is_const_scalar(arg1)?
  21690. cimg::mod((int)mem[arg1],imglist.width()):~0U);
  21691. if (*ss=='w' && *ss1=='h') { // wh#ind
  21692. if (!imglist) _cimg_mp_return(0);
  21693. if (p1!=~0U) _cimg_mp_const_scalar(imglist[p1]._width*imglist[p1]._height);
  21694. _cimg_mp_scalar1(mp_list_wh,arg1);
  21695. }
  21696. arg2 = ~0U;
  21697. if (*ss=='i') {
  21698. if (*ss1>='0' && *ss1<='9') { // i0#ind...i9#ind
  21699. if (!imglist) _cimg_mp_return(0);
  21700. _cimg_mp_scalar7(mp_list_ixyzc,arg1,_cimg_mp_slot_x,_cimg_mp_slot_y,_cimg_mp_slot_z,*ss1 - '0',
  21701. 0,_cimg_mp_boundary);
  21702. }
  21703. if (*ss1=='c') { // ic#ind
  21704. if (!imglist) _cimg_mp_return(0);
  21705. if (_cimg_mp_is_const_scalar(arg1)) {
  21706. if (!list_median) list_median.assign(imglist._width);
  21707. if (!list_median[p1]) CImg<doubleT>::vector(imglist[p1].median()).move_to(list_median[p1]);
  21708. _cimg_mp_const_scalar(*list_median[p1]);
  21709. }
  21710. _cimg_mp_scalar1(mp_list_id,arg1);
  21711. }
  21712. if (*ss1=='d') { // id#ind
  21713. if (!imglist) _cimg_mp_return(0);
  21714. if (_cimg_mp_is_const_scalar(arg1)) {
  21715. if (!list_stats) list_stats.assign(imglist._width);
  21716. if (!list_stats[p1]) list_stats[p1].assign(1,14,1,1,0).fill(imglist[p1].get_stats(),false);
  21717. _cimg_mp_const_scalar(std::sqrt(list_stats(p1,3)));
  21718. }
  21719. _cimg_mp_scalar1(mp_list_id,arg1);
  21720. }
  21721. if (*ss1=='n') { // in#ind
  21722. if (!imglist) _cimg_mp_return(0);
  21723. if (_cimg_mp_is_const_scalar(arg1)) {
  21724. if (!list_norm) list_norm.assign(imglist._width);
  21725. if (!list_norm[p1]) CImg<doubleT>::vector(imglist[p1].magnitude(2)).move_to(list_norm[p1]);
  21726. _cimg_mp_const_scalar(*list_norm[p1]);
  21727. }
  21728. _cimg_mp_scalar1(mp_list_norm,arg1);
  21729. }
  21730. switch (*ss1) {
  21731. case 'a' : arg2 = 2; break; // ia#ind
  21732. case 'm' : arg2 = 0; break; // im#ind
  21733. case 'M' : arg2 = 1; break; // iM#ind
  21734. case 'p' : arg2 = 13; break; // ip#ind
  21735. case 's' : arg2 = 12; break; // is#ind
  21736. case 'v' : arg2 = 3; break; // iv#ind
  21737. }
  21738. } else if (*ss1=='m') switch (*ss) {
  21739. case 'x' : arg2 = 4; break; // xm#ind
  21740. case 'y' : arg2 = 5; break; // ym#ind
  21741. case 'z' : arg2 = 6; break; // zm#ind
  21742. case 'c' : arg2 = 7; break; // cm#ind
  21743. } else if (*ss1=='M') switch (*ss) {
  21744. case 'x' : arg2 = 8; break; // xM#ind
  21745. case 'y' : arg2 = 9; break; // yM#ind
  21746. case 'z' : arg2 = 10; break; // zM#ind
  21747. case 'c' : arg2 = 11; break; // cM#ind
  21748. }
  21749. if (arg2!=~0U) {
  21750. if (!imglist) _cimg_mp_return(0);
  21751. if (_cimg_mp_is_const_scalar(arg1)) {
  21752. if (!list_stats) list_stats.assign(imglist._width);
  21753. if (!list_stats[p1]) list_stats[p1].assign(1,14,1,1,0).fill(imglist[p1].get_stats(),false);
  21754. _cimg_mp_const_scalar(list_stats(p1,arg2));
  21755. }
  21756. _cimg_mp_scalar2(mp_list_stats,arg1,arg2);
  21757. }
  21758. }
  21759. if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='#' && ss4<se) { // whd#ind
  21760. arg1 = compile(ss4,se,depth1,0,block_flags);
  21761. _cimg_mp_check_notnan_index(arg1);
  21762. if (!imglist) _cimg_mp_return(0);
  21763. p1 = (unsigned int)(_cimg_mp_is_const_scalar(arg1)?cimg::mod((int)mem[arg1],imglist.width()):~0U);
  21764. if (p1!=~0U) _cimg_mp_const_scalar(imglist[p1]._width*imglist[p1]._height*imglist[p1]._depth);
  21765. _cimg_mp_scalar1(mp_list_whd,arg1);
  21766. }
  21767. if (*ss=='w' && *ss1=='h' && *ss2=='d' && *ss3=='s' && *ss4=='#' && ss5<se) { // whds#ind
  21768. arg1 = compile(ss5,se,depth1,0,block_flags);
  21769. _cimg_mp_check_notnan_index(arg1);
  21770. if (!imglist) _cimg_mp_return(0);
  21771. p1 = (unsigned int)(_cimg_mp_is_const_scalar(arg1)?cimg::mod((int)mem[arg1],imglist.width()):~0U);
  21772. if (p1!=~0U)
  21773. _cimg_mp_const_scalar(imglist[p1]._width*imglist[p1]._height*imglist[p1]._depth*imglist[p1]._spectrum);
  21774. _cimg_mp_scalar1(mp_list_whds,arg1);
  21775. }
  21776. if (!std::strcmp(ss,"interpolation")) _cimg_mp_return(_cimg_mp_interpolation); // interpolation
  21777. if (!std::strcmp(ss,"boundary")) _cimg_mp_return(_cimg_mp_boundary); // boundary
  21778. variable_name.assign(ss,(unsigned int)(se + 1 - ss)).back() = 0;
  21779. #ifdef cimg_mp_operator_dollar
  21780. if (*ss=='$' && ss1<se) // External variable '$varname'.
  21781. _cimg_mp_const_scalar(cimg_mp_operator_dollar(variable_name._data + 1));
  21782. #endif
  21783. // No known item found, assuming this is an already initialized variable.
  21784. if (cimg::is_varname(variable_name)) { // Valid variable name
  21785. get_variable_pos(variable_name,arg1,arg2);
  21786. arg1 = arg2!=~0U?reserved_label[arg2]:arg1!=~0U?variable_pos[arg1]:~0U;
  21787. if (arg1!=~0U) _cimg_mp_return(arg1);
  21788. }
  21789. // Reached an unknown item -> error.
  21790. c1 = *se1;
  21791. _cimg_mp_strerr;
  21792. cimg::strellipsize(variable_name,64);
  21793. if (is_sth)
  21794. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  21795. "CImg<%s>::%s: Undefined variable '%s' in expression '%s'.",
  21796. pixel_type(),_cimg_mp_calling_function,
  21797. variable_name._data,s0);
  21798. s1 = std::strchr(ss,'(');
  21799. s_op = s1 && c1==')'?"function call":"item";
  21800. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  21801. "CImg<%s>::%s: Unrecognized %s '%s' in expression '%s'.",
  21802. pixel_type(),_cimg_mp_calling_function,
  21803. s_op,variable_name._data,s0);
  21804. }
  21805. // Evaluation procedure.
  21806. double operator()(const double x, const double y, const double z, const double c) {
  21807. mem[_cimg_mp_slot_x] = x; mem[_cimg_mp_slot_y] = y; mem[_cimg_mp_slot_z] = z; mem[_cimg_mp_slot_c] = c;
  21808. for (p_code = code; p_code<p_code_end; ++p_code) {
  21809. opcode._data = p_code->_data;
  21810. const ulongT target = opcode[1];
  21811. mem[target] = _cimg_mp_defunc(*this);
  21812. }
  21813. return *result;
  21814. }
  21815. // Evaluation procedure (return output values in vector 'output').
  21816. template<typename t>
  21817. void operator()(const double x, const double y, const double z, const double c, t *const output) {
  21818. mem[_cimg_mp_slot_x] = x; mem[_cimg_mp_slot_y] = y; mem[_cimg_mp_slot_z] = z; mem[_cimg_mp_slot_c] = c;
  21819. for (p_code = code; p_code<p_code_end; ++p_code) {
  21820. opcode._data = p_code->_data;
  21821. const ulongT target = opcode[1];
  21822. mem[target] = _cimg_mp_defunc(*this);
  21823. }
  21824. if (result_dim) {
  21825. const double *ptrs = result + 1;
  21826. t *ptrd = output;
  21827. for (unsigned int k = 0; k<result_dim; ++k) *(ptrd++) = (t)*(ptrs++);
  21828. } else *output = (t)*result;
  21829. }
  21830. // Evaluation procedure for begin_t() bloc.
  21831. void begin_t() {
  21832. if (!code_begin_t) return;
  21833. mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
  21834. p_code_end = code_begin_t.end();
  21835. for (p_code = code_begin_t; p_code<p_code_end; ++p_code) {
  21836. opcode._data = p_code->_data;
  21837. const ulongT target = opcode[1];
  21838. mem[target] = _cimg_mp_defunc(*this);
  21839. }
  21840. p_code_end = code.end();
  21841. }
  21842. // Evaluation procedure for end_t() bloc.
  21843. void end_t() {
  21844. if (!code_end_t) return;
  21845. if (imgin) {
  21846. mem[_cimg_mp_slot_x] = imgin._width - 1.;
  21847. mem[_cimg_mp_slot_y] = imgin._height - 1.;
  21848. mem[_cimg_mp_slot_z] = imgin._depth - 1.;
  21849. mem[_cimg_mp_slot_c] = imgin._spectrum - 1.;
  21850. } else mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
  21851. p_code_end = code_end_t.end();
  21852. for (p_code = code_end_t; p_code<p_code_end; ++p_code) {
  21853. opcode._data = p_code->_data;
  21854. const ulongT target = opcode[1];
  21855. mem[target] = _cimg_mp_defunc(*this);
  21856. }
  21857. }
  21858. // Evaluation procedure the end() bloc.
  21859. void end() {
  21860. if (!code_end) return;
  21861. if (imgin) {
  21862. mem[_cimg_mp_slot_x] = imgin._width - 1.;
  21863. mem[_cimg_mp_slot_y] = imgin._height - 1.;
  21864. mem[_cimg_mp_slot_z] = imgin._depth - 1.;
  21865. mem[_cimg_mp_slot_c] = imgin._spectrum - 1.;
  21866. } else mem[_cimg_mp_slot_x] = mem[_cimg_mp_slot_y] = mem[_cimg_mp_slot_z] = mem[_cimg_mp_slot_c] = 0;
  21867. p_code_end = code_end.end();
  21868. for (p_code = code_end; p_code<p_code_end; ++p_code) {
  21869. opcode._data = p_code->_data;
  21870. const ulongT target = opcode[1];
  21871. mem[target] = _cimg_mp_defunc(*this);
  21872. }
  21873. }
  21874. // Merge inter-thread variables.
  21875. // (argument 'mp' is the master instance).
  21876. void merge(_cimg_math_parser& mp) {
  21877. if (&mp==this) return;
  21878. cimg_rofY(mp.memmerge,k) {
  21879. const unsigned int
  21880. pos = (unsigned int)mp.memmerge(0,k),
  21881. siz = (unsigned int)mp.memmerge(1,k),
  21882. iop = (unsigned int)mp.memmerge(2,k);
  21883. if (!siz) switch (iop) { // Scalar value
  21884. case 0 : mp.mem[pos] = mem[pos]; break; // Assignment
  21885. case 1 : mp.mem[pos]+=mem[pos]; break; // Operator+
  21886. case 2 : mp.mem[pos]-=mem[pos]; break; // Operator-
  21887. case 3 : mp.mem[pos]*=mem[pos]; break; // Operator*
  21888. case 4 : mp.mem[pos]/=mem[pos]; break; // Operator/
  21889. case 5 : mp.mem[pos] = (double)((longT)mp.mem[pos] & (longT)mem[pos]); break; // Operator&
  21890. case 6 : mp.mem[pos] = (double)((longT)mp.mem[pos] | (longT)mem[pos]); break; // Operator|
  21891. case 7 : mp.mem[pos] = (double)((longT)mp.mem[pos] ^ (longT)mem[pos]); break; // Operator 'xor'
  21892. case 8 : mp.mem[pos] = mp.mem[pos] && mem[pos]; break; // Operator&&
  21893. case 9 : mp.mem[pos] = mp.mem[pos] || mem[pos]; break; // Operator||
  21894. case 10 : mp.mem[pos] = std::min(mp.mem[pos],mem[pos]); break; // Operator 'min'
  21895. case 11 : mp.mem[pos] = std::max(mp.mem[pos],mem[pos]); break; // Operator 'max'
  21896. } else switch (iop) { // Vector value
  21897. case 0 : // Assignment
  21898. CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true) = CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
  21899. break;
  21900. case 1 : // Operator+
  21901. CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)+=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
  21902. break;
  21903. case 2 : // Operator-
  21904. CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)-=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
  21905. break;
  21906. case 3 : // Operator*
  21907. CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)*=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
  21908. break;
  21909. case 4 : // Operator/
  21910. CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)/=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
  21911. break;
  21912. case 5 : // Operator&
  21913. CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)&=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
  21914. break;
  21915. case 6 : // Operator|
  21916. CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)|=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
  21917. break;
  21918. case 7 : // Operator 'xor'
  21919. CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true)^=CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true);
  21920. break;
  21921. case 8 : { // Operator&&
  21922. CImg<doubleT>
  21923. arg1(&mp.mem[pos + 1],siz,1,1,1,true),
  21924. arg2(&mem[pos + 1],siz,1,1,1,true);
  21925. cimg_foroff(arg1,off) arg1[off] = arg1[off] && arg2[off];
  21926. } break;
  21927. case 9 : { // Operator||
  21928. CImg<doubleT>
  21929. arg1(&mp.mem[pos + 1],siz,1,1,1,true),
  21930. arg2(&mem[pos + 1],siz,1,1,1,true);
  21931. cimg_foroff(arg1,off) arg1[off] = arg1[off] || arg2[off];
  21932. } break;
  21933. case 10 : // Operator 'min'
  21934. CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true).min(CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true));
  21935. break;
  21936. case 11 : // Operator 'max'
  21937. CImg<doubleT>(&mp.mem[pos + 1],siz,1,1,1,true).max(CImg<doubleT>(&mem[pos + 1],siz,1,1,1,true));
  21938. break;
  21939. }
  21940. }
  21941. }
  21942. // Return specified argument number as a string.
  21943. static const char *s_argth(const unsigned int n_arg) {
  21944. const char
  21945. *_s_arg[] = { "", "First", "Second", "Third", "Fourth", "Fifth", "Sixth", "Seventh", "Eighth","Ninth",
  21946. "10th", "11th", "12th", "13th", "14th", "15th", "16th", "17th", "18th", "19th",
  21947. "20th", "21st", "22nd", "23rd", "24th", "25th", "26th", "27th", "28th", "One of the" };
  21948. return _s_arg[n_arg<sizeof(_s_arg)/sizeof(char*)?n_arg:sizeof(_s_arg)/sizeof(char*)-1];
  21949. }
  21950. // Return a string that defines the calling function + the user-defined function scope.
  21951. CImg<charT> s_calling_function() const {
  21952. CImg<charT> res;
  21953. const unsigned int
  21954. l1 = calling_function?(unsigned int)std::strlen(calling_function):0U,
  21955. l2 = user_macro?(unsigned int)std::strlen(user_macro):0U;
  21956. if (l2) {
  21957. res.assign(l1 + l2 + 48);
  21958. cimg_snprintf(res,res._width,"%s(): When substituting function '%s()'",calling_function,user_macro);
  21959. } else {
  21960. res.assign(l1 + l2 + 4);
  21961. cimg_snprintf(res,res._width,"%s()",calling_function);
  21962. }
  21963. return res;
  21964. }
  21965. // Return type of a memory slot as a string.
  21966. CImg<charT> s_type(const unsigned int arg) const {
  21967. CImg<charT> res;
  21968. if (_cimg_mp_is_vector(arg)) { // Vector
  21969. CImg<charT>::string("vectorXXXXXXXXXXXXXXXX").move_to(res);
  21970. cimg_snprintf(res._data + 6,res._width - 6,"%u",_cimg_mp_size(arg));
  21971. } else if (_cimg_mp_is_const_scalar(arg)) CImg<charT>::string("const scalar").move_to(res); // Const scalar
  21972. else CImg<charT>::string("scalar").move_to(res); // Scalar
  21973. return res;
  21974. }
  21975. // Return reference state of a memory slot as a string.
  21976. CImg<charT> s_ref(const unsigned int *const p_ref) const {
  21977. CImg<charT> res;
  21978. if (!p_ref || !*p_ref) return res.assign(1,1,1,1,0);
  21979. res.assign(32);
  21980. switch (p_ref[0]) {
  21981. case 1 : // Reference to vector value as a scalar
  21982. cimg_snprintf(res,res._width,", ref: ([%u])[%u]",
  21983. p_ref[1],p_ref[2]);
  21984. break;
  21985. case 2 : // Reference to image value as a scalar (offset)
  21986. if (p_ref[1]==~0U)
  21987. cimg_snprintf(res,res._width,", ref: %c[%u]",
  21988. p_ref[2]?'j':'i',p_ref[3]);
  21989. else
  21990. cimg_snprintf(res,res._width,", ref: %c[#%u,%u]",
  21991. p_ref[2]?'j':'i',p_ref[1],p_ref[3]);
  21992. break;
  21993. case 3 : // Reference to image value as a scalar (coordinates)
  21994. if (p_ref[1]==~0U)
  21995. cimg_snprintf(res,res._width,", ref: %c(%u,%u,%u,%u)",
  21996. p_ref[2]?'j':'i',p_ref[3],p_ref[4],p_ref[5],p_ref[6]);
  21997. else
  21998. cimg_snprintf(res,res._width,", ref: %c(#%u,%u,%u,%u,%u)",
  21999. p_ref[2]?'j':'i',p_ref[1],p_ref[3],p_ref[4],p_ref[5],p_ref[6]);
  22000. break;
  22001. case 4 : // Reference to image value as a vector (offset)
  22002. if (p_ref[1]==~0U)
  22003. cimg_snprintf(res,res._width,", ref: %c[%u]",
  22004. p_ref[2]?'J':'I',p_ref[3]);
  22005. else
  22006. cimg_snprintf(res,res._width,", ref: %c[#%u,%u]",
  22007. p_ref[2]?'J':'I',p_ref[1],p_ref[3]);
  22008. break;
  22009. case 5 : // Reference to image value as a vector (coordinates)
  22010. if (p_ref[1]==~0U)
  22011. cimg_snprintf(res,res._width,", ref: %c(%u,%u,%u)",
  22012. p_ref[2]?'J':'I',p_ref[3],p_ref[4],p_ref[5]);
  22013. else
  22014. cimg_snprintf(res,res._width,", ref: %c(#%u,%u,%u,%u)",
  22015. p_ref[2]?'J':'I',p_ref[1],p_ref[3],p_ref[4],p_ref[5]);
  22016. break;
  22017. }
  22018. return res;
  22019. }
  22020. // Count parentheses/brackets level of each character of the expression.
  22021. CImg<uintT> get_level(CImg<charT>& _expr) const {
  22022. bool is_escaped = false, next_is_escaped = false;
  22023. unsigned int mode = 0, next_mode = 0; // { 0=normal | 1=char-string | 2=vector-string
  22024. CImg<uintT> res(_expr._width - 1);
  22025. unsigned int *pd = res._data;
  22026. int _level = 0;
  22027. for (const char *ps = _expr._data; *ps && _level>=0; ++ps) {
  22028. if (!is_escaped && !next_is_escaped && *ps=='\\') next_is_escaped = true;
  22029. if (!is_escaped && *ps=='\'') { // Non-escaped character
  22030. if (!mode && ps>_expr._data && *(ps - 1)=='[') next_mode = mode = 2; // Start vector-string
  22031. else if (mode==2 && *(ps + 1)==']') next_mode = !mode; // End vector-string
  22032. else if (mode<2) next_mode = mode?(mode = 0):1; // Start/end char-string
  22033. }
  22034. *(pd++) = (unsigned int)(mode>=1 || is_escaped?_level + (mode==1):
  22035. *ps=='(' || *ps=='['?_level++:
  22036. *ps==')' || *ps==']'?--_level:
  22037. _level);
  22038. mode = next_mode;
  22039. is_escaped = next_is_escaped;
  22040. next_is_escaped = false;
  22041. }
  22042. if (mode) {
  22043. cimg::strellipsize(_expr,64);
  22044. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  22045. "CImg<%s>::%s: Unterminated string literal, in expression '%s'.",
  22046. pixel_type(),_cimg_mp_calling_function,
  22047. _expr._data);
  22048. }
  22049. if (_level) {
  22050. cimg::strellipsize(_expr,64);
  22051. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  22052. "CImg<%s>::%s: Unbalanced parentheses/brackets, in expression '%s'.",
  22053. pixel_type(),_cimg_mp_calling_function,
  22054. _expr._data);
  22055. }
  22056. return res;
  22057. }
  22058. // Find and return index of current image 'imgin' within image list 'imglist'.
  22059. unsigned int get_mem_img_index() {
  22060. if (mem_img_index==~0U) {
  22061. if (&imgout>imglist.data() && &imgout<imglist.end())
  22062. mem_img_index = const_scalar((double)(&imgout - imglist.data()));
  22063. else {
  22064. unsigned int pos = ~0U;
  22065. cimglist_for(imglist,l)
  22066. if (imgout._data==imglist[l]._data && imgout.is_sameXYZC(imglist[l])) { pos = l; break; }
  22067. if (pos!=~0U) mem_img_index = const_scalar((double)pos);
  22068. }
  22069. }
  22070. return mem_img_index;
  22071. }
  22072. // Return indices for accessing math parser variables.
  22073. void get_variable_pos(const char *variable_name, unsigned int &pos, unsigned int &rpos) {
  22074. char c1, c2, c3, c4;
  22075. pos = rpos = ~0U;
  22076. if (!variable_name || !*variable_name) return;
  22077. unsigned int rp = variable_name[1]?~0U:*variable_name; // One-char variable
  22078. if (variable_name[1] && !variable_name[2]) { // Two-chars variable
  22079. c1 = variable_name[0];
  22080. c2 = variable_name[1];
  22081. if (c1=='w' && c2=='h') rp = 0; // wh
  22082. else if (c1=='p' && c2=='i') rp = 3; // pi
  22083. else if (c1=='i') {
  22084. if (c2>='0' && c2<='9') rp = 21 + c2 - '0'; // i0...i9
  22085. else if (c2=='m') rp = 4; // im
  22086. else if (c2=='M') rp = 5; // iM
  22087. else if (c2=='a') rp = 6; // ia
  22088. else if (c2=='v') rp = 7; // iv
  22089. else if (c2=='d') rp = 8; // id
  22090. else if (c2=='s') rp = 9; // is
  22091. else if (c2=='p') rp = 10; // ip
  22092. else if (c2=='c') rp = 11; // ic
  22093. else if (c2=='n') rp = 12; // in
  22094. } else if (c2=='m') {
  22095. if (c1=='x') rp = 13; // xm
  22096. else if (c1=='y') rp = 14; // ym
  22097. else if (c1=='z') rp = 15; // zm
  22098. else if (c1=='c') rp = 16; // cm
  22099. } else if (c2=='M') {
  22100. if (c1=='x') rp = 17; // xM
  22101. else if (c1=='y') rp = 18; // yM
  22102. else if (c1=='z') rp = 19; // zM
  22103. else if (c1=='c') rp = 20; // cM
  22104. }
  22105. } else if (variable_name[1] && variable_name[2] && !variable_name[3]) { // Three-chars variable
  22106. c1 = variable_name[0];
  22107. c2 = variable_name[1];
  22108. c3 = variable_name[2];
  22109. if (c1=='w' && c2=='h' && c3=='d') rp = 1; // whd
  22110. } else if (variable_name[1] && variable_name[2] && variable_name[3] &&
  22111. !variable_name[4]) { // Four-chars variable
  22112. c1 = variable_name[0];
  22113. c2 = variable_name[1];
  22114. c3 = variable_name[2];
  22115. c4 = variable_name[3];
  22116. if (c1=='w' && c2=='h' && c3=='d' && c4=='s') rp = 2; // whds
  22117. } else if (!std::strcmp(variable_name,"interpolation")) rp = 31; // interpolation
  22118. else if (!std::strcmp(variable_name,"boundary")) rp = 32; // boundary
  22119. if (rp!=~0U) { rpos = rp; return; } // One of the reserved labels
  22120. // Multi-char variable name : check for existing variable with same name
  22121. cimglist_for(variable_def,i)
  22122. if (!std::strcmp(variable_name,variable_def[i])) { pos = i; break; }
  22123. }
  22124. // Return true if all values of a vector are computation values.
  22125. bool is_comp_vector(const unsigned int arg) const {
  22126. unsigned int siz = _cimg_mp_size(arg);
  22127. if (siz>128) return false;
  22128. const int *ptr = memtype.data(arg + 1);
  22129. bool is_tmp = true;
  22130. while (siz-->0) if (*(ptr++)) { is_tmp = false; break; }
  22131. return is_tmp;
  22132. }
  22133. // Check if a memory slot is a positive integer constant scalar value.
  22134. // 'mode' can be:
  22135. // { 0=constant | 1=integer constant | 2=positive integer constant | 3=strictly-positive integer constant }
  22136. void check_const_scalar(const unsigned int arg, const unsigned int n_arg,
  22137. const unsigned int mode,
  22138. char *const ss, char *const se, const char saved_char) {
  22139. _cimg_mp_check_type(arg,n_arg,1,0);
  22140. if (!_cimg_mp_is_const_scalar(arg)) {
  22141. const char *const s_arg = s_argth(n_arg);
  22142. char *s0; _cimg_mp_strerr;
  22143. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  22144. "CImg<%s>::%s: %s%s %s%s (of type '%s') is not a constant, "
  22145. "in expression '%s'.",
  22146. pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
  22147. s_arg,*s_arg?" argument":" Argument",s_type(arg)._data,s0);
  22148. }
  22149. const double val = mem[arg];
  22150. if (!((!mode || (double)(int)mem[arg]==mem[arg]) &&
  22151. (mode<2 || mem[arg]>=(mode==3)))) {
  22152. const char *const s_arg = s_argth(n_arg);
  22153. char *s0; _cimg_mp_strerr;
  22154. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  22155. "CImg<%s>::%s: %s%s %s%s (of type '%s' and value %g) is not a%s constant, "
  22156. "in expression '%s'.",
  22157. pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
  22158. s_arg,*s_arg?" argument":" Argument",s_type(arg)._data,val,
  22159. !mode?"":mode==1?"n integer":
  22160. mode==2?" positive integer":" strictly positive integer",s0);
  22161. }
  22162. }
  22163. // Check if an image index is a constant value.
  22164. void check_const_index(const unsigned int arg,
  22165. char *const ss, char *const se, const char saved_char) {
  22166. if (arg!=~0U && !_cimg_mp_is_const_scalar(arg)) {
  22167. char *s0; _cimg_mp_strerr;
  22168. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  22169. "CImg<%s>::%s: %s%s Specified image index is not a constant, "
  22170. "in expression '%s'.",
  22171. pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",s0);
  22172. }
  22173. }
  22174. // Check that specified constant is not nan.
  22175. void check_notnan_index(const unsigned int arg,
  22176. char *const ss, char *const se, const char saved_char) {
  22177. if (arg!=~0U &&
  22178. (arg==_cimg_mp_slot_nan || (_cimg_mp_is_const_scalar(arg) && cimg::type<double>::is_nan(mem[arg])))) {
  22179. char *s0; _cimg_mp_strerr;
  22180. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  22181. "CImg<%s>::%s: %s%s Specified index is NaN.",
  22182. pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"");
  22183. }
  22184. }
  22185. // Check a matrix is square.
  22186. void check_matrix_square(const unsigned int arg, const unsigned int n_arg,
  22187. char *const ss, char *const se, const char saved_char) {
  22188. _cimg_mp_check_type(arg,n_arg,2,0);
  22189. const unsigned int
  22190. siz = _cimg_mp_size(arg),
  22191. n = (unsigned int)cimg::round(std::sqrt((float)siz));
  22192. if (n*n!=siz) {
  22193. const char *s_arg;
  22194. if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand":"Right-hand";
  22195. else s_arg = !n_arg?"":n_arg==1?"First":n_arg==2?"Second":n_arg==3?"Third":"One";
  22196. char *s0; _cimg_mp_strerr;
  22197. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  22198. "CImg<%s>::%s: %s%s %s%s (of type '%s') "
  22199. "cannot be considered as a square matrix, in expression '%s'.",
  22200. pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
  22201. s_arg,*s_op=='F'?(*s_arg?" argument":" Argument"):(*s_arg?" operand":" Operand"),
  22202. s_type(arg)._data,s0);
  22203. }
  22204. }
  22205. // Check type compatibility for one argument.
  22206. // Bits of 'mode' tells what types are allowed:
  22207. // { 1 = scalar | 2 = vectorN }.
  22208. // If 'N' is not zero, it also restricts the vectors to be of size N only.
  22209. void check_type(const unsigned int arg, const unsigned int n_arg,
  22210. const unsigned int mode, const unsigned int N,
  22211. char *const ss, char *const se, const char saved_char) {
  22212. const bool
  22213. is_scalar = _cimg_mp_is_scalar(arg),
  22214. is_vector = _cimg_mp_is_vector(arg) && (!N || _cimg_mp_size(arg)==N);
  22215. bool cond = false;
  22216. if (mode&1) cond|=is_scalar;
  22217. if (mode&2) cond|=is_vector;
  22218. if (!cond) {
  22219. const char *s_arg;
  22220. if (*s_op!='F') s_arg = !n_arg?"":n_arg==1?"Left-hand":"Right-hand";
  22221. else s_arg = s_argth(n_arg);
  22222. CImg<charT> sb_type(32);
  22223. if (mode==1) cimg_snprintf(sb_type,sb_type._width,"'scalar'");
  22224. else if (mode==2) {
  22225. if (N) cimg_snprintf(sb_type,sb_type._width,"'vector%u'",N);
  22226. else cimg_snprintf(sb_type,sb_type._width,"'vector'");
  22227. } else {
  22228. if (N) cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector%u'",N);
  22229. else cimg_snprintf(sb_type,sb_type._width,"'scalar' or 'vector'");
  22230. }
  22231. char *s0; _cimg_mp_strerr;
  22232. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  22233. "CImg<%s>::%s: %s%s %s%s has invalid type '%s' (should be %s), "
  22234. "in expression '%s'.",
  22235. pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",
  22236. s_arg,*s_op=='F'?(*s_arg?" argument":" Argument"):(*s_arg?" operand":" Operand"),
  22237. s_type(arg)._data,sb_type._data,s0);
  22238. }
  22239. }
  22240. // Check that imglist are not empty.
  22241. void check_list(char *const ss, char *const se, const char saved_char) {
  22242. if (!imglist) {
  22243. char *s0; _cimg_mp_strerr;
  22244. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  22245. "CImg<%s>::%s: %s%s Image list cannot be empty, for expression '%s'.",
  22246. pixel_type(),_cimg_mp_calling_function,s_op,*s_op?":":"",s0);
  22247. }
  22248. }
  22249. static void mp_check_list(_cimg_math_parser& mp, const char *const funcname) {
  22250. if (!mp.imglist)
  22251. throw CImgArgumentException("[" cimg_appname "_math_parser] "
  22252. "CImg<%s>: Function '%s()': Images list cannot be empty.",
  22253. pixel_type(),funcname);
  22254. }
  22255. // Insert constant value in memory.
  22256. unsigned int const_scalar(const double val) {
  22257. // Search for built-in constant.
  22258. if (cimg::type<double>::is_nan(val)) return _cimg_mp_slot_nan;
  22259. if (val==(double)(int)val) {
  22260. if (val>=0 && val<=10) return (unsigned int)val;
  22261. if (val<0 && val>=-5) return (unsigned int)(10 - val);
  22262. }
  22263. if (val==0.5) return 16;
  22264. // Search for constant already requested before (in const cache).
  22265. unsigned int ind = ~0U;
  22266. if (constcache_size<1024) {
  22267. if (!constcache_size) {
  22268. constcache_vals.assign(16,1,1,1,0);
  22269. constcache_inds.assign(16,1,1,1,0);
  22270. *constcache_vals = val;
  22271. constcache_size = 1;
  22272. ind = 0;
  22273. } else { // Dichotomic search
  22274. const double val_beg = *constcache_vals, val_end = constcache_vals[constcache_size - 1];
  22275. if (val_beg>=val) ind = 0;
  22276. else if (val_end==val) ind = constcache_size - 1;
  22277. else if (val_end<val) ind = constcache_size;
  22278. else {
  22279. unsigned int i0 = 1, i1 = constcache_size - 2;
  22280. while (i0<=i1) {
  22281. const unsigned int mid = (i0 + i1)/2;
  22282. if (constcache_vals[mid]==val) { i0 = mid; break; }
  22283. else if (constcache_vals[mid]<val) i0 = mid + 1;
  22284. else i1 = mid - 1;
  22285. }
  22286. ind = i0;
  22287. }
  22288. if (ind>=constcache_size || constcache_vals[ind]!=val) {
  22289. ++constcache_size;
  22290. if (constcache_size>constcache_vals._width) {
  22291. constcache_vals.resize(-200,1,1,1,0);
  22292. constcache_inds.resize(-200,1,1,1,0);
  22293. }
  22294. const int l = constcache_size - (int)ind - 1;
  22295. if (l>0) {
  22296. std::memmove(&constcache_vals[ind + 1],&constcache_vals[ind],l*sizeof(double));
  22297. std::memmove(&constcache_inds[ind + 1],&constcache_inds[ind],l*sizeof(unsigned int));
  22298. }
  22299. constcache_vals[ind] = val;
  22300. constcache_inds[ind] = 0;
  22301. }
  22302. }
  22303. if (constcache_inds[ind]) return constcache_inds[ind];
  22304. }
  22305. // Insert new constant in memory if necessary.
  22306. if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(-200,1,1,1,0); }
  22307. const unsigned int pos = mempos++;
  22308. mem[pos] = val;
  22309. memtype[pos] = 1; // Set constant property
  22310. if (ind!=~0U) constcache_inds[ind] = pos;
  22311. return pos;
  22312. }
  22313. // Insert new scalar in memory.
  22314. unsigned int scalar() {
  22315. if (mempos>=mem._width) { mem.resize(-200,1,1,1,0); memtype.resize(mem._width,1,1,1,0); }
  22316. return mempos++;
  22317. }
  22318. // Insert new vector of specified size in memory.
  22319. unsigned int vector(const unsigned int siz) {
  22320. if (mempos + siz>=mem._width) {
  22321. mem.resize(2*mem._width + siz,1,1,1,0);
  22322. memtype.resize(mem._width,1,1,1,0);
  22323. }
  22324. const unsigned int pos = mempos++;
  22325. mem[pos] = cimg::type<double>::nan();
  22326. memtype[pos] = siz + 1;
  22327. mempos+=siz;
  22328. return pos;
  22329. }
  22330. // Insert new initialized vector.
  22331. unsigned int vector(const unsigned int siz, const double value) {
  22332. const unsigned int pos = vector(siz);
  22333. double *ptr = &mem[pos] + 1;
  22334. for (unsigned int i = 0; i<siz; ++i) *(ptr++) = value;
  22335. return pos;
  22336. }
  22337. // Insert new copy of specified vector in memory.
  22338. unsigned int vector_copy(const unsigned int arg) {
  22339. const unsigned int
  22340. siz = _cimg_mp_size(arg),
  22341. pos = vector(siz);
  22342. CImg<ulongT>::vector((ulongT)mp_vector_copy,pos,arg,siz).move_to(code);
  22343. return pos;
  22344. }
  22345. // Set reserved status to all values of a vector.
  22346. void set_reserved_vector(const unsigned int arg) {
  22347. unsigned int siz = _cimg_mp_size(arg);
  22348. int *ptr = memtype.data(arg + 1);
  22349. while (siz-->0) *(ptr++) = -1;
  22350. }
  22351. unsigned int scalar0(const mp_func op) {
  22352. const unsigned int pos = scalar();
  22353. CImg<ulongT>::vector((ulongT)op,pos).move_to(code);
  22354. return_new_comp = true;
  22355. return pos;
  22356. }
  22357. unsigned int scalar1(const mp_func op, const unsigned int arg1) {
  22358. const unsigned int pos =
  22359. arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1) && op!=mp_copy?arg1:
  22360. ((return_new_comp = true), scalar());
  22361. CImg<ulongT>::vector((ulongT)op,pos,arg1).move_to(code);
  22362. return pos;
  22363. }
  22364. unsigned int scalar2(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
  22365. const unsigned int pos =
  22366. arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
  22367. arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
  22368. ((return_new_comp = true), scalar());
  22369. CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2).move_to(code);
  22370. return pos;
  22371. }
  22372. unsigned int scalar3(const mp_func op,
  22373. const unsigned int arg1, const unsigned int arg2, const unsigned int arg3) {
  22374. const unsigned int pos =
  22375. arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
  22376. arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
  22377. arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
  22378. ((return_new_comp = true), scalar());
  22379. CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3).move_to(code);
  22380. return pos;
  22381. }
  22382. unsigned int scalar4(const mp_func op,
  22383. const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
  22384. const unsigned int arg4) {
  22385. const unsigned int pos =
  22386. arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
  22387. arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
  22388. arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
  22389. arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
  22390. ((return_new_comp = true), scalar());
  22391. CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4).move_to(code);
  22392. return pos;
  22393. }
  22394. unsigned int scalar5(const mp_func op,
  22395. const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
  22396. const unsigned int arg4, const unsigned int arg5) {
  22397. const unsigned int pos =
  22398. arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
  22399. arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
  22400. arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
  22401. arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
  22402. arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:
  22403. ((return_new_comp = true), scalar());
  22404. CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5).move_to(code);
  22405. return pos;
  22406. }
  22407. unsigned int scalar6(const mp_func op,
  22408. const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
  22409. const unsigned int arg4, const unsigned int arg5, const unsigned int arg6) {
  22410. const unsigned int pos =
  22411. arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
  22412. arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
  22413. arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
  22414. arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
  22415. arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:
  22416. arg6!=~0U && arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6:
  22417. ((return_new_comp = true), scalar());
  22418. CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6).move_to(code);
  22419. return pos;
  22420. }
  22421. unsigned int scalar7(const mp_func op,
  22422. const unsigned int arg1, const unsigned int arg2, const unsigned int arg3,
  22423. const unsigned int arg4, const unsigned int arg5, const unsigned int arg6,
  22424. const unsigned int arg7) {
  22425. const unsigned int pos =
  22426. arg1!=~0U && arg1>_cimg_mp_slot_c && _cimg_mp_is_comp(arg1)?arg1:
  22427. arg2!=~0U && arg2>_cimg_mp_slot_c && _cimg_mp_is_comp(arg2)?arg2:
  22428. arg3!=~0U && arg3>_cimg_mp_slot_c && _cimg_mp_is_comp(arg3)?arg3:
  22429. arg4!=~0U && arg4>_cimg_mp_slot_c && _cimg_mp_is_comp(arg4)?arg4:
  22430. arg5!=~0U && arg5>_cimg_mp_slot_c && _cimg_mp_is_comp(arg5)?arg5:
  22431. arg6!=~0U && arg6>_cimg_mp_slot_c && _cimg_mp_is_comp(arg6)?arg6:
  22432. arg7!=~0U && arg7>_cimg_mp_slot_c && _cimg_mp_is_comp(arg7)?arg7:
  22433. ((return_new_comp = true), scalar());
  22434. CImg<ulongT>::vector((ulongT)op,pos,arg1,arg2,arg3,arg4,arg5,arg6,arg7).move_to(code);
  22435. return pos;
  22436. }
  22437. void self_vector_s(const unsigned int pos, const mp_func op, const unsigned int arg1) {
  22438. const unsigned int siz = _cimg_mp_size(pos);
  22439. if (siz>24) CImg<ulongT>::vector((ulongT)mp_self_map_vector_s,pos,siz,(ulongT)op,arg1).move_to(code);
  22440. else {
  22441. code.insert(siz);
  22442. for (unsigned int k = 1; k<=siz; ++k)
  22443. CImg<ulongT>::vector((ulongT)op,pos + k,arg1).move_to(code[code._width - 1 - siz + k]);
  22444. }
  22445. }
  22446. void self_vector_v(const unsigned int pos, const mp_func op, const unsigned int arg1) {
  22447. const unsigned int siz = _cimg_mp_size(pos);
  22448. if (siz>24) CImg<ulongT>::vector((ulongT)mp_self_map_vector_v,pos,siz,(ulongT)op,arg1).move_to(code);
  22449. else {
  22450. code.insert(siz);
  22451. for (unsigned int k = 1; k<=siz; ++k)
  22452. CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]);
  22453. }
  22454. }
  22455. unsigned int vector1_v(const mp_func op, const unsigned int arg1) {
  22456. const unsigned int
  22457. siz = _cimg_mp_size(arg1),
  22458. pos = is_comp_vector(arg1)?arg1:
  22459. ((return_new_comp = true), vector(siz));
  22460. if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_v,pos,1,siz,(ulongT)op,arg1).move_to(code);
  22461. else {
  22462. code.insert(siz);
  22463. for (unsigned int k = 1; k<=siz; ++k)
  22464. CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k).move_to(code[code._width - 1 - siz + k]);
  22465. }
  22466. return pos;
  22467. }
  22468. unsigned int vector2_vv(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
  22469. const unsigned int
  22470. siz = _cimg_mp_size(arg1),
  22471. pos = is_comp_vector(arg1)?arg1:is_comp_vector(arg2)?arg2:
  22472. ((return_new_comp = true), vector(siz));
  22473. if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_vv,pos,2,siz,(ulongT)op,arg1,arg2).move_to(code);
  22474. else {
  22475. code.insert(siz);
  22476. for (unsigned int k = 1; k<=siz; ++k)
  22477. CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2 + k).move_to(code[code._width - 1 - siz + k]);
  22478. }
  22479. return pos;
  22480. }
  22481. unsigned int vector2_vs(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
  22482. const unsigned int
  22483. siz = _cimg_mp_size(arg1),
  22484. pos = is_comp_vector(arg1)?arg1:
  22485. ((return_new_comp = true), vector(siz));
  22486. if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_v,pos,2,siz,(ulongT)op,arg1,arg2).move_to(code);
  22487. else {
  22488. code.insert(siz);
  22489. for (unsigned int k = 1; k<=siz; ++k)
  22490. CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2).move_to(code[code._width - 1 - siz + k]);
  22491. }
  22492. return pos;
  22493. }
  22494. unsigned int vector2_sv(const mp_func op, const unsigned int arg1, const unsigned int arg2) {
  22495. const unsigned int
  22496. siz = _cimg_mp_size(arg2),
  22497. pos = is_comp_vector(arg2)?arg2:
  22498. ((return_new_comp = true), vector(siz));
  22499. if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_sv,pos,2,siz,(ulongT)op,arg1,arg2).move_to(code);
  22500. else {
  22501. code.insert(siz);
  22502. for (unsigned int k = 1; k<=siz; ++k)
  22503. CImg<ulongT>::vector((ulongT)op,pos + k,arg1,arg2 + k).move_to(code[code._width - 1 - siz + k]);
  22504. }
  22505. return pos;
  22506. }
  22507. unsigned int vector3_vss(const mp_func op, const unsigned int arg1, const unsigned int arg2,
  22508. const unsigned int arg3) {
  22509. const unsigned int
  22510. siz = _cimg_mp_size(arg1),
  22511. pos = is_comp_vector(arg1)?arg1:
  22512. ((return_new_comp = true), vector(siz));
  22513. if (siz>24) CImg<ulongT>::vector((ulongT)mp_vector_map_v,pos,3,siz,(ulongT)op,arg1,arg2,arg3).move_to(code);
  22514. else {
  22515. code.insert(siz);
  22516. for (unsigned int k = 1; k<=siz; ++k)
  22517. CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2,arg3).move_to(code[code._width - 1 - siz + k]);
  22518. }
  22519. return pos;
  22520. }
  22521. unsigned int vector4_vvss(const mp_func op, const unsigned int arg1, const unsigned int arg2,
  22522. const unsigned int arg3, const unsigned int arg4) {
  22523. const unsigned int
  22524. siz = _cimg_mp_size(arg1),
  22525. pos = is_comp_vector(arg1)?arg1:is_comp_vector(arg2)?arg2:
  22526. ((return_new_comp = true), vector(siz));
  22527. if (siz>24)
  22528. CImg<ulongT>::vector((ulongT)mp_vector_map_vv,pos,4,siz,(ulongT)op,arg1,arg2,arg3,arg4).move_to(code);
  22529. else {
  22530. code.insert(siz);
  22531. for (unsigned int k = 1; k<=siz; ++k)
  22532. CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2 + k,arg3,arg4).
  22533. move_to(code[code._width - 1 - siz + k]);
  22534. }
  22535. return pos;
  22536. }
  22537. unsigned int vector4_vsss(const mp_func op, const unsigned int arg1, const unsigned int arg2,
  22538. const unsigned int arg3, const unsigned int arg4) {
  22539. const unsigned int
  22540. siz = _cimg_mp_size(arg1),
  22541. pos = is_comp_vector(arg1)?arg1:
  22542. ((return_new_comp = true), vector(siz));
  22543. if (siz>24)
  22544. CImg<ulongT>::vector((ulongT)mp_vector_map_v,pos,4,siz,(ulongT)op,arg1,arg2,arg3,arg4).move_to(code);
  22545. else {
  22546. code.insert(siz);
  22547. for (unsigned int k = 1; k<=siz; ++k)
  22548. CImg<ulongT>::vector((ulongT)op,pos + k,arg1 + k,arg2,arg3,arg4).
  22549. move_to(code[code._width - 1 - siz + k]);
  22550. }
  22551. return pos;
  22552. }
  22553. unsigned int vector4_svss(const mp_func op, const unsigned int arg1, const unsigned int arg2,
  22554. const unsigned int arg3, const unsigned int arg4) {
  22555. const unsigned int
  22556. siz = _cimg_mp_size(arg2),
  22557. pos = is_comp_vector(arg2)?arg2:
  22558. ((return_new_comp = true), vector(siz));
  22559. if (siz>24)
  22560. CImg<ulongT>::vector((ulongT)mp_vector_map_sv,pos,4,siz,(ulongT)op,arg1,arg2,arg3,arg4).move_to(code);
  22561. else {
  22562. code.insert(siz);
  22563. for (unsigned int k = 1; k<=siz; ++k)
  22564. CImg<ulongT>::vector((ulongT)op,pos + k,arg1,arg2 + k,arg3,arg4).
  22565. move_to(code[code._width - 1 - siz + k]);
  22566. }
  22567. return pos;
  22568. }
  22569. // Evaluation functions, known by the parser.
  22570. // Defining these functions 'static' ensures that sizeof(mp_func)==sizeof(ulongT),
  22571. // so we can store pointers to them directly in the opcode vectors.
  22572. #ifdef _mp_arg
  22573. #undef _mp_arg
  22574. #endif
  22575. #define _mp_arg(x) mp.mem[mp.opcode[x]]
  22576. #ifdef cimg_mp_func_abort
  22577. static double mp_abort(_cimg_math_parser& mp) {
  22578. cimg::unused(mp);
  22579. cimg_mp_func_abort();
  22580. return cimg::type<double>::nan();
  22581. }
  22582. #endif
  22583. static double mp_abs(_cimg_math_parser& mp) {
  22584. return cimg::abs(_mp_arg(2));
  22585. }
  22586. static double mp_add(_cimg_math_parser& mp) {
  22587. return _mp_arg(2) + _mp_arg(3);
  22588. }
  22589. static double mp_acos(_cimg_math_parser& mp) {
  22590. return std::acos(_mp_arg(2));
  22591. }
  22592. static double mp_acosh(_cimg_math_parser& mp) {
  22593. return cimg::acosh(_mp_arg(2));
  22594. }
  22595. static double mp_asinh(_cimg_math_parser& mp) {
  22596. return cimg::asinh(_mp_arg(2));
  22597. }
  22598. static double mp_atanh(_cimg_math_parser& mp) {
  22599. return cimg::atanh(_mp_arg(2));
  22600. }
  22601. static double mp_arg(_cimg_math_parser& mp) {
  22602. const int _ind = (int)_mp_arg(4);
  22603. const unsigned int
  22604. nb_args = (unsigned int)mp.opcode[2] - 4,
  22605. ind = _ind<0?_ind + nb_args:(unsigned int)_ind,
  22606. siz = (unsigned int)mp.opcode[3];
  22607. if (siz>0) {
  22608. if (ind>=nb_args) std::memset(&_mp_arg(1) + 1,0,siz*sizeof(double));
  22609. else std::memcpy(&_mp_arg(1) + 1,&_mp_arg(ind + 4) + 1,siz*sizeof(double));
  22610. return cimg::type<double>::nan();
  22611. }
  22612. if (ind>=nb_args) return 0;
  22613. return _mp_arg(ind + 4);
  22614. }
  22615. static double mp_arg0(_cimg_math_parser& mp) {
  22616. const int _ind = (int)_mp_arg(4);
  22617. const unsigned int
  22618. nb_args = (unsigned int)mp.opcode[2] - 4,
  22619. ind = _ind<0?_ind + nb_args:_ind + 1U,
  22620. siz = (unsigned int)mp.opcode[3];
  22621. if (siz>0) {
  22622. if (ind>=nb_args) std::memset(&_mp_arg(1) + 1,0,siz*sizeof(double));
  22623. else std::memcpy(&_mp_arg(1) + 1,&_mp_arg(ind + 4) + 1,siz*sizeof(double));
  22624. return cimg::type<double>::nan();
  22625. }
  22626. if (ind>=nb_args) return 0;
  22627. return _mp_arg(ind + 4);
  22628. }
  22629. static double mp_argkth(_cimg_math_parser& mp) {
  22630. const unsigned int i_end = (unsigned int)mp.opcode[2];
  22631. CImg<double> values;
  22632. if (i_end==5) values.assign(&_mp_arg(3),(unsigned int)mp.opcode[4],1,1,1,true); // Only a single argument
  22633. else {
  22634. unsigned int siz = 0;
  22635. for (unsigned int i = 4; i<i_end; i+=2) siz+=(unsigned int)mp.opcode[i];
  22636. values.assign(siz);
  22637. double *ptr = values;
  22638. for (unsigned int i = 3; i<i_end; i+=2) {
  22639. const unsigned int len = (unsigned int)mp.opcode[i + 1];
  22640. if (len>1) std::memcpy(ptr,&_mp_arg(i),len*sizeof(double));
  22641. else *ptr = _mp_arg(i);
  22642. ptr+=len;
  22643. }
  22644. }
  22645. longT ind = (longT)cimg::round(_mp_arg(3));
  22646. ++values._data; --values._width; // Skip first value
  22647. if (ind<0) ind+=values.width() + 1;
  22648. ind = cimg::cut(ind,(longT)1,(longT)values.width());
  22649. const double kth = values.kth_smallest((ulongT)(ind - 1));
  22650. --values._data; ++values._width;
  22651. for (unsigned int argkth = 1; argkth<values._width; ++argkth)
  22652. if (values[argkth]==kth) return argkth;
  22653. return cimg::type<double>::nan();
  22654. }
  22655. static double mp_argmin(_cimg_math_parser& mp) {
  22656. const unsigned int i_end = (unsigned int)mp.opcode[2];
  22657. double val, valmin = cimg::type<double>::inf();
  22658. unsigned int siz = 0, argmin = 0;
  22659. for (unsigned int i = 3; i<i_end; i+=2) {
  22660. const unsigned int len = (unsigned int)mp.opcode[i + 1];
  22661. if (len>1) {
  22662. const double *ptr = &_mp_arg(i);
  22663. for (unsigned int k = 0; k<len; ++k) { val = *(ptr++); if (val<valmin) { valmin = val; argmin = siz + k; } }
  22664. } else { val = _mp_arg(i); if (val<valmin) { valmin = val; argmin = siz; } }
  22665. siz+=len;
  22666. }
  22667. return (double)argmin;
  22668. }
  22669. static double mp_argminabs(_cimg_math_parser& mp) {
  22670. const unsigned int i_end = (unsigned int)mp.opcode[2];
  22671. double val, abs_val, abs_valminabs = cimg::type<double>::inf();
  22672. unsigned int siz = 0, argminabs = 0;
  22673. for (unsigned int i = 3; i<i_end; i+=2) {
  22674. const unsigned int len = (unsigned int)mp.opcode[i + 1];
  22675. if (len>1) {
  22676. const double *ptr = &_mp_arg(i);
  22677. for (unsigned int k = 0; k<len; ++k) {
  22678. val = *(ptr++);
  22679. abs_val = cimg::abs(val);
  22680. if (abs_val<abs_valminabs) { abs_valminabs = abs_val; argminabs = siz + k; }
  22681. }
  22682. } else {
  22683. val = _mp_arg(i);
  22684. abs_val = cimg::abs(val);
  22685. if (abs_val<abs_valminabs) { abs_valminabs = abs_val; argminabs = siz; }
  22686. }
  22687. siz+=len;
  22688. }
  22689. return (double)argminabs;
  22690. }
  22691. static double mp_argmax(_cimg_math_parser& mp) {
  22692. const unsigned int i_end = (unsigned int)mp.opcode[2];
  22693. double val, valmax = -cimg::type<double>::inf();
  22694. unsigned int siz = 0, argmax = 0;
  22695. for (unsigned int i = 3; i<i_end; i+=2) {
  22696. const unsigned int len = (unsigned int)mp.opcode[i + 1];
  22697. if (len>1) {
  22698. const double *ptr = &_mp_arg(i);
  22699. for (unsigned int k = 0; k<len; ++k) { val = *(ptr++); if (val>valmax) { valmax = val; argmax = siz + k; } }
  22700. } else { val = _mp_arg(i); if (val>valmax) { valmax = val; argmax = siz; } }
  22701. siz+=len;
  22702. }
  22703. return (double)argmax;
  22704. }
  22705. static double mp_argmaxabs(_cimg_math_parser& mp) {
  22706. const unsigned int i_end = (unsigned int)mp.opcode[2];
  22707. double val, abs_val, abs_valmaxabs = 0;
  22708. unsigned int siz = 0, argmaxabs = 0;
  22709. for (unsigned int i = 3; i<i_end; i+=2) {
  22710. const unsigned int len = (unsigned int)mp.opcode[i + 1];
  22711. if (len>1) {
  22712. const double *ptr = &_mp_arg(i);
  22713. for (unsigned int k = 0; k<len; ++k) {
  22714. val = *(ptr++);
  22715. abs_val = cimg::abs(val);
  22716. if (abs_val>abs_valmaxabs) { abs_valmaxabs = abs_val; argmaxabs = siz + k; }
  22717. }
  22718. } else {
  22719. val = _mp_arg(i);
  22720. abs_val = cimg::abs(val);
  22721. if (abs_val>abs_valmaxabs) { abs_valmaxabs = abs_val; argmaxabs = siz; }
  22722. }
  22723. siz+=len;
  22724. }
  22725. return (double)argmaxabs;
  22726. }
  22727. static double mp_asin(_cimg_math_parser& mp) {
  22728. return std::asin(_mp_arg(2));
  22729. }
  22730. static double mp_atan(_cimg_math_parser& mp) {
  22731. return std::atan(_mp_arg(2));
  22732. }
  22733. static double mp_atan2(_cimg_math_parser& mp) {
  22734. return std::atan2(_mp_arg(2),_mp_arg(3));
  22735. }
  22736. static double mp_avg(_cimg_math_parser& mp) {
  22737. const unsigned int i_end = (unsigned int)mp.opcode[2];
  22738. unsigned int siz = 0;
  22739. double sum = 0;
  22740. for (unsigned int i = 3; i<i_end; i+=2) {
  22741. const unsigned int len = (unsigned int)mp.opcode[i + 1];
  22742. if (len>1) {
  22743. const double *ptr = &_mp_arg(i);
  22744. for (unsigned int k = 0; k<len; ++k) sum+=*(ptr++);
  22745. } else sum+=_mp_arg(i);
  22746. siz+=len;
  22747. }
  22748. return sum/siz;
  22749. }
  22750. static double mp_bitwise_and(_cimg_math_parser& mp) {
  22751. return (double)((longT)_mp_arg(2) & (longT)_mp_arg(3));
  22752. }
  22753. static double mp_bitwise_left_shift(_cimg_math_parser& mp) {
  22754. return (double)((longT)_mp_arg(2)<<(unsigned int)_mp_arg(3));
  22755. }
  22756. static double mp_bitwise_not(_cimg_math_parser& mp) {
  22757. // Limit result to 32bits such that it can be entirely represented as a 'double'.
  22758. return (double)~(unsigned int)_mp_arg(2);
  22759. }
  22760. static double mp_bitwise_or(_cimg_math_parser& mp) {
  22761. return (double)((longT)_mp_arg(2) | (longT)_mp_arg(3));
  22762. }
  22763. static double mp_bitwise_right_shift(_cimg_math_parser& mp) {
  22764. return (double)((longT)_mp_arg(2)>>(unsigned int)_mp_arg(3));
  22765. }
  22766. static double mp_bitwise_xor(_cimg_math_parser& mp) {
  22767. return (double)((longT)_mp_arg(2) ^ (longT)_mp_arg(3));
  22768. }
  22769. static double mp_bool(_cimg_math_parser& mp) {
  22770. return (double)(bool)_mp_arg(2);
  22771. }
  22772. static double mp_break(_cimg_math_parser& mp) {
  22773. mp.break_type = 1;
  22774. mp.p_code = mp.p_break - 1;
  22775. return cimg::type<double>::nan();
  22776. }
  22777. static double mp_breakpoint(_cimg_math_parser& mp) {
  22778. cimg_abort_init;
  22779. cimg_abort_test;
  22780. cimg::unused(mp);
  22781. return cimg::type<double>::nan();
  22782. }
  22783. static double mp_c2o(_cimg_math_parser& mp) {
  22784. mp_check_list(mp,"c2o");
  22785. unsigned int ind = (unsigned int)mp.opcode[2];
  22786. if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  22787. const CImg<T> &img = ind==~0U?mp.imgin:mp.imglist[ind];
  22788. const int
  22789. x = (int)_mp_arg(3),
  22790. y = (int)_mp_arg(4),
  22791. z = (int)_mp_arg(5),
  22792. c = (int)_mp_arg(6);
  22793. return (double)img.offset(x,y,z,c);
  22794. }
  22795. static double mp_cbrt(_cimg_math_parser& mp) {
  22796. return cimg::cbrt(_mp_arg(2));
  22797. }
  22798. static double mp_ceil(_cimg_math_parser& mp) {
  22799. return std::ceil(_mp_arg(2));
  22800. }
  22801. static double mp_complex_abs(_cimg_math_parser& mp) {
  22802. return cimg::hypot(_mp_arg(2),_mp_arg(3));
  22803. }
  22804. static double mp_complex_conj(_cimg_math_parser& mp) {
  22805. const double real = _mp_arg(2), imag = _mp_arg(3);
  22806. double *ptrd = &_mp_arg(1) + 1;
  22807. ptrd[0] = real;
  22808. ptrd[1] = -imag;
  22809. return cimg::type<double>::nan();
  22810. }
  22811. static double mp_complex_div_sv(_cimg_math_parser& mp) {
  22812. const double
  22813. *ptr2 = &_mp_arg(3) + 1,
  22814. r1 = _mp_arg(2),
  22815. r2 = *(ptr2++), i2 = *ptr2;
  22816. double *ptrd = &_mp_arg(1) + 1;
  22817. const double denom = r2*r2 + i2*i2;
  22818. *(ptrd++) = r1*r2/denom;
  22819. *ptrd = -r1*i2/denom;
  22820. return cimg::type<double>::nan();
  22821. }
  22822. static double mp_complex_div_vv(_cimg_math_parser& mp) {
  22823. const double
  22824. *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1,
  22825. r1 = *(ptr1++), i1 = *ptr1,
  22826. r2 = *(ptr2++), i2 = *ptr2;
  22827. double *ptrd = &_mp_arg(1) + 1;
  22828. const double denom = r2*r2 + i2*i2;
  22829. *(ptrd++) = (r1*r2 + i1*i2)/denom;
  22830. *ptrd = (r2*i1 - r1*i2)/denom;
  22831. return cimg::type<double>::nan();
  22832. }
  22833. static double mp_complex_exp(_cimg_math_parser& mp) {
  22834. const double real = _mp_arg(2), imag = _mp_arg(3), exp_real = std::exp(real);
  22835. double *ptrd = &_mp_arg(1) + 1;
  22836. ptrd[0] = exp_real*std::cos(imag);
  22837. ptrd[1] = exp_real*std::sin(imag);
  22838. return cimg::type<double>::nan();
  22839. }
  22840. static double mp_complex_log(_cimg_math_parser& mp) {
  22841. const double real = _mp_arg(2), imag = _mp_arg(3);
  22842. double *ptrd = &_mp_arg(1) + 1;
  22843. ptrd[0] = 0.5*std::log(real*real + imag*imag);
  22844. ptrd[1] = std::atan2(imag,real);
  22845. return cimg::type<double>::nan();
  22846. }
  22847. static double mp_complex_mul(_cimg_math_parser& mp) {
  22848. const double
  22849. *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1,
  22850. r1 = *(ptr1++), i1 = *ptr1,
  22851. r2 = *(ptr2++), i2 = *ptr2;
  22852. double *ptrd = &_mp_arg(1) + 1;
  22853. *(ptrd++) = r1*r2 - i1*i2;
  22854. *(ptrd++) = r1*i2 + r2*i1;
  22855. return cimg::type<double>::nan();
  22856. }
  22857. static void _mp_complex_pow(const double r1, const double i1,
  22858. const double r2, const double i2,
  22859. double *ptrd) {
  22860. double ro, io;
  22861. if (cimg::abs(i2)<1e-15) { // Exponent is real
  22862. if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) {
  22863. if (cimg::abs(r2)<1e-15) { ro = 1; io = 0; }
  22864. else ro = io = 0;
  22865. } else {
  22866. const double
  22867. mod1_2 = r1*r1 + i1*i1,
  22868. phi1 = std::atan2(i1,r1),
  22869. modo = std::pow(mod1_2,0.5*r2),
  22870. phio = r2*phi1;
  22871. ro = modo*std::cos(phio);
  22872. io = modo*std::sin(phio);
  22873. }
  22874. } else { // Exponent is complex
  22875. if (cimg::abs(r1)<1e-15 && cimg::abs(i1)<1e-15) ro = io = 0;
  22876. const double
  22877. mod1_2 = r1*r1 + i1*i1,
  22878. phi1 = std::atan2(i1,r1),
  22879. modo = std::pow(mod1_2,0.5*r2)*std::exp(-i2*phi1),
  22880. phio = r2*phi1 + 0.5*i2*std::log(mod1_2);
  22881. ro = modo*std::cos(phio);
  22882. io = modo*std::sin(phio);
  22883. }
  22884. *(ptrd++) = ro;
  22885. *ptrd = io;
  22886. }
  22887. static double mp_complex_pow_ss(_cimg_math_parser& mp) {
  22888. const double val1 = _mp_arg(2), val2 = _mp_arg(3);
  22889. double *ptrd = &_mp_arg(1) + 1;
  22890. _mp_complex_pow(val1,0,val2,0,ptrd);
  22891. return cimg::type<double>::nan();
  22892. }
  22893. static double mp_complex_pow_sv(_cimg_math_parser& mp) {
  22894. const double val1 = _mp_arg(2), *ptr2 = &_mp_arg(3) + 1;
  22895. double *ptrd = &_mp_arg(1) + 1;
  22896. _mp_complex_pow(val1,0,ptr2[0],ptr2[1],ptrd);
  22897. return cimg::type<double>::nan();
  22898. }
  22899. static double mp_complex_pow_vs(_cimg_math_parser& mp) {
  22900. const double *ptr1 = &_mp_arg(2) + 1, val2 = _mp_arg(3);
  22901. double *ptrd = &_mp_arg(1) + 1;
  22902. _mp_complex_pow(ptr1[0],ptr1[1],val2,0,ptrd);
  22903. return cimg::type<double>::nan();
  22904. }
  22905. static double mp_complex_pow_vv(_cimg_math_parser& mp) {
  22906. const double *ptr1 = &_mp_arg(2) + 1, *ptr2 = &_mp_arg(3) + 1;
  22907. double *ptrd = &_mp_arg(1) + 1;
  22908. _mp_complex_pow(ptr1[0],ptr1[1],ptr2[0],ptr2[1],ptrd);
  22909. return cimg::type<double>::nan();
  22910. }
  22911. static double mp_complex_cos(_cimg_math_parser& mp) {
  22912. const double real = _mp_arg(2), imag = _mp_arg(3);
  22913. double *ptrd = &_mp_arg(1) + 1;
  22914. ptrd[0] = std::cos(real)*std::cosh(imag);
  22915. ptrd[1] = -std::sin(real)*std::sinh(imag);
  22916. return cimg::type<double>::nan();
  22917. }
  22918. static double mp_complex_sin(_cimg_math_parser& mp) {
  22919. const double real = _mp_arg(2), imag = _mp_arg(3);
  22920. double *ptrd = &_mp_arg(1) + 1;
  22921. ptrd[0] = std::sin(real)*std::cosh(imag);
  22922. ptrd[1] = std::cos(real)*std::sinh(imag);
  22923. return cimg::type<double>::nan();
  22924. }
  22925. static double mp_complex_sqrt(_cimg_math_parser& mp) {
  22926. const double
  22927. real = _mp_arg(2), imag = _mp_arg(3),
  22928. r = std::sqrt(cimg::hypot(real,imag)),
  22929. theta = std::atan2(imag,real)/2;
  22930. double *ptrd = &_mp_arg(1) + 1;
  22931. ptrd[0] = r*std::cos(theta);
  22932. ptrd[1] = r*std::sin(theta);
  22933. return cimg::type<double>::nan();
  22934. }
  22935. static double mp_complex_tan(_cimg_math_parser& mp) {
  22936. const double real = _mp_arg(2), imag = _mp_arg(3), denom = std::cos(2*real) + std::cosh(2*imag);
  22937. double *ptrd = &_mp_arg(1) + 1;
  22938. ptrd[0] = std::sin(2*real)/denom;
  22939. ptrd[1] = std::sinh(2*imag)/denom;
  22940. return cimg::type<double>::nan();
  22941. }
  22942. static double mp_complex_cosh(_cimg_math_parser& mp) {
  22943. const double real = _mp_arg(2), imag = _mp_arg(3);
  22944. double *ptrd = &_mp_arg(1) + 1;
  22945. ptrd[0] = std::cosh(real)*std::cos(imag);
  22946. ptrd[1] = std::sinh(real)*std::sin(imag);
  22947. return cimg::type<double>::nan();
  22948. }
  22949. static double mp_complex_sinh(_cimg_math_parser& mp) {
  22950. const double real = _mp_arg(2), imag = _mp_arg(3);
  22951. double *ptrd = &_mp_arg(1) + 1;
  22952. ptrd[0] = std::sinh(real)*std::cos(imag);
  22953. ptrd[1] = std::cosh(real)*std::sin(imag);
  22954. return cimg::type<double>::nan();
  22955. }
  22956. static double mp_complex_tanh(_cimg_math_parser& mp) {
  22957. const double real = _mp_arg(2), imag = _mp_arg(3), denom = std::cosh(2*real) + std::cos(2*imag);
  22958. double *ptrd = &_mp_arg(1) + 1;
  22959. ptrd[0] = std::sinh(2*real)/denom;
  22960. ptrd[1] = std::sin(2*imag)/denom;
  22961. return cimg::type<double>::nan();
  22962. }
  22963. static double mp_continue(_cimg_math_parser& mp) {
  22964. mp.break_type = 2;
  22965. mp.p_code = mp.p_break - 1;
  22966. return cimg::type<double>::nan();
  22967. }
  22968. static double mp_convolve(_cimg_math_parser &mp) {
  22969. return _mp_correlate(mp,true);
  22970. }
  22971. static double mp_copy(_cimg_math_parser& mp) {
  22972. return _mp_arg(2);
  22973. }
  22974. static double mp_correlate(_cimg_math_parser &mp) {
  22975. return _mp_correlate(mp,false);
  22976. }
  22977. static double _mp_correlate(_cimg_math_parser &mp, bool is_convolve) {
  22978. double *ptrd = &_mp_arg(1) + 1;
  22979. const double *const ptrA = &_mp_arg(2) + 1, *const ptrM = &_mp_arg(7) + 1;
  22980. const unsigned int
  22981. wA = (unsigned int)mp.opcode[3],
  22982. hA = (unsigned int)mp.opcode[4],
  22983. dA = (unsigned int)mp.opcode[5],
  22984. sA = (unsigned int)mp.opcode[6],
  22985. wM = (unsigned int)mp.opcode[8],
  22986. hM = (unsigned int)mp.opcode[9],
  22987. dM = (unsigned int)mp.opcode[10],
  22988. sM = (unsigned int)mp.opcode[11],
  22989. boundary_conditions = (unsigned int)_mp_arg(12),
  22990. channel_mode = (unsigned int)mp.opcode[14];
  22991. const bool
  22992. is_normalized = (bool)_mp_arg(13),
  22993. interpolation_type = (bool)_mp_arg(30);
  22994. const int
  22995. xcenter = mp.opcode[15]!=~0U?(int)_mp_arg(15):(int)(~0U>>1),
  22996. ycenter = mp.opcode[16]!=~0U?(int)_mp_arg(16):(int)(~0U>>1),
  22997. zcenter = mp.opcode[17]!=~0U?(int)_mp_arg(17):(int)(~0U>>1),
  22998. xstart = (int)mp.opcode[18],
  22999. ystart = (int)mp.opcode[19],
  23000. zstart = (int)mp.opcode[20],
  23001. xend = (int)mp.opcode[21],
  23002. yend = (int)mp.opcode[22],
  23003. zend = (int)mp.opcode[23];
  23004. const float
  23005. xstride = (float)_mp_arg(24),
  23006. ystride = (float)_mp_arg(25),
  23007. zstride = (float)_mp_arg(26),
  23008. xdilation = (float)_mp_arg(27),
  23009. ydilation = (float)_mp_arg(28),
  23010. zdilation = (float)_mp_arg(29);
  23011. CImg<doubleT> res;
  23012. if (is_convolve) res = CImg<doubleT>(ptrA,wA,hA,dA,sA,true).
  23013. get_convolve(CImg<doubleT>(ptrM,wM,hM,dM,sM,true),
  23014. boundary_conditions,is_normalized,channel_mode,
  23015. xcenter,ycenter,zcenter,
  23016. xstart,ystart,zstart,
  23017. xend,yend,zend,
  23018. xstride,ystride,zstride,
  23019. xdilation,ydilation,zdilation,
  23020. interpolation_type);
  23021. else res = CImg<doubleT>(ptrA,wA,hA,dA,sA,true).
  23022. get_correlate(CImg<doubleT>(ptrM,wM,hM,dM,sM,true),
  23023. boundary_conditions,is_normalized,channel_mode,
  23024. xcenter,ycenter,zcenter,
  23025. xstart,ystart,zstart,
  23026. xend,yend,zend,
  23027. xstride,ystride,zstride,
  23028. xdilation,ydilation,zdilation,
  23029. interpolation_type);
  23030. CImg<doubleT>(ptrd,res._width,res._height,res._depth,res._spectrum,true) = res;
  23031. return cimg::type<double>::nan();
  23032. }
  23033. static double mp_cos(_cimg_math_parser& mp) {
  23034. return std::cos(_mp_arg(2));
  23035. }
  23036. static double mp_cosh(_cimg_math_parser& mp) {
  23037. return std::cosh(_mp_arg(2));
  23038. }
  23039. static double mp_cov(_cimg_math_parser& mp) {
  23040. const unsigned int
  23041. _siz = (unsigned int)mp.opcode[4],
  23042. siz = std::max(_siz,1U),
  23043. off = _siz?1:0,
  23044. sizm1 = siz>1?siz - 1:1;
  23045. const CImg<doubleT>
  23046. A(&_mp_arg(2) + off,1,siz,1,1,true),
  23047. B(&_mp_arg(3) + off,1,siz,1,1,true);
  23048. const double
  23049. avgA = (unsigned int)mp.opcode[5]==~0U?A.mean():_mp_arg(5),
  23050. avgB = (unsigned int)mp.opcode[6]==~0U?B.mean():_mp_arg(6);
  23051. double res = 0;
  23052. cimg_forY(A,k) res+=(A[k] - avgA)*(B[k] - avgB);
  23053. return res/sizm1;
  23054. }
  23055. static double mp_critical(_cimg_math_parser& mp) {
  23056. const ulongT g_target = mp.opcode[1];
  23057. cimg_pragma_openmp(critical(mp_critical))
  23058. {
  23059. for (const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[2];
  23060. mp.p_code<p_end; ++mp.p_code) { // Evaluate body
  23061. mp.opcode._data = mp.p_code->_data;
  23062. const ulongT target = mp.opcode[1];
  23063. mp.mem[target] = _cimg_mp_defunc(mp);
  23064. }
  23065. }
  23066. --mp.p_code;
  23067. return mp.mem[g_target];
  23068. }
  23069. static double mp_image_crop(_cimg_math_parser& mp) {
  23070. double *ptrd = &_mp_arg(1) + 1;
  23071. const int x = (int)_mp_arg(3), y = (int)_mp_arg(4), z = (int)_mp_arg(5), c = (int)_mp_arg(6);
  23072. const unsigned int
  23073. dx = (unsigned int)mp.opcode[7],
  23074. dy = (unsigned int)mp.opcode[8],
  23075. dz = (unsigned int)mp.opcode[9],
  23076. dc = (unsigned int)mp.opcode[10];
  23077. const unsigned int boundary_conditions = (unsigned int)_mp_arg(11);
  23078. unsigned int ind = (unsigned int)mp.opcode[2];
  23079. if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  23080. const CImg<T> &img = ind==~0U?mp.imgin:mp.imglist[ind];
  23081. if (!img) std::memset(ptrd,0,dx*dy*dz*dc*sizeof(double));
  23082. else CImg<doubleT>(ptrd,dx,dy,dz,dc,true) = img.get_crop(x,y,z,c,
  23083. x + dx - 1,y + dy - 1,
  23084. z + dz - 1,c + dc - 1,
  23085. boundary_conditions);
  23086. return cimg::type<double>::nan();
  23087. }
  23088. static double mp_cross(_cimg_math_parser& mp) {
  23089. CImg<doubleT>
  23090. vout(&_mp_arg(1) + 1,1,3,1,1,true),
  23091. v1(&_mp_arg(2) + 1,1,3,1,1,true),
  23092. v2(&_mp_arg(3) + 1,1,3,1,1,true);
  23093. (vout = v1).cross(v2);
  23094. return cimg::type<double>::nan();
  23095. }
  23096. static double mp_cut(_cimg_math_parser& mp) {
  23097. double val = _mp_arg(2), cmin = _mp_arg(3), cmax = _mp_arg(4);
  23098. return val<cmin?cmin:val>cmax?cmax:val;
  23099. }
  23100. static double mp_da_back_or_pop(_cimg_math_parser& mp) {
  23101. const bool is_pop_heap = mp.opcode[4]==2, is_pop = (bool)mp.opcode[4];
  23102. const char *const s_op = is_pop_heap?"da_pop_heap":is_pop?"da_pop":"da_back";
  23103. mp_check_list(mp,s_op);
  23104. const unsigned int
  23105. dim = (unsigned int)mp.opcode[2],
  23106. ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.imglist.width());
  23107. double *const ptrd = &_mp_arg(1) + (dim>1?1:0);
  23108. CImg<T> &img = mp.imglist[ind];
  23109. int siz = img?(int)cimg::float2uint((float)img[img._height - 1]):0;
  23110. if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1))
  23111. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': "
  23112. "Specified image #%u of size (%d,%d,%d,%d) cannot be used as dynamic array%s.",
  23113. mp.imgout.pixel_type(),s_op,ind,
  23114. img.width(),img.height(),img.depth(),img.spectrum(),
  23115. img._width==1 && img._depth==1?"":" (contains invalid element counter)");
  23116. if (!siz)
  23117. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': "
  23118. "Specified dynamic array #%u contains no elements.",
  23119. mp.imgout.pixel_type(),s_op,ind);
  23120. const int siz1 = siz - 1;
  23121. if (is_pop_heap) { // Heapify-down
  23122. if (dim==1) cimg::swap(img[0],img[siz1]);
  23123. else {
  23124. T *ptr0 = img.data(), *ptr1 = img.data(0,siz1);
  23125. cimg_forC(img,c) { cimg::swap(*ptr0,*ptr1); ptr0+=img._height; ptr1+=img._height; }
  23126. }
  23127. int index = 0;
  23128. while (true) {
  23129. const int child_left = 2*index + 1, child_right = child_left + 1;
  23130. int smallest = index;
  23131. if (child_left<siz1 && img[child_left]<img[smallest]) smallest = child_left;
  23132. if (child_right<siz1 && img[child_right]<img[smallest]) smallest = child_right;
  23133. if (smallest!=index) {
  23134. if (dim==1) cimg::swap(img[index],img[smallest]);
  23135. else {
  23136. T *ptr0 = img.data(0,index), *ptr1 = img.data(0,smallest);
  23137. cimg_forC(img,c) { cimg::swap(*ptr0,*ptr1); ptr0+=img._height; ptr1+=img._height; }
  23138. }
  23139. index = smallest;
  23140. } else break;
  23141. }
  23142. }
  23143. double ret = cimg::type<double>::nan();
  23144. if (dim==1) ret = img[siz1]; // Scalar element
  23145. else {
  23146. const T *ptrs = img.data(0,siz1);
  23147. cimg_forC(img,c) { ptrd[c] = *ptrs; ptrs+=img._height; } // Vector element
  23148. }
  23149. if (is_pop) { // Remove element from array
  23150. --siz;
  23151. if (img.height()>32 && siz<img.height()/8) // Reduce size of dynamic array
  23152. img.resize(1,std::max(2*siz + 1,32),1,-100,0);
  23153. img[img._height - 1] = (T)cimg::uint2float(siz);
  23154. }
  23155. return ret;
  23156. }
  23157. static double mp_da_freeze(_cimg_math_parser& mp) {
  23158. const char *const s_op = "da_freeze";
  23159. mp_check_list(mp,s_op);
  23160. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  23161. CImg<T> &img = mp.imglist[ind];
  23162. int siz = img?(int)cimg::float2uint((float)img[img._height - 1]):0;
  23163. if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1))
  23164. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': "
  23165. "Specified image #%u of size (%d,%d,%d,%d) cannot be used as dynamic array%s.",
  23166. mp.imgout.pixel_type(),s_op,ind,
  23167. img.width(),img.height(),img.depth(),img.spectrum(),
  23168. img._width==1 && img._depth==1?"":" (contains invalid element counter)");
  23169. if (siz) img.resize(1,siz,1,-100,0,0); else img.assign();
  23170. return cimg::type<double>::nan();
  23171. }
  23172. static double mp_da_insert_or_push(_cimg_math_parser& mp) {
  23173. const bool is_push_heap = mp.opcode[3]==~0U - 1, is_push = mp.opcode[3]>=~0U - 1;
  23174. const char *const s_op = is_push_heap?"da_push_heap":is_push?"da_push":"da_insert";
  23175. mp_check_list(mp,s_op);
  23176. const unsigned int
  23177. dim = (unsigned int)mp.opcode[4],
  23178. _dim = std::max(1U,dim),
  23179. nb_elts = (unsigned int)mp.opcode[5] - 6,
  23180. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  23181. CImg<T> &img = mp.imglist[ind];
  23182. const int
  23183. siz = img?(int)cimg::float2uint((float)img[img._height - 1]):0,
  23184. pos0 = is_push?siz:(int)_mp_arg(3),
  23185. pos = pos0<0?pos0 + siz:pos0;
  23186. if (img && _dim!=img._spectrum)
  23187. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': "
  23188. "Element to insert has invalid size %u (should be %u).",
  23189. mp.imgout.pixel_type(),s_op,_dim,img._spectrum);
  23190. if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1))
  23191. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': "
  23192. "Specified image #%u of size (%d,%d,%d,%d) cannot be used as dynamic array%s.",
  23193. mp.imgout.pixel_type(),s_op,ind,
  23194. img.width(),img.height(),img.depth(),img.spectrum(),
  23195. img._width==1 && img._depth==1?"":" (contains invalid element counter)");
  23196. if (pos<0 || pos>siz)
  23197. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function '%s()': "
  23198. "Invalid position %d (not in range -%d...%d).",
  23199. mp.imgout.pixel_type(),s_op,pos0,siz,siz);
  23200. if (siz + nb_elts + 1>=img._height) // Increase size of dynamic array, if necessary
  23201. img.resize(1,2*siz + nb_elts + 1,1,_dim,0);
  23202. if (pos!=siz) // Move existing data in dynamic array
  23203. cimg_forC(img,c) std::memmove(img.data(0,pos + nb_elts,0,c),img.data(0,pos,0,c),(siz - pos)*sizeof(T));
  23204. if (!dim) // Scalar or vector1() elements
  23205. for (unsigned int k = 0; k<nb_elts; ++k) {
  23206. int index = pos + k;
  23207. img[index] = (T)_mp_arg(6 + k);
  23208. if (is_push_heap) while (index>0) { // Heapify-up
  23209. const int index_parent = (index - 1)/2;
  23210. if (img[index]<img[index_parent]) {
  23211. cimg::swap(img[index],img[index_parent]);
  23212. index = index_parent; }
  23213. else break;
  23214. }
  23215. }
  23216. else // vectorN() elements, with N>1
  23217. for (unsigned int k = 0; k<nb_elts; ++k) {
  23218. int index = pos + k;
  23219. const double *const ptrs = &_mp_arg(6 + k) + 1;
  23220. T *ptrd = img.data(0,index);
  23221. cimg_forC(img,c) { *ptrd = ptrs[c]; ptrd+=img._height; }
  23222. if (is_push_heap) while (index>0) { // Heapify-up
  23223. const int index_parent = (index - 1)/2;
  23224. if (img[index]<img[index_parent]) {
  23225. T *ptr0 = img.data(0,index), *ptr1 = img.data(0,index_parent);
  23226. cimg_forC(img,c) { cimg::swap(*ptr0,*ptr1); ptr0+=img._height; ptr1+=img._height; }
  23227. index = index_parent;
  23228. }
  23229. else break;
  23230. }
  23231. }
  23232. img[img._height - 1] = (T)cimg::uint2float(siz + nb_elts);
  23233. return cimg::type<double>::nan();
  23234. }
  23235. static double mp_da_remove(_cimg_math_parser& mp) {
  23236. mp_check_list(mp,"da_remove");
  23237. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  23238. CImg<T> &img = mp.imglist[ind];
  23239. int siz = img?(int)cimg::float2uint((float)img[img._height - 1]):0;
  23240. if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1))
  23241. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'da_remove()': "
  23242. "Specified image #%u of size (%d,%d,%d,%d) cannot be used as dynamic array%s.",
  23243. mp.imgout.pixel_type(),ind,
  23244. img.width(),img.height(),img.depth(),img.spectrum(),
  23245. img._width==1 && img._depth==1?"":" (contains invalid element counter)");
  23246. if (!siz)
  23247. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'da_remove()': "
  23248. "Dynamic array is empty.",
  23249. mp.imgout.pixel_type());
  23250. int
  23251. start0 = mp.opcode[3]==~0U?siz - 1:_mp_arg(3),
  23252. end0 = mp.opcode[4]==~0U?start0:_mp_arg(4),
  23253. start = start0<0?start0 + siz:start0,
  23254. end = end0<0?end0 + siz:end0;
  23255. if (start<0 || start>=siz || end<0 || end>=siz || start>end)
  23256. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'da_remove()': "
  23257. "Invalid starting (%d) and ending (%d) positions "
  23258. "(not ordered, in range -%d...%d).",
  23259. mp.imgout.pixel_type(),start0,end0,siz,siz - 1);
  23260. if (end<siz - 1) // Move remaining data in dynamic array
  23261. cimg_forC(img,c) std::memmove(img.data(0,start,0,c),img.data(0,end + 1,0,c),(siz - 1 - end)*sizeof(T));
  23262. siz-=end - start + 1;
  23263. if (img.height()>32 && siz<img.height()/8) // Reduce size of dynamic array
  23264. img.resize(1,std::max(2*siz + 1,32),1,-100,0);
  23265. img[img._height - 1] = (T)cimg::uint2float(siz);
  23266. return cimg::type<double>::nan();
  23267. }
  23268. static double mp_da_size(_cimg_math_parser& mp) {
  23269. mp_check_list(mp,"da_size");
  23270. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  23271. CImg<T> &img = mp.imglist[ind];
  23272. const int siz = img?(int)cimg::float2uint((float)img[img._height - 1]):0;
  23273. if (img && (img._width!=1 || img._depth!=1 || siz<0 || siz>img.height() - 1))
  23274. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'da_size()': "
  23275. "Specified image #%u of size (%d,%d,%d,%d) cannot be used as dynamic array%s.",
  23276. mp.imgout.pixel_type(),ind,
  23277. img.width(),img.height(),img.depth(),img.spectrum(),
  23278. img._width==1 && img._depth==1?"":" (contains invalid element counter)");
  23279. return siz;
  23280. }
  23281. static double mp_date(_cimg_math_parser& mp) {
  23282. const unsigned int
  23283. siz_out = (unsigned int)mp.opcode[2],
  23284. siz_arg1 = (unsigned int)mp.opcode[4],
  23285. siz_arg2 = (unsigned int)mp.opcode[6];
  23286. double *ptr_out = &_mp_arg(1) + (siz_out?1:0);
  23287. const double
  23288. *ptr_arg1 = siz_arg1==~0U?0:&_mp_arg(3) + (siz_arg1?1:0),
  23289. *ptr_arg2 = siz_arg2==~0U?0:&_mp_arg(5) + 1;
  23290. if (!ptr_arg2) { // No filename specified
  23291. if (!siz_arg1) return cimg::date((unsigned int)*ptr_arg1);
  23292. if (siz_arg1==~0U) for (unsigned int k = 0; k<siz_out; ++k) ptr_out[k] = k;
  23293. else for (unsigned int k = 0; k<siz_out; ++k) ptr_out[k] = ptr_arg1[k];
  23294. cimg::date(ptr_out,siz_out);
  23295. return cimg::type<double>::nan();
  23296. }
  23297. // Filename specified.
  23298. CImg<charT> ss(siz_arg2 + 1);
  23299. cimg_forX(ss,i) ss[i] = (char)ptr_arg2[i];
  23300. ss.back() = 0;
  23301. if (!siz_arg1) return cimg::fdate(ss,(unsigned int)*ptr_arg1);
  23302. for (unsigned int k = 0; k<siz_out; ++k) ptr_out[k] = ptr_arg1[k];
  23303. cimg::fdate(ss,ptr_out,siz_out);
  23304. return cimg::type<double>::nan();
  23305. }
  23306. static double mp_debug(_cimg_math_parser& mp) {
  23307. CImg<charT> expr(mp.opcode[2] - 4);
  23308. {
  23309. const ulongT *ptrs = mp.opcode._data + 4;
  23310. cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++);
  23311. }
  23312. cimg::strellipsize(expr);
  23313. const ulongT g_target = mp.opcode[1];
  23314. #if cimg_use_openmp==0
  23315. const unsigned int n_thread = 0;
  23316. #else
  23317. const unsigned int n_thread = omp_get_thread_num();
  23318. #endif
  23319. cimg_pragma_openmp(critical(mp_debug))
  23320. {
  23321. std::fprintf(cimg::output(),
  23322. "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c"
  23323. "Start debugging '%s', code length: %u -> mem[%u] (memsize: %u)",
  23324. (void*)&mp,n_thread,mp.debug_indent,' ',
  23325. expr._data,(unsigned int)mp.opcode[3],(unsigned int)g_target,mp.mem._width);
  23326. std::fflush(cimg::output());
  23327. mp.debug_indent+=3;
  23328. }
  23329. const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[3];
  23330. CImg<ulongT> _op;
  23331. for ( ; mp.p_code<p_end; ++mp.p_code) {
  23332. const CImg<ulongT> &op = *mp.p_code;
  23333. mp.opcode._data = op._data;
  23334. _op.assign(1,op._height - 1);
  23335. const ulongT *ptrs = op._data + 1;
  23336. for (ulongT *ptrd = _op._data, *const ptrde = _op._data + _op._height; ptrd<ptrde; ++ptrd)
  23337. *ptrd = *(ptrs++);
  23338. const ulongT target = mp.opcode[1];
  23339. mp.mem[target] = _cimg_mp_defunc(mp);
  23340. cimg_pragma_openmp(critical(mp_debug))
  23341. {
  23342. std::fprintf(cimg::output(),
  23343. "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c"
  23344. "Opcode %p = [ %p,%s ] -> mem[%u] = %.17g",
  23345. (void*)&mp,n_thread,mp.debug_indent,' ',
  23346. (void*)mp.opcode._data,(void*)*mp.opcode,_op.value_string().data(),
  23347. (unsigned int)target,mp.mem[target]);
  23348. std::fflush(cimg::output());
  23349. }
  23350. }
  23351. cimg_pragma_openmp(critical(mp_debug))
  23352. {
  23353. mp.debug_indent-=3;
  23354. std::fprintf(cimg::output(),
  23355. "\n[" cimg_appname "_math_parser] %p[thread #%u]:%*c"
  23356. "End debugging '%s' -> mem[%u] = %.17g (memsize: %u)",
  23357. (void*)&mp,n_thread,mp.debug_indent,' ',
  23358. expr._data,(unsigned int)g_target,mp.mem[g_target],mp.mem._width);
  23359. std::fflush(cimg::output());
  23360. }
  23361. --mp.p_code;
  23362. return mp.mem[g_target];
  23363. }
  23364. static double mp_decrement(_cimg_math_parser& mp) {
  23365. return _mp_arg(2) - 1;
  23366. }
  23367. static double mp_deg2rad(_cimg_math_parser& mp) {
  23368. return _mp_arg(2)*cimg::PI/180;
  23369. }
  23370. static double mp_det(_cimg_math_parser& mp) {
  23371. const double *ptrs = &_mp_arg(2) + 1;
  23372. const unsigned int k = (unsigned int)mp.opcode[3];
  23373. return CImg<doubleT>(ptrs,k,k,1,1,true).det();
  23374. }
  23375. static double mp_diag(_cimg_math_parser& mp) {
  23376. const unsigned int i_end = (unsigned int)mp.opcode[2], siz = mp.opcode[2] - 3;
  23377. double *ptrd = &_mp_arg(1) + 1;
  23378. std::memset(ptrd,0,siz*siz*sizeof(double));
  23379. for (unsigned int i = 3; i<i_end; ++i) { *(ptrd++) = _mp_arg(i); ptrd+=siz; }
  23380. return cimg::type<double>::nan();
  23381. }
  23382. static double mp_display_memory(_cimg_math_parser& mp) {
  23383. cimg::unused(mp);
  23384. std::fputc('\n',cimg::output());
  23385. CImg<charT> title(128);
  23386. cimg_snprintf(title,title._width,"%s (%u)","[" cimg_appname "_math_parser] Memory snapshot",mp.mem._width);
  23387. mp.mem.display(title);
  23388. return cimg::type<double>::nan();
  23389. }
  23390. static double mp_display(_cimg_math_parser& mp) {
  23391. const unsigned int
  23392. _siz = (unsigned int)mp.opcode[3],
  23393. siz = _siz?_siz:1;
  23394. const double *const ptr = &_mp_arg(1) + (_siz?1:0);
  23395. const int
  23396. w = (int)_mp_arg(4),
  23397. h = (int)_mp_arg(5),
  23398. d = (int)_mp_arg(6),
  23399. s = (int)_mp_arg(7);
  23400. CImg<doubleT> img;
  23401. if (w>0 && h>0 && d>0 && s>0) {
  23402. if ((unsigned int)w*h*d*s<=siz) img.assign(ptr,w,h,d,s,true);
  23403. else img.assign(ptr,siz).resize(w,h,d,s,-1);
  23404. } else img.assign(ptr,1,siz,1,1,true);
  23405. CImg<charT> expr(mp.opcode[2] - 8);
  23406. const ulongT *ptrs = mp.opcode._data + 8;
  23407. cimg_for(expr,ptrd,char) *ptrd = (char)*(ptrs++);
  23408. ((CImg<charT>::string("[" cimg_appname "_math_parser] ",false,true),expr)>'x').move_to(expr);
  23409. cimg::strellipsize(expr);
  23410. std::fputc('\n',cimg::output());
  23411. img.display(expr._data);
  23412. return cimg::type<double>::nan();
  23413. }
  23414. static double mp_div(_cimg_math_parser& mp) {
  23415. return _mp_arg(2)/_mp_arg(3);
  23416. }
  23417. static double mp_dot(_cimg_math_parser& mp) {
  23418. const unsigned int siz = (unsigned int)mp.opcode[4];
  23419. return CImg<doubleT>(&_mp_arg(2) + 1,1,siz,1,1,true).
  23420. dot(CImg<doubleT>(&_mp_arg(3) + 1,1,siz,1,1,true));
  23421. }
  23422. static double mp_do(_cimg_math_parser& mp) {
  23423. const ulongT
  23424. mem_body = mp.opcode[1],
  23425. mem_cond = mp.opcode[2];
  23426. const CImg<ulongT>
  23427. *const p_body = ++mp.p_code,
  23428. *const p_cond = p_body + mp.opcode[3],
  23429. *const p_end = p_cond + mp.opcode[4];
  23430. const unsigned int vsiz = (unsigned int)mp.opcode[5];
  23431. if (mp.opcode[6]) { // Set default value for result and condition if necessary
  23432. if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
  23433. else mp.mem[mem_body] = cimg::type<double>::nan();
  23434. }
  23435. if (mp.opcode[7]) mp.mem[mem_cond] = 0;
  23436. const unsigned int _break_type = mp.break_type;
  23437. mp.break_type = 0;
  23438. do {
  23439. for (mp.p_code = p_body; mp.p_code<p_cond; ++mp.p_code) { // Evaluate body
  23440. mp.opcode._data = mp.p_code->_data;
  23441. const ulongT target = mp.opcode[1];
  23442. mp.mem[target] = _cimg_mp_defunc(mp);
  23443. }
  23444. if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
  23445. for (mp.p_code = p_cond; mp.p_code<p_end; ++mp.p_code) { // Evaluate condition
  23446. mp.opcode._data = mp.p_code->_data;
  23447. const ulongT target = mp.opcode[1];
  23448. mp.mem[target] = _cimg_mp_defunc(mp);
  23449. }
  23450. if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
  23451. } while (mp.mem[mem_cond]);
  23452. mp.break_type = _break_type;
  23453. mp.p_code = p_end - 1;
  23454. return mp.mem[mem_body];
  23455. }
  23456. static double mp_echo(_cimg_math_parser& mp) {
  23457. const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2;
  23458. if (!nb_args) { std::fputc('\n',cimg::output()); return cimg::type<double>::nan(); } // No arguments
  23459. CImgList<charT> _str;
  23460. CImg<charT> it;
  23461. for (unsigned int n = 0; n<nb_args; ++n) {
  23462. const unsigned int siz = (unsigned int)mp.opcode[4 + 2*n];
  23463. if (siz) { // Vector argument -> string
  23464. const double *ptr = &_mp_arg(3 + 2*n) + 1;
  23465. unsigned int l = 0;
  23466. while (l<siz && ptr[l]) ++l;
  23467. CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str);
  23468. } else { // Scalar argument -> number
  23469. it.assign(24);
  23470. cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n));
  23471. CImg<charT>::string(it,false,true).move_to(_str);
  23472. }
  23473. }
  23474. CImg(1,1,1,1,0).move_to(_str);
  23475. const CImg<charT> str = _str>'x';
  23476. std::fprintf(cimg::output(),"\n%s",str._data);
  23477. return cimg::type<double>::nan();
  23478. }
  23479. static double mp_ellipse(_cimg_math_parser& mp) {
  23480. mp_check_list(mp,"ellipse");
  23481. const unsigned int i_end = (unsigned int)mp.opcode[2];
  23482. unsigned int ind = (unsigned int)mp.opcode[3];
  23483. if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.imglist.width());
  23484. CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
  23485. CImg<T> color(img._spectrum,1,1,1,0);
  23486. bool is_invalid_arguments = false, is_outlined = false;
  23487. float r1 = 0, r2 = 0, angle = 0, opacity = 1;
  23488. unsigned int i = 4, pattern = ~0U;
  23489. int x0 = 0, y0 = 0;
  23490. if (i>=i_end) is_invalid_arguments = true;
  23491. else {
  23492. x0 = (int)cimg::round(_mp_arg(i++));
  23493. if (i>=i_end) is_invalid_arguments = true;
  23494. else {
  23495. y0 = (int)cimg::round(_mp_arg(i++));
  23496. if (i>=i_end) is_invalid_arguments = true;
  23497. else {
  23498. r1 = (float)_mp_arg(i++);
  23499. if (i>=i_end) r2 = r1;
  23500. else {
  23501. r2 = (float)_mp_arg(i++);
  23502. if (i<i_end) {
  23503. angle = (float)(_mp_arg(i++)*180/cimg::PI);
  23504. if (i<i_end) {
  23505. opacity = (float)_mp_arg(i++);
  23506. if (r1<0 && r2<0) {
  23507. pattern = (unsigned int)_mp_arg(i++);
  23508. is_outlined = true;
  23509. r1 = -r1; r2 = -r2;
  23510. }
  23511. if (i<i_end) {
  23512. cimg_forX(color,k) if (i<i_end) color[k] = (T)_mp_arg(i++);
  23513. else { color.resize(k,1,1,1,-1); break; }
  23514. color.resize(img._spectrum,1,1,1,0,2);
  23515. }
  23516. }
  23517. }
  23518. }
  23519. }
  23520. }
  23521. }
  23522. if (!is_invalid_arguments) {
  23523. if (is_outlined) img.draw_ellipse(x0,y0,r1,r2,angle,color._data,opacity,pattern);
  23524. else img.draw_ellipse(x0,y0,r1,r2,angle,color._data,opacity);
  23525. } else {
  23526. CImg<doubleT> args(i_end - 4);
  23527. cimg_forX(args,k) args[k] = _mp_arg(4 + k);
  23528. if (ind==~0U)
  23529. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'ellipse()': "
  23530. "Invalid arguments '%s'. ",
  23531. mp.imgin.pixel_type(),args.value_string()._data);
  23532. else
  23533. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'ellipse()': "
  23534. "Invalid arguments '#%u%s%s'. ",
  23535. mp.imgin.pixel_type(),ind,args._width?",":"",args.value_string()._data);
  23536. }
  23537. return cimg::type<double>::nan();
  23538. }
  23539. static double mp_eq(_cimg_math_parser& mp) {
  23540. return (double)(_mp_arg(2)==_mp_arg(3));
  23541. }
  23542. #if cimg_use_cpp11==1
  23543. static double mp_erf(_cimg_math_parser& mp) {
  23544. return std::erf(_mp_arg(2));
  23545. }
  23546. #endif
  23547. static double mp_erfinv(_cimg_math_parser& mp) {
  23548. return cimg::erfinv(_mp_arg(2));
  23549. }
  23550. static double mp_exp(_cimg_math_parser& mp) {
  23551. return std::exp(_mp_arg(2));
  23552. }
  23553. static double mp_expr(_cimg_math_parser& mp) {
  23554. const unsigned int
  23555. sizs = (unsigned int)mp.opcode[3],
  23556. w = (unsigned int)mp.opcode[4],
  23557. h = (unsigned int)mp.opcode[5],
  23558. d = (unsigned int)mp.opcode[6],
  23559. s = (unsigned int)mp.opcode[7],
  23560. sizd = w*h*d*s;
  23561. const double *ptrs = &_mp_arg(2) + 1;
  23562. double *ptrd = &_mp_arg(1);
  23563. CImg<charT> ss(sizs + 1);
  23564. cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptrs[i];
  23565. ss.back() = 0;
  23566. if (!sizd) return CImg<T>(w,h,d,s,0).eval(ss,0,0,0,0,&mp.imglist); // Scalar result
  23567. CImg<doubleT>(++ptrd,w,h,d,s,true) = CImg<T>(w,h,d,s,0).fill(ss,true,true,&mp.imglist);
  23568. return cimg::type<double>::nan();
  23569. }
  23570. static double mp_eye(_cimg_math_parser& mp) {
  23571. double *ptrd = &_mp_arg(1) + 1;
  23572. const unsigned int k = (unsigned int)mp.opcode[2];
  23573. CImg<doubleT>(ptrd,k,k,1,1,true).identity_matrix();
  23574. return cimg::type<double>::nan();
  23575. }
  23576. static double mp_f2ui(_cimg_math_parser& mp) {
  23577. return (double)cimg::float2uint((float)_mp_arg(2));
  23578. }
  23579. static double mp_factorial(_cimg_math_parser& mp) {
  23580. return cimg::factorial((int)_mp_arg(2));
  23581. }
  23582. static double mp_fibonacci(_cimg_math_parser& mp) {
  23583. return cimg::fibonacci((int)_mp_arg(2));
  23584. }
  23585. static double mp_fill(_cimg_math_parser& mp) {
  23586. unsigned int siz = (unsigned int)mp.opcode[2];
  23587. double
  23588. *ptrd = &_mp_arg(1),
  23589. *const ptrc = mp.opcode[3]!=~0U?&_mp_arg(3):0,
  23590. *const ptrs = &_mp_arg(4);
  23591. if (siz) ++ptrd; else ++siz; // Fill vector-valued slot
  23592. const CImg<ulongT>
  23593. *const p_body = ++mp.p_code,
  23594. *const p_end = p_body + mp.opcode[5];
  23595. const unsigned int _break_type = mp.break_type;
  23596. mp.break_type = 0;
  23597. unsigned int it = 0;
  23598. if (ptrc) { // Version with loop variable (3 arguments)
  23599. while (it<siz) {
  23600. *ptrc = (double)it;
  23601. for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
  23602. mp.opcode._data = mp.p_code->_data;
  23603. const ulongT target = mp.opcode[1];
  23604. mp.mem[target] = _cimg_mp_defunc(mp);
  23605. }
  23606. if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
  23607. else ptrd[it] = *ptrs;
  23608. ++it;
  23609. }
  23610. *ptrc = (double)it;
  23611. } else // Version without loop variable (2 arguments)
  23612. while (it<siz) {
  23613. for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
  23614. mp.opcode._data = mp.p_code->_data;
  23615. const ulongT target = mp.opcode[1];
  23616. mp.mem[target] = _cimg_mp_defunc(mp);
  23617. }
  23618. if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
  23619. else ptrd[it] = *ptrs;
  23620. ++it;
  23621. }
  23622. mp.break_type = _break_type;
  23623. mp.p_code = p_end - 1;
  23624. return *ptrd;
  23625. }
  23626. static double mp_find(_cimg_math_parser& mp) {
  23627. const int _step = (int)_mp_arg(6), step = _step?_step:-1;
  23628. const ulongT siz = (ulongT)mp.opcode[3];
  23629. longT ind = (longT)(mp.opcode[5]!=_cimg_mp_slot_nan?_mp_arg(5):step>0?0:siz - 1);
  23630. if (ind<0 || ind>=(longT)siz) return -1.;
  23631. const double
  23632. *const ptrb = &_mp_arg(2) + 1,
  23633. *const ptre = ptrb + siz,
  23634. val = _mp_arg(4),
  23635. *ptr = ptrb + ind;
  23636. // Forward search
  23637. if (step>0) {
  23638. while (ptr<ptre && *ptr!=val) ptr+=step;
  23639. return ptr>=ptre?-1.:(double)(ptr - ptrb);
  23640. }
  23641. // Backward search.
  23642. while (ptr>=ptrb && *ptr!=val) ptr+=step;
  23643. return ptr<ptrb?-1.:(double)(ptr - ptrb);
  23644. }
  23645. static double mp_find_seq(_cimg_math_parser& mp) {
  23646. const int _step = (int)_mp_arg(7), step = _step?_step:-1;
  23647. const ulongT
  23648. siz1 = (ulongT)mp.opcode[3],
  23649. siz2 = (ulongT)mp.opcode[5];
  23650. longT ind = (longT)(mp.opcode[6]!=_cimg_mp_slot_nan?_mp_arg(6):step>0?0:siz1 - 1);
  23651. if (ind<0 || ind>=(longT)siz1) return -1.;
  23652. const double
  23653. *const ptr1b = &_mp_arg(2) + 1,
  23654. *const ptr1e = ptr1b + siz1,
  23655. *const ptr2b = &_mp_arg(4) + 1,
  23656. *const ptr2e = ptr2b + siz2,
  23657. *ptr1 = ptr1b + ind,
  23658. *p1 = 0,
  23659. *p2 = 0;
  23660. // Forward search.
  23661. if (step>0) {
  23662. do {
  23663. while (ptr1<ptr1e && *ptr1!=*ptr2b) ptr1+=step;
  23664. if (ptr1>=ptr1e) return -1.;
  23665. p1 = ptr1 + 1;
  23666. p2 = ptr2b + 1;
  23667. while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
  23668. } while (p2<ptr2e && (ptr1+=step)<ptr1e);
  23669. return p2<ptr2e?-1.:(double)(ptr1 - ptr1b);
  23670. }
  23671. // Backward search.
  23672. do {
  23673. while (ptr1>=ptr1b && *ptr1!=*ptr2b) ptr1+=step;
  23674. if (ptr1<ptr1b) return -1.;
  23675. p1 = ptr1 + 1;
  23676. p2 = ptr2b + 1;
  23677. while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
  23678. } while (p2<ptr2e && (ptr1+=step)>=ptr1b);
  23679. return p2<ptr2e?-1.:(double)(ptr1 - ptr1b);
  23680. }
  23681. static double mp_floor(_cimg_math_parser& mp) {
  23682. return std::floor(_mp_arg(2));
  23683. }
  23684. static double mp_for(_cimg_math_parser& mp) {
  23685. const ulongT
  23686. mem_body = mp.opcode[1],
  23687. mem_cond = mp.opcode[3];
  23688. const CImg<ulongT>
  23689. *const p_init = ++mp.p_code,
  23690. *const p_cond = p_init + mp.opcode[4],
  23691. *const p_body = p_cond + mp.opcode[5],
  23692. *const p_post = p_body + mp.opcode[6],
  23693. *const p_end = p_post + mp.opcode[7];
  23694. const unsigned int vsiz = (unsigned int)mp.opcode[2];
  23695. bool is_cond = false;
  23696. if (mp.opcode[8]) { // Set default value for result and condition if necessary
  23697. if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
  23698. else mp.mem[mem_body] = cimg::type<double>::nan();
  23699. }
  23700. if (mp.opcode[9]) mp.mem[mem_cond] = 0;
  23701. const unsigned int _break_type = mp.break_type;
  23702. mp.break_type = 0;
  23703. for (mp.p_code = p_init; mp.p_code<p_cond; ++mp.p_code) { // Evaluate init
  23704. mp.opcode._data = mp.p_code->_data;
  23705. const ulongT target = mp.opcode[1];
  23706. mp.mem[target] = _cimg_mp_defunc(mp);
  23707. }
  23708. if (!mp.break_type) do {
  23709. for (mp.p_code = p_cond; mp.p_code<p_body; ++mp.p_code) { // Evaluate condition
  23710. mp.opcode._data = mp.p_code->_data;
  23711. const ulongT target = mp.opcode[1];
  23712. mp.mem[target] = _cimg_mp_defunc(mp);
  23713. }
  23714. if (mp.break_type==1) break;
  23715. is_cond = (bool)mp.mem[mem_cond];
  23716. if (is_cond && !mp.break_type) {
  23717. for (mp.p_code = p_body; mp.p_code<p_post; ++mp.p_code) { // Evaluate body
  23718. mp.opcode._data = mp.p_code->_data;
  23719. const ulongT target = mp.opcode[1];
  23720. mp.mem[target] = _cimg_mp_defunc(mp);
  23721. }
  23722. if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
  23723. for (mp.p_code = p_post; mp.p_code<p_end; ++mp.p_code) { // Evaluate post-code
  23724. mp.opcode._data = mp.p_code->_data;
  23725. const ulongT target = mp.opcode[1];
  23726. mp.mem[target] = _cimg_mp_defunc(mp);
  23727. }
  23728. if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
  23729. }
  23730. } while (is_cond);
  23731. mp.break_type = _break_type;
  23732. mp.p_code = p_end - 1;
  23733. return mp.mem[mem_body];
  23734. }
  23735. static double mp_fsize(_cimg_math_parser& mp) {
  23736. const double *ptrs = &_mp_arg(2) + 1;
  23737. const ulongT siz = (ulongT)mp.opcode[3];
  23738. CImg<charT> ss(siz + 1);
  23739. cimg_forX(ss,i) ss[i] = (char)ptrs[i];
  23740. ss.back() = 0;
  23741. return (double)cimg::fsize(ss);
  23742. }
  23743. static double mp_g(_cimg_math_parser& mp) {
  23744. cimg::unused(mp);
  23745. return cimg::grand(&mp.rng);
  23746. }
  23747. #if cimg_use_cpp11==1
  23748. static double mp_gamma(_cimg_math_parser& mp) {
  23749. return std::tgamma(_mp_arg(2));
  23750. }
  23751. #endif
  23752. static double mp_gauss(_cimg_math_parser& mp) {
  23753. const double x = _mp_arg(2), s = _mp_arg(3);
  23754. return std::exp(-x*x/(2*s*s))/(_mp_arg(4)?std::sqrt(2*s*s*cimg::PI):1);
  23755. }
  23756. #ifdef cimg_mp_func_get
  23757. static double mp_get(_cimg_math_parser& mp) {
  23758. const double *ptrs = &_mp_arg(2) + 1;
  23759. double *ptrd = &_mp_arg(1);
  23760. const unsigned int
  23761. sizs = (unsigned int)mp.opcode[3],
  23762. sizd = (unsigned int)mp.opcode[4];
  23763. const bool to_string = (bool)mp.opcode[5];
  23764. CImg<charT> ss(sizs + 1);
  23765. cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptrs[i];
  23766. ss.back() = 0;
  23767. if (sizd) cimg_mp_func_get(ptrd + 1,sizd,to_string,ss._data);
  23768. else cimg_mp_func_get(ptrd,0,to_string,ss._data);
  23769. return cimg::type<double>::nan();
  23770. }
  23771. #endif
  23772. static double mp_gcd(_cimg_math_parser& mp) {
  23773. return cimg::gcd((long)_mp_arg(2),(long)_mp_arg(3));
  23774. }
  23775. #ifdef cimg_mp_func_name
  23776. static double mp_name(_cimg_math_parser& mp) {
  23777. double *const ptr = &_mp_arg(1) + 1;
  23778. const unsigned int siz = (unsigned int)mp.opcode[3];
  23779. unsigned int ind = (unsigned int)mp.opcode[2];
  23780. if (ind==~0U) std::memset(ptr,0,siz*sizeof(double));
  23781. else {
  23782. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  23783. cimg_mp_func_name(ind,ptr,siz);
  23784. }
  23785. return cimg::type<double>::nan();
  23786. }
  23787. #endif
  23788. static double mp_gt(_cimg_math_parser& mp) {
  23789. return (double)(_mp_arg(2)>_mp_arg(3));
  23790. }
  23791. static double mp_gte(_cimg_math_parser& mp) {
  23792. return (double)(_mp_arg(2)>=_mp_arg(3));
  23793. }
  23794. static double mp_i(_cimg_math_parser& mp) {
  23795. return (double)mp.imgin((int)mp.mem[_cimg_mp_slot_x],(int)mp.mem[_cimg_mp_slot_y],
  23796. (int)mp.mem[_cimg_mp_slot_z],(int)mp.mem[_cimg_mp_slot_c]);
  23797. }
  23798. static double mp_if(_cimg_math_parser& mp) {
  23799. const bool is_cond = (bool)_mp_arg(2);
  23800. const ulongT
  23801. mem_left = mp.opcode[3],
  23802. mem_right = mp.opcode[4];
  23803. const CImg<ulongT>
  23804. *const p_right = ++mp.p_code + mp.opcode[5],
  23805. *const p_end = p_right + mp.opcode[6];
  23806. const unsigned int vtarget = (unsigned int)mp.opcode[1], vsiz = (unsigned int)mp.opcode[7];
  23807. if (is_cond) for ( ; mp.p_code<p_right; ++mp.p_code) {
  23808. mp.opcode._data = mp.p_code->_data;
  23809. const ulongT target = mp.opcode[1];
  23810. mp.mem[target] = _cimg_mp_defunc(mp);
  23811. }
  23812. else for (mp.p_code = p_right; mp.p_code<p_end; ++mp.p_code) {
  23813. mp.opcode._data = mp.p_code->_data;
  23814. const ulongT target = mp.opcode[1];
  23815. mp.mem[target] = _cimg_mp_defunc(mp);
  23816. }
  23817. if (mp.p_code==mp.p_break) --mp.p_code;
  23818. else mp.p_code = p_end - 1;
  23819. if (vsiz) std::memcpy(&mp.mem[vtarget] + 1,&mp.mem[is_cond?mem_left:mem_right] + 1,sizeof(double)*vsiz);
  23820. return mp.mem[is_cond?mem_left:mem_right];
  23821. }
  23822. static double mp_image_d(_cimg_math_parser& mp) {
  23823. unsigned int ind = (unsigned int)mp.opcode[2];
  23824. if (ind!=~0U) {
  23825. if (!mp.imglist.width()) return cimg::type<double>::nan();
  23826. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  23827. }
  23828. const CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
  23829. return (double)img.depth();
  23830. }
  23831. static double mp_image_display(_cimg_math_parser& mp) {
  23832. mp_check_list(mp,"display");
  23833. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  23834. cimg::mutex(6);
  23835. CImg<T> &img = mp.imglist[ind];
  23836. CImg<charT> title(256);
  23837. std::fputc('\n',cimg::output());
  23838. cimg_snprintf(title,title._width,"[ Image #%u ]",ind);
  23839. img.display(title);
  23840. cimg::mutex(6,0);
  23841. return cimg::type<double>::nan();
  23842. }
  23843. static double mp_image_draw(_cimg_math_parser& mp) {
  23844. const int x = (int)_mp_arg(4), y = (int)_mp_arg(5), z = (int)_mp_arg(6), c = (int)_mp_arg(7);
  23845. unsigned int ind = (unsigned int)mp.opcode[3];
  23846. if (ind!=~0U) {
  23847. if (!mp.imglist.width()) return cimg::type<double>::nan();
  23848. ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.imglist.width());
  23849. }
  23850. CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
  23851. unsigned int
  23852. dx = (unsigned int)mp.opcode[8],
  23853. dy = (unsigned int)mp.opcode[9],
  23854. dz = (unsigned int)mp.opcode[10],
  23855. dc = (unsigned int)mp.opcode[11];
  23856. dx = dx==~0U?img._width:(unsigned int)_mp_arg(8);
  23857. dy = dy==~0U?img._height:(unsigned int)_mp_arg(9);
  23858. dz = dz==~0U?img._depth:(unsigned int)_mp_arg(10);
  23859. dc = dc==~0U?img._spectrum:(unsigned int)_mp_arg(11);
  23860. const ulongT sizS = mp.opcode[2];
  23861. if (sizS<(ulongT)dx*dy*dz*dc)
  23862. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
  23863. "Sprite vector (%lu values) and its specified geometry (%u,%u,%u,%u) "
  23864. "(%lu values) do not match.",
  23865. mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc);
  23866. const CImg<doubleT> S(&_mp_arg(1) + 1,dx,dy,dz,dc,true);
  23867. const float opacity = (float)_mp_arg(12);
  23868. if (img._data) {
  23869. if (mp.opcode[13]!=~0U) { // Opacity mask specified
  23870. const ulongT sizM = mp.opcode[14];
  23871. if (sizM<(ulongT)dx*dy*dz)
  23872. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
  23873. "Mask vector (%lu values) and specified sprite geometry (%u,%u,%u,%u) "
  23874. "(%lu values) do not match.",
  23875. mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc);
  23876. const CImg<doubleT> M(&_mp_arg(13) + 1,dx,dy,dz,(unsigned int)(sizM/(dx*dy*dz)),true);
  23877. img.draw_image(x,y,z,c,S,M,opacity,(float)_mp_arg(15));
  23878. } else img.draw_image(x,y,z,c,S,opacity);
  23879. }
  23880. return cimg::type<double>::nan();
  23881. }
  23882. static double mp_image_h(_cimg_math_parser& mp) {
  23883. unsigned int ind = (unsigned int)mp.opcode[2];
  23884. if (ind!=~0U) {
  23885. if (!mp.imglist.width()) return cimg::type<double>::nan();
  23886. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  23887. }
  23888. const CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
  23889. return (double)img.height();
  23890. }
  23891. static double mp_image_median(_cimg_math_parser& mp) {
  23892. unsigned int ind = (unsigned int)mp.opcode[2];
  23893. if (ind!=~0U) {
  23894. if (!mp.imglist.width()) return cimg::type<double>::nan();
  23895. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  23896. }
  23897. const CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
  23898. return (double)img.median();
  23899. }
  23900. static double mp_image_norm(_cimg_math_parser& mp) {
  23901. unsigned int ind = (unsigned int)mp.opcode[2];
  23902. if (ind!=~0U) {
  23903. if (!mp.imglist.width()) return cimg::type<double>::nan();
  23904. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  23905. }
  23906. const CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
  23907. return (double)img.magnitude(2);
  23908. }
  23909. static double mp_image_print(_cimg_math_parser& mp) {
  23910. mp_check_list(mp,"print");
  23911. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  23912. cimg::mutex(6);
  23913. CImg<T> &img = mp.imglist[ind];
  23914. CImg<charT> title(256);
  23915. std::fputc('\n',cimg::output());
  23916. cimg_snprintf(title,title._width,"[ Image #%u ]",ind);
  23917. img.print(title);
  23918. cimg::mutex(6,0);
  23919. return cimg::type<double>::nan();
  23920. }
  23921. static double mp_image_resize(_cimg_math_parser& mp) {
  23922. mp_check_list(mp,"resize");
  23923. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  23924. cimg::mutex(6);
  23925. CImg<T> &img = mp.imglist[ind];
  23926. const double
  23927. _w = mp.opcode[3]==~0U?-100:_mp_arg(3),
  23928. _h = mp.opcode[4]==~0U?-100:_mp_arg(4),
  23929. _d = mp.opcode[5]==~0U?-100:_mp_arg(5),
  23930. _s = mp.opcode[6]==~0U?-100:_mp_arg(6);
  23931. const unsigned int
  23932. w = (unsigned int)(_w>=0?_w:-_w*img.width()/100),
  23933. h = (unsigned int)(_h>=0?_h:-_h*img.height()/100),
  23934. d = (unsigned int)(_d>=0?_d:-_d*img.depth()/100),
  23935. s = (unsigned int)(_s>=0?_s:-_s*img.spectrum()/100),
  23936. interp = (int)_mp_arg(7);
  23937. if (mp.is_fill && img._data==mp.imgout._data) {
  23938. cimg::mutex(6,0);
  23939. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'resize()': "
  23940. "Cannot both fill and resize image (%u,%u,%u,%u) "
  23941. "to new dimensions (%u,%u,%u,%u).",
  23942. img.pixel_type(),img._width,img._height,img._depth,img._spectrum,w,h,d,s);
  23943. }
  23944. const unsigned int
  23945. boundary = (int)_mp_arg(8);
  23946. const float
  23947. cx = (float)_mp_arg(9),
  23948. cy = (float)_mp_arg(10),
  23949. cz = (float)_mp_arg(11),
  23950. cc = (float)_mp_arg(12);
  23951. img.resize(w,h,d,s,interp,boundary,cx,cy,cz,cc);
  23952. cimg::mutex(6,0);
  23953. return cimg::type<double>::nan();
  23954. }
  23955. static double mp_image_s(_cimg_math_parser& mp) {
  23956. unsigned int ind = (unsigned int)mp.opcode[2];
  23957. if (ind!=~0U) {
  23958. if (!mp.imglist.width()) return cimg::type<double>::nan();
  23959. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  23960. }
  23961. const CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
  23962. return (double)img.spectrum();
  23963. }
  23964. static double mp_image_sort(_cimg_math_parser& mp) {
  23965. mp_check_list(mp,"sort");
  23966. const bool is_increasing = (bool)_mp_arg(3);
  23967. const unsigned int
  23968. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()),
  23969. axis = (unsigned int)_mp_arg(4);
  23970. cimg::mutex(6);
  23971. CImg<T> &img = mp.imglist[ind];
  23972. img.sort(is_increasing,
  23973. axis==0 || axis=='x'?'x':
  23974. axis==1 || axis=='y'?'y':
  23975. axis==2 || axis=='z'?'z':
  23976. axis==3 || axis=='c'?'c':0);
  23977. cimg::mutex(6,0);
  23978. return cimg::type<double>::nan();
  23979. }
  23980. static double mp_image_stats(_cimg_math_parser& mp) {
  23981. double *ptrd = &_mp_arg(1) + 1;
  23982. unsigned int ind = (unsigned int)mp.opcode[2];
  23983. if (ind==~0U)
  23984. CImg<doubleT>(ptrd,14,1,1,1,true) = mp.imgout.get_stats();
  23985. else {
  23986. if (!mp.imglist.width()) return cimg::type<double>::nan();
  23987. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  23988. CImg<doubleT>(ptrd,14,1,1,1,true) = mp.imglist[ind].get_stats();
  23989. }
  23990. return cimg::type<double>::nan();
  23991. }
  23992. static double mp_image_w(_cimg_math_parser& mp) {
  23993. unsigned int ind = (unsigned int)mp.opcode[2];
  23994. if (ind!=~0U) {
  23995. if (!mp.imglist.width()) return cimg::type<double>::nan();
  23996. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  23997. }
  23998. const CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
  23999. return (double)img.width();
  24000. }
  24001. static double mp_image_wh(_cimg_math_parser& mp) {
  24002. unsigned int ind = (unsigned int)mp.opcode[2];
  24003. if (ind!=~0U) {
  24004. if (!mp.imglist.width()) return cimg::type<double>::nan();
  24005. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24006. }
  24007. const CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
  24008. return (double)img.width()*img.height();
  24009. }
  24010. static double mp_image_whd(_cimg_math_parser& mp) {
  24011. unsigned int ind = (unsigned int)mp.opcode[2];
  24012. if (ind!=~0U) {
  24013. if (!mp.imglist.width()) return cimg::type<double>::nan();
  24014. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24015. }
  24016. const CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
  24017. return (double)img.width()*img.height()*img.depth();
  24018. }
  24019. static double mp_image_whds(_cimg_math_parser& mp) {
  24020. unsigned int ind = (unsigned int)mp.opcode[2];
  24021. if (ind!=~0U) {
  24022. if (!mp.imglist.width()) return cimg::type<double>::nan();
  24023. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24024. }
  24025. const CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
  24026. return (double)img.width()*img.height()*img.depth()*img.spectrum();
  24027. }
  24028. static double mp_increment(_cimg_math_parser& mp) {
  24029. return _mp_arg(2) + 1;
  24030. }
  24031. static double mp_inrange(_cimg_math_parser& mp) {
  24032. const unsigned int sizd = (unsigned int)mp.opcode[2];
  24033. const bool
  24034. include_m = (bool)_mp_arg(9),
  24035. include_M = (bool)_mp_arg(10);
  24036. if (!sizd) { // Scalar result
  24037. const double val = _mp_arg(3);
  24038. const double m = _mp_arg(5), M = _mp_arg(7);
  24039. if (M>=m) return (double)((include_m?(val>=m):(val>m)) && (include_M?(val<=M):(val<M)));
  24040. else return (double)((include_M?(val>=M):(val>M)) && (include_m?(val<=m):(val<m)));
  24041. }
  24042. // Vector result
  24043. const unsigned int
  24044. siz1 = (unsigned int)mp.opcode[4],
  24045. siz2 = (unsigned int)mp.opcode[6],
  24046. siz3 = (unsigned int)mp.opcode[8],
  24047. off1 = siz1?1:0,
  24048. off2 = siz2?1:0,
  24049. off3 = siz3?1:0;
  24050. double *ptrd = &_mp_arg(1) + 1;
  24051. const double
  24052. *ptr1 = &_mp_arg(3) + off1,
  24053. *ptr2 = &_mp_arg(5) + off2,
  24054. *ptr3 = &_mp_arg(7) + off3;
  24055. for (unsigned int k = 0; k<sizd; ++k) {
  24056. const double val = *ptr1;
  24057. const double m = *ptr2, M = *ptr3;
  24058. if (M>=m)
  24059. ptrd[k] = (double)((include_m?(val>=m):(val>m)) && (include_M?(val<=M):(val<M)));
  24060. else
  24061. ptrd[k] = (double)((include_M?(val>=M):(val>M)) && (include_m?(val<=m):(val<m)));
  24062. ptr1+=off1;
  24063. ptr2+=off2;
  24064. ptr3+=off3;
  24065. }
  24066. return cimg::type<double>::nan();
  24067. }
  24068. static double mp_int(_cimg_math_parser& mp) {
  24069. return (double)(longT)_mp_arg(2);
  24070. }
  24071. static double mp_ioff(_cimg_math_parser& mp) {
  24072. const unsigned int
  24073. boundary_conditions = (unsigned int)_mp_arg(3);
  24074. const CImg<T> &img = mp.imgin;
  24075. const longT
  24076. off = (longT)_mp_arg(2),
  24077. whds = (longT)img.size();
  24078. if (off>=0 && off<whds) return (double)img[off];
  24079. if (img._data) switch (boundary_conditions) {
  24080. case 3 : { // Mirror
  24081. const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
  24082. return (double)img[moff<whds?moff:whds2 - moff - 1];
  24083. }
  24084. case 2 : // Periodic
  24085. return (double)img[cimg::mod(off,whds)];
  24086. case 1 : // Neumann
  24087. return (double)img[off<0?0:whds - 1];
  24088. default : // Dirichlet
  24089. return 0;
  24090. }
  24091. return 0;
  24092. }
  24093. static double mp_isbool(_cimg_math_parser& mp) {
  24094. const double val = _mp_arg(2);
  24095. return (double)(val==0. || val==1.);
  24096. }
  24097. static double mp_isdir(_cimg_math_parser& mp) {
  24098. const unsigned int siz = (unsigned int)mp.opcode[3];
  24099. const double *const ptrs = &_mp_arg(2) + (siz?1:0);
  24100. if (!siz) { char str[2] = {}; *str = *ptrs; return (double)cimg::is_directory(str); }
  24101. CImg<charT> ss(siz + 1);
  24102. cimg_forX(ss,i) ss[i] = (char)ptrs[i];
  24103. ss.back() = 0;
  24104. return (double)cimg::is_directory(ss);
  24105. }
  24106. static double mp_isin(_cimg_math_parser& mp) {
  24107. const unsigned int
  24108. i_end = (unsigned int)mp.opcode[2],
  24109. siz_ref = (unsigned int)mp.opcode[4];
  24110. bool res = false;
  24111. if (siz_ref) { // Reference value is a vector
  24112. const CImg<doubleT> ref(&_mp_arg(3) + 1,siz_ref,1,1,1,true);
  24113. for (unsigned int i = 5; i<i_end; i+=2) {
  24114. const unsigned int siz = (unsigned int)mp.opcode[i + 1];
  24115. if (siz==siz_ref && CImg<doubleT>(&_mp_arg(i) + 1,siz,1,1,1,true)==ref) { res = true; break; }
  24116. }
  24117. } else { // Reference value is a scalar
  24118. const double ref = _mp_arg(3);
  24119. for (unsigned i = 5; i<i_end; i+=2) {
  24120. const unsigned int siz = (unsigned int)mp.opcode[i + 1];
  24121. if (!siz && _mp_arg(i)==ref) { res = true; break; }
  24122. }
  24123. }
  24124. return res?1.:0.;
  24125. }
  24126. static double mp_isinf(_cimg_math_parser& mp) {
  24127. return (double)cimg::type<double>::is_inf(_mp_arg(2));
  24128. }
  24129. static double mp_isint(_cimg_math_parser& mp) {
  24130. return (double)((double)(longT)_mp_arg(2)==_mp_arg(2));
  24131. }
  24132. static double mp_isfile(_cimg_math_parser& mp) {
  24133. const unsigned int siz = (unsigned int)mp.opcode[3];
  24134. const double *const ptrs = &_mp_arg(2) + (siz?1:0);
  24135. if (!siz) { char str[2] = {}; *str = *ptrs; return (double)cimg::is_file(str); }
  24136. CImg<charT> ss(siz + 1);
  24137. cimg_forX(ss,i) ss[i] = (char)ptrs[i];
  24138. ss.back() = 0;
  24139. return (double)cimg::is_file(ss);
  24140. }
  24141. static double mp_isnan(_cimg_math_parser& mp) {
  24142. return (double)cimg::type<double>::is_nan(_mp_arg(2));
  24143. }
  24144. static double mp_isvarname(_cimg_math_parser& mp) {
  24145. const unsigned int siz = (unsigned int)mp.opcode[3];
  24146. const double *ptrs = &_mp_arg(2) + (siz?1:0);
  24147. if (!siz) {
  24148. const char c = (char)*ptrs;
  24149. return (c>='a' && c<='z') || (c>='A' && c<='Z') || c=='_';
  24150. }
  24151. if (*ptrs>='0' && *ptrs<='9') return 0;
  24152. for (unsigned int k = 0; k<siz; ++k) if (!cimg::is_varchar((char)ptrs[k])) return 0;
  24153. return 1;
  24154. }
  24155. static double mp_ixyzc(_cimg_math_parser& mp) {
  24156. const unsigned int
  24157. interpolation = (unsigned int)_mp_arg(6),
  24158. boundary_conditions = (unsigned int)_mp_arg(7);
  24159. const CImg<T> &img = mp.imgin;
  24160. const double
  24161. x = _mp_arg(2), y = _mp_arg(3),
  24162. z = _mp_arg(4), c = _mp_arg(5);
  24163. switch (interpolation) {
  24164. case 2 : // Cubic interpolation
  24165. switch (boundary_conditions) {
  24166. case 3 : { // Mirror
  24167. const float
  24168. w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
  24169. mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
  24170. mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
  24171. return (double)img._cubic_atXYZ(mx<img.width()?mx:w2 - mx - 1,
  24172. my<img.height()?my:h2 - my - 1,
  24173. mz<img.depth()?mz:d2 - mz - 1,
  24174. (int)(mc<img.spectrum()?mc:s2 - mc - 1));
  24175. }
  24176. case 2 : // Periodic
  24177. return (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,
  24178. (int)cimg::mod(c,(double)img._spectrum));
  24179. case 1 : // Neumann
  24180. return (double)img._cubic_atXYZ((float)x,(float)y,(float)z,
  24181. (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
  24182. default : // Dirichlet
  24183. if (c<0 || c>=img._spectrum) return (T)0;
  24184. return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
  24185. }
  24186. case 1 : // Linear interpolation
  24187. switch (boundary_conditions) {
  24188. case 3 : { // Mirror
  24189. const float
  24190. w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
  24191. mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
  24192. mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
  24193. return (double)img._linear_atXYZ(mx<img.width()?mx:w2 - mx - 1,
  24194. my<img.height()?my:h2 - my - 1,
  24195. mz<img.depth()?mz:d2 - mz - 1,
  24196. (int)(mc<img.spectrum()?mc:s2 - mc - 1));
  24197. }
  24198. case 2 : // Periodic
  24199. return (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,
  24200. (int)cimg::mod(c,(double)img._spectrum));
  24201. case 1 : // Neumann
  24202. return (double)img._linear_atXYZ((float)x,(float)y,(float)z,
  24203. (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
  24204. default : // Dirichlet
  24205. if (c<0 || c>=img._spectrum) return (T)0;
  24206. return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
  24207. }
  24208. default : // Nearest neighbor interpolation
  24209. switch (boundary_conditions) {
  24210. case 3 : { // Mirror
  24211. const int
  24212. w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
  24213. mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
  24214. mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
  24215. return (double)img(mx<img.width()?mx:w2 - mx - 1,
  24216. my<img.height()?my:h2 - my - 1,
  24217. mz<img.depth()?mz:d2 - mz - 1,
  24218. mc<img.spectrum()?mc:s2 - mc - 1);
  24219. }
  24220. case 2 : // Periodic
  24221. return (double)img((int)cimg::mod(x,(double)img._width),
  24222. (int)cimg::mod(y,(double)img._height),
  24223. (int)cimg::mod(z,(double)img._depth),
  24224. (int)cimg::mod(c,(double)img._spectrum));
  24225. case 1 : // Neumann
  24226. return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
  24227. default : // Dirichlet
  24228. return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
  24229. }
  24230. }
  24231. }
  24232. static double mp_joff(_cimg_math_parser& mp) {
  24233. const unsigned int
  24234. boundary_conditions = (unsigned int)_mp_arg(3);
  24235. const int
  24236. ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
  24237. oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
  24238. const CImg<T> &img = mp.imgin;
  24239. const longT
  24240. off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
  24241. whds = (longT)img.size();
  24242. if (off>=0 && off<whds) return (double)img[off];
  24243. if (img._data) switch (boundary_conditions) {
  24244. case 3 : { // Mirror
  24245. const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
  24246. return (double)img[moff<whds?moff:whds2 - moff - 1];
  24247. }
  24248. case 2 : // Periodic
  24249. return (double)img[cimg::mod(off,whds)];
  24250. case 1 : // Neumann
  24251. return (double)img[off<0?0:whds - 1];
  24252. default : // Dirichlet
  24253. return 0;
  24254. }
  24255. return 0;
  24256. }
  24257. static double mp_jxyzc(_cimg_math_parser& mp) {
  24258. const unsigned int
  24259. interpolation = (unsigned int)_mp_arg(6),
  24260. boundary_conditions = (unsigned int)_mp_arg(7);
  24261. const CImg<T> &img = mp.imgin;
  24262. const double
  24263. ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
  24264. oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c],
  24265. x = ox + _mp_arg(2), y = oy + _mp_arg(3),
  24266. z = oz + _mp_arg(4), c = oc + _mp_arg(5);
  24267. switch (interpolation) {
  24268. case 2 : // Cubic interpolation
  24269. switch (boundary_conditions) {
  24270. case 3 : { // Mirror
  24271. const float
  24272. w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
  24273. mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
  24274. mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
  24275. return (double)img._cubic_atXYZ(mx<img.width()?mx:w2 - mx - 1,
  24276. my<img.height()?my:h2 - my - 1,
  24277. mz<img.depth()?mz:d2 - mz - 1,
  24278. (int)(mc<img.spectrum()?mc:s2 - mc - 1));
  24279. }
  24280. case 2 : // Periodic
  24281. return (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,
  24282. (int)cimg::mod(c,(double)img._spectrum));
  24283. case 1 : // Neumann
  24284. return (double)img._cubic_atXYZ((float)x,(float)y,(float)z,
  24285. (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
  24286. default : // Dirichlet
  24287. if (c<0 || c>=img._spectrum) return (T)0;
  24288. return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
  24289. }
  24290. case 1 : // Linear interpolation
  24291. switch (boundary_conditions) {
  24292. case 3 : { // Mirror
  24293. const float
  24294. w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
  24295. mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
  24296. mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
  24297. return (double)img._linear_atXYZ(mx<img.width()?mx:w2 - mx - 1,
  24298. my<img.height()?my:h2 - my - 1,
  24299. mz<img.depth()?mz:d2 - mz - 1,
  24300. (int)(mc<img.spectrum()?mc:s2 - mc - 1));
  24301. }
  24302. case 2 : // Periodic
  24303. return (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,
  24304. (int)cimg::mod(c,(double)img._spectrum));
  24305. case 1 : // Neumann
  24306. return (double)img._linear_atXYZ((float)x,(float)y,(float)z,
  24307. (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
  24308. default : // Dirichlet
  24309. if (c<0 || c>=img._spectrum) return (T)0;
  24310. return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
  24311. }
  24312. default : // Nearest neighbor interpolation
  24313. switch (boundary_conditions) {
  24314. case 3 : { // Mirror
  24315. const int
  24316. w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
  24317. mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
  24318. mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
  24319. return (double)img(mx<img.width()?mx:w2 - mx - 1,
  24320. my<img.height()?my:h2 - my - 1,
  24321. mz<img.depth()?mz:d2 - mz - 1,
  24322. mc<img.spectrum()?mc:s2 - mc - 1);
  24323. }
  24324. case 2 : // Periodic
  24325. return (double)img((int)cimg::mod(x,(double)img._width),
  24326. (int)cimg::mod(y,(double)img._height),
  24327. (int)cimg::mod(z,(double)img._depth),
  24328. (int)cimg::mod(c,(double)img._spectrum));
  24329. case 1 : // Neumann
  24330. return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
  24331. default : // Dirichlet
  24332. return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
  24333. }
  24334. }
  24335. }
  24336. static double mp_kth(_cimg_math_parser& mp) {
  24337. const unsigned int i_end = (unsigned int)mp.opcode[2];
  24338. CImg<double> values;
  24339. if (i_end==5) values.assign(&_mp_arg(3),(unsigned int)mp.opcode[4],1,1,1,true); // Only a single argument
  24340. else {
  24341. unsigned int siz = 0;
  24342. for (unsigned int i = 4; i<i_end; i+=2) siz+=(unsigned int)mp.opcode[i];
  24343. values.assign(siz);
  24344. double *ptr = values;
  24345. for (unsigned int i = 3; i<i_end; i+=2) {
  24346. const unsigned int len = (unsigned int)mp.opcode[i + 1];
  24347. if (len>1) std::memcpy(ptr,&_mp_arg(i),len*sizeof(double));
  24348. else *ptr = _mp_arg(i);
  24349. ptr+=len;
  24350. }
  24351. }
  24352. longT ind = (longT)values[0];
  24353. ++values._data; --values._width; // Skip first value
  24354. if (ind<0) ind+=values.width() + 1;
  24355. ind = cimg::cut(ind,(longT)1,(longT)values.width());
  24356. const double &kth = values.kth_smallest((ulongT)(ind - 1));
  24357. --values._data; ++values._width;
  24358. return kth;
  24359. }
  24360. static double mp_lerp(_cimg_math_parser& mp) {
  24361. const double t = _mp_arg(4);
  24362. return _mp_arg(2)*(1-t) + _mp_arg(3)*t;
  24363. }
  24364. static double mp_linear_add(_cimg_math_parser& mp) {
  24365. return _mp_arg(2)*_mp_arg(3) + _mp_arg(4);
  24366. }
  24367. static double mp_linear_sub_left(_cimg_math_parser& mp) {
  24368. return _mp_arg(2)*_mp_arg(3) - _mp_arg(4);
  24369. }
  24370. static double mp_linear_sub_right(_cimg_math_parser& mp) {
  24371. return _mp_arg(4) - _mp_arg(2)*_mp_arg(3);
  24372. }
  24373. static double mp_list_depth(_cimg_math_parser& mp) {
  24374. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24375. return (double)mp.imglist[ind]._depth;
  24376. }
  24377. static double mp_list_find(_cimg_math_parser& mp) {
  24378. const unsigned int
  24379. indi = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24380. const CImg<T> &img = mp.imglist[indi];
  24381. const int _step = (int)_mp_arg(5), step = _step?_step:-1;
  24382. const ulongT siz = (ulongT)img.size();
  24383. longT ind = (longT)(mp.opcode[4]!=_cimg_mp_slot_nan?_mp_arg(4):step>0?0:siz - 1);
  24384. if (ind<0 || ind>=(longT)siz) return -1.;
  24385. const T
  24386. *const ptrb = img.data(),
  24387. *const ptre = img.end(),
  24388. *ptr = ptrb + ind;
  24389. const double val = _mp_arg(3);
  24390. // Forward search
  24391. if (step>0) {
  24392. while (ptr<ptre && (double)*ptr!=val) ptr+=step;
  24393. return ptr>=ptre?-1.:(double)(ptr - ptrb);
  24394. }
  24395. // Backward search.
  24396. while (ptr>=ptrb && (double)*ptr!=val) ptr+=step;
  24397. return ptr<ptrb?-1.:(double)(ptr - ptrb);
  24398. }
  24399. static double mp_list_find_seq(_cimg_math_parser& mp) {
  24400. const unsigned int
  24401. indi = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24402. const CImg<T> &img = mp.imglist[indi];
  24403. const int _step = (int)_mp_arg(6), step = _step?_step:-1;
  24404. const ulongT
  24405. siz1 = (ulongT)img.size(),
  24406. siz2 = (ulongT)mp.opcode[4];
  24407. longT ind = (longT)(mp.opcode[5]!=_cimg_mp_slot_nan?_mp_arg(5):step>0?0:siz1 - 1);
  24408. if (ind<0 || ind>=(longT)siz1) return -1.;
  24409. const T
  24410. *const ptr1b = img.data(),
  24411. *const ptr1e = ptr1b + siz1,
  24412. *ptr1 = ptr1b + ind,
  24413. *p1 = 0;
  24414. const double
  24415. *const ptr2b = &_mp_arg(3) + 1,
  24416. *const ptr2e = ptr2b + siz2,
  24417. *p2 = 0;
  24418. // Forward search.
  24419. if (step>0) {
  24420. do {
  24421. while (ptr1<ptr1e && *ptr1!=*ptr2b) ptr1+=step;
  24422. if (ptr1>=ptr1e) return -1.;
  24423. p1 = ptr1 + 1;
  24424. p2 = ptr2b + 1;
  24425. while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
  24426. } while (p2<ptr2e && (ptr1+=step)<ptr1e);
  24427. return p2<ptr2e?-1.:(double)(ptr1 - ptr1b);
  24428. }
  24429. // Backward search.
  24430. do {
  24431. while (ptr1>=ptr1b && *ptr1!=*ptr2b) ptr1+=step;
  24432. if (ptr1<ptr1b) return -1.;
  24433. p1 = ptr1 + 1;
  24434. p2 = ptr2b + 1;
  24435. while (p1<ptr1e && p2<ptr2e && *p1==*p2) { ++p1; ++p2; }
  24436. } while (p2<ptr2e && (ptr1+=step)>=ptr1b);
  24437. return p2<ptr2e?-1.:(double)(ptr1 - ptr1b);
  24438. }
  24439. static double mp_list_height(_cimg_math_parser& mp) {
  24440. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24441. return (double)mp.imglist[ind]._height;
  24442. }
  24443. static double mp_list_ioff(_cimg_math_parser& mp) {
  24444. const unsigned int
  24445. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()),
  24446. boundary_conditions = (unsigned int)_mp_arg(4);
  24447. const CImg<T> &img = mp.imglist[ind];
  24448. const longT
  24449. off = (longT)_mp_arg(3),
  24450. whds = (longT)img.size();
  24451. if (off>=0 && off<whds) return (double)img[off];
  24452. if (img._data) switch (boundary_conditions) {
  24453. case 3 : { // Mirror
  24454. const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
  24455. return (double)img[moff<whds?moff:whds2 - moff - 1];
  24456. }
  24457. case 2 : // Periodic
  24458. return (double)img[cimg::mod(off,whds)];
  24459. case 1 : // Neumann
  24460. return (double)img[off<0?0:whds - 1];
  24461. default : // Dirichlet
  24462. return 0;
  24463. }
  24464. return 0;
  24465. }
  24466. static double mp_list_is_shared(_cimg_math_parser& mp) {
  24467. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24468. return (double)mp.imglist[ind]._is_shared;
  24469. }
  24470. static double mp_list_ixyzc(_cimg_math_parser& mp) {
  24471. const unsigned int
  24472. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()),
  24473. interpolation = (unsigned int)_mp_arg(7),
  24474. boundary_conditions = (unsigned int)_mp_arg(8);
  24475. const CImg<T> &img = mp.imglist[ind];
  24476. const double
  24477. x = _mp_arg(3), y = _mp_arg(4),
  24478. z = _mp_arg(5), c = _mp_arg(6);
  24479. switch (interpolation) {
  24480. case 2 : // Cubic interpolation
  24481. switch (boundary_conditions) {
  24482. case 3 : { // Mirror
  24483. const float
  24484. w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
  24485. mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
  24486. mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
  24487. return (double)img._cubic_atXYZ(mx<img.width()?mx:w2 - mx - 1,
  24488. my<img.height()?my:h2 - my - 1,
  24489. mz<img.depth()?mz:d2 - mz - 1,
  24490. (int)(mc<img.spectrum()?mc:s2 - mc - 1));
  24491. }
  24492. case 2 : // Periodic
  24493. return (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,
  24494. (int)cimg::mod(c,(double)img._spectrum));
  24495. case 1 : // Neumann
  24496. return (double)img._cubic_atXYZ((float)x,(float)y,(float)z,
  24497. (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
  24498. default : // Dirichlet
  24499. if (c<0 || c>=img._spectrum) return (T)0;
  24500. return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
  24501. }
  24502. case 1 : // Linear interpolation
  24503. switch (boundary_conditions) {
  24504. case 3 : { // Mirror
  24505. const float
  24506. w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
  24507. mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
  24508. mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
  24509. return (double)img._linear_atXYZ(mx<img.width()?mx:w2 - mx - 1,
  24510. my<img.height()?my:h2 - my - 1,
  24511. mz<img.depth()?mz:d2 - mz - 1,
  24512. (int)(mc<img.spectrum()?mc:s2 - mc - 1));
  24513. }
  24514. case 2 : // Periodic
  24515. return (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,
  24516. (int)cimg::mod(c,(double)img._spectrum));
  24517. case 1 : // Neumann
  24518. return (double)img._linear_atXYZ((float)x,(float)y,(float)z,
  24519. (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
  24520. default : // Dirichlet
  24521. if (c<0 || c>=img._spectrum) return (T)0;
  24522. return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
  24523. }
  24524. default : // Nearest neighbor interpolation
  24525. switch (boundary_conditions) {
  24526. case 3 : { // Mirror
  24527. const int
  24528. w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
  24529. mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
  24530. mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
  24531. return (double)img(mx<img.width()?mx:w2 - mx - 1,
  24532. my<img.height()?my:h2 - my - 1,
  24533. mz<img.depth()?mz:d2 - mz - 1,
  24534. mc<img.spectrum()?mc:s2 - mc - 1);
  24535. }
  24536. case 2 : // Periodic
  24537. return (double)img((int)cimg::mod(x,(double)img._width),
  24538. (int)cimg::mod(y,(double)img._height),
  24539. (int)cimg::mod(z,(double)img._depth),
  24540. (int)cimg::mod(c,(double)img._spectrum));
  24541. case 1 : // Neumann
  24542. return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
  24543. default : // Dirichlet
  24544. return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
  24545. }
  24546. }
  24547. }
  24548. static double mp_list_joff(_cimg_math_parser& mp) {
  24549. const unsigned int
  24550. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()),
  24551. boundary_conditions = (unsigned int)_mp_arg(4);
  24552. const int
  24553. ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
  24554. oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
  24555. const CImg<T> &img = mp.imglist[ind];
  24556. const longT
  24557. off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
  24558. whds = (longT)img.size();
  24559. if (off>=0 && off<whds) return (double)img[off];
  24560. if (img._data) switch (boundary_conditions) {
  24561. case 3 : { // Mirror
  24562. const longT whds2 = 2*whds, moff = cimg::mod(off,whds2);
  24563. return (double)img[moff<whds?moff:whds2 - moff - 1];
  24564. }
  24565. case 2 : // Periodic
  24566. return (double)img[cimg::mod(off,whds)];
  24567. case 1 : // Neumann
  24568. return (double)img[off<0?0:whds - 1];
  24569. default : // Dirichlet
  24570. return 0;
  24571. }
  24572. return 0;
  24573. }
  24574. static double mp_list_jxyzc(_cimg_math_parser& mp) {
  24575. const unsigned int
  24576. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()),
  24577. interpolation = (unsigned int)_mp_arg(7),
  24578. boundary_conditions = (unsigned int)_mp_arg(8);
  24579. const CImg<T> &img = mp.imglist[ind];
  24580. const double
  24581. ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
  24582. oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c],
  24583. x = ox + _mp_arg(3), y = oy + _mp_arg(4),
  24584. z = oz + _mp_arg(5), c = oc + _mp_arg(6);
  24585. switch (interpolation) {
  24586. case 2 : // Cubic interpolation
  24587. switch (boundary_conditions) {
  24588. case 3 : { // Mirror
  24589. const float
  24590. w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
  24591. mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
  24592. mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
  24593. return (double)img._cubic_atXYZ(mx<img.width()?mx:w2 - mx - 1,
  24594. my<img.height()?my:h2 - my - 1,
  24595. mz<img.depth()?mz:d2 - mz - 1,
  24596. (int)(mc<img.spectrum()?mc:s2 - mc - 1));
  24597. }
  24598. case 2 : // Periodic
  24599. return (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,
  24600. (int)cimg::mod(c,(double)img._spectrum));
  24601. case 1 : // Neumann
  24602. return (double)img._cubic_atXYZ((float)x,(float)y,(float)z,
  24603. (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
  24604. default : // Dirichlet
  24605. if (c<0 || c>=img._spectrum) return (T)0;
  24606. return (double)img.cubic_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
  24607. }
  24608. case 1 : // Linear interpolation
  24609. switch (boundary_conditions) {
  24610. case 3 : { // Mirror
  24611. const float
  24612. w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(), s2 = 2.f*img.spectrum(),
  24613. mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2),
  24614. mz = cimg::mod((float)z,d2), mc = cimg::mod((float)c,s2);
  24615. return (double)img._linear_atXYZ(mx<img.width()?mx:w2 - mx - 1,
  24616. my<img.height()?my:h2 - my - 1,
  24617. mz<img.depth()?mz:d2 - mz - 1,
  24618. (int)(mc<img.spectrum()?mc:s2 - mc - 1));
  24619. }
  24620. case 2 : // Periodic
  24621. return (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,
  24622. (int)cimg::mod(c,(double)img._spectrum));
  24623. case 1 : // Neumann
  24624. return (double)img._linear_atXYZ((float)x,(float)y,(float)z,
  24625. (int)(c<0?0:c>=img._spectrum?img._spectrum - 1:c));
  24626. default : // Dirichlet
  24627. if (c<0 || c>=img._spectrum) return (T)0;
  24628. return (double)img.linear_atXYZ((float)x,(float)y,(float)z,(int)c,(T)0);
  24629. }
  24630. default : // Nearest neighbor interpolation
  24631. switch (boundary_conditions) {
  24632. case 3 : { // Mirror
  24633. const int
  24634. w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(), s2 = 2*img.spectrum(),
  24635. mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2),
  24636. mz = cimg::mod((int)z,d2), mc = cimg::mod((int)c,s2);
  24637. return (double)img(mx<img.width()?mx:w2 - mx - 1,
  24638. my<img.height()?my:h2 - my - 1,
  24639. mz<img.depth()?mz:d2 - mz - 1,
  24640. mc<img.spectrum()?mc:s2 - mc - 1);
  24641. }
  24642. case 2 : // Periodic
  24643. return (double)img((int)cimg::mod(x,(double)img._width),
  24644. (int)cimg::mod(y,(double)img._height),
  24645. (int)cimg::mod(z,(double)img._depth),
  24646. (int)cimg::mod(c,(double)img._spectrum));
  24647. case 1 : // Neumann
  24648. return (double)img._atXYZC((int)x,(int)y,(int)z,(int)c);
  24649. default : // Dirichlet
  24650. return (double)img.atXYZC((int)x,(int)y,(int)z,(int)c,(T)0);
  24651. }
  24652. }
  24653. }
  24654. static double mp_list_l(_cimg_math_parser& mp) {
  24655. return (double)mp.imglist.width();
  24656. }
  24657. static double mp_list_median(_cimg_math_parser& mp) {
  24658. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24659. if (!mp.list_median) mp.list_median.assign(mp.imglist._width);
  24660. if (!mp.list_median[ind]) CImg<doubleT>::vector(mp.imglist[ind].median()).move_to(mp.list_median[ind]);
  24661. return *mp.list_median[ind];
  24662. }
  24663. static double mp_list_norm(_cimg_math_parser& mp) {
  24664. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24665. if (!mp.list_norm) mp.list_norm.assign(mp.imglist._width);
  24666. if (!mp.list_norm[ind]) CImg<doubleT>::vector(mp.imglist[ind].magnitude(2)).move_to(mp.list_norm[ind]);
  24667. return *mp.list_norm[ind];
  24668. }
  24669. static double mp_list_id(_cimg_math_parser& mp) {
  24670. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24671. bool get_stats = false;
  24672. cimg::mutex(13);
  24673. if (!mp.list_stats || mp.list_stats.size()!=mp.imglist._width) mp.list_stats.assign(mp.imglist._width);
  24674. if (!mp.list_stats[ind]) get_stats = true;
  24675. cimg::mutex(13,0);
  24676. if (get_stats) {
  24677. CImg<Tdouble> st = mp.imglist[ind].get_stats();
  24678. cimg::mutex(13);
  24679. st.move_to(mp.list_stats[ind]);
  24680. cimg::mutex(13,0);
  24681. }
  24682. return std::sqrt(mp.list_stats(ind,3));
  24683. }
  24684. static double mp_list_set_ioff(_cimg_math_parser& mp) {
  24685. if (!mp.imglist.width()) return cimg::type<double>::nan();
  24686. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24687. CImg<T> &img = mp.imglist[ind];
  24688. const longT
  24689. off = (longT)_mp_arg(3),
  24690. whds = (longT)img.size();
  24691. const double val = _mp_arg(1);
  24692. if (off>=0 && off<whds) img[off] = (T)val;
  24693. return val;
  24694. }
  24695. static double mp_list_set_ixyzc(_cimg_math_parser& mp) {
  24696. if (!mp.imglist.width()) return cimg::type<double>::nan();
  24697. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24698. CImg<T> &img = mp.imglist[ind];
  24699. const int
  24700. x = (int)_mp_arg(3), y = (int)_mp_arg(4),
  24701. z = (int)_mp_arg(5), c = (int)_mp_arg(6);
  24702. const double val = _mp_arg(1);
  24703. if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
  24704. z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
  24705. img(x,y,z,c) = (T)val;
  24706. return val;
  24707. }
  24708. static double mp_list_set_joff(_cimg_math_parser& mp) {
  24709. if (!mp.imglist.width()) return cimg::type<double>::nan();
  24710. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24711. CImg<T> &img = mp.imglist[ind];
  24712. const int
  24713. ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
  24714. oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
  24715. const longT
  24716. off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
  24717. whds = (longT)img.size();
  24718. const double val = _mp_arg(1);
  24719. if (off>=0 && off<whds) img[off] = (T)val;
  24720. return val;
  24721. }
  24722. static double mp_list_set_jxyzc(_cimg_math_parser& mp) {
  24723. if (!mp.imglist.width()) return cimg::type<double>::nan();
  24724. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24725. CImg<T> &img = mp.imglist[ind];
  24726. const double
  24727. ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
  24728. oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c];
  24729. const int
  24730. x = (int)(ox + _mp_arg(3)), y = (int)(oy + _mp_arg(4)),
  24731. z = (int)(oz + _mp_arg(5)), c = (int)(oc + _mp_arg(6));
  24732. const double val = _mp_arg(1);
  24733. if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
  24734. z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
  24735. img(x,y,z,c) = (T)val;
  24736. return val;
  24737. }
  24738. static double mp_list_set_Ioff_s(_cimg_math_parser& mp) {
  24739. if (!mp.imglist.width()) return cimg::type<double>::nan();
  24740. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24741. CImg<T> &img = mp.imglist[ind];
  24742. const longT
  24743. off = (longT)_mp_arg(3),
  24744. whd = (longT)img.width()*img.height()*img.depth();
  24745. const T val = (T)_mp_arg(1);
  24746. if (off>=0 && off<whd) {
  24747. T *ptrd = &img[off];
  24748. cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
  24749. }
  24750. return _mp_arg(1);
  24751. }
  24752. static double mp_list_set_Ioff_v(_cimg_math_parser& mp) {
  24753. if (!mp.imglist.width()) return cimg::type<double>::nan();
  24754. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24755. CImg<T> &img = mp.imglist[ind];
  24756. const longT
  24757. off = (longT)_mp_arg(3),
  24758. whd = (longT)img.width()*img.height()*img.depth();
  24759. const double *ptrs = &_mp_arg(1) + 1;
  24760. if (off>=0 && off<whd) {
  24761. const unsigned int vsiz = (unsigned int)mp.opcode[4];
  24762. T *ptrd = &img[off];
  24763. cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
  24764. }
  24765. return cimg::type<double>::nan();
  24766. }
  24767. static double mp_list_set_Ixyz_s(_cimg_math_parser& mp) {
  24768. if (!mp.imglist.width()) return cimg::type<double>::nan();
  24769. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24770. CImg<T> &img = mp.imglist[ind];
  24771. const int
  24772. x = (int)_mp_arg(3),
  24773. y = (int)_mp_arg(4),
  24774. z = (int)_mp_arg(5);
  24775. const T val = (T)_mp_arg(1);
  24776. if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
  24777. T *ptrd = &img(x,y,z);
  24778. const ulongT whd = (ulongT)img._width*img._height*img._depth;
  24779. cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
  24780. }
  24781. return _mp_arg(1);
  24782. }
  24783. static double mp_list_set_Ixyz_v(_cimg_math_parser& mp) {
  24784. if (!mp.imglist.width()) return cimg::type<double>::nan();
  24785. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24786. CImg<T> &img = mp.imglist[ind];
  24787. const int
  24788. x = (int)_mp_arg(3),
  24789. y = (int)_mp_arg(4),
  24790. z = (int)_mp_arg(5);
  24791. const double *ptrs = &_mp_arg(1) + 1;
  24792. if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
  24793. const unsigned int vsiz = (unsigned int)mp.opcode[6];
  24794. T *ptrd = &img(x,y,z);
  24795. const ulongT whd = (ulongT)img._width*img._height*img._depth;
  24796. cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
  24797. }
  24798. return cimg::type<double>::nan();
  24799. }
  24800. static double mp_list_set_Joff_s(_cimg_math_parser& mp) {
  24801. if (!mp.imglist.width()) return cimg::type<double>::nan();
  24802. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24803. CImg<T> &img = mp.imglist[ind];
  24804. const int
  24805. ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
  24806. oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
  24807. const longT
  24808. off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
  24809. whd = (longT)img.width()*img.height()*img.depth();
  24810. const T val = (T)_mp_arg(1);
  24811. if (off>=0 && off<whd) {
  24812. T *ptrd = &img[off];
  24813. cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
  24814. }
  24815. return _mp_arg(1);
  24816. }
  24817. static double mp_list_set_Joff_v(_cimg_math_parser& mp) {
  24818. if (!mp.imglist.width()) return cimg::type<double>::nan();
  24819. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24820. CImg<T> &img = mp.imglist[ind];
  24821. const int
  24822. ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
  24823. oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
  24824. const longT
  24825. off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(3),
  24826. whd = (longT)img.width()*img.height()*img.depth();
  24827. const double *ptrs = &_mp_arg(1) + 1;
  24828. if (off>=0 && off<whd) {
  24829. const unsigned int vsiz = (unsigned int)mp.opcode[4];
  24830. T *ptrd = &img[off];
  24831. cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
  24832. }
  24833. return cimg::type<double>::nan();
  24834. }
  24835. static double mp_list_set_Jxyz_s(_cimg_math_parser& mp) {
  24836. if (!mp.imglist.width()) return cimg::type<double>::nan();
  24837. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24838. CImg<T> &img = mp.imglist[ind];
  24839. const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
  24840. const int
  24841. x = (int)(ox + _mp_arg(3)),
  24842. y = (int)(oy + _mp_arg(4)),
  24843. z = (int)(oz + _mp_arg(5));
  24844. const T val = (T)_mp_arg(1);
  24845. if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
  24846. T *ptrd = &img(x,y,z);
  24847. const ulongT whd = (ulongT)img._width*img._height*img._depth;
  24848. cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
  24849. }
  24850. return _mp_arg(1);
  24851. }
  24852. static double mp_list_set_Jxyz_v(_cimg_math_parser& mp) {
  24853. if (!mp.imglist.width()) return cimg::type<double>::nan();
  24854. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24855. CImg<T> &img = mp.imglist[ind];
  24856. const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
  24857. const int
  24858. x = (int)(ox + _mp_arg(3)),
  24859. y = (int)(oy + _mp_arg(4)),
  24860. z = (int)(oz + _mp_arg(5));
  24861. const double *ptrs = &_mp_arg(1) + 1;
  24862. if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
  24863. const unsigned int vsiz = (unsigned int)mp.opcode[6];
  24864. T *ptrd = &img(x,y,z);
  24865. const ulongT whd = (ulongT)img._width*img._height*img._depth;
  24866. cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
  24867. }
  24868. return cimg::type<double>::nan();
  24869. }
  24870. static double mp_list_spectrum(_cimg_math_parser& mp) {
  24871. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24872. return (double)mp.imglist[ind]._spectrum;
  24873. }
  24874. static double mp_list_stats(_cimg_math_parser& mp) {
  24875. const unsigned int
  24876. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()),
  24877. k = (unsigned int)mp.opcode[3];
  24878. bool get_stats = false;
  24879. cimg::mutex(13);
  24880. if (!mp.list_stats || mp.list_stats.size()!=mp.imglist._width) mp.list_stats.assign(mp.imglist._width);
  24881. if (!mp.list_stats[ind]) get_stats = true;
  24882. cimg::mutex(13,0);
  24883. if (get_stats) {
  24884. CImg<Tdouble> st = mp.imglist[ind].get_stats();
  24885. cimg::mutex(13);
  24886. st.move_to(mp.list_stats[ind]);
  24887. cimg::mutex(13,0);
  24888. }
  24889. return mp.list_stats(ind,k);
  24890. }
  24891. static double mp_list_wh(_cimg_math_parser& mp) {
  24892. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24893. return (double)mp.imglist[ind]._width*mp.imglist[ind]._height;
  24894. }
  24895. static double mp_list_whd(_cimg_math_parser& mp) {
  24896. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24897. return (double)mp.imglist[ind]._width*mp.imglist[ind]._height*mp.imglist[ind]._depth;
  24898. }
  24899. static double mp_list_whds(_cimg_math_parser& mp) {
  24900. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24901. return (double)mp.imglist[ind]._width*mp.imglist[ind]._height*mp.imglist[ind]._depth*mp.imglist[ind]._spectrum;
  24902. }
  24903. static double mp_list_width(_cimg_math_parser& mp) {
  24904. const unsigned int ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  24905. return (double)mp.imglist[ind]._width;
  24906. }
  24907. static double mp_list_Ioff(_cimg_math_parser& mp) {
  24908. double *ptrd = &_mp_arg(1) + 1;
  24909. const unsigned int
  24910. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()),
  24911. boundary_conditions = (unsigned int)_mp_arg(4),
  24912. vsiz = (unsigned int)mp.opcode[5];
  24913. const CImg<T> &img = mp.imglist[ind];
  24914. const longT
  24915. off = (longT)_mp_arg(3),
  24916. whd = (longT)img.width()*img.height()*img.depth();
  24917. const T *ptrs;
  24918. if (off>=0 && off<whd) {
  24919. ptrs = &img[off];
  24920. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  24921. return cimg::type<double>::nan();
  24922. }
  24923. if (img._data) switch (boundary_conditions) {
  24924. case 3 : { // Mirror
  24925. const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
  24926. ptrs = &img[moff<whd?moff:whd2 - moff - 1];
  24927. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  24928. return cimg::type<double>::nan();
  24929. }
  24930. case 2 : // Periodic
  24931. ptrs = &img[cimg::mod(off,whd)];
  24932. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  24933. return cimg::type<double>::nan();
  24934. case 1 : // Neumann
  24935. ptrs = off<0?&img[0]:&img[whd - 1];
  24936. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  24937. return cimg::type<double>::nan();
  24938. default : // Dirichlet
  24939. std::memset(ptrd,0,vsiz*sizeof(double));
  24940. return cimg::type<double>::nan();
  24941. }
  24942. std::memset(ptrd,0,vsiz*sizeof(double));
  24943. return cimg::type<double>::nan();
  24944. }
  24945. static double mp_list_Ixyz(_cimg_math_parser& mp) {
  24946. double *ptrd = &_mp_arg(1) + 1;
  24947. const unsigned int
  24948. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()),
  24949. interpolation = (unsigned int)_mp_arg(6),
  24950. boundary_conditions = (unsigned int)_mp_arg(7),
  24951. vsiz = (unsigned int)mp.opcode[8];
  24952. const CImg<T> &img = mp.imglist[ind];
  24953. const double x = _mp_arg(3), y = _mp_arg(4), z = _mp_arg(5);
  24954. const ulongT whd = (ulongT)img._width*img._height*img._depth;
  24955. const T *ptrs;
  24956. switch (interpolation) {
  24957. case 2 : // Cubic interpolation
  24958. switch (boundary_conditions) {
  24959. case 3 : { // Mirror
  24960. const float
  24961. w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
  24962. mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
  24963. cx = mx<img.width()?mx:w2 - mx - 1,
  24964. cy = my<img.height()?my:h2 - my - 1,
  24965. cz = mz<img.depth()?mz:d2 - mz - 1;
  24966. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ(cx,cy,cz,c);
  24967. } break;
  24968. case 2 : // Periodic
  24969. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,c);
  24970. break;
  24971. case 1 : // Neumann
  24972. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ((float)x,(float)y,(float)z,c);
  24973. break;
  24974. default : // Dirichlet
  24975. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.cubic_atXYZ((float)x,(float)y,(float)z,c,(T)0);
  24976. } break;
  24977. case 1 : // Linear interpolation
  24978. switch (boundary_conditions) {
  24979. case 3 : { // Mirror
  24980. const float
  24981. w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
  24982. mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
  24983. cx = mx<img.width()?mx:w2 - mx - 1,
  24984. cy = my<img.height()?my:h2 - my - 1,
  24985. cz = mz<img.depth()?mz:d2 - mz - 1;
  24986. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
  24987. } break;
  24988. case 2 : // Periodic
  24989. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,c);
  24990. break;
  24991. case 1 : // Neumann
  24992. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
  24993. break;
  24994. default : // Dirichlet
  24995. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
  24996. } break;
  24997. default : // Nearest neighbor interpolation
  24998. switch (boundary_conditions) {
  24999. case 3 : { // Mirror
  25000. const int
  25001. w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
  25002. mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
  25003. cx = mx<img.width()?mx:w2 - mx - 1,
  25004. cy = my<img.height()?my:h2 - my - 1,
  25005. cz = mz<img.depth()?mz:d2 - mz - 1;
  25006. ptrs = &img(cx,cy,cz);
  25007. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  25008. } break;
  25009. case 2 : { // Periodic
  25010. const int
  25011. cx = (int)cimg::mod(x,(double)img._width),
  25012. cy = (int)cimg::mod(y,(double)img._height),
  25013. cz = (int)cimg::mod(z,(double)img._depth);
  25014. ptrs = &img(cx,cy,cz);
  25015. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  25016. } break;
  25017. case 1 : { // Neumann
  25018. ptrs = &img._atXYZ((int)x,(int)y,(int)z);
  25019. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  25020. } break;
  25021. default : // Dirichlet
  25022. if (img.containsXYZC((int)x,(int)y,(int)z)) {
  25023. ptrs = &img((int)x,(int)y,(int)z);
  25024. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  25025. } else std::memset(ptrd,0,vsiz*sizeof(double));
  25026. }
  25027. }
  25028. return cimg::type<double>::nan();
  25029. }
  25030. static double mp_list_Joff(_cimg_math_parser& mp) {
  25031. double *ptrd = &_mp_arg(1) + 1;
  25032. const unsigned int
  25033. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()),
  25034. boundary_conditions = (unsigned int)_mp_arg(4),
  25035. vsiz = (unsigned int)mp.opcode[5];
  25036. const int
  25037. ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y], oz = (int)mp.mem[_cimg_mp_slot_z];
  25038. const CImg<T> &img = mp.imglist[ind];
  25039. const longT
  25040. off = img.offset(ox,oy,oz) + (longT)_mp_arg(3),
  25041. whd = (longT)img.width()*img.height()*img.depth();
  25042. const T *ptrs;
  25043. if (off>=0 && off<whd) {
  25044. ptrs = &img[off];
  25045. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  25046. return cimg::type<double>::nan();
  25047. }
  25048. if (img._data) switch (boundary_conditions) {
  25049. case 3 : { // Mirror
  25050. const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
  25051. ptrs = &img[moff<whd?moff:whd2 - moff - 1];
  25052. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  25053. return cimg::type<double>::nan();
  25054. }
  25055. case 2 : // Periodic
  25056. ptrs = &img[cimg::mod(off,whd)];
  25057. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  25058. return cimg::type<double>::nan();
  25059. case 1 : // Neumann
  25060. ptrs = off<0?&img[0]:&img[whd - 1];
  25061. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  25062. return cimg::type<double>::nan();
  25063. default : // Dirichlet
  25064. std::memset(ptrd,0,vsiz*sizeof(double));
  25065. return cimg::type<double>::nan();
  25066. }
  25067. std::memset(ptrd,0,vsiz*sizeof(double));
  25068. return cimg::type<double>::nan();
  25069. }
  25070. static double mp_list_Jxyz(_cimg_math_parser& mp) {
  25071. double *ptrd = &_mp_arg(1) + 1;
  25072. const unsigned int
  25073. ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width()),
  25074. interpolation = (unsigned int)_mp_arg(6),
  25075. boundary_conditions = (unsigned int)_mp_arg(7),
  25076. vsiz = (unsigned int)mp.opcode[8];
  25077. const CImg<T> &img = mp.imglist[ind];
  25078. const double
  25079. ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z],
  25080. x = ox + _mp_arg(3), y = oy + _mp_arg(4), z = oz + _mp_arg(5);
  25081. const ulongT whd = (ulongT)img._width*img._height*img._depth;
  25082. const T *ptrs;
  25083. switch (interpolation) {
  25084. case 2 : // Cubic interpolation
  25085. switch (boundary_conditions) {
  25086. case 3 : { // Mirror
  25087. const float
  25088. w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
  25089. mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
  25090. cx = mx<img.width()?mx:w2 - mx - 1,
  25091. cy = my<img.height()?my:h2 - my - 1,
  25092. cz = mz<img.depth()?mz:d2 - mz - 1;
  25093. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ(cx,cy,cz,c);
  25094. } break;
  25095. case 2 : // Periodic
  25096. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,c);
  25097. break;
  25098. case 1 : // Neumann
  25099. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ((float)x,(float)y,(float)z,c);
  25100. break;
  25101. default : // Dirichlet
  25102. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.cubic_atXYZ((float)x,(float)y,(float)z,c,(T)0);
  25103. } break;
  25104. case 1 : // Linear interpolation
  25105. switch (boundary_conditions) {
  25106. case 3 : { // Mirror
  25107. const float
  25108. w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
  25109. mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
  25110. cx = mx<img.width()?mx:w2 - mx - 1,
  25111. cy = my<img.height()?my:h2 - my - 1,
  25112. cz = mz<img.depth()?mz:d2 - mz - 1;
  25113. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
  25114. } break;
  25115. case 2 : // Periodic
  25116. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,c);
  25117. break;
  25118. case 1 : // Neumann
  25119. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
  25120. break;
  25121. default : // Dirichlet
  25122. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
  25123. } break;
  25124. case 0 : // Nearest neighbor interpolation
  25125. switch (boundary_conditions) {
  25126. case 3 : { // Mirror
  25127. const int
  25128. w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
  25129. mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
  25130. cx = mx<img.width()?mx:w2 - mx - 1,
  25131. cy = my<img.height()?my:h2 - my - 1,
  25132. cz = mz<img.depth()?mz:d2 - mz - 1;
  25133. ptrs = &img(cx,cy,cz);
  25134. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  25135. } break;
  25136. case 2 : { // Periodic
  25137. const int
  25138. cx = (int)cimg::mod(x,(double)img._width),
  25139. cy = (int)cimg::mod(y,(double)img._height),
  25140. cz = (int)cimg::mod(z,(double)img._depth);
  25141. ptrs = &img(cx,cy,cz);
  25142. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  25143. } break;
  25144. case 1 : { // Neumann
  25145. ptrs = &img._atXYZ((int)x,(int)y,(int)z);
  25146. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  25147. } break;
  25148. default : // Dirichlet
  25149. if (img.containsXYZC((int)x,(int)y,(int)z)) {
  25150. ptrs = &img((int)x,(int)y,(int)z);
  25151. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  25152. } else std::memset(ptrd,0,vsiz*sizeof(double));
  25153. }
  25154. }
  25155. return cimg::type<double>::nan();
  25156. }
  25157. static double mp_log(_cimg_math_parser& mp) {
  25158. return std::log(_mp_arg(2));
  25159. }
  25160. static double mp_log10(_cimg_math_parser& mp) {
  25161. return std::log10(_mp_arg(2));
  25162. }
  25163. static double mp_log2(_cimg_math_parser& mp) {
  25164. return cimg::log2(_mp_arg(2));
  25165. }
  25166. static double mp_logical_and(_cimg_math_parser& mp) {
  25167. const bool val_left = (bool)_mp_arg(2);
  25168. const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[4];
  25169. if (!val_left) { mp.p_code = p_end - 1; return 0; }
  25170. const ulongT mem_right = mp.opcode[3];
  25171. for ( ; mp.p_code<p_end; ++mp.p_code) {
  25172. mp.opcode._data = mp.p_code->_data;
  25173. const ulongT target = mp.opcode[1];
  25174. mp.mem[target] = _cimg_mp_defunc(mp);
  25175. }
  25176. --mp.p_code;
  25177. return (double)(bool)mp.mem[mem_right];
  25178. }
  25179. static double mp_logical_not(_cimg_math_parser& mp) {
  25180. return (double)!_mp_arg(2);
  25181. }
  25182. static double mp_logical_or(_cimg_math_parser& mp) {
  25183. const bool val_left = (bool)_mp_arg(2);
  25184. const CImg<ulongT> *const p_end = ++mp.p_code + mp.opcode[4];
  25185. if (val_left) { mp.p_code = p_end - 1; return 1; }
  25186. const ulongT mem_right = mp.opcode[3];
  25187. for ( ; mp.p_code<p_end; ++mp.p_code) {
  25188. mp.opcode._data = mp.p_code->_data;
  25189. const ulongT target = mp.opcode[1];
  25190. mp.mem[target] = _cimg_mp_defunc(mp);
  25191. }
  25192. --mp.p_code;
  25193. return (double)(bool)mp.mem[mem_right];
  25194. }
  25195. static double mp_lowercase(_cimg_math_parser& mp) {
  25196. return cimg::lowercase(_mp_arg(2));
  25197. }
  25198. static double mp_lt(_cimg_math_parser& mp) {
  25199. return (double)(_mp_arg(2)<_mp_arg(3));
  25200. }
  25201. static double mp_lte(_cimg_math_parser& mp) {
  25202. return (double)(_mp_arg(2)<=_mp_arg(3));
  25203. }
  25204. static double mp_map(_cimg_math_parser& mp) {
  25205. double *ptrd = &_mp_arg(1) + 1;
  25206. const double
  25207. *ptrX = &_mp_arg(2) + 1,
  25208. *ptrP = &_mp_arg(3) + 1;
  25209. const unsigned int
  25210. sizX = (unsigned int)mp.opcode[4],
  25211. sizP = (unsigned int)mp.opcode[5],
  25212. nb_channelsX = (unsigned int)mp.opcode[6],
  25213. nb_channelsP = (unsigned int)mp.opcode[7],
  25214. boundary_conditions = (unsigned int)_mp_arg(8);
  25215. CImg<doubleT>(ptrd,sizX/nb_channelsX,1,1,nb_channelsX*nb_channelsP,true) =
  25216. CImg<doubleT>(ptrX,sizX/nb_channelsX,1,1,nb_channelsX,true).
  25217. get_map(CImg<doubleT>(ptrP,sizP/nb_channelsP,1,1,nb_channelsP,true),boundary_conditions);
  25218. return cimg::type<double>::nan();
  25219. }
  25220. static double mp_matrix_eig(_cimg_math_parser& mp) {
  25221. double *ptrd = &_mp_arg(1) + 1;
  25222. const double *ptr1 = &_mp_arg(2) + 1;
  25223. const unsigned int k = (unsigned int)mp.opcode[3];
  25224. CImg<doubleT> val, vec;
  25225. CImg<doubleT>(ptr1,k,k,1,1,true).symmetric_eigen(val,vec);
  25226. CImg<doubleT>(ptrd,1,k,1,1,true) = val;
  25227. CImg<doubleT>(ptrd + k,k,k,1,1,true) = vec.get_transpose();
  25228. return cimg::type<double>::nan();
  25229. }
  25230. static double mp_matrix_invert(_cimg_math_parser& mp) {
  25231. double *const ptrd = &_mp_arg(1) + 1;
  25232. const double *const ptr1 = &_mp_arg(2) + 1;
  25233. const unsigned int
  25234. w = (unsigned int)mp.opcode[3],
  25235. h = (unsigned int)mp.opcode[4];
  25236. const bool use_LU = (bool)_mp_arg(5);
  25237. const float lambda = (float)_mp_arg(6);
  25238. CImg<doubleT>(ptrd,h,w,1,1,true) = CImg<doubleT>(ptr1,w,h,1,1,true).get_invert(use_LU,lambda);
  25239. return cimg::type<double>::nan();
  25240. }
  25241. static double mp_matrix_mul(_cimg_math_parser& mp) {
  25242. double *ptrd = &_mp_arg(1) + 1;
  25243. const double
  25244. *ptr1 = &_mp_arg(2) + 1,
  25245. *ptr2 = &_mp_arg(3) + 1;
  25246. const unsigned int
  25247. k = (unsigned int)mp.opcode[4],
  25248. l = (unsigned int)mp.opcode[5],
  25249. m = (unsigned int)mp.opcode[6];
  25250. CImg<doubleT>(ptrd,m,k,1,1,true) = CImg<doubleT>(ptr1,l,k,1,1,true)*CImg<doubleT>(ptr2,m,l,1,1,true);
  25251. return cimg::type<double>::nan();
  25252. }
  25253. static double mp_matrix_svd(_cimg_math_parser& mp) {
  25254. double *ptrd = &_mp_arg(1) + 1;
  25255. const double *ptr1 = &_mp_arg(2) + 1;
  25256. const unsigned int
  25257. k = (unsigned int)mp.opcode[3],
  25258. l = (unsigned int)mp.opcode[4];
  25259. CImg<doubleT> U, S, V;
  25260. CImg<doubleT>(ptr1,k,l,1,1,true).SVD(U,S,V);
  25261. CImg<doubleT>(ptrd,k,l,1,1,true) = U;
  25262. CImg<doubleT>(ptrd + k*l,1,k,1,1,true) = S;
  25263. CImg<doubleT>(ptrd + k*l + k,k,k,1,1,true) = V;
  25264. return cimg::type<double>::nan();
  25265. }
  25266. static double mp_max(_cimg_math_parser& mp) {
  25267. const unsigned int i_end = (unsigned int)mp.opcode[2];
  25268. double val, valmax = -cimg::type<double>::inf();
  25269. for (unsigned int i = 3; i<i_end; i+=2) {
  25270. const unsigned int len = (unsigned int)mp.opcode[i + 1];
  25271. if (len>1) {
  25272. const double *ptr = &_mp_arg(i);
  25273. for (unsigned int k = 0; k<len; ++k) { val = *(ptr++); if (val>valmax) valmax = val; }
  25274. } else { val = _mp_arg(i); if (val>valmax) valmax = val; }
  25275. }
  25276. return valmax;
  25277. }
  25278. static double mp_maxabs(_cimg_math_parser& mp) {
  25279. const unsigned int i_end = (unsigned int)mp.opcode[2];
  25280. double val, abs_val, valmaxabs = 0, abs_valmaxabs = 0;
  25281. for (unsigned int i = 3; i<i_end; i+=2) {
  25282. const unsigned int len = (unsigned int)mp.opcode[i + 1];
  25283. if (len>1) {
  25284. const double *ptr = &_mp_arg(i);
  25285. for (unsigned int k = 0; k<len; ++k) {
  25286. val = *(ptr++);
  25287. abs_val = cimg::abs(val);
  25288. if (abs_val>abs_valmaxabs) { valmaxabs = val; abs_valmaxabs = abs_val; }
  25289. }
  25290. } else {
  25291. val = _mp_arg(i);
  25292. abs_val = cimg::abs(val);
  25293. if (abs_val>abs_valmaxabs) { valmaxabs = val; abs_valmaxabs = abs_val; }
  25294. }
  25295. }
  25296. return valmaxabs;
  25297. }
  25298. static double* _mp_memcopy_double(_cimg_math_parser& mp, const unsigned int ind, const ulongT *const p_ref,
  25299. const longT siz, const long inc) {
  25300. const longT
  25301. off = *p_ref?p_ref[1] + (longT)mp.mem[(longT)p_ref[2]] + 1:ind,
  25302. eoff = off + (siz - 1)*inc;
  25303. if (off<0 || eoff>=mp.mem.width())
  25304. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': "
  25305. "Out-of-bounds variable pointer "
  25306. "(length: %ld, increment: %ld, offset start: %ld, "
  25307. "offset end: %ld, offset max: %u).",
  25308. mp.imgin.pixel_type(),siz,inc,off,eoff,mp.mem._width - 1);
  25309. return &mp.mem[off];
  25310. }
  25311. static float* _mp_memcopy_float(_cimg_math_parser& mp, const ulongT *const p_ref,
  25312. const longT siz, const long inc, const bool is_out) {
  25313. const unsigned ind = (unsigned int)p_ref[1];
  25314. const CImg<T> &img = is_out?
  25315. (ind==~0U?mp.imgout:mp.imglist[cimg::mod((int)mp.mem[ind],mp.imglist.width())]):
  25316. (ind==~0U?mp.imgin:mp.imglist[cimg::mod((int)mp.mem[ind],mp.imglist.width())]);
  25317. const bool is_relative = (bool)p_ref[2];
  25318. int ox, oy, oz, oc;
  25319. longT off = 0;
  25320. if (is_relative) {
  25321. ox = (int)mp.mem[_cimg_mp_slot_x];
  25322. oy = (int)mp.mem[_cimg_mp_slot_y];
  25323. oz = (int)mp.mem[_cimg_mp_slot_z];
  25324. oc = (int)mp.mem[_cimg_mp_slot_c];
  25325. off = img.offset(ox,oy,oz,oc);
  25326. }
  25327. if ((*p_ref)%2) {
  25328. const int
  25329. x = (int)mp.mem[p_ref[3]],
  25330. y = (int)mp.mem[p_ref[4]],
  25331. z = (int)mp.mem[p_ref[5]],
  25332. c = *p_ref==5?0:(int)mp.mem[p_ref[6]];
  25333. off+=img.offset(x,y,z,c);
  25334. } else off+=(longT)mp.mem[p_ref[3]];
  25335. const longT eoff = off + (siz - 1)*inc;
  25336. if (off<0 || eoff>=(longT)img.size())
  25337. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'copy()': "
  25338. "Out-of-bounds image pointer "
  25339. "(length: %ld, increment: %ld, offset start: %ld, "
  25340. "offset end: %ld, offset max: %lu).",
  25341. mp.imgin.pixel_type(),siz,inc,off,eoff,img.size() - 1);
  25342. return (float*)&img[off];
  25343. }
  25344. static double mp_memcopy(_cimg_math_parser& mp) {
  25345. longT siz = (longT)_mp_arg(4);
  25346. const longT inc_d = (longT)_mp_arg(5), inc_s = (longT)_mp_arg(6);
  25347. const float
  25348. _opacity = (float)_mp_arg(7),
  25349. opacity = (float)cimg::abs(_opacity),
  25350. omopacity = 1 - std::max(_opacity,0.f);
  25351. if (siz>0) {
  25352. const bool
  25353. is_doubled = mp.opcode[8]<=1,
  25354. is_doubles = mp.opcode[15]<=1;
  25355. if (is_doubled && is_doubles) { // (double*) <- (double*)
  25356. double *ptrd = _mp_memcopy_double(mp,(unsigned int)mp.opcode[2],&mp.opcode[8],siz,inc_d);
  25357. const double *ptrs = _mp_memcopy_double(mp,(unsigned int)mp.opcode[3],&mp.opcode[15],siz,inc_s);
  25358. if (inc_d==1 && inc_s==1 && _opacity>=1) {
  25359. if (ptrs + siz - 1<ptrd || ptrs>ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(double));
  25360. else std::memmove(ptrd,ptrs,siz*sizeof(double));
  25361. } else {
  25362. if (ptrs + (siz - 1)*inc_s<ptrd || ptrs>ptrd + (siz - 1)*inc_d) {
  25363. if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; }
  25364. else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; }
  25365. } else { // Overlapping buffers
  25366. CImg<doubleT> buf((unsigned int)siz);
  25367. cimg_for(buf,ptr,double) { *ptr = *ptrs; ptrs+=inc_s; }
  25368. ptrs = buf;
  25369. if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; }
  25370. else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**(ptrs++); ptrd+=inc_d; }
  25371. }
  25372. }
  25373. } else if (is_doubled && !is_doubles) { // (double*) <- (float*)
  25374. double *ptrd = _mp_memcopy_double(mp,(unsigned int)mp.opcode[2],&mp.opcode[8],siz,inc_d);
  25375. const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[15],siz,inc_s,false);
  25376. if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; }
  25377. else while (siz-->0) { *ptrd = omopacity**ptrd + _opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; }
  25378. } else if (!is_doubled && is_doubles) { // (float*) <- (double*)
  25379. float *ptrd = _mp_memcopy_float(mp,&mp.opcode[8],siz,inc_d,true);
  25380. const double *ptrs = _mp_memcopy_double(mp,(unsigned int)mp.opcode[3],&mp.opcode[15],siz,inc_s);
  25381. if (_opacity>=1) while (siz-->0) { *ptrd = (float)*ptrs; ptrd+=inc_d; ptrs+=inc_s; }
  25382. else while (siz-->0) { *ptrd = (float)(omopacity**ptrd + opacity**ptrs); ptrd+=inc_d; ptrs+=inc_s; }
  25383. } else { // (float*) <- (float*)
  25384. float *ptrd = _mp_memcopy_float(mp,&mp.opcode[8],siz,inc_d,true);
  25385. const float *ptrs = _mp_memcopy_float(mp,&mp.opcode[15],siz,inc_s,false);
  25386. if (inc_d==1 && inc_s==1 && _opacity>=1) {
  25387. if (ptrs + siz - 1<ptrd || ptrs>ptrd + siz - 1) std::memcpy(ptrd,ptrs,siz*sizeof(float));
  25388. else std::memmove(ptrd,ptrs,siz*sizeof(float));
  25389. } else {
  25390. if (ptrs + (siz - 1)*inc_s<ptrd || ptrs>ptrd + (siz - 1)*inc_d) {
  25391. if (_opacity>=1) while (siz-->0) { *ptrd = *ptrs; ptrd+=inc_d; ptrs+=inc_s; }
  25392. else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**ptrs; ptrd+=inc_d; ptrs+=inc_s; }
  25393. } else { // Overlapping buffers
  25394. CImg<floatT> buf((unsigned int)siz);
  25395. cimg_for(buf,ptr,float) { *ptr = *ptrs; ptrs+=inc_s; }
  25396. ptrs = buf;
  25397. if (_opacity>=1) while (siz-->0) { *ptrd = *(ptrs++); ptrd+=inc_d; }
  25398. else while (siz-->0) { *ptrd = omopacity**ptrd + opacity**(ptrs++); ptrd+=inc_d; }
  25399. }
  25400. }
  25401. }
  25402. }
  25403. return _mp_arg(1);
  25404. }
  25405. static double mp_min(_cimg_math_parser& mp) {
  25406. const unsigned int i_end = (unsigned int)mp.opcode[2];
  25407. double val, valmin = cimg::type<double>::inf();
  25408. for (unsigned int i = 3; i<i_end; i+=2) {
  25409. const unsigned int len = (unsigned int)mp.opcode[i + 1];
  25410. if (len>1) {
  25411. const double *ptr = &_mp_arg(i);
  25412. for (unsigned int k = 0; k<len; ++k) { val = *(ptr++); if (val<valmin) valmin = val; }
  25413. } else { val = _mp_arg(i); if (val<valmin) valmin = val; }
  25414. }
  25415. return valmin;
  25416. }
  25417. static double mp_minabs(_cimg_math_parser& mp) {
  25418. const unsigned int i_end = (unsigned int)mp.opcode[2];
  25419. double val, abs_val, valminabs = cimg::type<double>::inf(), abs_valminabs = cimg::type<double>::inf();
  25420. for (unsigned int i = 3; i<i_end; i+=2) {
  25421. const unsigned int len = (unsigned int)mp.opcode[i + 1];
  25422. if (len>1) {
  25423. const double *ptr = &_mp_arg(i);
  25424. for (unsigned int k = 0; k<len; ++k) {
  25425. val = *(ptr++);
  25426. abs_val = cimg::abs(val);
  25427. if (abs_val<abs_valminabs) { valminabs = val; abs_valminabs = abs_val; }
  25428. }
  25429. } else {
  25430. val = _mp_arg(i);
  25431. abs_val = cimg::abs(val);
  25432. if (abs_val<abs_valminabs) { valminabs = val; abs_valminabs = abs_val; }
  25433. }
  25434. }
  25435. return valminabs;
  25436. }
  25437. static double mp_minus(_cimg_math_parser& mp) {
  25438. return -_mp_arg(2);
  25439. }
  25440. static double mp_med(_cimg_math_parser& mp) {
  25441. const unsigned int i_end = (unsigned int)mp.opcode[2];
  25442. CImg<double> values;
  25443. if (i_end==5) { // Only a single argument
  25444. if ((unsigned)mp.opcode[4]==1) return _mp_arg(3); // Real value
  25445. else values.assign(&_mp_arg(3),(unsigned int)mp.opcode[4],1,1,1,true); // Vector value
  25446. } else {
  25447. unsigned int siz = 0;
  25448. for (unsigned int i = 4; i<i_end; i+=2) siz+=(unsigned int)mp.opcode[i];
  25449. values.assign(siz);
  25450. double *ptr = values;
  25451. for (unsigned int i = 3; i<i_end; i+=2) {
  25452. const unsigned int len = (unsigned int)mp.opcode[i + 1];
  25453. if (len>1) std::memcpy(ptr,&_mp_arg(i),len*sizeof(double));
  25454. else *ptr = _mp_arg(i);
  25455. ptr+=len;
  25456. }
  25457. }
  25458. return values.median();
  25459. }
  25460. static double mp_modulo(_cimg_math_parser& mp) {
  25461. return cimg::mod(_mp_arg(2),_mp_arg(3));
  25462. }
  25463. static double mp_mproj(_cimg_math_parser& mp) {
  25464. double *ptrd = &_mp_arg(1) + 1;
  25465. const double
  25466. *ptrS = &_mp_arg(2) + 1,
  25467. *ptrD = &_mp_arg(5) + 1;
  25468. const unsigned int
  25469. wS = (unsigned int)mp.opcode[3],
  25470. hS = (unsigned int)mp.opcode[4],
  25471. wD = (unsigned int)mp.opcode[6];
  25472. const int
  25473. method = std::max(0,(int)_mp_arg(7)),
  25474. max_iter = std::max(0,(int)_mp_arg(8));
  25475. const double
  25476. max_residual = std::max(0.,_mp_arg(9));
  25477. CImg<doubleT>(ptrd,wS,wD,1,1,true) = CImg<doubleT>(ptrS,wS,hS,1,1,false).
  25478. project_matrix(CImg<doubleT>(ptrD,wD,hS,1,1,true),method,max_iter,max_residual);
  25479. return cimg::type<double>::nan();
  25480. }
  25481. static double mp_mse(_cimg_math_parser& mp) {
  25482. const unsigned int
  25483. _siz = (unsigned int)mp.opcode[4],
  25484. siz = std::max(_siz,1U),
  25485. off = _siz?1:0;
  25486. return CImg<doubleT>(&_mp_arg(2) + off,1,siz,1,1,true).
  25487. MSE(CImg<doubleT>(&_mp_arg(3) + off,1,siz,1,1,true));
  25488. }
  25489. static double mp_mul(_cimg_math_parser& mp) {
  25490. return _mp_arg(2)*_mp_arg(3);
  25491. }
  25492. static double mp_mul2(_cimg_math_parser& mp) {
  25493. return _mp_arg(2)*_mp_arg(3)*_mp_arg(4);
  25494. }
  25495. static double mp_neq(_cimg_math_parser& mp) {
  25496. return (double)(_mp_arg(2)!=_mp_arg(3));
  25497. }
  25498. static double mp_o2c(_cimg_math_parser& mp) {
  25499. mp_check_list(mp,"o2c");
  25500. unsigned int ind = (unsigned int)mp.opcode[2];
  25501. if (ind!=~0U) ind = (unsigned int)cimg::mod((int)_mp_arg(2),mp.imglist.width());
  25502. const CImg<T> &img = ind==~0U?mp.imgin:mp.imglist[ind];
  25503. longT offset = (longT)_mp_arg(3);
  25504. double *ptrd = &_mp_arg(1) + 1;
  25505. if (!img)
  25506. ptrd[0] = ptrd[1] = ptrd[2] = ptrd[3] = cimg::type<double>::nan();
  25507. else {
  25508. *(ptrd++) = (double)(offset%img.width());
  25509. offset/=img.width();
  25510. *(ptrd++) = (double)(offset%img.height());
  25511. offset/=img.height();
  25512. *(ptrd++) = (double)(offset%img.depth());
  25513. offset/=img.depth();
  25514. *ptrd = (double)(offset%img.spectrum());
  25515. }
  25516. return cimg::type<double>::nan();
  25517. }
  25518. static double mp_permutations(_cimg_math_parser& mp) {
  25519. return cimg::permutations((int)_mp_arg(2),(int)_mp_arg(3),(bool)_mp_arg(4));
  25520. }
  25521. static double mp_polygon(_cimg_math_parser& mp) {
  25522. const unsigned int i_end = (unsigned int)mp.opcode[2];
  25523. unsigned int ind = (unsigned int)mp.opcode[3];
  25524. if (ind!=~0U) {
  25525. if (!mp.imglist.width()) return cimg::type<double>::nan();
  25526. ind = (unsigned int)cimg::mod((int)_mp_arg(3),mp.imglist.width());
  25527. }
  25528. CImg<T> &img = ind==~0U?mp.imgout:mp.imglist[ind];
  25529. bool is_invalid_arguments = i_end<=4, is_outlined = false;
  25530. if (!is_invalid_arguments) {
  25531. int nbv = (int)_mp_arg(4);
  25532. if (!nbv) is_invalid_arguments = true;
  25533. else {
  25534. if (nbv<0) { nbv = -nbv; is_outlined = true; }
  25535. CImg<intT> points(nbv,2,1,1,0);
  25536. CImg<T> color(img._spectrum,1,1,1,0);
  25537. float opacity = 1;
  25538. unsigned int i = 5, pattern=~0U;
  25539. cimg_foroff(points,k) if (i<i_end) points(k/2,k%2) = (int)cimg::round(_mp_arg(i++));
  25540. else { is_invalid_arguments = true; break; }
  25541. if (!is_invalid_arguments) {
  25542. if (i<i_end) opacity = (float)_mp_arg(i++);
  25543. if (is_outlined && i<i_end) pattern = (unsigned int)_mp_arg(i++);
  25544. cimg_forX(color,k) if (i<i_end) color[k] = (T)_mp_arg(i++);
  25545. else { color.resize(k,1,1,1,-1); break; }
  25546. color.resize(img._spectrum,1,1,1,0,2);
  25547. if (is_outlined) img.draw_polygon(points,color._data,opacity,pattern);
  25548. else img.draw_polygon(points,color._data,opacity);
  25549. }
  25550. }
  25551. }
  25552. if (is_invalid_arguments) {
  25553. CImg<doubleT> args(i_end - 4);
  25554. cimg_forX(args,k) args[k] = _mp_arg(4 + k);
  25555. if (ind==~0U)
  25556. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'polygon()': "
  25557. "Invalid arguments '%s'. ",
  25558. mp.imgin.pixel_type(),args.value_string()._data);
  25559. else
  25560. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'polygon()': "
  25561. "Invalid arguments '#%u%s%s'. ",
  25562. mp.imgin.pixel_type(),ind,args._width?",":"",args.value_string()._data);
  25563. }
  25564. return cimg::type<double>::nan();
  25565. }
  25566. static double mp_pow(_cimg_math_parser& mp) {
  25567. const double v = _mp_arg(2), p = _mp_arg(3);
  25568. return std::pow(v,p);
  25569. }
  25570. static double mp_pow0_25(_cimg_math_parser& mp) {
  25571. const double val = _mp_arg(2);
  25572. return std::sqrt(std::sqrt(val));
  25573. }
  25574. static double mp_pow3(_cimg_math_parser& mp) {
  25575. const double val = _mp_arg(2);
  25576. return val*val*val;
  25577. }
  25578. static double mp_pow4(_cimg_math_parser& mp) {
  25579. const double val = _mp_arg(2);
  25580. return val*val*val*val;
  25581. }
  25582. static double mp_print(_cimg_math_parser& mp) {
  25583. const double val = _mp_arg(1);
  25584. const bool print_char = (bool)mp.opcode[3];
  25585. cimg_pragma_openmp(critical(mp_print))
  25586. {
  25587. CImg<charT> _expr(mp.opcode[2] - 4);
  25588. const ulongT *ptrs = mp.opcode._data + 4;
  25589. cimg_for(_expr,ptrd,char) *ptrd = (char)*(ptrs++);
  25590. cimg::strellipsize(_expr);
  25591. cimg::mutex(6);
  25592. if (print_char)
  25593. std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %.17g = '%c'",
  25594. _expr._data,val,(int)val);
  25595. else
  25596. std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = %.17g",
  25597. _expr._data,val);
  25598. std::fflush(cimg::output());
  25599. cimg::mutex(6,0);
  25600. }
  25601. return val;
  25602. }
  25603. static double mp_prod(_cimg_math_parser& mp) {
  25604. const unsigned int i_end = (unsigned int)mp.opcode[2];
  25605. double prod = 1;
  25606. for (unsigned int i = 3; i<i_end; i+=2) {
  25607. const unsigned int len = (unsigned int)mp.opcode[i + 1];
  25608. if (len>1) {
  25609. const double *ptr = &_mp_arg(i);
  25610. for (unsigned int k = 0; k<len; ++k) prod*=*(ptr++);
  25611. } else prod*=_mp_arg(i);
  25612. }
  25613. return prod;
  25614. }
  25615. static double mp_rad2deg(_cimg_math_parser& mp) {
  25616. return _mp_arg(2)*180/cimg::PI;
  25617. }
  25618. static double mp_repeat(_cimg_math_parser& mp) {
  25619. const double nb_it = _mp_arg(2), nb_itm1 = nb_it - 1;
  25620. double
  25621. *const ptrc = mp.opcode[3]!=~0U?&_mp_arg(3):0,
  25622. *const ptrs = &_mp_arg(1);
  25623. const CImg<ulongT>
  25624. *const p_body = ++mp.p_code,
  25625. *const p_end = p_body + mp.opcode[4];
  25626. if (nb_it>=1) {
  25627. const unsigned int _break_type = mp.break_type;
  25628. mp.break_type = 0;
  25629. double it = 0;
  25630. if (ptrc) { // Version with loop variable (3 arguments)
  25631. while (it<=nb_itm1) {
  25632. *ptrc = it;
  25633. for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
  25634. mp.opcode._data = mp.p_code->_data;
  25635. const ulongT target = mp.opcode[1];
  25636. mp.mem[target] = _cimg_mp_defunc(mp);
  25637. }
  25638. if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
  25639. ++it;
  25640. }
  25641. *ptrc = it;
  25642. } else // Version without loop variable (2 arguments)
  25643. while (it<=nb_itm1) {
  25644. for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
  25645. mp.opcode._data = mp.p_code->_data;
  25646. const ulongT target = mp.opcode[1];
  25647. mp.mem[target] = _cimg_mp_defunc(mp);
  25648. }
  25649. if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
  25650. ++it;
  25651. }
  25652. mp.break_type = _break_type;
  25653. }
  25654. mp.p_code = p_end - 1;
  25655. return *ptrs;
  25656. }
  25657. static double mp_rol(_cimg_math_parser& mp) {
  25658. return cimg::rol(_mp_arg(2),(unsigned int)_mp_arg(3));
  25659. }
  25660. static double mp_ror(_cimg_math_parser& mp) {
  25661. return cimg::ror(_mp_arg(2),(unsigned int)_mp_arg(3));
  25662. }
  25663. static double mp_rot2d(_cimg_math_parser& mp) {
  25664. double *ptrd = &_mp_arg(1) + 1;
  25665. const float
  25666. theta = (float)_mp_arg(2),
  25667. ca = std::cos(theta),
  25668. sa = std::sin(theta);
  25669. *(ptrd++) = ca;
  25670. *(ptrd++) = -sa;
  25671. *(ptrd++) = sa;
  25672. *ptrd = ca;
  25673. return cimg::type<double>::nan();
  25674. }
  25675. static double mp_rot3d(_cimg_math_parser& mp) {
  25676. double *ptrd = &_mp_arg(1) + 1;
  25677. const float
  25678. x = (float)_mp_arg(2),
  25679. y = (float)_mp_arg(3),
  25680. z = (float)_mp_arg(4),
  25681. theta = (float)_mp_arg(5);
  25682. CImg<doubleT>(ptrd,3,3,1,1,true) = CImg<doubleT>::rotation_matrix(x,y,z,theta*180/cimg::PI);
  25683. return cimg::type<double>::nan();
  25684. }
  25685. static double mp_round(_cimg_math_parser& mp) {
  25686. return cimg::round(_mp_arg(2),_mp_arg(3),(int)_mp_arg(4));
  25687. }
  25688. #ifdef cimg_mp_func_run
  25689. static double mp_run(_cimg_math_parser& mp) {
  25690. const unsigned int nb_args = (unsigned int)(mp.opcode[2] - 3)/2;
  25691. CImgList<charT> _str;
  25692. CImg<charT> it;
  25693. for (unsigned int n = 0; n<nb_args; ++n) {
  25694. const unsigned int siz = (unsigned int)mp.opcode[4 + 2*n];
  25695. if (siz) { // Vector argument -> string
  25696. const double *ptr = &_mp_arg(3 + 2*n) + 1;
  25697. unsigned int l = 0;
  25698. while (l<siz && ptr[l]) ++l;
  25699. CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str);
  25700. } else { // Scalar argument -> number
  25701. it.assign(24);
  25702. cimg_snprintf(it,it._width,"%.17g",_mp_arg(3 + 2*n));
  25703. CImg<charT>::string(it,false,true).move_to(_str);
  25704. }
  25705. }
  25706. CImg(1,1,1,1,0).move_to(_str);
  25707. CImg<charT> str = _str>'x';
  25708. #if cimg_use_openmp==0
  25709. const unsigned int n_thread = 0;
  25710. #else
  25711. const unsigned int n_thread = omp_get_thread_num();
  25712. #endif
  25713. cimg_mp_func_run(str._data,n_thread && mp.is_noncritical_run);
  25714. return cimg::type<double>::nan();
  25715. }
  25716. #endif
  25717. static double mp_self_add(_cimg_math_parser& mp) {
  25718. return _mp_arg(1)+=_mp_arg(2);
  25719. }
  25720. static double mp_self_bitwise_and(_cimg_math_parser& mp) {
  25721. double &val = _mp_arg(1);
  25722. return val = (double)((longT)val & (longT)_mp_arg(2));
  25723. }
  25724. static double mp_self_bitwise_left_shift(_cimg_math_parser& mp) {
  25725. double &val = _mp_arg(1);
  25726. return val = (double)((longT)val<<(unsigned int)_mp_arg(2));
  25727. }
  25728. static double mp_self_bitwise_or(_cimg_math_parser& mp) {
  25729. double &val = _mp_arg(1);
  25730. return val = (double)((longT)val | (longT)_mp_arg(2));
  25731. }
  25732. static double mp_self_bitwise_right_shift(_cimg_math_parser& mp) {
  25733. double &val = _mp_arg(1);
  25734. return val = (double)((longT)val>>(unsigned int)_mp_arg(2));
  25735. }
  25736. static double mp_self_decrement(_cimg_math_parser& mp) {
  25737. return --_mp_arg(1);
  25738. }
  25739. static double mp_self_increment(_cimg_math_parser& mp) {
  25740. return ++_mp_arg(1);
  25741. }
  25742. static double mp_self_map_vector_s(_cimg_math_parser& mp) { // Vector += scalar
  25743. unsigned int
  25744. ptrd = (unsigned int)mp.opcode[1] + 1,
  25745. siz = (unsigned int)mp.opcode[2];
  25746. mp_func op = (mp_func)mp.opcode[3];
  25747. CImg<ulongT> l_opcode(1,3);
  25748. l_opcode[2] = mp.opcode[4]; // Scalar argument
  25749. l_opcode.swap(mp.opcode);
  25750. ulongT &target = mp.opcode[1];
  25751. while (siz-->0) { target = ptrd++; (*op)(mp); }
  25752. l_opcode.swap(mp.opcode);
  25753. return cimg::type<double>::nan();
  25754. }
  25755. static double mp_self_map_vector_v(_cimg_math_parser& mp) { // Vector += vector
  25756. unsigned int
  25757. ptrd = (unsigned int)mp.opcode[1] + 1,
  25758. siz = (unsigned int)mp.opcode[2],
  25759. ptrs = (unsigned int)mp.opcode[4] + 1;
  25760. mp_func op = (mp_func)mp.opcode[3];
  25761. CImg<ulongT> l_opcode(1,4);
  25762. l_opcode.swap(mp.opcode);
  25763. ulongT &target = mp.opcode[1], &argument = mp.opcode[2];
  25764. while (siz-->0) { target = ptrd++; argument = ptrs++; (*op)(mp); }
  25765. l_opcode.swap(mp.opcode);
  25766. return cimg::type<double>::nan();
  25767. }
  25768. static double mp_self_mul(_cimg_math_parser& mp) {
  25769. return _mp_arg(1)*=_mp_arg(2);
  25770. }
  25771. static double mp_self_div(_cimg_math_parser& mp) {
  25772. return _mp_arg(1)/=_mp_arg(2);
  25773. }
  25774. static double mp_self_modulo(_cimg_math_parser& mp) {
  25775. double &val = _mp_arg(1);
  25776. return val = cimg::mod(val,_mp_arg(2));
  25777. }
  25778. static double mp_self_pow(_cimg_math_parser& mp) {
  25779. double &val = _mp_arg(1);
  25780. return val = std::pow(val,_mp_arg(2));
  25781. }
  25782. static double mp_self_sub(_cimg_math_parser& mp) {
  25783. return _mp_arg(1)-=_mp_arg(2);
  25784. }
  25785. #ifdef cimg_mp_func_set
  25786. static double mp_set(_cimg_math_parser& mp) {
  25787. const double *ptrs = &_mp_arg(1);
  25788. double *ptrd = &_mp_arg(3) + 1;
  25789. const unsigned int
  25790. sizs = (unsigned int)mp.opcode[2],
  25791. sizd = (unsigned int)mp.opcode[4];
  25792. CImg<charT> sd(sizd + 1);
  25793. cimg_for_inX(sd,0,sd.width() - 1,i) sd[i] = (char)ptrd[i];
  25794. sd.back() = 0;
  25795. if (sizs) cimg_mp_func_set(ptrs + 1,sizs,sd._data);
  25796. else cimg_mp_func_set(ptrs,0,sd._data);
  25797. return *ptrs;
  25798. }
  25799. #endif
  25800. static double mp_set_ioff(_cimg_math_parser& mp) {
  25801. CImg<T> &img = mp.imgout;
  25802. const longT
  25803. off = (longT)_mp_arg(2),
  25804. whds = (longT)img.size();
  25805. const double val = _mp_arg(1);
  25806. if (off>=0 && off<whds) img[off] = (T)val;
  25807. return val;
  25808. }
  25809. static double mp_set_ixyzc(_cimg_math_parser& mp) {
  25810. CImg<T> &img = mp.imgout;
  25811. const int
  25812. x = (int)_mp_arg(2), y = (int)_mp_arg(3),
  25813. z = (int)_mp_arg(4), c = (int)_mp_arg(5);
  25814. const double val = _mp_arg(1);
  25815. if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
  25816. z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
  25817. img(x,y,z,c) = (T)val;
  25818. return val;
  25819. }
  25820. static double mp_set_joff(_cimg_math_parser& mp) {
  25821. CImg<T> &img = mp.imgout;
  25822. const int
  25823. ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
  25824. oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
  25825. const longT
  25826. off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
  25827. whds = (longT)img.size();
  25828. const double val = _mp_arg(1);
  25829. if (off>=0 && off<whds) img[off] = (T)val;
  25830. return val;
  25831. }
  25832. static double mp_set_jxyzc(_cimg_math_parser& mp) {
  25833. CImg<T> &img = mp.imgout;
  25834. const double
  25835. ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y],
  25836. oz = mp.mem[_cimg_mp_slot_z], oc = mp.mem[_cimg_mp_slot_c];
  25837. const int
  25838. x = (int)(ox + _mp_arg(2)), y = (int)(oy + _mp_arg(3)),
  25839. z = (int)(oz + _mp_arg(4)), c = (int)(oc + _mp_arg(5));
  25840. const double val = _mp_arg(1);
  25841. if (x>=0 && x<img.width() && y>=0 && y<img.height() &&
  25842. z>=0 && z<img.depth() && c>=0 && c<img.spectrum())
  25843. img(x,y,z,c) = (T)val;
  25844. return val;
  25845. }
  25846. static double mp_set_Ioff_s(_cimg_math_parser& mp) {
  25847. CImg<T> &img = mp.imgout;
  25848. const longT
  25849. off = (longT)_mp_arg(2),
  25850. whd = (longT)img.width()*img.height()*img.depth();
  25851. const T val = (T)_mp_arg(1);
  25852. if (off>=0 && off<whd) {
  25853. T *ptrd = &img[off];
  25854. cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
  25855. }
  25856. return _mp_arg(1);
  25857. }
  25858. static double mp_set_Ioff_v(_cimg_math_parser& mp) {
  25859. CImg<T> &img = mp.imgout;
  25860. const longT
  25861. off = (longT)_mp_arg(2),
  25862. whd = (longT)img.width()*img.height()*img.depth();
  25863. const double *ptrs = &_mp_arg(1) + 1;
  25864. if (off>=0 && off<whd) {
  25865. const unsigned int vsiz = (unsigned int)mp.opcode[3];
  25866. T *ptrd = &img[off];
  25867. cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
  25868. }
  25869. return cimg::type<double>::nan();
  25870. }
  25871. static double mp_set_Ixyz_s(_cimg_math_parser& mp) {
  25872. CImg<T> &img = mp.imgout;
  25873. const int
  25874. x = (int)_mp_arg(2),
  25875. y = (int)_mp_arg(3),
  25876. z = (int)_mp_arg(4);
  25877. const T val = (T)_mp_arg(1);
  25878. if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
  25879. T *ptrd = &img(x,y,z);
  25880. const ulongT whd = (ulongT)img._width*img._height*img._depth;
  25881. cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
  25882. }
  25883. return _mp_arg(1);
  25884. }
  25885. static double mp_set_Ixyz_v(_cimg_math_parser& mp) {
  25886. CImg<T> &img = mp.imgout;
  25887. const int
  25888. x = (int)_mp_arg(2),
  25889. y = (int)_mp_arg(3),
  25890. z = (int)_mp_arg(4);
  25891. const double *ptrs = &_mp_arg(1) + 1;
  25892. if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
  25893. const unsigned int vsiz = (unsigned int)mp.opcode[5];
  25894. T *ptrd = &img(x,y,z);
  25895. const ulongT whd = (ulongT)img._width*img._height*img._depth;
  25896. cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
  25897. }
  25898. return cimg::type<double>::nan();
  25899. }
  25900. static double mp_set_Joff_s(_cimg_math_parser& mp) {
  25901. CImg<T> &img = mp.imgout;
  25902. const int
  25903. ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
  25904. oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
  25905. const longT
  25906. off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
  25907. whd = (longT)img.width()*img.height()*img.depth();
  25908. const T val = (T)_mp_arg(1);
  25909. if (off>=0 && off<whd) {
  25910. T *ptrd = &img[off];
  25911. cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
  25912. }
  25913. return _mp_arg(1);
  25914. }
  25915. static double mp_set_Joff_v(_cimg_math_parser& mp) {
  25916. CImg<T> &img = mp.imgout;
  25917. const int
  25918. ox = (int)mp.mem[_cimg_mp_slot_x], oy = (int)mp.mem[_cimg_mp_slot_y],
  25919. oz = (int)mp.mem[_cimg_mp_slot_z], oc = (int)mp.mem[_cimg_mp_slot_c];
  25920. const longT
  25921. off = img.offset(ox,oy,oz,oc) + (longT)_mp_arg(2),
  25922. whd = (longT)img.width()*img.height()*img.depth();
  25923. const double *ptrs = &_mp_arg(1) + 1;
  25924. if (off>=0 && off<whd) {
  25925. const unsigned int vsiz = (unsigned int)mp.opcode[3];
  25926. T *ptrd = &img[off];
  25927. cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
  25928. }
  25929. return cimg::type<double>::nan();
  25930. }
  25931. static double mp_set_Jxyz_s(_cimg_math_parser& mp) {
  25932. CImg<T> &img = mp.imgout;
  25933. const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
  25934. const int
  25935. x = (int)(ox + _mp_arg(2)),
  25936. y = (int)(oy + _mp_arg(3)),
  25937. z = (int)(oz + _mp_arg(4));
  25938. const T val = (T)_mp_arg(1);
  25939. if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
  25940. T *ptrd = &img(x,y,z);
  25941. const ulongT whd = (ulongT)img._width*img._height*img._depth;
  25942. cimg_forC(img,c) { *ptrd = val; ptrd+=whd; }
  25943. }
  25944. return _mp_arg(1);
  25945. }
  25946. static double mp_set_Jxyz_v(_cimg_math_parser& mp) {
  25947. CImg<T> &img = mp.imgout;
  25948. const double ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z];
  25949. const int
  25950. x = (int)(ox + _mp_arg(2)),
  25951. y = (int)(oy + _mp_arg(3)),
  25952. z = (int)(oz + _mp_arg(4));
  25953. const double *ptrs = &_mp_arg(1) + 1;
  25954. if (x>=0 && x<img.width() && y>=0 && y<img.height() && z>=0 && z<img.depth()) {
  25955. const unsigned int vsiz = (unsigned int)mp.opcode[5];
  25956. T *ptrd = &img(x,y,z);
  25957. const ulongT whd = (ulongT)img._width*img._height*img._depth;
  25958. cimg_for_inC(img,0,vsiz - 1,c) { *ptrd = (T)*(ptrs++); ptrd+=whd; }
  25959. }
  25960. return cimg::type<double>::nan();
  25961. }
  25962. static double mp_shift(_cimg_math_parser& mp) {
  25963. double *const ptrd = &_mp_arg(1) + 1;
  25964. const double *const ptrs = &_mp_arg(2) + 1;
  25965. const unsigned int siz = (unsigned int)mp.opcode[3];
  25966. const int
  25967. shift = (int)_mp_arg(4),
  25968. boundary_conditions = (int)_mp_arg(5);
  25969. CImg<doubleT>(ptrd,siz,1,1,1,true) = CImg<doubleT>(ptrs,siz,1,1,1,true).shift(shift,0,0,0,boundary_conditions);
  25970. return cimg::type<double>::nan();
  25971. }
  25972. static double mp_sign(_cimg_math_parser& mp) {
  25973. return cimg::sign(_mp_arg(2));
  25974. }
  25975. static double mp_sin(_cimg_math_parser& mp) {
  25976. return std::sin(_mp_arg(2));
  25977. }
  25978. static double mp_sinc(_cimg_math_parser& mp) {
  25979. return cimg::sinc(_mp_arg(2));
  25980. }
  25981. static double mp_sinh(_cimg_math_parser& mp) {
  25982. return std::sinh(_mp_arg(2));
  25983. }
  25984. static double mp_solve(_cimg_math_parser& mp) {
  25985. double *ptrd = &_mp_arg(1) + 1;
  25986. const double
  25987. *ptr1 = &_mp_arg(2) + 1,
  25988. *ptr2 = &_mp_arg(3) + 1;
  25989. const unsigned int
  25990. k = (unsigned int)mp.opcode[4],
  25991. l = (unsigned int)mp.opcode[5],
  25992. m = (unsigned int)mp.opcode[6];
  25993. const bool use_LU = (bool)_mp_arg(7);
  25994. CImg<doubleT>(ptrd,m,k,1,1,true) = CImg<doubleT>(ptr2,m,l,1,1,false).
  25995. solve(CImg<doubleT>(ptr1,k,l,1,1,true),use_LU);
  25996. return cimg::type<double>::nan();
  25997. }
  25998. static double mp_sort(_cimg_math_parser& mp) {
  25999. double *const ptrd = &_mp_arg(1) + 1;
  26000. const double *const ptrs = &_mp_arg(2) + 1;
  26001. const bool is_increasing = (bool)_mp_arg(4);
  26002. const unsigned int
  26003. siz = (unsigned int)mp.opcode[3],
  26004. nb_elts = mp.opcode[5]==~0U?siz:(unsigned int)_mp_arg(5),
  26005. siz_elt = (unsigned int)_mp_arg(6);
  26006. const ulongT sn = siz_elt*nb_elts;
  26007. if (sn>siz || siz_elt<1)
  26008. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'sort()': "
  26009. "Arguments 'nb_elts=%g' and 'siz_elt=%g' are invalid "
  26010. "for sorting a vector of size %u.",
  26011. mp.imgin.pixel_type(),_mp_arg(5),_mp_arg(6),siz);
  26012. CImg<doubleT>(ptrd,siz_elt,nb_elts,1,1,true) = CImg<doubleT>(ptrs,siz_elt,nb_elts,1,1,true).
  26013. get_sort(is_increasing,siz_elt>1?'y':0);
  26014. if (sn<siz) CImg<doubleT>(ptrd + sn,siz - sn,1,1,1,true) = CImg<doubleT>(ptrs + sn,siz - sn,1,1,1,true);
  26015. return cimg::type<double>::nan();
  26016. }
  26017. static double mp_sqr(_cimg_math_parser& mp) {
  26018. return cimg::sqr(_mp_arg(2));
  26019. }
  26020. static double mp_sqrt(_cimg_math_parser& mp) {
  26021. return std::sqrt(_mp_arg(2));
  26022. }
  26023. static double mp_srand(_cimg_math_parser& mp) {
  26024. mp.rng = (cimg_uint64)_mp_arg(2);
  26025. return cimg::type<double>::nan();
  26026. }
  26027. static double mp_srand0(_cimg_math_parser& mp) {
  26028. cimg::srand(&mp.rng);
  26029. #if cimg_use_openmp!=0
  26030. mp.rng+=omp_get_thread_num();
  26031. #endif
  26032. return cimg::type<double>::nan();
  26033. }
  26034. static double mp_std(_cimg_math_parser& mp) {
  26035. return std::sqrt(mp_var(mp));
  26036. }
  26037. static double mp_string_init(_cimg_math_parser& mp) {
  26038. const unsigned char *ptrs = (unsigned char*)&mp.opcode[3];
  26039. unsigned int
  26040. ptrd = (unsigned int)mp.opcode[1] + 1,
  26041. siz = (unsigned int)mp.opcode[2];
  26042. while (siz-->0) mp.mem[ptrd++] = (double)*(ptrs++);
  26043. return cimg::type<double>::nan();
  26044. }
  26045. #ifdef cimg_mp_func_store
  26046. static double mp_store(_cimg_math_parser& mp) {
  26047. const double
  26048. *ptr1 = &_mp_arg(2),
  26049. *ptr2 = &_mp_arg(4) + 1;
  26050. const unsigned int
  26051. siz1 = (unsigned int)mp.opcode[3],
  26052. siz2 = (unsigned int)mp.opcode[5];
  26053. const int
  26054. w = (int)_mp_arg(6),
  26055. h = (int)_mp_arg(7),
  26056. d = (int)_mp_arg(8),
  26057. s = (int)_mp_arg(9);
  26058. const bool is_compressed = (bool)_mp_arg(10);
  26059. if (w<0 || h<0 || d<0 || s<0)
  26060. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'store()': "
  26061. "Specified image dimensions (%d,%d,%d,%d) are invalid.",
  26062. pixel_type(),w,h,d,s);
  26063. CImg<charT> ss(siz2 + 1);
  26064. cimg_for_inX(ss,0,ss.width() - 1,i) ss[i] = (char)ptr2[i];
  26065. ss.back() = 0;
  26066. if (siz1) cimg_mp_func_store(ptr1 + 1,siz1,
  26067. (unsigned int)w,(unsigned int)h,(unsigned int)d,(unsigned int)s,
  26068. is_compressed,ss._data);
  26069. else cimg_mp_func_store(ptr1,1,(unsigned int)w,(unsigned int)h,(unsigned int)d,(unsigned int)s,
  26070. is_compressed,ss._data);
  26071. return cimg::type<double>::nan();
  26072. }
  26073. #endif
  26074. static double mp_s2v(_cimg_math_parser& mp) {
  26075. const double *ptrs = &_mp_arg(2);
  26076. const ulongT siz = (ulongT)mp.opcode[3];
  26077. longT ind = (longT)_mp_arg(4);
  26078. const bool is_strict = (bool)_mp_arg(5);
  26079. double val = cimg::type<double>::nan();
  26080. if (ind<0 || ind>=(longT)siz) return val;
  26081. if (!siz) return *ptrs>='0' && *ptrs<='9'?*ptrs - '0':val;
  26082. CImg<charT> ss(siz + 1 - ind);
  26083. ptrs+=1 + ind;
  26084. cimg_forX(ss,i) ss[i] = (char)ptrs[i];
  26085. ss.back() = 0;
  26086. const char *s = ss._data;
  26087. while (*s && *s<=32) ++s;
  26088. const bool is_negative = *s=='-';
  26089. if (is_negative || *s=='+') ++s;
  26090. int err = 0;
  26091. char sep;
  26092. if (*s=='0' && (s[1]=='x' || s[1]=='X') && s[2]>32) { // Hexadecimal number
  26093. unsigned int ival;
  26094. err = cimg_sscanf(s + 2,"%x%c",&ival,&sep);
  26095. if (err>0) val = (double)ival;
  26096. } else if (*s>32) { // Decimal number
  26097. err = cimg_sscanf(s,"%lf%c",&val,&sep);
  26098. #if cimg_OS==2
  26099. // Check for +/-NaN and +/-inf as Microsoft's sscanf() version is not able
  26100. // to read those particular values.
  26101. if (!err && (*s=='i' || *s=='I' || *s=='n' || *s=='N')) {
  26102. if (!cimg::strncasecmp(s,"inf",3)) { val = cimg::type<double>::inf(); err = 1 + (s[3]!=0); }
  26103. else if (!cimg::strncasecmp(s,"nan",3)) { val = cimg::type<double>::nan(); err = 1 + (s[3]!=0); }
  26104. }
  26105. #endif
  26106. }
  26107. if (err<=0 || (is_strict && err!=1)) return cimg::type<double>::nan();
  26108. if (is_negative) val = -val;
  26109. return val;
  26110. }
  26111. static double mp_string(_cimg_math_parser& mp) {
  26112. double *const ptrd = &_mp_arg(1) + 1;
  26113. const unsigned int nb_args = (unsigned int)(mp.opcode[3] - 3)/2;
  26114. CImgList<charT> _str;
  26115. CImg<charT> it;
  26116. for (unsigned int n = 0; n<nb_args; ++n) {
  26117. const unsigned int siz = (unsigned int)mp.opcode[5 + 2*n];
  26118. if (siz) { // Vector argument -> string
  26119. const double *ptr = &_mp_arg(4 + 2*n) + 1;
  26120. unsigned int l = 0;
  26121. while (l<siz && ptr[l]) ++l;
  26122. CImg<doubleT>(ptr,l,1,1,1,true).move_to(_str);
  26123. } else { // Scalar argument -> number
  26124. it.assign(24);
  26125. cimg_snprintf(it,it._width,"%.17g",_mp_arg(4 + 2*n));
  26126. CImg<charT>::string(it,false,true).move_to(_str);
  26127. }
  26128. }
  26129. const CImg<charT> str = _str>'x';
  26130. const unsigned int sizd = std::min(str._width,(unsigned int)mp.opcode[2]);
  26131. std::memset(ptrd,0,mp.opcode[2]*sizeof(double));
  26132. for (unsigned int k = 0; k<sizd; ++k) ptrd[k] = (double)str[k];
  26133. return cimg::type<double>::nan();
  26134. }
  26135. static double mp_sub(_cimg_math_parser& mp) {
  26136. return _mp_arg(2) - _mp_arg(3);
  26137. }
  26138. static double mp_sum(_cimg_math_parser& mp) {
  26139. const unsigned int i_end = (unsigned int)mp.opcode[2];
  26140. double sum = 0;
  26141. for (unsigned int i = 3; i<i_end; i+=2) {
  26142. const unsigned int len = (unsigned int)mp.opcode[i + 1];
  26143. if (len>1) {
  26144. const double *ptr = &_mp_arg(i);
  26145. for (unsigned int k = 0; k<len; ++k) sum+=*(ptr++);
  26146. } else sum+=_mp_arg(i);
  26147. }
  26148. return sum;
  26149. }
  26150. static double mp_swap(_cimg_math_parser& mp) {
  26151. const unsigned int siz = (unsigned int)mp.opcode[3];
  26152. if (!siz) { // Scalar
  26153. double &arg1 = _mp_arg(1), &arg2 = _mp_arg(2);
  26154. cimg::swap(arg1,arg2);
  26155. } else { // Vector
  26156. double *const ptr1 = &_mp_arg(1) + 1, *const ptr2 = &_mp_arg(2) + 1;
  26157. for (unsigned int k = 0; k<siz; ++k) cimg::swap(ptr1[k],ptr2[k]);
  26158. }
  26159. return _mp_arg(1);
  26160. }
  26161. static double mp_tan(_cimg_math_parser& mp) {
  26162. return std::tan(_mp_arg(2));
  26163. }
  26164. static double mp_tanh(_cimg_math_parser& mp) {
  26165. return std::tanh(_mp_arg(2));
  26166. }
  26167. static double mp_trace(_cimg_math_parser& mp) {
  26168. const double *ptrs = &_mp_arg(2) + 1;
  26169. const unsigned int k = (unsigned int)mp.opcode[3];
  26170. return CImg<doubleT>(ptrs,k,k,1,1,true).trace();
  26171. }
  26172. static double mp_transpose(_cimg_math_parser& mp) {
  26173. double *ptrd = &_mp_arg(1) + 1;
  26174. const double *ptrs = &_mp_arg(2) + 1;
  26175. const unsigned int
  26176. k = (unsigned int)mp.opcode[3],
  26177. l = (unsigned int)mp.opcode[4];
  26178. CImg<doubleT>(ptrd,l,k,1,1,true) = CImg<doubleT>(ptrs,k,l,1,1,true).get_transpose();
  26179. return cimg::type<double>::nan();
  26180. }
  26181. static double mp_u(_cimg_math_parser& mp) {
  26182. return cimg::rand(_mp_arg(2),_mp_arg(3),&mp.rng);
  26183. }
  26184. static double mp_u_ext(_cimg_math_parser& mp) { // Extended version with extremum control
  26185. const double eps = 1e-5;
  26186. const bool
  26187. include_min = (bool)_mp_arg(4),
  26188. include_max = (bool)_mp_arg(5);
  26189. double
  26190. m = _mp_arg(2),
  26191. M = _mp_arg(3);
  26192. if (m>M) cimg::swap(m,M);
  26193. if (!include_min) m = m>0?m*(1 + eps):m<0?m*(1 - eps):eps;
  26194. if (!include_max) M = M>0?M*(1 - eps):M<0?M*(1 + eps):-eps;
  26195. return cimg::rand(m,M,&mp.rng);
  26196. }
  26197. static double mp_ui2f(_cimg_math_parser& mp) {
  26198. return (double)cimg::uint2float((unsigned int)_mp_arg(2));
  26199. }
  26200. static double mp_uppercase(_cimg_math_parser& mp) {
  26201. return cimg::uppercase(_mp_arg(2));
  26202. }
  26203. static double mp_var(_cimg_math_parser& mp) {
  26204. const unsigned int i_end = (unsigned int)mp.opcode[2];
  26205. unsigned int siz = 0;
  26206. double val, S = 0, S2 = 0;
  26207. for (unsigned int i = 3; i<i_end; i+=2) {
  26208. const unsigned int len = (unsigned int)mp.opcode[i + 1];
  26209. if (len>1) {
  26210. const double *ptr = &_mp_arg(i);
  26211. for (unsigned int k = 0; k<len; ++k) { val = *(ptr++); S+=val; S2+=val*val; }
  26212. } else { val = _mp_arg(i); S+=val; S2+=val*val; }
  26213. siz+=len;
  26214. }
  26215. return (S2 - S*S/siz)/(siz - 1);
  26216. }
  26217. static double mp_vector_copy(_cimg_math_parser& mp) {
  26218. std::memcpy(&_mp_arg(1) + 1,&_mp_arg(2) + 1,sizeof(double)*mp.opcode[3]);
  26219. return cimg::type<double>::nan();
  26220. }
  26221. static double mp_vector_crop(_cimg_math_parser& mp) {
  26222. double *ptrd = &_mp_arg(1) + 1;
  26223. const double *ptrs = &_mp_arg(2) + 1;
  26224. const longT
  26225. length = (longT)mp.opcode[3],
  26226. start = (longT)_mp_arg(4),
  26227. sublength = (longT)mp.opcode[5],
  26228. step = (longT)_mp_arg(6);
  26229. if (start<0 || start + step*(sublength-1)>=length)
  26230. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Value accessor '[]': "
  26231. "Out-of-bounds sub-vector request "
  26232. "(length: %ld, start: %ld, sub-length: %ld, step: %ld).",
  26233. mp.imgin.pixel_type(),length,start,sublength,step);
  26234. ptrs+=start;
  26235. if (step==1) std::memcpy(ptrd,ptrs,sublength*sizeof(double));
  26236. else for (longT k = 0; k<sublength; ++k) { *(ptrd++) = *ptrs; ptrs+=step; }
  26237. return cimg::type<double>::nan();
  26238. }
  26239. static double mp_vector_crop_ext(_cimg_math_parser& mp) {
  26240. double *const ptrd = &_mp_arg(1) + 1;
  26241. const double *const ptrs = &_mp_arg(2) + 1;
  26242. const unsigned int
  26243. w = (unsigned int)mp.opcode[3],
  26244. h = (unsigned int)mp.opcode[4],
  26245. d = (unsigned int)mp.opcode[5],
  26246. s = (unsigned int)mp.opcode[6],
  26247. dx = (unsigned int)mp.opcode[11],
  26248. dy = (unsigned int)mp.opcode[12],
  26249. dz = (unsigned int)mp.opcode[13],
  26250. dc = (unsigned int)mp.opcode[14],
  26251. boundary_conditions = (int)_mp_arg(15);
  26252. const int x = (int)_mp_arg(7), y = (int)_mp_arg(8), z = (int)_mp_arg(9), c = (int)_mp_arg(10);
  26253. CImg<doubleT>(ptrd,dx,dy,dz,dc,true) = CImg<doubleT>(ptrs,w,h,d,s,true).
  26254. get_crop(x,y,z,c,x + dx - 1,y + dy - 1,z + dz - 1,c + dc - 1,boundary_conditions);
  26255. return cimg::type<double>::nan();
  26256. }
  26257. static double mp_vector_draw(_cimg_math_parser& mp) {
  26258. double *const ptrd = &_mp_arg(1) + 1;
  26259. const double *const ptrs = &_mp_arg(7) + 1;
  26260. const unsigned int
  26261. sizD = (unsigned int)mp.opcode[2],
  26262. sizS = (unsigned int)mp.opcode[8];
  26263. const int
  26264. w = (int)_mp_arg(3), h = (int)_mp_arg(4), d = (int)_mp_arg(5), s = (int)_mp_arg(6),
  26265. x = (int)_mp_arg(9), y = (int)_mp_arg(10), z = (int)_mp_arg(11), c = (int)_mp_arg(12);
  26266. int dx = (int)mp.opcode[13], dy = (int)mp.opcode[14], dz = (int)mp.opcode[15], dc = (int)mp.opcode[16];
  26267. dx = (unsigned int)dx==~0U?w:(int)_mp_arg(13);
  26268. dy = (unsigned int)dy==~0U?h:(int)_mp_arg(14);
  26269. dz = (unsigned int)dz==~0U?d:(int)_mp_arg(15);
  26270. dc = (unsigned int)dc==~0U?s:(int)_mp_arg(16);
  26271. if (w<=0 || h<=0 || d<=0 || s<=0)
  26272. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
  26273. "Invalid specified target vector geometry (%d,%d,%d,%d).",
  26274. mp.imgin.pixel_type(),w,h,d,s);
  26275. if (sizD<(ulongT)w*h*d*s)
  26276. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
  26277. "Target vector (%lu values) and its specified target geometry (%d,%d,%d,%d) "
  26278. "(%lu values) do not match.",
  26279. mp.imgin.pixel_type(),sizD,w,h,d,s,(ulongT)w*h*d*s);
  26280. if (dx<=0 || dy<=0 || dz<=0 || dc<=0)
  26281. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
  26282. "Invalid specified sprite geometry (%d,%d,%d,%d).",
  26283. mp.imgin.pixel_type(),dx,dy,dz,dc);
  26284. if (sizS<(ulongT)dx*dy*dz*dc)
  26285. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
  26286. "Sprite vector (%lu values) and its specified sprite geometry (%d,%d,%d,%d) "
  26287. "(%lu values) do not match.",
  26288. mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc);
  26289. CImg<doubleT> D(ptrd,w,h,d,s,true);
  26290. const CImg<doubleT> S(ptrs,dx,dy,dz,dc,true);
  26291. const float opacity = (float)_mp_arg(17);
  26292. if (mp.opcode[18]!=~0U) { // Opacity mask specified
  26293. const ulongT sizM = mp.opcode[19];
  26294. if (sizM<(ulongT)dx*dy*dz)
  26295. throw CImgArgumentException("[" cimg_appname "_math_parser] CImg<%s>: Function 'draw()': "
  26296. "Mask vector (%lu values) and specified sprite geometry (%u,%u,%u,%u) "
  26297. "(%lu values) do not match.",
  26298. mp.imgin.pixel_type(),sizS,dx,dy,dz,dc,(ulongT)dx*dy*dz*dc);
  26299. const CImg<doubleT> M(&_mp_arg(18) + 1,dx,dy,dz,(unsigned int)(sizM/(dx*dy*dz)),true);
  26300. D.draw_image(x,y,z,c,S,M,opacity,(float)_mp_arg(20));
  26301. } else D.draw_image(x,y,z,c,S,opacity);
  26302. return cimg::type<double>::nan();
  26303. }
  26304. static double mp_vector_init(_cimg_math_parser& mp) {
  26305. unsigned int
  26306. ptrs = 4U,
  26307. ptrd = (unsigned int)mp.opcode[1] + 1,
  26308. siz = (unsigned int)mp.opcode[3];
  26309. switch (mp.opcode[2] - 4) {
  26310. case 0 : std::memset(mp.mem._data + ptrd,0,siz*sizeof(double)); break; // 0 values given
  26311. case 1 : { const double val = _mp_arg(ptrs); while (siz-->0) mp.mem[ptrd++] = val; } break;
  26312. default : while (siz-->0) { mp.mem[ptrd++] = _mp_arg(ptrs++); if (ptrs>=mp.opcode[2]) ptrs = 4U; }
  26313. }
  26314. return cimg::type<double>::nan();
  26315. }
  26316. static double mp_vector_eq(_cimg_math_parser& mp) {
  26317. const double
  26318. *ptr1 = &_mp_arg(2) + 1,
  26319. *ptr2 = &_mp_arg(4) + 1;
  26320. unsigned int p1 = (unsigned int)mp.opcode[3], p2 = (unsigned int)mp.opcode[5], n;
  26321. const int N = (int)_mp_arg(6);
  26322. const bool case_sensitive = (bool)_mp_arg(7);
  26323. bool still_equal = true;
  26324. double value;
  26325. if (!N) return true;
  26326. // Compare all values.
  26327. if (N<0) {
  26328. if (p1>0 && p2>0) { // Vector == vector
  26329. if (p1!=p2) return false;
  26330. if (case_sensitive)
  26331. while (still_equal && p1--) still_equal = *(ptr1++)==*(ptr2++);
  26332. else
  26333. while (still_equal && p1--)
  26334. still_equal = cimg::lowercase(*(ptr1++))==cimg::lowercase(*(ptr2++));
  26335. return still_equal;
  26336. } else if (p1>0 && !p2) { // Vector == scalar
  26337. value = _mp_arg(4);
  26338. if (!case_sensitive) value = cimg::lowercase(value);
  26339. while (still_equal && p1--) still_equal = *(ptr1++)==value;
  26340. return still_equal;
  26341. } else if (!p1 && p2>0) { // Scalar == vector
  26342. value = _mp_arg(2);
  26343. if (!case_sensitive) value = cimg::lowercase(value);
  26344. while (still_equal && p2--) still_equal = *(ptr2++)==value;
  26345. return still_equal;
  26346. } else { // Scalar == scalar
  26347. if (case_sensitive) return _mp_arg(2)==_mp_arg(4);
  26348. else return cimg::lowercase(_mp_arg(2))==cimg::lowercase(_mp_arg(4));
  26349. }
  26350. }
  26351. // Compare only first N values.
  26352. if (p1>0 && p2>0) { // Vector == vector
  26353. n = cimg::min((unsigned int)N,p1,p2);
  26354. if (case_sensitive)
  26355. while (still_equal && n--) still_equal = *(ptr1++)==(*ptr2++);
  26356. else
  26357. while (still_equal && n--) still_equal = cimg::lowercase(*(ptr1++))==cimg::lowercase(*(ptr2++));
  26358. return still_equal;
  26359. } else if (p1>0 && !p2) { // Vector == scalar
  26360. n = std::min((unsigned int)N,p1);
  26361. value = _mp_arg(4);
  26362. if (!case_sensitive) value = cimg::lowercase(value);
  26363. while (still_equal && n--) still_equal = *(ptr1++)==value;
  26364. return still_equal;
  26365. } else if (!p1 && p2>0) { // Scalar == vector
  26366. n = std::min((unsigned int)N,p2);
  26367. value = _mp_arg(2);
  26368. if (!case_sensitive) value = cimg::lowercase(value);
  26369. while (still_equal && n--) still_equal = *(ptr2++)==value;
  26370. return still_equal;
  26371. } // Scalar == scalar
  26372. if (case_sensitive) return _mp_arg(2)==_mp_arg(4);
  26373. return cimg::lowercase(_mp_arg(2))==cimg::lowercase(_mp_arg(4));
  26374. }
  26375. static double mp_vector_lerp(_cimg_math_parser& mp) {
  26376. unsigned int siz = (unsigned int)mp.opcode[2];
  26377. double *ptrd = &_mp_arg(1) + 1;
  26378. const double
  26379. *ptrs1 = &_mp_arg(3) + 1,
  26380. *ptrs2 = &_mp_arg(4) + 1,
  26381. t = _mp_arg(5);
  26382. for (unsigned int k = 0; k<siz; ++k) ptrd[k] = ptrs1[k]*(1-t) + ptrs2[k]*t;
  26383. return cimg::type<double>::nan();
  26384. }
  26385. static double mp_vector_off(_cimg_math_parser& mp) {
  26386. const unsigned int
  26387. ptr = (unsigned int)mp.opcode[2] + 1,
  26388. siz = (unsigned int)mp.opcode[3];
  26389. const int off = (int)_mp_arg(4);
  26390. return off>=0 && off<(int)siz?mp.mem[ptr + off]:cimg::type<double>::nan();
  26391. }
  26392. static double mp_vector_map_sv(_cimg_math_parser& mp) { // Operator(scalar,vector,[...])
  26393. unsigned int
  26394. nb_args = (unsigned int)mp.opcode[2],
  26395. siz_vector = (unsigned int)mp.opcode[3],
  26396. ptrs = (unsigned int)mp.opcode[6] + 1;
  26397. double *ptrd = &_mp_arg(1) + 1;
  26398. mp_func op = (mp_func)mp.opcode[4];
  26399. CImg<ulongT> l_opcode(mp.opcode._data + 3,nb_args + 2);
  26400. l_opcode[0] = mp.opcode[1];
  26401. l_opcode.swap(mp.opcode);
  26402. ulongT &argument2 = mp.opcode[3];
  26403. while (siz_vector-->0) { argument2 = ptrs++; *(ptrd++) = (*op)(mp); }
  26404. l_opcode.swap(mp.opcode);
  26405. return cimg::type<double>::nan();
  26406. }
  26407. static double mp_vector_map_v(_cimg_math_parser& mp) { // Operator(vector,[...])
  26408. unsigned int
  26409. nb_args = (unsigned int)mp.opcode[2],
  26410. siz_vector = (unsigned int)mp.opcode[3],
  26411. ptrs = (unsigned int)mp.opcode[5] + 1;
  26412. double *ptrd = &_mp_arg(1) + 1;
  26413. mp_func op = (mp_func)mp.opcode[4];
  26414. CImg<ulongT> l_opcode(mp.opcode._data + 3,nb_args + 2);
  26415. l_opcode[0] = l_opcode[1];
  26416. l_opcode.swap(mp.opcode);
  26417. ulongT &argument = mp.opcode[2];
  26418. while (siz_vector-->0) { argument = ptrs++; *(ptrd++) = (*op)(mp); }
  26419. l_opcode.swap(mp.opcode);
  26420. return cimg::type<double>::nan();
  26421. }
  26422. static double mp_vector_map_vv(_cimg_math_parser& mp) { // Operator(vector,vector,[...])
  26423. unsigned int
  26424. nb_args = (unsigned int)mp.opcode[2],
  26425. siz_vector = (unsigned int)mp.opcode[3],
  26426. ptrs1 = (unsigned int)mp.opcode[5] + 1,
  26427. ptrs2 = (unsigned int)mp.opcode[6] + 1;
  26428. double *ptrd = &_mp_arg(1) + 1;
  26429. mp_func op = (mp_func)mp.opcode[4];
  26430. CImg<ulongT> l_opcode(mp.opcode._data + 3,nb_args + 2);
  26431. l_opcode[0] = l_opcode[1];
  26432. l_opcode.swap(mp.opcode);
  26433. ulongT &argument1 = mp.opcode[2], &argument2 = mp.opcode[3];
  26434. while (siz_vector-->0) { argument1 = ptrs1++; argument2 = ptrs2++; *(ptrd++) = (*op)(mp); }
  26435. l_opcode.swap(mp.opcode);
  26436. return cimg::type<double>::nan();
  26437. }
  26438. static double mp_vector_neq(_cimg_math_parser& mp) {
  26439. return !mp_vector_eq(mp);
  26440. }
  26441. static double _mp_vector_norm0(_cimg_math_parser& mp) {
  26442. const unsigned int siz = (unsigned int)mp.opcode[2];
  26443. double res = 0;
  26444. for (unsigned int i = siz - 1; i>3; --i) res+=(double)(_mp_arg(i)?1:0);
  26445. return res;
  26446. }
  26447. static double _mp_vector_norm1(_cimg_math_parser& mp) {
  26448. const unsigned int siz = (unsigned int)mp.opcode[2];
  26449. double res = 0;
  26450. for (unsigned int i = siz - 1; i>3; --i) res+=(double)cimg::abs(_mp_arg(i));
  26451. return res;
  26452. }
  26453. static double _mp_vector_norm2(_cimg_math_parser& mp) {
  26454. const unsigned int siz = (unsigned int)mp.opcode[2];
  26455. double res = 0;
  26456. for (unsigned int i = siz - 1; i>3; --i) res+=(double)cimg::sqr(_mp_arg(i));
  26457. return (double)std::sqrt(res);
  26458. }
  26459. static double _mp_vector_norminf(_cimg_math_parser& mp) {
  26460. const unsigned int siz = (unsigned int)mp.opcode[2];
  26461. double res = 0;
  26462. for (unsigned int i = siz - 1; i>3; --i) {
  26463. const double val = (double)cimg::abs(_mp_arg(i));
  26464. if (val>res) res = val;
  26465. }
  26466. return res;
  26467. }
  26468. static double _mp_vector_normp(_cimg_math_parser& mp) {
  26469. const unsigned int siz = (unsigned int)mp.opcode[2];
  26470. const double p = _mp_arg(3);
  26471. double res = 0;
  26472. for (unsigned int i = siz - 1; i>3; --i) res+=(double)std::pow(cimg::abs(_mp_arg(i)),p);
  26473. res = (double)std::pow(res,1.0/p);
  26474. return res;
  26475. }
  26476. static double mp_vector_normp(_cimg_math_parser& mp) {
  26477. const unsigned int siz = (unsigned int)mp.opcode[3];
  26478. const double p = _mp_arg(4);
  26479. if (siz>0) { // Vector-valued argument
  26480. const double *ptrs = &_mp_arg(2) + 1;
  26481. double res = 0;
  26482. if (p==2) { // L2
  26483. for (unsigned int i = 0; i<siz; ++i) res+=(double)cimg::sqr(*(ptrs++));
  26484. res = (double)std::sqrt(res);
  26485. } else if (p==1) // L1
  26486. for (unsigned int i = 0; i<siz; ++i) res+=(double)cimg::abs(*(ptrs++));
  26487. else if (!p) // L0
  26488. for (unsigned int i = 0; i<siz; ++i) res+=(double)(*(ptrs++)?1:0);
  26489. else if (cimg::type<float>::is_inf(p)) { // L-inf
  26490. for (unsigned int i = 0; i<siz; ++i) {
  26491. const double val = (double)cimg::abs(*(ptrs++));
  26492. if (val>res) res = val;
  26493. }
  26494. } else { // L-p
  26495. for (unsigned int i = 0; i<siz; ++i) res+=(double)std::pow(cimg::abs(*(ptrs++)),p);
  26496. res = (double)std::pow(res,1.0/p);
  26497. }
  26498. return res>0?res:0;
  26499. }
  26500. // Scalar-valued argument.
  26501. const double val = _mp_arg(2);
  26502. return p?cimg::abs(val):(val!=0);
  26503. }
  26504. static double _mp_vector_hypot(_cimg_math_parser& mp) {
  26505. switch ((unsigned int)mp.opcode[2]) {
  26506. case 5 : return cimg::abs(_mp_arg(4));
  26507. case 6 : return cimg::hypot(_mp_arg(4),_mp_arg(5));
  26508. case 7 : return cimg::hypot(_mp_arg(4),_mp_arg(5),_mp_arg(6));
  26509. };
  26510. return _mp_vector_norm2(mp);
  26511. }
  26512. static double mp_vector_print(_cimg_math_parser& mp) {
  26513. const bool print_string = (bool)mp.opcode[4];
  26514. cimg_pragma_openmp(critical(mp_vector_print))
  26515. {
  26516. CImg<charT> _expr(mp.opcode[2] - 5);
  26517. const ulongT *ptrs = mp.opcode._data + 5;
  26518. cimg_for(_expr,ptrd,char) *ptrd = (char)*(ptrs++);
  26519. cimg::strellipsize(_expr);
  26520. unsigned int
  26521. ptr = (unsigned int)mp.opcode[1] + 1,
  26522. siz0 = (unsigned int)mp.opcode[3],
  26523. siz = siz0;
  26524. cimg::mutex(6);
  26525. std::fprintf(cimg::output(),"\n[" cimg_appname "_math_parser] %s = [ ",_expr._data);
  26526. unsigned int count = 0;
  26527. while (siz-->0) {
  26528. if (count>=64 && siz>=64) {
  26529. std::fprintf(cimg::output(),"...,");
  26530. ptr = (unsigned int)mp.opcode[1] + 1 + siz0 - 64;
  26531. siz = 64;
  26532. } else std::fprintf(cimg::output(),"%.17g%s",mp.mem[ptr++],siz?",":"");
  26533. ++count;
  26534. }
  26535. if (print_string) {
  26536. CImg<charT> str(siz0 + 1);
  26537. ptr = (unsigned int)mp.opcode[1] + 1;
  26538. for (unsigned int k = 0; k<siz0; ++k) str[k] = (char)mp.mem[ptr++];
  26539. str[siz0] = 0;
  26540. cimg::strellipsize(str,1024,false);
  26541. std::fprintf(cimg::output()," ] = '%s' (size: %u)",str._data,siz0);
  26542. } else std::fprintf(cimg::output()," ] (size: %u)",siz0);
  26543. std::fflush(cimg::output());
  26544. cimg::mutex(6,0);
  26545. }
  26546. return cimg::type<double>::nan();
  26547. }
  26548. static double mp_vector_resize(_cimg_math_parser& mp) {
  26549. double *const ptrd = &_mp_arg(1) + 1;
  26550. const unsigned int p1 = (unsigned int)mp.opcode[2], p2 = (unsigned int)mp.opcode[4];
  26551. const int
  26552. interpolation = (int)_mp_arg(5),
  26553. boundary_conditions = (int)_mp_arg(6);
  26554. if (p2) { // Resize vector
  26555. const double *const ptrs = &_mp_arg(3) + 1;
  26556. CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(ptrs,p2,1,1,1,true).
  26557. get_resize(p1,1,1,1,interpolation,boundary_conditions);
  26558. } else { // Resize scalar
  26559. const double value = _mp_arg(3);
  26560. CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(1,1,1,1,value).resize(p1,1,1,1,interpolation,
  26561. boundary_conditions);
  26562. }
  26563. return cimg::type<double>::nan();
  26564. }
  26565. static double mp_vector_resize_ext(_cimg_math_parser& mp) {
  26566. double *const ptrd = &_mp_arg(1) + 1;
  26567. const unsigned int
  26568. siz = (unsigned int)mp.opcode[2],
  26569. ow = (unsigned int)mp.opcode[4],
  26570. oh = (unsigned int)mp.opcode[5],
  26571. od = (unsigned int)mp.opcode[6],
  26572. os = (unsigned int)mp.opcode[7],
  26573. nw = (unsigned int)mp.opcode[8],
  26574. nh = (unsigned int)mp.opcode[9],
  26575. nd = (unsigned int)mp.opcode[10],
  26576. ns = (unsigned int)mp.opcode[11];
  26577. const int
  26578. interpolation = (int)_mp_arg(12),
  26579. boundary_conditions = (int)_mp_arg(13);
  26580. const float
  26581. ax = (float)_mp_arg(14),
  26582. ay = (float)_mp_arg(15),
  26583. az = (float)_mp_arg(16),
  26584. ac = (float)_mp_arg(17);
  26585. if (siz) { // Resize vector
  26586. const double *const ptrs = &_mp_arg(3) + 1;
  26587. CImg<doubleT>(ptrd,nw,nh,nd,ns,true) = CImg<doubleT>(ptrs,ow,oh,od,os,true).
  26588. get_resize(nw,nh,nd,ns,interpolation,boundary_conditions,ax,ay,az,ac);
  26589. } else { // Resize scalar
  26590. const double value = _mp_arg(3);
  26591. CImg<doubleT>(ptrd,nw,nh,nd,ns,true) = CImg<doubleT>(1,1,1,1,value).
  26592. resize(nw,nh,nd,ns,interpolation,boundary_conditions,ax,ay,az,ac);
  26593. }
  26594. return cimg::type<double>::nan();
  26595. }
  26596. static double mp_vector_reverse(_cimg_math_parser& mp) {
  26597. double *const ptrd = &_mp_arg(1) + 1;
  26598. const double *const ptrs = &_mp_arg(2) + 1;
  26599. const unsigned int p1 = (unsigned int)mp.opcode[3];
  26600. CImg<doubleT>(ptrd,p1,1,1,1,true) = CImg<doubleT>(ptrs,p1,1,1,1,true).get_mirror('x');
  26601. return cimg::type<double>::nan();
  26602. }
  26603. static double mp_vector_set_off(_cimg_math_parser& mp) {
  26604. const unsigned int
  26605. ptr = (unsigned int)mp.opcode[2] + 1,
  26606. siz = (unsigned int)mp.opcode[3];
  26607. const int off = (int)_mp_arg(4);
  26608. if (off>=0 && off<(int)siz) mp.mem[ptr + off] = _mp_arg(1);
  26609. return _mp_arg(1);
  26610. }
  26611. static double mp_vector_unitnorm(_cimg_math_parser& mp) {
  26612. const unsigned int siz = (unsigned int)mp.opcode[3];
  26613. const double p = _mp_arg(4);
  26614. if (siz>0) { // Vector-valued argument
  26615. double *const ptrd = &_mp_arg(1) + 1;
  26616. const double *const ptrs = &_mp_arg(2) + 1;
  26617. if (ptrd!=ptrs) std::memcpy(ptrd,ptrs,siz*sizeof(double));
  26618. CImg<doubleT> vec(ptrd,siz,1,1,1,true);
  26619. const double mag = vec.magnitude(p);
  26620. if (mag>0) vec/=mag;
  26621. return cimg::type<double>::nan();
  26622. }
  26623. // Scalar-valued argument.
  26624. const double val = _mp_arg(2);
  26625. return val?(_mp_arg(2)?1:val):0;
  26626. }
  26627. #define _cimg_mp_vfunc(func) \
  26628. const longT sizd = (longT)mp.opcode[2];\
  26629. const unsigned int nbargs = (unsigned int)(mp.opcode[3] - 4)/2; \
  26630. double *const ptrd = &_mp_arg(1) + (sizd?1:0); \
  26631. cimg_pragma_openmp(parallel cimg_openmp_if_size(sizd,256)) \
  26632. { CImg<doubleT> vec(nbargs); double res; \
  26633. cimg_pragma_openmp(for) for (longT k = sizd?sizd - 1:0; k>=0; --k) { \
  26634. cimg_forX(vec,n) vec[n] = *(&_mp_arg(4 + 2*n) + (k+1)*(mp.opcode[4 + 2*n + 1]?1:0)); \
  26635. func; ptrd[k] = res; \
  26636. }} \
  26637. return sizd?cimg::type<double>::nan():*ptrd;
  26638. static double _mp_vargkth(CImg<doubleT>& vec) {
  26639. const double val = (+vec).get_shared_points(1,vec.width() - 1).
  26640. kth_smallest((ulongT)cimg::cut((longT)*vec - 1,(longT)0,(longT)vec.width() - 2));
  26641. cimg_for_inX(vec,1,vec.width()-1,ind) if (vec[ind]==val) return ind - 1.;
  26642. return 1.;
  26643. }
  26644. static double mp_vargkth(_cimg_math_parser& mp) {
  26645. _cimg_mp_vfunc(res = _mp_vargkth(vec));
  26646. }
  26647. static double mp_vargmax(_cimg_math_parser& mp) {
  26648. _cimg_mp_vfunc(res = (double)(&vec.max() - vec.data()));
  26649. }
  26650. static double mp_vargmaxabs(_cimg_math_parser& mp) {
  26651. _cimg_mp_vfunc(res = (double)(&vec.maxabs() - vec.data()));
  26652. }
  26653. static double mp_vargmin(_cimg_math_parser& mp) {
  26654. _cimg_mp_vfunc(res = (double)(&vec.min() - vec.data()));
  26655. }
  26656. static double mp_vargminabs(_cimg_math_parser& mp) {
  26657. _cimg_mp_vfunc(res = (double)(&vec.minabs() - vec.data()));
  26658. }
  26659. static double mp_vavg(_cimg_math_parser& mp) {
  26660. _cimg_mp_vfunc(res = vec.mean());
  26661. }
  26662. static double mp_vkth(_cimg_math_parser& mp) {
  26663. _cimg_mp_vfunc(res = vec.get_shared_points(1,vec.width() - 1).
  26664. kth_smallest((ulongT)cimg::cut((longT)*vec - 1,(longT)0,(longT)vec.width() - 2)));
  26665. }
  26666. static double mp_vmax(_cimg_math_parser& mp) {
  26667. _cimg_mp_vfunc(res = vec.max());
  26668. }
  26669. static double mp_vmaxabs(_cimg_math_parser& mp) {
  26670. _cimg_mp_vfunc(res = vec.maxabs());
  26671. }
  26672. static double mp_vmedian(_cimg_math_parser& mp) {
  26673. _cimg_mp_vfunc(res = vec.median());
  26674. }
  26675. static double mp_vmin(_cimg_math_parser& mp) {
  26676. _cimg_mp_vfunc(res = vec.min());
  26677. }
  26678. static double mp_vminabs(_cimg_math_parser& mp) {
  26679. _cimg_mp_vfunc(res = vec.minabs());
  26680. }
  26681. static double mp_vprod(_cimg_math_parser& mp) {
  26682. _cimg_mp_vfunc(res = vec.product());
  26683. }
  26684. static double mp_vstd(_cimg_math_parser& mp) {
  26685. _cimg_mp_vfunc(res = std::sqrt(vec.get_stats()[3]));
  26686. }
  26687. static double mp_vsum(_cimg_math_parser& mp) {
  26688. _cimg_mp_vfunc(res = vec.sum());
  26689. }
  26690. static double mp_vvar(_cimg_math_parser& mp) {
  26691. _cimg_mp_vfunc(res = vec.get_stats()[3]);
  26692. }
  26693. static double mp_v2s(_cimg_math_parser& mp) {
  26694. double *ptrd = &_mp_arg(1) + 1;
  26695. const unsigned int
  26696. sizd = (unsigned int)mp.opcode[2],
  26697. sizs = (unsigned int)mp.opcode[4];
  26698. std::memset(ptrd,0,sizd*sizeof(double));
  26699. const int nb_digits = (int)_mp_arg(5);
  26700. CImg<charT> format(8);
  26701. switch (nb_digits) {
  26702. case -1 : std::strcpy(format,"%g"); break;
  26703. case 0 : std::strcpy(format,"%.17g"); break;
  26704. default :
  26705. if (nb_digits>=-1) cimg_snprintf(format,format._width,"%%.%dg",nb_digits);
  26706. else cimg_snprintf(format,format._width,"%%.%dld",-nb_digits);
  26707. }
  26708. CImg<charT> str;
  26709. if (sizs) { // Vector expression
  26710. const double *ptrs = &_mp_arg(3) + 1;
  26711. if (nb_digits>=-1) CImg<doubleT>(ptrs,sizs,1,1,1,true).value_string(',',sizd + 1,format).move_to(str);
  26712. else CImg<longT>(ptrs,sizs,1,1,1).value_string(',',sizd + 1,format).move_to(str);
  26713. } else { // Scalar expression
  26714. str.assign(sizd + 1);
  26715. if (nb_digits>=-1) cimg_snprintf(str,sizd + 1,format,_mp_arg(3));
  26716. else cimg_snprintf(str,sizd + 1,format,(long)_mp_arg(3));
  26717. }
  26718. const unsigned int l = std::min(sizd,(unsigned int)std::strlen(str) + 1);
  26719. CImg<doubleT>(ptrd,l,1,1,1,true) = str.get_shared_points(0,l - 1);
  26720. return cimg::type<double>::nan();
  26721. }
  26722. static double mp_while(_cimg_math_parser& mp) {
  26723. const ulongT
  26724. mem_body = mp.opcode[1],
  26725. mem_cond = mp.opcode[2];
  26726. const CImg<ulongT>
  26727. *const p_cond = ++mp.p_code,
  26728. *const p_body = p_cond + mp.opcode[3],
  26729. *const p_end = p_body + mp.opcode[4];
  26730. const unsigned int vsiz = (unsigned int)mp.opcode[5];
  26731. bool is_cond = false;
  26732. if (mp.opcode[6]) { // Set default value for result and condition if necessary
  26733. if (vsiz) CImg<doubleT>(&mp.mem[mem_body] + 1,vsiz,1,1,1,true).fill(cimg::type<double>::nan());
  26734. else mp.mem[mem_body] = cimg::type<double>::nan();
  26735. }
  26736. if (mp.opcode[7]) mp.mem[mem_cond] = 0;
  26737. const unsigned int _break_type = mp.break_type;
  26738. mp.break_type = 0;
  26739. do {
  26740. for (mp.p_code = p_cond; mp.p_code<p_body; ++mp.p_code) { // Evaluate condition
  26741. mp.opcode._data = mp.p_code->_data;
  26742. const ulongT target = mp.opcode[1];
  26743. mp.mem[target] = _cimg_mp_defunc(mp);
  26744. }
  26745. if (mp.break_type==1) break;
  26746. is_cond = (bool)mp.mem[mem_cond];
  26747. if (is_cond && !mp.break_type) // Evaluate body
  26748. for (mp.p_code = p_body; mp.p_code<p_end; ++mp.p_code) {
  26749. mp.opcode._data = mp.p_code->_data;
  26750. const ulongT target = mp.opcode[1];
  26751. mp.mem[target] = _cimg_mp_defunc(mp);
  26752. }
  26753. if (mp.break_type==1) break; else if (mp.break_type==2) mp.break_type = 0;
  26754. } while (is_cond);
  26755. mp.break_type = _break_type;
  26756. mp.p_code = p_end - 1;
  26757. return mp.mem[mem_body];
  26758. }
  26759. static double mp_Ioff(_cimg_math_parser& mp) {
  26760. double *ptrd = &_mp_arg(1) + 1;
  26761. const unsigned int
  26762. boundary_conditions = (unsigned int)_mp_arg(3),
  26763. vsiz = (unsigned int)mp.opcode[4];
  26764. const CImg<T> &img = mp.imgin;
  26765. const longT
  26766. off = (longT)_mp_arg(2),
  26767. whd = (longT)img.width()*img.height()*img.depth();
  26768. const T *ptrs;
  26769. if (off>=0 && off<whd) {
  26770. ptrs = &img[off];
  26771. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  26772. return cimg::type<double>::nan();
  26773. }
  26774. if (img._data) switch (boundary_conditions) {
  26775. case 3 : { // Mirror
  26776. const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
  26777. ptrs = &img[moff<whd?moff:whd2 - moff - 1];
  26778. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  26779. return cimg::type<double>::nan();
  26780. }
  26781. case 2 : // Periodic
  26782. ptrs = &img[cimg::mod(off,whd)];
  26783. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  26784. return cimg::type<double>::nan();
  26785. case 1 : // Neumann
  26786. ptrs = off<0?&img[0]:&img[whd - 1];
  26787. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  26788. return cimg::type<double>::nan();
  26789. default : // Dirichlet
  26790. std::memset(ptrd,0,vsiz*sizeof(double));
  26791. return cimg::type<double>::nan();
  26792. }
  26793. std::memset(ptrd,0,vsiz*sizeof(double));
  26794. return cimg::type<double>::nan();
  26795. }
  26796. static double mp_Ixyz(_cimg_math_parser& mp) {
  26797. double *ptrd = &_mp_arg(1) + 1;
  26798. const unsigned int
  26799. interpolation = (unsigned int)_mp_arg(5),
  26800. boundary_conditions = (unsigned int)_mp_arg(6),
  26801. vsiz = (unsigned int)mp.opcode[7];
  26802. const CImg<T> &img = mp.imgin;
  26803. const double x = _mp_arg(2), y = _mp_arg(3), z = _mp_arg(4);
  26804. const ulongT whd = (ulongT)img._width*img._height*img._depth;
  26805. const T *ptrs;
  26806. switch (interpolation) {
  26807. case 2 : // Cubic interpolation
  26808. switch (boundary_conditions) {
  26809. case 3 : { // Mirror
  26810. const float
  26811. w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
  26812. mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
  26813. cx = mx<img.width()?mx:w2 - mx - 1,
  26814. cy = my<img.height()?my:h2 - my - 1,
  26815. cz = mz<img.depth()?mz:d2 - mz - 1;
  26816. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ(cx,cy,cz,c);
  26817. } break;
  26818. case 2 : // Periodic
  26819. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,c);
  26820. break;
  26821. case 1 : // Neumann
  26822. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ((float)x,(float)y,(float)z,c);
  26823. break;
  26824. default : // Dirichlet
  26825. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.cubic_atXYZ((float)x,(float)y,(float)z,c,(T)0);
  26826. } break;
  26827. case 1 : // Linear interpolation
  26828. switch (boundary_conditions) {
  26829. case 3 : { // Mirror
  26830. const float
  26831. w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
  26832. mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
  26833. cx = mx<img.width()?mx:w2 - mx - 1,
  26834. cy = my<img.height()?my:h2 - my - 1,
  26835. cz = mz<img.depth()?mz:d2 - mz - 1;
  26836. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
  26837. } break;
  26838. case 2 : // Periodic
  26839. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,c);
  26840. break;
  26841. case 1 : // Neumann
  26842. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
  26843. break;
  26844. default : // Dirichlet
  26845. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
  26846. } break;
  26847. default : // Nearest neighbor interpolation
  26848. switch (boundary_conditions) {
  26849. case 3 : { // Mirror
  26850. const int
  26851. w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
  26852. mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
  26853. cx = mx<img.width()?mx:w2 - mx - 1,
  26854. cy = my<img.height()?my:h2 - my - 1,
  26855. cz = mz<img.depth()?mz:d2 - mz - 1;
  26856. ptrs = &img(cx,cy,cz);
  26857. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  26858. } break;
  26859. case 2 : { // Periodic
  26860. const int
  26861. cx = (int)cimg::mod(x,(double)img._width),
  26862. cy = (int)cimg::mod(y,(double)img._height),
  26863. cz = (int)cimg::mod(z,(double)img._depth);
  26864. ptrs = &img(cx,cy,cz);
  26865. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  26866. } break;
  26867. case 1 : { // Neumann
  26868. ptrs = &img._atXYZ((int)x,(int)y,(int)z);
  26869. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  26870. } break;
  26871. default : // Dirichlet
  26872. if (img.containsXYZC((int)x,(int)y,(int)z)) {
  26873. ptrs = &img((int)x,(int)y,(int)z);
  26874. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  26875. } else std::memset(ptrd,0,vsiz*sizeof(double));
  26876. }
  26877. }
  26878. return cimg::type<double>::nan();
  26879. }
  26880. static double mp_Joff(_cimg_math_parser& mp) {
  26881. double *ptrd = &_mp_arg(1) + 1;
  26882. const unsigned int
  26883. boundary_conditions = (unsigned int)_mp_arg(3),
  26884. vsiz = (unsigned int)mp.opcode[4];
  26885. const CImg<T> &img = mp.imgin;
  26886. const int
  26887. ox = (int)mp.mem[_cimg_mp_slot_x],
  26888. oy = (int)mp.mem[_cimg_mp_slot_y],
  26889. oz = (int)mp.mem[_cimg_mp_slot_z];
  26890. const longT
  26891. off = img.offset(ox,oy,oz) + (longT)_mp_arg(2),
  26892. whd = (longT)img.width()*img.height()*img.depth();
  26893. const T *ptrs;
  26894. if (off>=0 && off<whd) {
  26895. ptrs = &img[off];
  26896. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  26897. return cimg::type<double>::nan();
  26898. }
  26899. if (img._data) switch (boundary_conditions) {
  26900. case 3 : { // Mirror
  26901. const longT whd2 = 2*whd, moff = cimg::mod(off,whd2);
  26902. ptrs = &img[moff<whd?moff:whd2 - moff - 1];
  26903. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  26904. return cimg::type<double>::nan();
  26905. }
  26906. case 2 : // Periodic
  26907. ptrs = &img[cimg::mod(off,whd)];
  26908. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  26909. return cimg::type<double>::nan();
  26910. case 1 : // Neumann
  26911. ptrs = off<0?&img[0]:&img[whd - 1];
  26912. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  26913. return cimg::type<double>::nan();
  26914. default : // Dirichlet
  26915. std::memset(ptrd,0,vsiz*sizeof(double));
  26916. return cimg::type<double>::nan();
  26917. }
  26918. std::memset(ptrd,0,vsiz*sizeof(double));
  26919. return cimg::type<double>::nan();
  26920. }
  26921. static double mp_Jxyz(_cimg_math_parser& mp) {
  26922. double *ptrd = &_mp_arg(1) + 1;
  26923. const unsigned int
  26924. interpolation = (unsigned int)_mp_arg(5),
  26925. boundary_conditions = (unsigned int)_mp_arg(6),
  26926. vsiz = (unsigned int)mp.opcode[7];
  26927. const CImg<T> &img = mp.imgin;
  26928. const double
  26929. ox = mp.mem[_cimg_mp_slot_x], oy = mp.mem[_cimg_mp_slot_y], oz = mp.mem[_cimg_mp_slot_z],
  26930. x = ox + _mp_arg(2), y = oy + _mp_arg(3), z = oz + _mp_arg(4);
  26931. const ulongT whd = (ulongT)img._width*img._height*img._depth;
  26932. const T *ptrs;
  26933. switch (interpolation) {
  26934. case 2 : // Cubic interpolation
  26935. switch (boundary_conditions) {
  26936. case 3 : { // Mirror
  26937. const float
  26938. w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
  26939. mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
  26940. cx = mx<img.width()?mx:w2 - mx - 1,
  26941. cy = my<img.height()?my:h2 - my - 1,
  26942. cz = mz<img.depth()?mz:d2 - mz - 1;
  26943. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ(cx,cy,cz,c);
  26944. } break;
  26945. case 2 : // Periodic
  26946. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ_p((float)x,(float)y,(float)z,c);
  26947. break;
  26948. case 1 : // Neumann
  26949. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._cubic_atXYZ((float)x,(float)y,(float)z,c);
  26950. break;
  26951. default : // Dirichlet
  26952. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.cubic_atXYZ((float)x,(float)y,(float)z,c,(T)0);
  26953. } break;
  26954. case 1 : // Linear interpolation
  26955. switch (boundary_conditions) {
  26956. case 3 : { // Mirror
  26957. const float
  26958. w2 = 2.f*img.width(), h2 = 2.f*img.height(), d2 = 2.f*img.depth(),
  26959. mx = cimg::mod((float)x,w2), my = cimg::mod((float)y,h2), mz = cimg::mod((float)z,d2),
  26960. cx = mx<img.width()?mx:w2 - mx - 1,
  26961. cy = my<img.height()?my:h2 - my - 1,
  26962. cz = mz<img.depth()?mz:d2 - mz - 1;
  26963. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ(cx,cy,cz,c);
  26964. } break;
  26965. case 2 : // Periodic
  26966. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ_p((float)x,(float)y,(float)z,c);
  26967. break;
  26968. case 1 : // Neumann
  26969. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img._linear_atXYZ((float)x,(float)y,(float)z,c);
  26970. break;
  26971. default : // Dirichlet
  26972. cimg_for_inC(img,0,vsiz - 1,c) *(ptrd++) = (double)img.linear_atXYZ((float)x,(float)y,(float)z,c,(T)0);
  26973. } break;
  26974. default : // Nearest neighbor interpolation
  26975. switch (boundary_conditions) {
  26976. case 3 : { // Mirror
  26977. const int
  26978. w2 = 2*img.width(), h2 = 2*img.height(), d2 = 2*img.depth(),
  26979. mx = cimg::mod((int)x,w2), my = cimg::mod((int)y,h2), mz = cimg::mod((int)z,d2),
  26980. cx = mx<img.width()?mx:w2 - mx - 1,
  26981. cy = my<img.height()?my:h2 - my - 1,
  26982. cz = mz<img.depth()?mz:d2 - mz - 1;
  26983. ptrs = &img(cx,cy,cz);
  26984. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  26985. } break;
  26986. case 2 : { // Periodic
  26987. const int
  26988. cx = (int)cimg::mod(x,(double)img._width),
  26989. cy = (int)cimg::mod(y,(double)img._height),
  26990. cz = (int)cimg::mod(z,(double)img._depth);
  26991. ptrs = &img(cx,cy,cz);
  26992. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  26993. } break;
  26994. case 1 : { // Neumann
  26995. ptrs = &img._atXYZ((int)x,(int)y,(int)z);
  26996. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  26997. } break;
  26998. default : // Dirichlet
  26999. if (img.containsXYZC((int)x,(int)y,(int)z)) {
  27000. ptrs = &img((int)x,(int)y,(int)z);
  27001. cimg_for_inC(img,0,vsiz - 1,c) { *(ptrd++) = (double)*ptrs; ptrs+=whd; }
  27002. } else std::memset(ptrd,0,vsiz*sizeof(double));
  27003. }
  27004. }
  27005. return cimg::type<double>::nan();
  27006. }
  27007. #undef _mp_arg
  27008. }; // struct _cimg_math_parser {}
  27009. #define _cimg_create_pointwise_functions(name,func,min_size) \
  27010. CImg<T>& name() { \
  27011. if (is_empty()) return *this; \
  27012. cimg_openmp_for(*this,func((typename cimg::superset<T,float>::type)*ptr),min_size); \
  27013. return *this; \
  27014. } \
  27015. CImg<Tfloat> get_##name() const { \
  27016. return CImg<Tfloat>(*this,false).name(); \
  27017. }
  27018. //! Compute the square value of each pixel value.
  27019. /**
  27020. 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$.
  27021. \note
  27022. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  27023. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  27024. \par Example
  27025. \code
  27026. const CImg<float> img("reference.jpg");
  27027. (img,img.get_sqr().normalize(0,255)).display();
  27028. \endcode
  27029. \image html ref_sqr.jpg
  27030. **/
  27031. _cimg_create_pointwise_functions(sqr,cimg::sqr,524288)
  27032. //! Compute the square root of each pixel value.
  27033. /**
  27034. 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$.
  27035. \note
  27036. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  27037. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  27038. \par Example
  27039. \code
  27040. const CImg<float> img("reference.jpg");
  27041. (img,img.get_sqrt().normalize(0,255)).display();
  27042. \endcode
  27043. \image html ref_sqrt.jpg
  27044. **/
  27045. _cimg_create_pointwise_functions(sqrt,std::sqrt,8192)
  27046. //! Compute the exponential of each pixel value.
  27047. /**
  27048. 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$.
  27049. \note
  27050. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  27051. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  27052. **/
  27053. _cimg_create_pointwise_functions(exp,std::exp,4096)
  27054. //! Compute the error function of each pixel value.
  27055. /**
  27056. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its error function.
  27057. \note
  27058. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  27059. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  27060. **/
  27061. #if cimg_use_cpp11==1
  27062. _cimg_create_pointwise_functions(erf,std::erf,4096)
  27063. #endif
  27064. //! Compute the logarithm of each pixel value.
  27065. /**
  27066. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its logarithm
  27067. \f$\mathrm{log}_{e}(I_{(x,y,z,c)})\f$.
  27068. \note
  27069. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  27070. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  27071. **/
  27072. _cimg_create_pointwise_functions(log,std::log,262144)
  27073. //! Compute the base-2 logarithm of each pixel value.
  27074. /**
  27075. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-2 logarithm
  27076. \f$\mathrm{log}_{2}(I_{(x,y,z,c)})\f$.
  27077. \note
  27078. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  27079. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  27080. **/
  27081. _cimg_create_pointwise_functions(log2,cimg::log2,4096)
  27082. //! Compute the base-10 logarithm of each pixel value.
  27083. /**
  27084. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its base-10 logarithm
  27085. \f$\mathrm{log}_{10}(I_{(x,y,z,c)})\f$.
  27086. \note
  27087. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  27088. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  27089. **/
  27090. _cimg_create_pointwise_functions(log10,std::log10,4096)
  27091. //! Compute the absolute value of each pixel value.
  27092. /**
  27093. 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$.
  27094. \note
  27095. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  27096. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  27097. **/
  27098. _cimg_create_pointwise_functions(abs,cimg::abs,524288)
  27099. //! Compute the sign of each pixel value.
  27100. /**
  27101. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sign
  27102. \f$\mathrm{sign}(I_{(x,y,z,c)})\f$.
  27103. \note
  27104. - The sign is set to:
  27105. - \c 1 if pixel value is strictly positive.
  27106. - \c -1 if pixel value is strictly negative.
  27107. - \c 0 if pixel value is equal to \c 0.
  27108. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  27109. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  27110. **/
  27111. _cimg_create_pointwise_functions(sign,cimg::sign,32768)
  27112. //! Compute the cosine of each pixel value.
  27113. /**
  27114. 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$.
  27115. \note
  27116. - Pixel values are regarded as being in \e radian.
  27117. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  27118. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  27119. **/
  27120. _cimg_create_pointwise_functions(cos,std::cos,8192)
  27121. //! Compute the sine of each pixel value.
  27122. /**
  27123. 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$.
  27124. \note
  27125. - Pixel values are regarded as being in \e radian.
  27126. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  27127. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  27128. **/
  27129. _cimg_create_pointwise_functions(sin,std::sin,8192)
  27130. //! Compute the sinc of each pixel value.
  27131. /**
  27132. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its sinc
  27133. \f$\mathrm{sinc}(I_{(x,y,z,c)})\f$.
  27134. \note
  27135. - Pixel values are regarded as being exin \e radian.
  27136. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  27137. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  27138. **/
  27139. _cimg_create_pointwise_functions(sinc,cimg::sinc,2048)
  27140. //! Compute the tangent of each pixel value.
  27141. /**
  27142. 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$.
  27143. \note
  27144. - Pixel values are regarded as being exin \e radian.
  27145. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  27146. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  27147. **/
  27148. _cimg_create_pointwise_functions(tan,std::tan,2048)
  27149. //! Compute the hyperbolic cosine of each pixel value.
  27150. /**
  27151. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic cosine
  27152. \f$\mathrm{cosh}(I_{(x,y,z,c)})\f$.
  27153. \note
  27154. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  27155. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  27156. **/
  27157. _cimg_create_pointwise_functions(cosh,std::cosh,2048)
  27158. //! Compute the hyperbolic sine of each pixel value.
  27159. /**
  27160. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic sine
  27161. \f$\mathrm{sinh}(I_{(x,y,z,c)})\f$.
  27162. \note
  27163. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  27164. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  27165. **/
  27166. _cimg_create_pointwise_functions(sinh,std::sinh,2048)
  27167. //! Compute the hyperbolic tangent of each pixel value.
  27168. /**
  27169. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic tangent
  27170. \f$\mathrm{tanh}(I_{(x,y,z,c)})\f$.
  27171. \note
  27172. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  27173. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  27174. **/
  27175. _cimg_create_pointwise_functions(tanh,std::tanh,2048)
  27176. //! Compute the arccosine of each pixel value.
  27177. /**
  27178. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosine
  27179. \f$\mathrm{acos}(I_{(x,y,z,c)})\f$.
  27180. \note
  27181. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  27182. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  27183. **/
  27184. _cimg_create_pointwise_functions(acos,std::acos,8192)
  27185. //! Compute the arcsine of each pixel value.
  27186. /**
  27187. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arcsine
  27188. \f$\mathrm{asin}(I_{(x,y,z,c)})\f$.
  27189. \note
  27190. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  27191. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  27192. **/
  27193. _cimg_create_pointwise_functions(asin,std::asin,8192)
  27194. //! Compute the arctangent of each pixel value.
  27195. /**
  27196. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent
  27197. \f$\mathrm{atan}(I_{(x,y,z,c)})\f$.
  27198. \note
  27199. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  27200. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  27201. **/
  27202. _cimg_create_pointwise_functions(atan,std::atan,8192)
  27203. //! Compute the arctangent2 of each pixel value.
  27204. /**
  27205. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arctangent2
  27206. \f$\mathrm{atan2}(I_{(x,y,z,c)})\f$.
  27207. \param img Image whose pixel values specify the second argument of the \c atan2() function.
  27208. \note
  27209. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  27210. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  27211. \par Example
  27212. \code
  27213. const CImg<float>
  27214. img_x(100,100,1,1,"x-w/2",false), // Define an horizontal centered gradient, from '-width/2' to 'width/2'
  27215. img_y(100,100,1,1,"y-h/2",false), // Define a vertical centered gradient, from '-height/2' to 'height/2'
  27216. img_atan2 = img_y.get_atan2(img_x); // Compute atan2(y,x) for each pixel value
  27217. (img_x,img_y,img_atan2).display();
  27218. \endcode
  27219. **/
  27220. template<typename t>
  27221. CImg<T>& atan2(const CImg<t>& img) {
  27222. const ulongT siz = size(), isiz = img.size();
  27223. if (siz && isiz) {
  27224. if (is_overlapped(img)) return atan2(+img);
  27225. T *ptrd = _data, *const ptre = _data + siz;
  27226. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  27227. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  27228. *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++));
  27229. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::atan2((double)*ptrd,(double)*(ptrs++));
  27230. }
  27231. return *this;
  27232. }
  27233. //! Compute the arctangent2 of each pixel value \newinstance.
  27234. template<typename t>
  27235. CImg<Tfloat> get_atan2(const CImg<t>& img) const {
  27236. return CImg<Tfloat>(*this,false).atan2(img);
  27237. }
  27238. //! Compute the hyperbolic arccosine of each pixel value.
  27239. /**
  27240. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its arccosineh
  27241. \f$\mathrm{acosh}(I_{(x,y,z,c)})\f$.
  27242. \note
  27243. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  27244. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  27245. **/
  27246. _cimg_create_pointwise_functions(acosh,cimg::acosh,8192)
  27247. //! Compute the hyperbolic arcsine of each pixel value.
  27248. /**
  27249. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic arcsine
  27250. \f$\mathrm{asinh}(I_{(x,y,z,c)})\f$.
  27251. \note
  27252. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  27253. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  27254. **/
  27255. _cimg_create_pointwise_functions(asinh,cimg::asinh,8192)
  27256. //! Compute the hyperbolic arctangent of each pixel value.
  27257. /**
  27258. Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by its hyperbolic arctangent
  27259. \f$\mathrm{atanh}(I_{(x,y,z,c)})\f$.
  27260. \note
  27261. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  27262. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  27263. **/
  27264. _cimg_create_pointwise_functions(atanh,cimg::atanh,8192)
  27265. //! In-place pointwise multiplication.
  27266. /**
  27267. Compute the pointwise multiplication between the image instance and the specified input image \c img.
  27268. \param img Input image, as the second operand of the multiplication.
  27269. \note
  27270. - Similar to operator+=(const CImg<t>&), except that it performs a pointwise multiplication
  27271. instead of an addition.
  27272. - It does \e not perform a \e matrix multiplication. For this purpose, use operator*=(const CImg<t>&) instead.
  27273. \par Example
  27274. \code
  27275. CImg<float>
  27276. img("reference.jpg"),
  27277. shade(img.width,img.height(),1,1,"-(x-w/2)^2-(y-h/2)^2",false);
  27278. shade.normalize(0,1);
  27279. (img,shade,img.get_mul(shade)).display();
  27280. \endcode
  27281. **/
  27282. template<typename t>
  27283. CImg<T>& mul(const CImg<t>& img) {
  27284. const ulongT siz = size(), isiz = img.size();
  27285. if (siz && isiz) {
  27286. if (is_overlapped(img)) return mul(+img);
  27287. T *ptrd = _data, *const ptre = _data + siz;
  27288. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  27289. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  27290. *ptrd = (T)(*ptrd * *(ptrs++));
  27291. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd * *(ptrs++));
  27292. }
  27293. return *this;
  27294. }
  27295. //! In-place pointwise multiplication \newinstance.
  27296. template<typename t>
  27297. CImg<_cimg_Tt> get_mul(const CImg<t>& img) const {
  27298. return CImg<_cimg_Tt>(*this,false).mul(img);
  27299. }
  27300. //! In-place pointwise division.
  27301. /**
  27302. Similar to mul(const CImg<t>&), except that it performs a pointwise division instead of a multiplication.
  27303. **/
  27304. template<typename t>
  27305. CImg<T>& div(const CImg<t>& img) {
  27306. const ulongT siz = size(), isiz = img.size();
  27307. if (siz && isiz) {
  27308. if (is_overlapped(img)) return div(+img);
  27309. T *ptrd = _data, *const ptre = _data + siz;
  27310. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  27311. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  27312. *ptrd = (T)(*ptrd / *(ptrs++));
  27313. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)(*ptrd / *(ptrs++));
  27314. }
  27315. return *this;
  27316. }
  27317. //! In-place pointwise division \newinstance.
  27318. template<typename t>
  27319. CImg<_cimg_Tt> get_div(const CImg<t>& img) const {
  27320. return CImg<_cimg_Tt>(*this,false).div(img);
  27321. }
  27322. //! Raise each pixel value to a specified power.
  27323. /**
  27324. 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$.
  27325. \param p Exponent value.
  27326. \note
  27327. - The \inplace of this method statically casts the computed values to the pixel type \c T.
  27328. - The \newinstance returns a \c CImg<float> image, if the pixel type \c T is \e not float-valued.
  27329. \par Example
  27330. \code
  27331. const CImg<float>
  27332. img0("reference.jpg"), // Load reference color image
  27333. img1 = (img0/255).pow(1.8)*=255, // Compute gamma correction, with gamma = 1.8
  27334. img2 = (img0/255).pow(0.5)*=255; // Compute gamma correction, with gamma = 0.5
  27335. (img0,img1,img2).display();
  27336. \endcode
  27337. **/
  27338. CImg<T>& pow(const double p) {
  27339. if (is_empty()) return *this;
  27340. if (p==-4) { cimg_openmp_for(*this,1/(Tfloat)cimg::pow4(*ptr),32768); return *this; }
  27341. if (p==-3) { cimg_openmp_for(*this,1/(Tfloat)cimg::pow3(*ptr),32768); return *this; }
  27342. if (p==-2) { cimg_openmp_for(*this,1/(Tfloat)cimg::sqr(*ptr),32768); return *this; }
  27343. if (p==-1) { cimg_openmp_for(*this,1/(Tfloat)*ptr,32768); return *this; }
  27344. if (p==-0.5) { cimg_openmp_for(*this,1/std::sqrt((Tfloat)*ptr),8192); return *this; }
  27345. if (p==0) return fill((T)1);
  27346. if (p==0.5) return sqrt();
  27347. if (p==1) return *this;
  27348. if (p==2) return sqr();
  27349. if (p==3) { cimg_openmp_for(*this,cimg::pow3(*ptr),262144); return *this; }
  27350. if (p==4) { cimg_openmp_for(*this,cimg::pow4(*ptr),131072); return *this; }
  27351. cimg_openmp_for(*this,std::pow((Tfloat)*ptr,(Tfloat)p),1024);
  27352. return *this;
  27353. }
  27354. //! Raise each pixel value to a specified power \newinstance.
  27355. CImg<Tfloat> get_pow(const double p) const {
  27356. return CImg<Tfloat>(*this,false).pow(p);
  27357. }
  27358. //! Raise each pixel value to a power, specified from an expression.
  27359. /**
  27360. Similar to operator+=(const char*), except it performs a pointwise exponentiation instead of an addition.
  27361. **/
  27362. CImg<T>& pow(const char *const expression) {
  27363. return pow((+*this)._fill(expression,true,3,0,"pow",this,0));
  27364. }
  27365. //! Raise each pixel value to a power, specified from an expression \newinstance.
  27366. CImg<Tfloat> get_pow(const char *const expression) const {
  27367. return CImg<Tfloat>(*this,false).pow(expression);
  27368. }
  27369. //! Raise each pixel value to a power, pointwisely specified from another image.
  27370. /**
  27371. Similar to operator+=(const CImg<t>& img), except that it performs an exponentiation instead of an addition.
  27372. **/
  27373. template<typename t>
  27374. CImg<T>& pow(const CImg<t>& img) {
  27375. const ulongT siz = size(), isiz = img.size();
  27376. if (siz && isiz) {
  27377. if (is_overlapped(img)) return pow(+img);
  27378. T *ptrd = _data, *const ptre = _data + siz;
  27379. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  27380. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  27381. *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
  27382. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)std::pow((double)*ptrd,(double)(*(ptrs++)));
  27383. }
  27384. return *this;
  27385. }
  27386. //! Raise each pixel value to a power, pointwisely specified from another image \newinstance.
  27387. template<typename t>
  27388. CImg<Tfloat> get_pow(const CImg<t>& img) const {
  27389. return CImg<Tfloat>(*this,false).pow(img);
  27390. }
  27391. //! Compute the bitwise left rotation of each pixel value.
  27392. /**
  27393. Similar to operator<<=(unsigned int), except that it performs a left rotation instead of a left shift.
  27394. **/
  27395. CImg<T>& rol(const unsigned int n=1) {
  27396. if (is_empty()) return *this;
  27397. cimg_openmp_for(*this,cimg::rol(*ptr,n),32768);
  27398. return *this;
  27399. }
  27400. //! Compute the bitwise left rotation of each pixel value \newinstance.
  27401. CImg<T> get_rol(const unsigned int n=1) const {
  27402. return (+*this).rol(n);
  27403. }
  27404. //! Compute the bitwise left rotation of each pixel value.
  27405. /**
  27406. Similar to operator<<=(const char*), except that it performs a left rotation instead of a left shift.
  27407. **/
  27408. CImg<T>& rol(const char *const expression) {
  27409. return rol((+*this)._fill(expression,true,3,0,"rol",this,0));
  27410. }
  27411. //! Compute the bitwise left rotation of each pixel value \newinstance.
  27412. CImg<T> get_rol(const char *const expression) const {
  27413. return (+*this).rol(expression);
  27414. }
  27415. //! Compute the bitwise left rotation of each pixel value.
  27416. /**
  27417. Similar to operator<<=(const CImg<t>&), except that it performs a left rotation instead of a left shift.
  27418. **/
  27419. template<typename t>
  27420. CImg<T>& rol(const CImg<t>& img) {
  27421. const ulongT siz = size(), isiz = img.size();
  27422. if (siz && isiz) {
  27423. if (is_overlapped(img)) return rol(+img);
  27424. T *ptrd = _data, *const ptre = _data + siz;
  27425. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  27426. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  27427. *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
  27428. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::rol(*ptrd,(unsigned int)(*(ptrs++)));
  27429. }
  27430. return *this;
  27431. }
  27432. //! Compute the bitwise left rotation of each pixel value \newinstance.
  27433. template<typename t>
  27434. CImg<T> get_rol(const CImg<t>& img) const {
  27435. return (+*this).rol(img);
  27436. }
  27437. //! Compute the bitwise right rotation of each pixel value.
  27438. /**
  27439. Similar to operator>>=(unsigned int), except that it performs a right rotation instead of a right shift.
  27440. **/
  27441. CImg<T>& ror(const unsigned int n=1) {
  27442. if (is_empty()) return *this;
  27443. cimg_openmp_for(*this,cimg::ror(*ptr,n),32768);
  27444. return *this;
  27445. }
  27446. //! Compute the bitwise right rotation of each pixel value \newinstance.
  27447. CImg<T> get_ror(const unsigned int n=1) const {
  27448. return (+*this).ror(n);
  27449. }
  27450. //! Compute the bitwise right rotation of each pixel value.
  27451. /**
  27452. Similar to operator>>=(const char*), except that it performs a right rotation instead of a right shift.
  27453. **/
  27454. CImg<T>& ror(const char *const expression) {
  27455. return ror((+*this)._fill(expression,true,3,0,"ror",this,0));
  27456. }
  27457. //! Compute the bitwise right rotation of each pixel value \newinstance.
  27458. CImg<T> get_ror(const char *const expression) const {
  27459. return (+*this).ror(expression);
  27460. }
  27461. //! Compute the bitwise right rotation of each pixel value.
  27462. /**
  27463. Similar to operator>>=(const CImg<t>&), except that it performs a right rotation instead of a right shift.
  27464. **/
  27465. template<typename t>
  27466. CImg<T>& ror(const CImg<t>& img) {
  27467. const ulongT siz = size(), isiz = img.size();
  27468. if (siz && isiz) {
  27469. if (is_overlapped(img)) return ror(+img);
  27470. T *ptrd = _data, *const ptre = _data + siz;
  27471. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  27472. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  27473. *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
  27474. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = (T)cimg::ror(*ptrd,(unsigned int)(*(ptrs++)));
  27475. }
  27476. return *this;
  27477. }
  27478. //! Compute the bitwise right rotation of each pixel value \newinstance.
  27479. template<typename t>
  27480. CImg<T> get_ror(const CImg<t>& img) const {
  27481. return (+*this).ror(img);
  27482. }
  27483. //! Pointwise min operator between instance image and a value.
  27484. /**
  27485. \param val Value used as the reference argument of the min operator.
  27486. \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
  27487. \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{val})\f$.
  27488. **/
  27489. CImg<T>& min(const T& value) {
  27490. if (is_empty()) return *this;
  27491. cimg_openmp_for(*this,std::min(*ptr,value),65536);
  27492. return *this;
  27493. }
  27494. //! Pointwise min operator between instance image and a value \newinstance.
  27495. CImg<T> get_min(const T& value) const {
  27496. return (+*this).min(value);
  27497. }
  27498. //! Pointwise min operator between two images.
  27499. /**
  27500. \param img Image used as the reference argument of the min operator.
  27501. \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
  27502. \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
  27503. **/
  27504. template<typename t>
  27505. CImg<T>& min(const CImg<t>& img) {
  27506. const ulongT siz = size(), isiz = img.size();
  27507. if (siz && isiz) {
  27508. if (is_overlapped(img)) return min(+img);
  27509. T *ptrd = _data, *const ptre = _data + siz;
  27510. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  27511. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  27512. *ptrd = std::min((T)*(ptrs++),*ptrd);
  27513. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = std::min((T)*(ptrs++),*ptrd);
  27514. }
  27515. return *this;
  27516. }
  27517. //! Pointwise min operator between two images \newinstance.
  27518. template<typename t>
  27519. CImg<_cimg_Tt> get_min(const CImg<t>& img) const {
  27520. return CImg<_cimg_Tt>(*this,false).min(img);
  27521. }
  27522. //! Pointwise min operator between an image and an expression.
  27523. /**
  27524. \param expression Math formula as a C-string.
  27525. \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
  27526. \f$\mathrm{min}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
  27527. **/
  27528. CImg<T>& min(const char *const expression) {
  27529. return min((+*this)._fill(expression,true,3,0,"min",this,0));
  27530. }
  27531. //! Pointwise min operator between an image and an expression \newinstance.
  27532. CImg<Tfloat> get_min(const char *const expression) const {
  27533. return CImg<Tfloat>(*this,false).min(expression);
  27534. }
  27535. //! Pointwise max operator between instance image and a value.
  27536. /**
  27537. \param val Value used as the reference argument of the max operator.
  27538. \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
  27539. \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{val})\f$.
  27540. **/
  27541. CImg<T>& max(const T& value) {
  27542. if (is_empty()) return *this;
  27543. cimg_openmp_for(*this,std::max(*ptr,value),65536);
  27544. return *this;
  27545. }
  27546. //! Pointwise max operator between instance image and a value \newinstance.
  27547. CImg<T> get_max(const T& value) const {
  27548. return (+*this).max(value);
  27549. }
  27550. //! Pointwise max operator between two images.
  27551. /**
  27552. \param img Image used as the reference argument of the max operator.
  27553. \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
  27554. \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
  27555. **/
  27556. template<typename t>
  27557. CImg<T>& max(const CImg<t>& img) {
  27558. const ulongT siz = size(), isiz = img.size();
  27559. if (siz && isiz) {
  27560. if (is_overlapped(img)) return max(+img);
  27561. T *ptrd = _data, *const ptre = _data + siz;
  27562. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  27563. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  27564. *ptrd = std::max((T)*(ptrs++),*ptrd);
  27565. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = std::max((T)*(ptrs++),*ptrd);
  27566. }
  27567. return *this;
  27568. }
  27569. //! Pointwise max operator between two images \newinstance.
  27570. template<typename t>
  27571. CImg<_cimg_Tt> get_max(const CImg<t>& img) const {
  27572. return CImg<_cimg_Tt>(*this,false).max(img);
  27573. }
  27574. //! Pointwise max operator between an image and an expression.
  27575. /**
  27576. \param expression Math formula as a C-string.
  27577. \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
  27578. \f$\mathrm{max}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
  27579. **/
  27580. CImg<T>& max(const char *const expression) {
  27581. return max((+*this)._fill(expression,true,3,0,"max",this,0));
  27582. }
  27583. //! Pointwise max operator between an image and an expression \newinstance.
  27584. CImg<Tfloat> get_max(const char *const expression) const {
  27585. return CImg<Tfloat>(*this,false).max(expression);
  27586. }
  27587. //! Pointwise minabs operator between instance image and a value.
  27588. /**
  27589. \param val Value used as the reference argument of the minabs operator.
  27590. \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
  27591. \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{val})\f$.
  27592. **/
  27593. CImg<T>& minabs(const T& value) {
  27594. if (is_empty()) return *this;
  27595. const T absvalue = cimg::abs(value);
  27596. cimg_openmp_for(*this,cimg::minabs(*ptr,value,absvalue),65536);
  27597. return *this;
  27598. }
  27599. //! Pointwise minabs operator between instance image and a value \newinstance.
  27600. CImg<T> get_minabs(const T& value) const {
  27601. return (+*this).minabs(value);
  27602. }
  27603. //! Pointwise minabs operator between two images.
  27604. /**
  27605. \param img Image used as the reference argument of the minabs operator.
  27606. \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
  27607. \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
  27608. **/
  27609. template<typename t>
  27610. CImg<T>& minabs(const CImg<t>& img) {
  27611. const ulongT siz = size(), isiz = img.size();
  27612. if (siz && isiz) {
  27613. if (is_overlapped(img)) return minabs(+img);
  27614. T *ptrd = _data, *const ptre = _data + siz;
  27615. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  27616. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  27617. *ptrd = cimg::minabs((T)*(ptrs++),*ptrd);
  27618. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::minabs((T)*(ptrs++),*ptrd);
  27619. }
  27620. return *this;
  27621. }
  27622. //! Pointwise minabs operator between two images \newinstance.
  27623. template<typename t>
  27624. CImg<_cimg_Tt> get_minabs(const CImg<t>& img) const {
  27625. return CImg<_cimg_Tt>(*this,false).minabs(img);
  27626. }
  27627. //! Pointwise minabs operator between an image and an expression.
  27628. /**
  27629. \param expression Math formula as a C-string.
  27630. \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
  27631. \f$\mathrm{minabs}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
  27632. **/
  27633. CImg<T>& minabs(const char *const expression) {
  27634. return minabs((+*this)._fill(expression,true,3,0,"minabs",this,0));
  27635. }
  27636. //! Pointwise minabs operator between an image and an expression \newinstance.
  27637. CImg<Tfloat> get_minabs(const char *const expression) const {
  27638. return CImg<Tfloat>(*this,false).minabs(expression);
  27639. }
  27640. //! Pointwise maxabs operator between instance image and a value.
  27641. /**
  27642. \param val Value used as the reference argument of the maxabs operator.
  27643. \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
  27644. \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{val})\f$.
  27645. **/
  27646. CImg<T>& maxabs(const T& value) {
  27647. if (is_empty()) return *this;
  27648. const T absvalue = cimg::abs(value);
  27649. cimg_openmp_for(*this,cimg::maxabs(*ptr,value,absvalue),65536);
  27650. return *this;
  27651. }
  27652. //! Pointwise maxabs operator between instance image and a value \newinstance.
  27653. CImg<T> get_maxabs(const T& value) const {
  27654. return (+*this).maxabs(value);
  27655. }
  27656. //! Pointwise maxabs operator between two images.
  27657. /**
  27658. \param img Image used as the reference argument of the maxabs operator.
  27659. \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
  27660. \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{img}_{(x,y,z,c)})\f$.
  27661. **/
  27662. template<typename t>
  27663. CImg<T>& maxabs(const CImg<t>& img) {
  27664. const ulongT siz = size(), isiz = img.size();
  27665. if (siz && isiz) {
  27666. if (is_overlapped(img)) return maxabs(+img);
  27667. T *ptrd = _data, *const ptre = _data + siz;
  27668. if (siz>isiz) for (ulongT n = siz/isiz; n; --n)
  27669. for (const t *ptrs = img._data, *ptrs_end = ptrs + isiz; ptrs<ptrs_end; ++ptrd)
  27670. *ptrd = cimg::maxabs((T)*(ptrs++),*ptrd);
  27671. for (const t *ptrs = img._data; ptrd<ptre; ++ptrd) *ptrd = cimg::maxabs((T)*(ptrs++),*ptrd);
  27672. }
  27673. return *this;
  27674. }
  27675. //! Pointwise maxabs operator between two images \newinstance.
  27676. template<typename t>
  27677. CImg<_cimg_Tt> get_maxabs(const CImg<t>& img) const {
  27678. return CImg<_cimg_Tt>(*this,false).maxabs(img);
  27679. }
  27680. //! Pointwise maxabs operator between an image and an expression.
  27681. /**
  27682. \param expression Math formula as a C-string.
  27683. \note Replace each pixel value \f$I_{(x,y,z,c)}\f$ of the image instance by
  27684. \f$\mathrm{maxabs}(I_{(x,y,z,c)},\mathrm{expr}_{(x,y,z,c)})\f$.
  27685. **/
  27686. CImg<T>& maxabs(const char *const expression) {
  27687. return maxabs((+*this)._fill(expression,true,3,0,"maxabs",this,0));
  27688. }
  27689. //! Pointwise maxabs operator between an image and an expression \newinstance.
  27690. CImg<Tfloat> get_maxabs(const char *const expression) const {
  27691. return CImg<Tfloat>(*this,false).maxabs(expression);
  27692. }
  27693. //! Return a reference to the minimum pixel value.
  27694. /**
  27695. **/
  27696. T& min() {
  27697. if (is_empty())
  27698. throw CImgInstanceException(_cimg_instance
  27699. "min(): Empty instance.",
  27700. cimg_instance);
  27701. T *ptr_min = _data;
  27702. T min_value = *ptr_min;
  27703. cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
  27704. return *ptr_min;
  27705. }
  27706. //! Return a reference to the minimum pixel value \const.
  27707. const T& min() const {
  27708. if (is_empty())
  27709. throw CImgInstanceException(_cimg_instance
  27710. "min(): Empty instance.",
  27711. cimg_instance);
  27712. const T *ptr_min = _data;
  27713. T min_value = *ptr_min;
  27714. cimg_for(*this,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
  27715. return *ptr_min;
  27716. }
  27717. //! Return a reference to the minimum pixel value in absolute value.
  27718. /**
  27719. **/
  27720. T& minabs() {
  27721. if (is_empty())
  27722. throw CImgInstanceException(_cimg_instance
  27723. "minabs(): Empty instance.",
  27724. cimg_instance);
  27725. T *ptr_minabs = _data;
  27726. T minabs_value = *ptr_minabs;
  27727. cimg_for(*this,ptrs,T) {
  27728. const T ma = cimg::abs(*ptrs);
  27729. if (ma<minabs_value) { minabs_value = ma; ptr_minabs = ptrs; }
  27730. }
  27731. return *ptr_minabs;
  27732. }
  27733. //! Return a reference to the minimum pixel value in absolute value \const.
  27734. const T& minabs() const {
  27735. if (is_empty())
  27736. throw CImgInstanceException(_cimg_instance
  27737. "minabs(): Empty instance.",
  27738. cimg_instance);
  27739. const T *ptr_minabs = _data;
  27740. T minabs_value = *ptr_minabs;
  27741. cimg_for(*this,ptrs,T) {
  27742. const T ma = cimg::abs(*ptrs);
  27743. if (ma<minabs_value) { minabs_value = ma; ptr_minabs = ptrs; }
  27744. }
  27745. return *ptr_minabs;
  27746. }
  27747. //! Return a reference to the maximum pixel value.
  27748. /**
  27749. **/
  27750. T& max() {
  27751. if (is_empty())
  27752. throw CImgInstanceException(_cimg_instance
  27753. "max(): Empty instance.",
  27754. cimg_instance);
  27755. T *ptr_max = _data;
  27756. T max_value = *ptr_max;
  27757. cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
  27758. return *ptr_max;
  27759. }
  27760. //! Return a reference to the maximum pixel value \const.
  27761. const T& max() const {
  27762. if (is_empty())
  27763. throw CImgInstanceException(_cimg_instance
  27764. "max(): Empty instance.",
  27765. cimg_instance);
  27766. const T *ptr_max = _data;
  27767. T max_value = *ptr_max;
  27768. cimg_for(*this,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
  27769. return *ptr_max;
  27770. }
  27771. //! Return a reference to the maximum pixel value in absolute value.
  27772. /**
  27773. **/
  27774. T& maxabs() {
  27775. if (is_empty())
  27776. throw CImgInstanceException(_cimg_instance
  27777. "maxabs(): Empty instance.",
  27778. cimg_instance);
  27779. T *ptr_maxabs = _data;
  27780. T maxabs_value = *ptr_maxabs;
  27781. cimg_for(*this,ptrs,T) {
  27782. const T ma = cimg::abs(*ptrs);
  27783. if (ma>maxabs_value) { maxabs_value = ma; ptr_maxabs = ptrs; }
  27784. }
  27785. return *ptr_maxabs;
  27786. }
  27787. //! Return a reference to the maximum pixel value in absolute value \const.
  27788. const T& maxabs() const {
  27789. if (is_empty())
  27790. throw CImgInstanceException(_cimg_instance
  27791. "maxabs(): Empty instance.",
  27792. cimg_instance);
  27793. const T *ptr_maxabs = _data;
  27794. T maxabs_value = *ptr_maxabs;
  27795. cimg_for(*this,ptrs,T) {
  27796. const T ma = cimg::abs(*ptrs);
  27797. if (ma>maxabs_value) { maxabs_value = ma; ptr_maxabs = ptrs; }
  27798. }
  27799. return *ptr_maxabs;
  27800. }
  27801. //! Return a reference to the minimum pixel value as well as the maximum pixel value.
  27802. /**
  27803. \param[out] max_val Maximum pixel value.
  27804. **/
  27805. template<typename t>
  27806. T& min_max(t& max_val) {
  27807. if (is_empty())
  27808. throw CImgInstanceException(_cimg_instance
  27809. "min_max(): Empty instance.",
  27810. cimg_instance);
  27811. T *ptr_min = _data;
  27812. T min_value = *ptr_min, max_value = min_value;
  27813. cimg_for(*this,ptrs,T) {
  27814. const T val = *ptrs;
  27815. if (val<min_value) { min_value = val; ptr_min = ptrs; }
  27816. if (val>max_value) max_value = val;
  27817. }
  27818. max_val = (t)max_value;
  27819. return *ptr_min;
  27820. }
  27821. //! Return a reference to the minimum pixel value as well as the maximum pixel value \const.
  27822. template<typename t>
  27823. const T& min_max(t& max_val) const {
  27824. if (is_empty())
  27825. throw CImgInstanceException(_cimg_instance
  27826. "min_max(): Empty instance.",
  27827. cimg_instance);
  27828. const T *ptr_min = _data;
  27829. T min_value = *ptr_min, max_value = min_value;
  27830. cimg_for(*this,ptrs,T) {
  27831. const T val = *ptrs;
  27832. if (val<min_value) { min_value = val; ptr_min = ptrs; }
  27833. if (val>max_value) max_value = val;
  27834. }
  27835. max_val = (t)max_value;
  27836. return *ptr_min;
  27837. }
  27838. //! Return a reference to the maximum pixel value as well as the minimum pixel value.
  27839. /**
  27840. \param[out] min_val Minimum pixel value.
  27841. **/
  27842. template<typename t>
  27843. T& max_min(t& min_val) {
  27844. if (is_empty())
  27845. throw CImgInstanceException(_cimg_instance
  27846. "max_min(): Empty instance.",
  27847. cimg_instance);
  27848. T *ptr_max = _data;
  27849. T max_value = *ptr_max, min_value = max_value;
  27850. cimg_for(*this,ptrs,T) {
  27851. const T val = *ptrs;
  27852. if (val>max_value) { max_value = val; ptr_max = ptrs; }
  27853. if (val<min_value) min_value = val;
  27854. }
  27855. min_val = (t)min_value;
  27856. return *ptr_max;
  27857. }
  27858. //! Return a reference to the maximum pixel value as well as the minimum pixel value \const.
  27859. template<typename t>
  27860. const T& max_min(t& min_val) const {
  27861. if (is_empty())
  27862. throw CImgInstanceException(_cimg_instance
  27863. "max_min(): Empty instance.",
  27864. cimg_instance);
  27865. const T *ptr_max = _data;
  27866. T max_value = *ptr_max, min_value = max_value;
  27867. cimg_for(*this,ptrs,T) {
  27868. const T val = *ptrs;
  27869. if (val>max_value) { max_value = val; ptr_max = ptrs; }
  27870. if (val<min_value) min_value = val;
  27871. }
  27872. min_val = (t)min_value;
  27873. return *ptr_max;
  27874. }
  27875. //! Return the kth smallest pixel value.
  27876. /**
  27877. \param k Rank of the smallest element searched.
  27878. **/
  27879. T kth_smallest(const ulongT k) const {
  27880. if (is_empty())
  27881. throw CImgInstanceException(_cimg_instance
  27882. "kth_smallest(): Empty instance.",
  27883. cimg_instance);
  27884. if (k>=size()) return max();
  27885. CImg<T> arr(*this,false);
  27886. ulongT l = 0, ir = size() - 1;
  27887. for ( ; ; ) {
  27888. if (ir<=l + 1) {
  27889. if (ir==l + 1 && arr[ir]<arr[l]) cimg::swap(arr[l],arr[ir]);
  27890. return arr[k];
  27891. } else {
  27892. const ulongT mid = (l + ir)>>1;
  27893. cimg::swap(arr[mid],arr[l + 1]);
  27894. if (arr[l]>arr[ir]) cimg::swap(arr[l],arr[ir]);
  27895. if (arr[l + 1]>arr[ir]) cimg::swap(arr[l + 1],arr[ir]);
  27896. if (arr[l]>arr[l + 1]) cimg::swap(arr[l],arr[l + 1]);
  27897. ulongT i = l + 1, j = ir;
  27898. const T pivot = arr[l + 1];
  27899. for ( ; ; ) {
  27900. do ++i; while (arr[i]<pivot);
  27901. do --j; while (arr[j]>pivot);
  27902. if (j<i) break;
  27903. cimg::swap(arr[i],arr[j]);
  27904. }
  27905. arr[l + 1] = arr[j];
  27906. arr[j] = pivot;
  27907. if (j>=k) ir = j - 1;
  27908. if (j<=k) l = i;
  27909. }
  27910. }
  27911. }
  27912. //! Return the median pixel value.
  27913. /**
  27914. **/
  27915. T median() const {
  27916. if (is_empty())
  27917. throw CImgInstanceException(_cimg_instance
  27918. "median(): Empty instance.",
  27919. cimg_instance);
  27920. const ulongT s = size();
  27921. switch (s) {
  27922. case 1 : return _data[0];
  27923. case 2 : return cimg::median(_data[0],_data[1]);
  27924. case 3 : return cimg::median(_data[0],_data[1],_data[2]);
  27925. case 5 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4]);
  27926. case 7 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6]);
  27927. case 9 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6],_data[7],_data[8]);
  27928. case 13 : return cimg::median(_data[0],_data[1],_data[2],_data[3],_data[4],_data[5],_data[6],_data[7],_data[8],
  27929. _data[9],_data[10],_data[11],_data[12]);
  27930. }
  27931. const T res = kth_smallest(s>>1);
  27932. return (s%2)?res:(T)((res + kth_smallest((s>>1) - 1))/2);
  27933. }
  27934. //! Return the product of all the pixel values.
  27935. /**
  27936. **/
  27937. double product() const {
  27938. if (is_empty()) return 0;
  27939. double res = 1;
  27940. cimg_for(*this,ptrs,T) res*=(double)*ptrs;
  27941. return res;
  27942. }
  27943. //! Return the sum of all the pixel values.
  27944. /**
  27945. **/
  27946. double sum() const {
  27947. double res = 0;
  27948. cimg_for(*this,ptrs,T) res+=(double)*ptrs;
  27949. return res;
  27950. }
  27951. //! Return the average pixel value.
  27952. /**
  27953. **/
  27954. double mean() const {
  27955. double res = 0;
  27956. cimg_for(*this,ptrs,T) res+=(double)*ptrs;
  27957. return res/size();
  27958. }
  27959. //! Return the variance of the pixel values.
  27960. /**
  27961. \param variance_method Method used to estimate the variance. Can be:
  27962. - \c 0: Second moment, computed as
  27963. \f$1/N \sum\limits_{k=1}^{N} (x_k - \bar x)^2 =
  27964. 1/N \left( \sum\limits_{k=1}^N x_k^2 - \left( \sum\limits_{k=1}^N x_k \right)^2 / N \right)\f$
  27965. with \f$ \bar x = 1/N \sum\limits_{k=1}^N x_k \f$.
  27966. - \c 1: Best unbiased estimator, computed as \f$\frac{1}{N - 1} \sum\limits_{k=1}^{N} (x_k - \bar x)^2 \f$.
  27967. - \c 2: Least median of squares.
  27968. - \c 3: Least trimmed of squares.
  27969. **/
  27970. double variance(const unsigned int variance_method=1) const {
  27971. double foo;
  27972. return variance_mean(variance_method,foo);
  27973. }
  27974. //! Return the variance as well as the average of the pixel values.
  27975. /**
  27976. \param variance_method Method used to estimate the variance (see variance(const unsigned int) const).
  27977. \param[out] mean Average pixel value.
  27978. **/
  27979. template<typename t>
  27980. double variance_mean(const unsigned int variance_method, t& mean) const {
  27981. if (is_empty())
  27982. throw CImgInstanceException(_cimg_instance
  27983. "variance_mean(): Empty instance.",
  27984. cimg_instance);
  27985. double variance = 0, average = 0;
  27986. const ulongT siz = size();
  27987. switch (variance_method) {
  27988. case 0 : { // Least mean square (standard definition)
  27989. double S = 0, S2 = 0;
  27990. cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; }
  27991. variance = (S2 - S*S/siz)/siz;
  27992. average = S;
  27993. } break;
  27994. case 1 : { // Least mean square (robust definition)
  27995. double S = 0, S2 = 0;
  27996. cimg_for(*this,ptrs,T) { const double val = (double)*ptrs; S+=val; S2+=val*val; }
  27997. variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
  27998. average = S;
  27999. } break;
  28000. case 2 : { // Least Median of Squares (MAD)
  28001. CImg<Tfloat> buf(*this,false);
  28002. buf.sort();
  28003. const ulongT siz2 = siz>>1;
  28004. const double med_i = (double)buf[siz2];
  28005. cimg_for(buf,ptrs,Tfloat) {
  28006. const double val = (double)*ptrs; *ptrs = (Tfloat)cimg::abs(val - med_i); average+=val;
  28007. }
  28008. buf.sort();
  28009. const double sig = (double)(1.4828*buf[siz2]);
  28010. variance = sig*sig;
  28011. } break;
  28012. default : { // Least trimmed of Squares
  28013. CImg<Tfloat> buf(*this,false);
  28014. const ulongT siz2 = siz>>1;
  28015. cimg_for(buf,ptrs,Tfloat) {
  28016. const double val = (double)*ptrs; (*ptrs)=(Tfloat)((*ptrs)*val); average+=val;
  28017. }
  28018. buf.sort();
  28019. double a = 0;
  28020. const Tfloat *ptrs = buf._data;
  28021. for (ulongT j = 0; j<siz2; ++j) a+=(double)*(ptrs++);
  28022. const double sig = (double)(2.6477*std::sqrt(a/siz2));
  28023. variance = sig*sig;
  28024. }
  28025. }
  28026. mean = (t)(average/siz);
  28027. return variance>0?variance:0;
  28028. }
  28029. //! Return estimated variance of the noise.
  28030. /**
  28031. \param variance_method Method used to compute the variance (see variance(const unsigned int) const).
  28032. \note Because of structures such as edges in images it is
  28033. recommended to use a robust variance estimation. The variance of the
  28034. noise is estimated by computing the variance of the Laplacian \f$(\Delta
  28035. I)^2 \f$ scaled by a factor \f$c\f$ insuring \f$ c E[(\Delta I)^2]=
  28036. \sigma^2\f$ where \f$\sigma\f$ is the noise variance.
  28037. **/
  28038. double variance_noise(const unsigned int variance_method=2) const {
  28039. if (is_empty())
  28040. throw CImgInstanceException(_cimg_instance
  28041. "variance_noise(): Empty instance.",
  28042. cimg_instance);
  28043. const ulongT siz = size();
  28044. if (!siz || !_data) return 0;
  28045. if (variance_method>1) { // Compute a scaled version of the Laplacian
  28046. CImg<Tdouble> tmp(*this,false);
  28047. if (_depth==1) {
  28048. const double cste = 1./std::sqrt(20.); // Depends on how the Laplacian is computed
  28049. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*262144 &&
  28050. _spectrum>=2))
  28051. cimg_forC(*this,c) {
  28052. CImg_3x3(I,T);
  28053. cimg_for3x3(*this,x,y,0,c,I,T) {
  28054. tmp(x,y,c) = cste*((double)Inc + (double)Ipc + (double)Icn +
  28055. (double)Icp - 4*(double)Icc);
  28056. }
  28057. }
  28058. } else {
  28059. const double cste = 1./std::sqrt(42.); // Depends on how the Laplacian is computed
  28060. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*262144 &&
  28061. _spectrum>=2))
  28062. cimg_forC(*this,c) {
  28063. CImg_3x3x3(I,T);
  28064. cimg_for3x3x3(*this,x,y,z,c,I,T) {
  28065. tmp(x,y,z,c) = cste*(
  28066. (double)Incc + (double)Ipcc + (double)Icnc + (double)Icpc +
  28067. (double)Iccn + (double)Iccp - 6*(double)Iccc);
  28068. }
  28069. }
  28070. }
  28071. return tmp.variance(variance_method);
  28072. }
  28073. // Version that doesn't need intermediate images.
  28074. double variance = 0, S = 0, S2 = 0;
  28075. if (_depth==1) {
  28076. const double cste = 1./std::sqrt(20.);
  28077. CImg_3x3(I,T);
  28078. cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,T) {
  28079. const double val = cste*((double)Inc + (double)Ipc +
  28080. (double)Icn + (double)Icp - 4*(double)Icc);
  28081. S+=val; S2+=val*val;
  28082. }
  28083. } else {
  28084. const double cste = 1./std::sqrt(42.);
  28085. CImg_3x3x3(I,T);
  28086. cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,T) {
  28087. const double val = cste *
  28088. ((double)Incc + (double)Ipcc + (double)Icnc +
  28089. (double)Icpc +
  28090. (double)Iccn + (double)Iccp - 6*(double)Iccc);
  28091. S+=val; S2+=val*val;
  28092. }
  28093. }
  28094. if (variance_method) variance = siz>1?(S2 - S*S/siz)/(siz - 1):0;
  28095. else variance = (S2 - S*S/siz)/siz;
  28096. return variance>0?variance:0;
  28097. }
  28098. //! Compute the MSE (Mean-Squared Error) between two images.
  28099. /**
  28100. \param img Image used as the second argument of the MSE operator.
  28101. **/
  28102. template<typename t>
  28103. double MSE(const CImg<t>& img) const {
  28104. if (img.size()!=size())
  28105. throw CImgArgumentException(_cimg_instance
  28106. "MSE(): Instance and specified image (%u,%u,%u,%u,%p) have different dimensions.",
  28107. cimg_instance,
  28108. img._width,img._height,img._depth,img._spectrum,img._data);
  28109. double vMSE = 0;
  28110. const t* ptr2 = img._data;
  28111. cimg_for(*this,ptr1,T) {
  28112. const double diff = (double)*ptr1 - (double)*(ptr2++);
  28113. vMSE+=diff*diff;
  28114. }
  28115. const ulongT siz = img.size();
  28116. if (siz) vMSE/=siz;
  28117. return vMSE;
  28118. }
  28119. //! Compute the PSNR (Peak Signal-to-Noise Ratio) between two images.
  28120. /**
  28121. \param img Image used as the second argument of the PSNR operator.
  28122. \param max_value Maximum theoretical value of the signal.
  28123. **/
  28124. template<typename t>
  28125. double PSNR(const CImg<t>& img, const double max_value=255) const {
  28126. const double vMSE = (double)std::sqrt(MSE(img));
  28127. return (vMSE!=0)?(double)(20*std::log10(max_value/vMSE)):(double)(cimg::type<double>::max());
  28128. }
  28129. //! Evaluate math formula.
  28130. /**
  28131. \param expression Math formula, as a C-string.
  28132. \param x Value of the pre-defined variable \c x.
  28133. \param y Value of the pre-defined variable \c y.
  28134. \param z Value of the pre-defined variable \c z.
  28135. \param c Value of the pre-defined variable \c c.
  28136. \param list_images A list of images attached to the specified math formula.
  28137. **/
  28138. double eval(const char *const expression,
  28139. const double x=0, const double y=0, const double z=0, const double c=0,
  28140. CImgList<T> *const list_images=0) {
  28141. return _eval(this,expression,x,y,z,c,list_images);
  28142. }
  28143. //! Evaluate math formula \const.
  28144. double eval(const char *const expression,
  28145. const double x=0, const double y=0, const double z=0, const double c=0,
  28146. CImgList<T> *const list_images=0) const {
  28147. return _eval(0,expression,x,y,z,c,list_images);
  28148. }
  28149. // Fast function to pre-evaluate common expressions.
  28150. // (return 'true' in case of success, and set value of 'res').
  28151. template<typename t>
  28152. bool __eval(const char *const expression, t &res) const {
  28153. #define __eval_op(op) if (__eval_get(++ptr,val2) && !*ptr) { res = (t)(op); return true; } else return false;
  28154. double val1, val2;
  28155. if (!expression || !*expression || *expression==';' || *expression=='[') return false;
  28156. if (!expression[1]) switch (*expression) {
  28157. case 'w' : res = (t)_width; return true;
  28158. case 'h' : res = (t)_height; return true;
  28159. case 'd' : res = (t)_depth; return true;
  28160. case 's' : res = (t)_spectrum; return true;
  28161. case 'r' : res = (t)_is_shared; return true;
  28162. default : if (*expression>='0' && *expression<='9') { res = (t)(*expression - '0'); return true; }
  28163. }
  28164. if (*expression=='w' && expression[1]=='h') {
  28165. if (!expression[2]) { res = (t)(_width*_height); return true; }
  28166. if (expression[2]=='d') {
  28167. if (!expression[3]) { res = (t)(_width*_height*_depth); return true; }
  28168. if (expression[3]=='s' && !expression[4]) { res = (t)(_width*_height*_depth*_spectrum); return true; }
  28169. }
  28170. if (expression[2]=='s' && !expression[3]) { res = (t)(_width*_height*_spectrum); return true; }
  28171. }
  28172. const char *ptr = expression;
  28173. while (*ptr && cimg::is_blank(*ptr)) ++ptr;
  28174. if (*ptr=='\'' && *(++ptr)) { // Detect 'stringA' op 'stringB' (op='==' or '!=')
  28175. const char *ptr2 = std::strchr(ptr,'\'');
  28176. if (ptr2) {
  28177. const char *ptr3 = ptr2 + 1;
  28178. while (*ptr3 && cimg::is_blank(*ptr3)) ++ptr3;
  28179. const char *ptr4 = ptr3;
  28180. if ((*ptr3=='!' || *ptr3=='=') && *(++ptr4)=='=') {
  28181. ++ptr4;
  28182. while (*ptr4 && cimg::is_blank(*ptr4)) ++ptr4;
  28183. if (*ptr4=='\'' && *(++ptr4)) {
  28184. const char *const ptr5 = std::strchr(ptr4,'\'');
  28185. if (ptr5) {
  28186. const char *ptr6 = ptr5 + 1;
  28187. while (*ptr6 && cimg::is_blank(*ptr6)) ++ptr6;
  28188. if (!*ptr6) {
  28189. CImg<charT> str1(ptr,ptr2 - ptr,1,1,1,true), str2(ptr4,ptr5 - ptr4,1,1,1,true);
  28190. if (*ptr3=='!') res = (t)!(str1==str2); else res = (t)(str1==str2);
  28191. return true;
  28192. }
  28193. }
  28194. }
  28195. }
  28196. }
  28197. return false;
  28198. }
  28199. if (__eval_get(ptr,val1)) { // Detect 'value1' op 'value2'
  28200. switch (*ptr) {
  28201. case 0 : res = (t)val1; return true;
  28202. case '+' : __eval_op(val1 + val2);
  28203. case '-' : __eval_op(val1 - val2);
  28204. case '*' : __eval_op(val1 * val2);
  28205. case '/' : __eval_op(val1 / val2);
  28206. case '%' : __eval_op(cimg::mod(val1,val2));
  28207. case '&' : if (ptr[1]=='&') { ++ptr; __eval_op(val1 && val2); } else { __eval_op((long)val1 & (long)val2); }
  28208. case '|' : if (ptr[1]=='|') { ++ptr; __eval_op(val1 || val2); } else { __eval_op((long)val1 | (long)val2); }
  28209. case '>' : if (ptr[1]=='=') { ++ptr; __eval_op(val1>=val2); } else { __eval_op(val1>val2); }
  28210. case '<' : if (ptr[1]=='=') { ++ptr; __eval_op(val1<=val2); } else { __eval_op(val1<val2); }
  28211. case ';' : __eval_op(val2);
  28212. case '^' : __eval_op(std::pow(val1,val2));
  28213. case '=' : if (*++ptr=='=') { __eval_op(val1==val2); } else return false;
  28214. case '!' : if (*++ptr=='=') { __eval_op(val1!=val2); } else return false;
  28215. }
  28216. }
  28217. return false;
  28218. }
  28219. // Return 'true' is a single 'value' or '!value' has been succesfully read ('value' being a double or { w,h,d,s }).
  28220. bool __eval_get(const char* &ptr, double &value) const {
  28221. int n = 0;
  28222. while (*ptr && cimg::is_blank(*ptr)) ++ptr;
  28223. bool is_not = false; // Detect preceding '!' operator
  28224. if (*ptr=='!') { is_not = true; ++ptr; while (*ptr && cimg::is_blank(*ptr)) ++ptr; }
  28225. if ((*ptr=='w' || *ptr=='h' || *ptr=='d' || *ptr=='s' || *ptr=='r') || std::sscanf(ptr,"%lf %n",&value,&n)==1) {
  28226. if (!n) {
  28227. switch (*ptr) {
  28228. case 'w': value = (double)_width; break;
  28229. case 'h': value = (double)_height; break;
  28230. case 'd': value = (double)_depth; break;
  28231. case 's': value = (double)_spectrum; break;
  28232. case 'r': value = (double)_is_shared; break;
  28233. }
  28234. ++ptr; while (*ptr && cimg::is_blank(*ptr)) ++ptr;
  28235. } else ptr+=n;
  28236. if (is_not) value = (double)!value;
  28237. return true;
  28238. }
  28239. return false;
  28240. }
  28241. double _eval(CImg<T> *const img_output, const char *const expression,
  28242. const double x, const double y, const double z, const double c,
  28243. CImgList<T> *const list_images) const {
  28244. if (!expression || !*expression) return 0;
  28245. double _val = 0;
  28246. if (__eval(expression,_val)) return _val;
  28247. _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='+' ||
  28248. *expression=='*' || *expression==':'),"eval",
  28249. *this,img_output,list_images,false);
  28250. mp.begin_t();
  28251. const double val = mp(x,y,z,c);
  28252. mp.end_t();
  28253. mp.end();
  28254. return val;
  28255. }
  28256. //! Evaluate math formula.
  28257. /**
  28258. \param[out] output Contains values of output vector returned by the evaluated expression
  28259. (or is empty if the returned type is scalar).
  28260. \param expression Math formula, as a C-string.
  28261. \param x Value of the pre-defined variable \c x.
  28262. \param y Value of the pre-defined variable \c y.
  28263. \param z Value of the pre-defined variable \c z.
  28264. \param c Value of the pre-defined variable \c c.
  28265. \param list_images A list of input images attached to the specified math formula.
  28266. **/
  28267. template<typename t>
  28268. void eval(CImg<t> &output, const char *const expression,
  28269. const double x=0, const double y=0, const double z=0, const double c=0,
  28270. CImgList<T> *const list_images=0) {
  28271. _eval(output,this,expression,x,y,z,c,list_images);
  28272. }
  28273. //! Evaluate math formula \const.
  28274. template<typename t>
  28275. void eval(CImg<t>& output, const char *const expression,
  28276. const double x=0, const double y=0, const double z=0, const double c=0,
  28277. CImgList<T> *const list_images=0) const {
  28278. _eval(output,0,expression,x,y,z,c,list_images);
  28279. }
  28280. template<typename t>
  28281. void _eval(CImg<t>& output, CImg<T> *const img_output, const char *const expression,
  28282. const double x, const double y, const double z, const double c,
  28283. CImgList<T> *const list_images) const {
  28284. if (!expression || !*expression) { output.assign(1); *output = 0; return; }
  28285. double _val = 0;
  28286. if (__eval(expression,_val)) { output.assign(1); *output = _val; return; }
  28287. _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='+' ||
  28288. *expression=='*' || *expression==':'),"eval",
  28289. *this,img_output,list_images,false);
  28290. output.assign(1,std::max(1U,mp.result_dim));
  28291. mp.begin_t();
  28292. mp(x,y,z,c,output._data);
  28293. mp.end_t();
  28294. mp.end();
  28295. }
  28296. //! Evaluate math formula on a set of variables.
  28297. /**
  28298. \param expression Math formula, as a C-string.
  28299. \param xyzc Set of values (x,y,z,c) used for the evaluation.
  28300. \param list_images A list of input images attached to the specified math formula.
  28301. **/
  28302. template<typename t>
  28303. CImg<doubleT> eval(const char *const expression, const CImg<t>& xyzc,
  28304. CImgList<T> *const list_images=0) {
  28305. return _eval(this,expression,xyzc,list_images);
  28306. }
  28307. //! Evaluate math formula on a set of variables \const.
  28308. template<typename t>
  28309. CImg<doubleT> eval(const char *const expression, const CImg<t>& xyzc,
  28310. CImgList<T> *const list_images=0) const {
  28311. return _eval(0,expression,xyzc,list_images);
  28312. }
  28313. template<typename t>
  28314. CImg<doubleT> _eval(CImg<T> *const output, const char *const expression, const CImg<t>& xyzc,
  28315. CImgList<T> *const list_images=0) const {
  28316. CImg<doubleT> res(1,xyzc.size()/4);
  28317. if (!expression || !*expression) return res.fill(0);
  28318. _cimg_math_parser mp(expression,"eval",*this,output,list_images,false);
  28319. #if cimg_use_openmp!=0
  28320. cimg_pragma_openmp(parallel if (res._height>=512))
  28321. {
  28322. _cimg_math_parser
  28323. *const _mp = omp_get_thread_num()?new _cimg_math_parser(mp):&mp,
  28324. &lmp = *_mp;
  28325. cimg_pragma_openmp(barrier)
  28326. lmp.begin_t();
  28327. cimg_pragma_openmp(for)
  28328. for (int i = 0; i<res.height(); ++i) {
  28329. const unsigned int i4 = 4*i;
  28330. const double
  28331. x = (double)xyzc[i4], y = (double)xyzc[i4 + 1],
  28332. z = (double)xyzc[i4 + 2], c = (double)xyzc[i4 + 3];
  28333. res[i] = lmp(x,y,z,c);
  28334. }
  28335. lmp.end_t();
  28336. cimg_pragma_openmp(barrier) cimg_pragma_openmp(critical) { lmp.merge(mp); }
  28337. if (&lmp!=&mp) delete &lmp;
  28338. }
  28339. #else
  28340. mp.begin_t();
  28341. const t *ps = xyzc._data;
  28342. cimg_for(res,pd,double) {
  28343. const double x = (double)*(ps++), y = (double)*(ps++), z = (double)*(ps++), c = (double)*(ps++);
  28344. *pd = mp(x,y,z,c);
  28345. }
  28346. mp.end_t();
  28347. #endif
  28348. mp.end();
  28349. return res;
  28350. }
  28351. //! Compute statistics vector from the pixel values.
  28352. /**
  28353. \param variance_method Method used to compute the variance (see variance(const unsigned int) const).
  28354. \return Statistics vector as
  28355. <tt>[min, max, mean, variance, xmin, ymin, zmin, cmin, xmax, ymax, zmax, cmax, sum, product]</tt>.
  28356. **/
  28357. CImg<Tdouble> get_stats(const unsigned int variance_method=1) const {
  28358. if (is_empty()) return CImg<doubleT>();
  28359. const ulongT siz = size();
  28360. const longT off_end = (longT)siz;
  28361. double S = 0, S2 = 0, P = 1;
  28362. longT offm = 0, offM = 0;
  28363. T m = *_data, M = m;
  28364. cimg_pragma_openmp(parallel reduction(+:S,S2) reduction(*:P) cimg_openmp_if_size(siz,131072)) {
  28365. longT loffm = 0, loffM = 0;
  28366. T lm = *_data, lM = lm;
  28367. cimg_pragma_openmp(for)
  28368. for (longT off = 0; off<off_end; ++off) {
  28369. const T val = _data[off];
  28370. const double _val = (double)val;
  28371. if (val<lm) { lm = val; loffm = off; }
  28372. if (val>lM) { lM = val; loffM = off; }
  28373. S+=_val;
  28374. S2+=_val*_val;
  28375. P*=_val;
  28376. }
  28377. cimg_pragma_openmp(critical(get_stats)) {
  28378. if (lm<m || (lm==m && loffm<offm)) { m = lm; offm = loffm; }
  28379. if (lM>M || (lM==M && loffM<offM)) { M = lM; offM = loffM; }
  28380. }
  28381. }
  28382. const double
  28383. mean_value = S/siz,
  28384. _variance_value = variance_method==0?(S2 - S*S/siz)/siz:
  28385. (variance_method==1?(siz>1?(S2 - S*S/siz)/(siz - 1):0):
  28386. variance(variance_method)),
  28387. variance_value = _variance_value>0?_variance_value:0;
  28388. int
  28389. xm = 0, ym = 0, zm = 0, cm = 0,
  28390. xM = 0, yM = 0, zM = 0, cM = 0;
  28391. contains(_data[offm],xm,ym,zm,cm);
  28392. contains(_data[offM],xM,yM,zM,cM);
  28393. return CImg<Tdouble>(1,14).fill((double)m,(double)M,mean_value,variance_value,
  28394. (double)xm,(double)ym,(double)zm,(double)cm,
  28395. (double)xM,(double)yM,(double)zM,(double)cM,
  28396. S,P);
  28397. }
  28398. //! Compute statistics vector from the pixel values \inplace.
  28399. CImg<T>& stats(const unsigned int variance_method=1) {
  28400. return get_stats(variance_method).move_to(*this);
  28401. }
  28402. //@}
  28403. //-------------------------------------
  28404. //
  28405. //! \name Vector / Matrix Operations
  28406. //@{
  28407. //-------------------------------------
  28408. //! Compute norm of the image, viewed as a matrix.
  28409. /**
  28410. \param magnitude_type Can be:
  28411. - \c 0: L0-norm
  28412. - \c 1: L1-norm
  28413. - \c 2: L2-norm
  28414. - \c p>2 : Lp-norm
  28415. - \c ~0U: Linf-norm
  28416. **/
  28417. double magnitude(const float magnitude_type=2) const {
  28418. if (is_empty())
  28419. throw CImgInstanceException(_cimg_instance
  28420. "magnitude(): Empty instance.",
  28421. cimg_instance);
  28422. const ulongT siz = size();
  28423. double res = 0;
  28424. if (magnitude_type==2) { // L2
  28425. cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192))
  28426. for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::sqr(_data[off]);
  28427. res = (double)std::sqrt(res);
  28428. } else if (magnitude_type==1) { // L1
  28429. cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192))
  28430. for (longT off = 0; off<(longT)siz; ++off) res+=(double)cimg::abs(_data[off]);
  28431. } else if (!magnitude_type) { // L0
  28432. cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192))
  28433. for (longT off = 0; off<(longT)siz; ++off) res+=(double)(_data[off]?1:0);
  28434. } else if (cimg::type<float>::is_inf(magnitude_type)) { // L-inf
  28435. cimg_for(*this,ptrs,T) { const double val = (double)cimg::abs(*ptrs); if (val>res) res = val; }
  28436. } else { // L-p
  28437. cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(size(),8192))
  28438. for (longT off = 0; off<(longT)siz; ++off)
  28439. res+=(double)std::pow((double)cimg::abs(_data[off]),(double)magnitude_type);
  28440. res = (double)std::pow(res,1.0/magnitude_type);
  28441. }
  28442. return res;
  28443. }
  28444. //! Compute the trace of the image, viewed as a matrix.
  28445. /**
  28446. **/
  28447. double trace() const {
  28448. if (is_empty())
  28449. throw CImgInstanceException(_cimg_instance
  28450. "trace(): Empty instance.",
  28451. cimg_instance);
  28452. double res = 0;
  28453. cimg_forX(*this,k) res+=(double)(*this)(k,k);
  28454. return res;
  28455. }
  28456. //! Compute the determinant of the image, viewed as a matrix.
  28457. /**
  28458. **/
  28459. double det() const {
  28460. if (is_empty() || _width!=_height || _depth!=1 || _spectrum!=1)
  28461. throw CImgInstanceException(_cimg_instance
  28462. "det(): Instance is not a square matrix.",
  28463. cimg_instance);
  28464. switch (_width) {
  28465. case 1 : return (double)((*this)(0,0));
  28466. case 2 : return (double)((*this)(0,0))*(double)((*this)(1,1)) - (double)((*this)(0,1))*(double)((*this)(1,0));
  28467. case 3 : {
  28468. const double
  28469. a = (double)_data[0], d = (double)_data[1], g = (double)_data[2],
  28470. b = (double)_data[3], e = (double)_data[4], h = (double)_data[5],
  28471. c = (double)_data[6], f = (double)_data[7], i = (double)_data[8];
  28472. return i*a*e - a*h*f - i*b*d + b*g*f + c*d*h - c*g*e;
  28473. }
  28474. default : {
  28475. CImg<Tfloat> lu(*this,false);
  28476. CImg<uintT> indx;
  28477. bool d;
  28478. lu._LU(indx,d);
  28479. double res = d?(double)1:(double)-1;
  28480. cimg_forX(lu,i) res*=lu(i,i);
  28481. return res;
  28482. }
  28483. }
  28484. }
  28485. //! Compute the dot product between instance and argument, viewed as matrices.
  28486. /**
  28487. \param img Image used as a second argument of the dot product.
  28488. **/
  28489. template<typename t>
  28490. double dot(const CImg<t>& img) const {
  28491. const ulongT nb = std::min(size(),img.size());
  28492. double res = 0;
  28493. cimg_pragma_openmp(parallel for reduction(+:res) cimg_openmp_if_size(nb,8192))
  28494. for (longT off = 0; off<(longT)nb; ++off) res+=(double)_data[off]*(double)img[off];
  28495. return res;
  28496. }
  28497. //! Get vector-valued pixel located at specified position.
  28498. /**
  28499. \param x X-coordinate of the pixel value.
  28500. \param y Y-coordinate of the pixel value.
  28501. \param z Z-coordinate of the pixel value.
  28502. **/
  28503. CImg<T> get_vector_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
  28504. CImg<T> res;
  28505. if (res._height!=_spectrum) res.assign(1,_spectrum);
  28506. const ulongT whd = (ulongT)_width*_height*_depth;
  28507. const T *ptrs = data(x,y,z);
  28508. T *ptrd = res._data;
  28509. cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  28510. return res;
  28511. }
  28512. //! Get (square) matrix-valued pixel located at specified position.
  28513. /**
  28514. \param x X-coordinate of the pixel value.
  28515. \param y Y-coordinate of the pixel value.
  28516. \param z Z-coordinate of the pixel value.
  28517. \note - The spectrum() of the image must be a square.
  28518. **/
  28519. CImg<T> get_matrix_at(const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) const {
  28520. const int n = (int)cimg::round(std::sqrt((double)_spectrum));
  28521. const T *ptrs = data(x,y,z,0);
  28522. const ulongT whd = (ulongT)_width*_height*_depth;
  28523. CImg<T> res(n,n);
  28524. T *ptrd = res._data;
  28525. cimg_forC(*this,c) { *(ptrd++) = *ptrs; ptrs+=whd; }
  28526. return res;
  28527. }
  28528. //! Get tensor-valued pixel located at specified position.
  28529. /**
  28530. \param x X-coordinate of the pixel value.
  28531. \param y Y-coordinate of the pixel value.
  28532. \param z Z-coordinate of the pixel value.
  28533. **/
  28534. CImg<T> get_tensor_at(const unsigned int x, const unsigned int y=0, const unsigned int z=0) const {
  28535. const T *ptrs = data(x,y,z,0);
  28536. const ulongT whd = (ulongT)_width*_height*_depth;
  28537. if (_spectrum==6)
  28538. return tensor(*ptrs,*(ptrs + whd),*(ptrs + 2*whd),*(ptrs + 3*whd),*(ptrs + 4*whd),*(ptrs + 5*whd));
  28539. if (_spectrum==3)
  28540. return tensor(*ptrs,*(ptrs + whd),*(ptrs + 2*whd));
  28541. return tensor(*ptrs);
  28542. }
  28543. //! Set vector-valued pixel at specified position.
  28544. /**
  28545. \param vec Vector to put on the instance image.
  28546. \param x X-coordinate of the pixel value.
  28547. \param y Y-coordinate of the pixel value.
  28548. \param z Z-coordinate of the pixel value.
  28549. **/
  28550. template<typename t>
  28551. CImg<T>& set_vector_at(const CImg<t>& vec, const unsigned int x, const unsigned int y=0, const unsigned int z=0) {
  28552. if (x<_width && y<_height && z<_depth) {
  28553. const t *ptrs = vec._data;
  28554. const ulongT whd = (ulongT)_width*_height*_depth;
  28555. T *ptrd = data(x,y,z);
  28556. for (unsigned int k = std::min((unsigned int)vec.size(),_spectrum); k; --k) {
  28557. *ptrd = (T)*(ptrs++); ptrd+=whd;
  28558. }
  28559. }
  28560. return *this;
  28561. }
  28562. //! Set (square) matrix-valued pixel at specified position.
  28563. /**
  28564. \param mat Matrix to put on the instance image.
  28565. \param x X-coordinate of the pixel value.
  28566. \param y Y-coordinate of the pixel value.
  28567. \param z Z-coordinate of the pixel value.
  28568. **/
  28569. template<typename t>
  28570. CImg<T>& set_matrix_at(const CImg<t>& mat, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
  28571. return set_vector_at(mat,x,y,z);
  28572. }
  28573. //! Set tensor-valued pixel at specified position.
  28574. /**
  28575. \param ten Tensor to put on the instance image.
  28576. \param x X-coordinate of the pixel value.
  28577. \param y Y-coordinate of the pixel value.
  28578. \param z Z-coordinate of the pixel value.
  28579. **/
  28580. template<typename t>
  28581. CImg<T>& set_tensor_at(const CImg<t>& ten, const unsigned int x=0, const unsigned int y=0, const unsigned int z=0) {
  28582. T *ptrd = data(x,y,z,0);
  28583. const ulongT siz = (ulongT)_width*_height*_depth;
  28584. if (ten._height==2) {
  28585. *ptrd = (T)ten[0]; ptrd+=siz;
  28586. *ptrd = (T)ten[1]; ptrd+=siz;
  28587. *ptrd = (T)ten[3];
  28588. }
  28589. else {
  28590. *ptrd = (T)ten[0]; ptrd+=siz;
  28591. *ptrd = (T)ten[1]; ptrd+=siz;
  28592. *ptrd = (T)ten[2]; ptrd+=siz;
  28593. *ptrd = (T)ten[4]; ptrd+=siz;
  28594. *ptrd = (T)ten[5]; ptrd+=siz;
  28595. *ptrd = (T)ten[8];
  28596. }
  28597. return *this;
  28598. }
  28599. //! Resize image to become a diagonal matrix.
  28600. /**
  28601. \note Transform the image as a diagonal matrix so that each of its initial value becomes a diagonal coefficient.
  28602. **/
  28603. CImg<T>& diagonal() {
  28604. return get_diagonal().move_to(*this);
  28605. }
  28606. //! Resize image to become a diagonal matrix \newinstance.
  28607. CImg<T> get_diagonal() const {
  28608. if (is_empty()) return *this;
  28609. const unsigned int siz = (unsigned int)size();
  28610. CImg<T> res(siz,siz,1,1,0);
  28611. cimg_foroff(*this,off) res((unsigned int)off,(unsigned int)off) = (*this)[off];
  28612. return res;
  28613. }
  28614. //! Replace the image by an identity matrix.
  28615. /**
  28616. \note If the instance image is not square, it is resized to a square matrix using its maximum
  28617. dimension as a reference.
  28618. **/
  28619. CImg<T>& identity_matrix() {
  28620. return identity_matrix(std::max(_width,_height)).move_to(*this);
  28621. }
  28622. //! Replace the image by an identity matrix \newinstance.
  28623. CImg<T> get_identity_matrix() const {
  28624. return identity_matrix(std::max(_width,_height));
  28625. }
  28626. //! Fill image with a linear sequence of values.
  28627. /**
  28628. \param a0 Starting value of the sequence.
  28629. \param a1 Ending value of the sequence.
  28630. **/
  28631. CImg<T>& sequence(const T& a0, const T& a1) {
  28632. if (is_empty()) return *this;
  28633. const ulongT siz = size() - 1;
  28634. T* ptr = _data;
  28635. if (siz) {
  28636. const double delta = (double)a1 - (double)a0;
  28637. cimg_foroff(*this,l) *(ptr++) = (T)(a0 + delta*l/siz);
  28638. } else *ptr = a0;
  28639. return *this;
  28640. }
  28641. //! Fill image with a linear sequence of values \newinstance.
  28642. CImg<T> get_sequence(const T& a0, const T& a1) const {
  28643. return (+*this).sequence(a0,a1);
  28644. }
  28645. //! Transpose the image, viewed as a matrix.
  28646. /**
  28647. \note Equivalent to \code permute_axes("yxzc"); \endcode.
  28648. **/
  28649. CImg<T>& transpose() {
  28650. if (_width==1) { _width = _height; _height = 1; return *this; }
  28651. if (_height==1) { _height = _width; _width = 1; return *this; }
  28652. if (_width==_height) {
  28653. 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));
  28654. return *this;
  28655. }
  28656. return get_transpose().move_to(*this);
  28657. }
  28658. //! Transpose the image, viewed as a matrix \newinstance.
  28659. CImg<T> get_transpose() const {
  28660. return get_permute_axes("yxzc");
  28661. }
  28662. //! Compute the cross product between two \c 1x3 images, viewed as 3D vectors.
  28663. /**
  28664. \param img Image used as the second argument of the cross product.
  28665. \note The first argument of the cross product is \c *this.
  28666. **/
  28667. template<typename t>
  28668. CImg<T>& cross(const CImg<t>& img) {
  28669. if (_width!=1 || _height<3 || img._width!=1 || img._height<3)
  28670. throw CImgInstanceException(_cimg_instance
  28671. "cross(): Instance and/or specified image (%u,%u,%u,%u,%p) are not 3D vectors.",
  28672. cimg_instance,
  28673. img._width,img._height,img._depth,img._spectrum,img._data);
  28674. const T x = (*this)[0], y = (*this)[1], z = (*this)[2];
  28675. (*this)[0] = (T)(y*img[2] - z*img[1]);
  28676. (*this)[1] = (T)(z*img[0] - x*img[2]);
  28677. (*this)[2] = (T)(x*img[1] - y*img[0]);
  28678. return *this;
  28679. }
  28680. //! Compute the cross product between two \c 1x3 images, viewed as 3D vectors \newinstance.
  28681. template<typename t>
  28682. CImg<_cimg_Tt> get_cross(const CImg<t>& img) const {
  28683. return CImg<_cimg_Tt>(*this).cross(img);
  28684. }
  28685. //! Invert the instance image, viewed as a matrix.
  28686. /**
  28687. If the instance matrix is not square, the Moore-Penrose pseudo-inverse is computed instead.
  28688. \param use_LU Choose the inverting algorithm. Can be:
  28689. - \c true: LU solver (faster but sometimes less precise).
  28690. - \c false: SVD solver (more precise but slower).
  28691. \param lambda is used only in the Moore-Penrose pseudoinverse for estimating A^t.(A^t.A + lambda.Id)^-1.
  28692. **/
  28693. CImg<T>& invert(const bool use_LU=false, const float lambda=0) {
  28694. if (_depth!=1 || _spectrum!=1)
  28695. throw CImgInstanceException(_cimg_instance
  28696. "invert(): Instance is not a matrix.",
  28697. cimg_instance);
  28698. if (lambda<0)
  28699. throw CImgArgumentException(_cimg_instance
  28700. "invert(): Specified lambda (%g) should be >=0.",
  28701. cimg_instance);
  28702. if (_width!=_height) return get_invert(use_LU,lambda).move_to(*this); // Non-square matrix: Pseudoinverse
  28703. // Square matrix.
  28704. const double dete = _width>3?-1.:det();
  28705. if (dete!=0. && _width==2) {
  28706. const double
  28707. a = _data[0], c = _data[1],
  28708. b = _data[2], d = _data[3];
  28709. _data[0] = (T)(d/dete); _data[1] = (T)(-c/dete);
  28710. _data[2] = (T)(-b/dete); _data[3] = (T)(a/dete);
  28711. } else if (dete!=0. && _width==3) {
  28712. const double
  28713. a = _data[0], d = _data[1], g = _data[2],
  28714. b = _data[3], e = _data[4], h = _data[5],
  28715. c = _data[6], f = _data[7], i = _data[8];
  28716. _data[0] = (T)((i*e - f*h)/dete), _data[1] = (T)((g*f - i*d)/dete), _data[2] = (T)((d*h - g*e)/dete);
  28717. _data[3] = (T)((h*c - i*b)/dete), _data[4] = (T)((i*a - c*g)/dete), _data[5] = (T)((g*b - a*h)/dete);
  28718. _data[6] = (T)((b*f - e*c)/dete), _data[7] = (T)((d*c - a*f)/dete), _data[8] = (T)((a*e - d*b)/dete);
  28719. } else {
  28720. #ifdef cimg_use_lapack
  28721. int INFO = (int)use_LU, N = _width, LWORK = 4*N, *const IPIV = new int[N];
  28722. Tfloat
  28723. *const lapA = new Tfloat[N*N],
  28724. *const WORK = new Tfloat[LWORK];
  28725. cimg_forXY(*this,k,l) lapA[k*N + l] = (Tfloat)((*this)(k,l));
  28726. cimg::getrf(N,lapA,IPIV,INFO);
  28727. if (INFO)
  28728. cimg::warn(_cimg_instance
  28729. "invert(): LAPACK function dgetrf_() returned error code %d.",
  28730. cimg_instance,
  28731. INFO);
  28732. else {
  28733. cimg::getri(N,lapA,IPIV,WORK,LWORK,INFO);
  28734. if (INFO)
  28735. cimg::warn(_cimg_instance
  28736. "invert(): LAPACK function dgetri_() returned error code %d.",
  28737. cimg_instance,
  28738. INFO);
  28739. }
  28740. if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)(lapA[k*N + l]); else fill(0);
  28741. delete[] IPIV; delete[] lapA; delete[] WORK;
  28742. #else
  28743. if (use_LU) { // LU solver
  28744. CImg<Tfloat> A(*this,false), indx;
  28745. bool d;
  28746. A._LU(indx,d);
  28747. cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,16*16))
  28748. cimg_forX(*this,j) {
  28749. CImg<Tfloat> col(1,_width,1,1,0);
  28750. col(j) = 1;
  28751. col._solve(A,indx);
  28752. cimg_forX(*this,i) (*this)(j,i) = (T)col(i);
  28753. }
  28754. } else _get_invert_svd(false).move_to(*this); // SVD solver
  28755. #endif
  28756. }
  28757. return *this;
  28758. }
  28759. //! Invert the instance image, viewed as a matrix \newinstance.
  28760. CImg<Tfloat> get_invert(const bool use_LU=false, const float lambda=0) const {
  28761. if (_depth!=1 || _spectrum!=1)
  28762. throw CImgInstanceException(_cimg_instance
  28763. "invert(): Instance is not a matrix.",
  28764. cimg_instance);
  28765. if (lambda<0)
  28766. throw CImgArgumentException(_cimg_instance
  28767. "invert(): Specified lambda (%g) should be >=0.",
  28768. cimg_instance);
  28769. if (_width==_height) return CImg<Tfloat>(*this,false).invert(use_LU,lambda); // Square matrix
  28770. // Non-square matrix: Pseudoinverse
  28771. if (use_LU) {
  28772. if (_width<_height) { // under-solved system -> (A^t.A)^-1.A^t
  28773. CImg<Tfloat> AtA(width(),width());
  28774. cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,128*128))
  28775. cimg_forY(AtA,i)
  28776. for (int j = 0; j<=i; ++j) {
  28777. double res = 0;
  28778. cimg_forY(*this,k) res+=(*this)(i,k)*(*this)(j,k);
  28779. AtA(j,i) = AtA(i,j) = (Tfloat)res;
  28780. }
  28781. if (lambda!=0) cimg_forY(AtA,i) AtA(i,i)+=lambda;
  28782. AtA.invert(true);
  28783. return AtA*get_transpose();
  28784. } else { // over-resolved linear system -> A^t.(A.A^t)^-1
  28785. CImg<Tfloat> AAt(height(),height());
  28786. cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,128*128))
  28787. cimg_forY(AAt,i)
  28788. for (int j = 0; j<=i; ++j) {
  28789. double res = 0;
  28790. cimg_forX(*this,k) res+=(*this)(k,i)*(*this)(k,j);
  28791. AAt(j,i) = AAt(i,j) = (Tfloat)res;
  28792. }
  28793. if (lambda!=0) cimg_forY(AAt,i) AAt(i,i)+=lambda;
  28794. AAt.invert(true);
  28795. return get_transpose()*AAt;
  28796. }
  28797. }
  28798. return _get_invert_svd(lambda);
  28799. }
  28800. // SVD solver, both used for inverse and pseudoinverse.
  28801. CImg<Tfloat> _get_invert_svd(const float lambda) const {
  28802. CImg<Tfloat> U, S, V;
  28803. SVD(U,S,V,false);
  28804. const Tfloat epsilon = (sizeof(Tfloat)<=4?5.96e-8f:1.11e-16f)*std::max(_width,_height)*S.max();
  28805. cimg_forX(V,x) {
  28806. const Tfloat s = S(x), invs = lambda?1/(lambda + s):s>epsilon?1/s:0;
  28807. cimg_forY(V,y) V(x,y)*=invs;
  28808. }
  28809. return V*U.transpose();
  28810. }
  28811. //! Solve a system of linear equations.
  28812. /**
  28813. \param A Matrix of the linear system.
  28814. \param use_LU In case of non square system (least-square solution),
  28815. choose between SVD (\c false) or LU (\c true) solver.
  28816. LU solver is faster for large matrices, but numerically less stable.
  28817. \note Solve \c AX = B where \c B=*this.
  28818. **/
  28819. template<typename t>
  28820. CImg<T>& solve(const CImg<t>& A, const bool use_LU=false) {
  28821. if (_depth!=1 || _spectrum!=1 || _height!=A._height || A._depth!=1 || A._spectrum!=1)
  28822. throw CImgArgumentException(_cimg_instance
  28823. "solve(): Instance and specified matrix (%u,%u,%u,%u,%p) have "
  28824. "incompatible dimensions.",
  28825. cimg_instance,
  28826. A._width,A._height,A._depth,A._spectrum,A._data);
  28827. typedef _cimg_Ttfloat Ttfloat;
  28828. if (A.size()==1) return (*this)/=A[0];
  28829. if (A._width==2 && A._height==2 && _height==2) { // 2x2 linear system
  28830. const double a = (double)A[0], b = (double)A[1], c = (double)A[2], d = (double)A[3],
  28831. fa = std::fabs(a), fb = std::fabs(b), fc = std::fabs(c), fd = std::fabs(d),
  28832. det = a*d - b*c, fM = cimg::max(fa,fb,fc,fd);
  28833. if (fM==fa)
  28834. cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256))
  28835. cimg_forX(*this,k) {
  28836. const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), y = (a*v - c*u)/det;
  28837. (*this)(k,0) = (T)((u - b*y)/a); (*this)(k,1) = (T)y;
  28838. } else if (fM==fc)
  28839. cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256))
  28840. cimg_forX(*this,k) {
  28841. const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), y = (a*v - c*u)/det;
  28842. (*this)(k,0) = (T)((v - d*y)/c); (*this)(k,1) = (T)y;
  28843. } else if (fM==fb)
  28844. cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256))
  28845. cimg_forX(*this,k) {
  28846. const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), x = (d*u - b*v)/det;
  28847. (*this)(k,0) = (T)x; (*this)(k,1) = (T)((u - a*x)/b);
  28848. } else
  28849. cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=256))
  28850. cimg_forX(*this,k) {
  28851. const double u = (double)(*this)(k,0), v = (double)(*this)(k,1), x = (d*u - b*v)/det;
  28852. (*this)(k,0) = (T)x; (*this)(k,1) = (T)((v - c*x)/d);
  28853. }
  28854. return *this;
  28855. }
  28856. if (A._width==A._height) { // Square linear system
  28857. #ifdef cimg_use_lapack
  28858. char TRANS = 'N';
  28859. int INFO, N = _height, LWORK = 4*N, *const IPIV = new int[N];
  28860. Ttfloat
  28861. *const lapA = new Ttfloat[N*N],
  28862. *const lapB = new Ttfloat[N],
  28863. *const WORK = new Ttfloat[LWORK];
  28864. cimg_forXY(A,k,l) lapA[k*N + l] = (Ttfloat)(A(k,l));
  28865. cimg_forX(*this,i) {
  28866. cimg_forY(*this,j) lapB[j] = (Ttfloat)((*this)(i,j));
  28867. cimg::getrf(N,lapA,IPIV,INFO);
  28868. if (INFO)
  28869. cimg::warn(_cimg_instance
  28870. "solve(): LAPACK library function dgetrf_() returned error code %d.",
  28871. cimg_instance,
  28872. INFO);
  28873. else {
  28874. cimg::getrs(TRANS,N,lapA,IPIV,lapB,INFO);
  28875. if (INFO)
  28876. cimg::warn(_cimg_instance
  28877. "solve(): LAPACK library function dgetrs_() returned error code %d.",
  28878. cimg_instance,
  28879. INFO);
  28880. }
  28881. if (!INFO) cimg_forY(*this,j) (*this)(i,j) = (T)(lapB[j]); else cimg_forY(*this,j) (*this)(i,j) = (T)0;
  28882. }
  28883. delete[] IPIV; delete[] lapA; delete[] lapB; delete[] WORK;
  28884. #else
  28885. CImg<Ttfloat> lu(A,false);
  28886. CImg<Ttfloat> indx;
  28887. bool d;
  28888. lu._LU(indx,d);
  28889. CImg<T> res(_width,A._width);
  28890. cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width*_height,16))
  28891. cimg_forX(*this,i) res.draw_image(i,get_column(i)._solve(lu,indx));
  28892. res.move_to(*this);
  28893. #endif
  28894. } else { // Least-square solution for non-square systems
  28895. #ifdef cimg_use_lapack
  28896. char TRANS = 'N';
  28897. int INFO, N = A._width, M = A._height, LWORK = -1, LDA = M, LDB = M, NRHS = _width;
  28898. Ttfloat WORK_QUERY;
  28899. Ttfloat
  28900. * const lapA = new Ttfloat[M*N],
  28901. * const lapB = new Ttfloat[M*NRHS];
  28902. cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, &WORK_QUERY, LWORK, INFO);
  28903. LWORK = (int) WORK_QUERY;
  28904. Ttfloat *const WORK = new Ttfloat[LWORK];
  28905. cimg_forXY(A,k,l) lapA[k*M + l] = (Ttfloat)(A(k,l));
  28906. cimg_forXY(*this,k,l) lapB[k*M + l] = (Ttfloat)((*this)(k,l));
  28907. cimg::sgels(TRANS, M, N, NRHS, lapA, LDA, lapB, LDB, WORK, LWORK, INFO);
  28908. if (INFO != 0)
  28909. cimg::warn(_cimg_instance
  28910. "solve(): LAPACK library function sgels() returned error code %d.",
  28911. cimg_instance,
  28912. INFO);
  28913. assign(NRHS, N);
  28914. if (!INFO) cimg_forXY(*this,k,l) (*this)(k,l) = (T)lapB[k*M + l];
  28915. else (A.get_invert(use_LU)*(*this)).move_to(*this);
  28916. delete[] lapA; delete[] lapB; delete[] WORK;
  28917. #else
  28918. (A.get_invert(use_LU)*(*this)).move_to(*this);
  28919. #endif
  28920. }
  28921. return *this;
  28922. }
  28923. //! Solve a system of linear equations \newinstance.
  28924. template<typename t>
  28925. CImg<_cimg_Ttfloat> get_solve(const CImg<t>& A, const bool use_LU=false) const {
  28926. typedef _cimg_Ttfloat Ttfloat;
  28927. return CImg<Ttfloat>(*this,false).solve(A,use_LU);
  28928. }
  28929. template<typename t, typename ti>
  28930. CImg<T>& _solve(const CImg<t>& A, const CImg<ti>& indx) {
  28931. typedef _cimg_Ttfloat Ttfloat;
  28932. const int N = height();
  28933. int ii = -1;
  28934. Ttfloat sum;
  28935. for (int i = 0; i<N; ++i) {
  28936. const int ip = (int)indx[i];
  28937. sum = (*this)(ip);
  28938. (*this)(ip) = (*this)(i);
  28939. if (ii>=0) for (int j = ii; j<=i - 1; ++j) sum-=A(j,i)*(*this)(j);
  28940. else if (sum!=0) ii = i;
  28941. (*this)(i) = (T)sum;
  28942. }
  28943. for (int i = N - 1; i>=0; --i) {
  28944. sum = (*this)(i);
  28945. for (int j = i + 1; j<N; ++j) sum-=A(j,i)*(*this)(j);
  28946. (*this)(i) = (T)(sum/A(i,i));
  28947. }
  28948. return *this;
  28949. }
  28950. //! Solve a tridiagonal system of linear equations.
  28951. /**
  28952. \param A Coefficients of the tridiagonal system.
  28953. A is a tridiagonal matrix A = [ b0,c0,0,...; a1,b1,c1,0,... ; ... ; ...,0,aN,bN ],
  28954. stored as a 3 columns matrix
  28955. \note Solve AX=B where \c B=*this, using the Thomas algorithm.
  28956. **/
  28957. template<typename t>
  28958. CImg<T>& solve_tridiagonal(const CImg<t>& A) {
  28959. const unsigned int siz = (unsigned int)size();
  28960. if (A._width!=3 || A._height!=siz)
  28961. throw CImgArgumentException(_cimg_instance
  28962. "solve_tridiagonal(): Instance and tridiagonal matrix "
  28963. "(%u,%u,%u,%u,%p) have incompatible dimensions.",
  28964. cimg_instance,
  28965. A._width,A._height,A._depth,A._spectrum,A._data);
  28966. typedef _cimg_Ttfloat Ttfloat;
  28967. const Ttfloat epsilon = 1e-4f;
  28968. CImg<Ttfloat> B = A.get_column(1), V(*this,false);
  28969. for (int i = 1; i<(int)siz; ++i) {
  28970. const Ttfloat m = A(0,i)/(B[i - 1]?B[i - 1]:epsilon);
  28971. B[i] -= m*A(2,i - 1);
  28972. V[i] -= m*V[i - 1];
  28973. }
  28974. (*this)[siz - 1] = (T)(V[siz - 1]/(B[siz - 1]?B[siz - 1]:epsilon));
  28975. 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));
  28976. return *this;
  28977. }
  28978. //! Solve a tridiagonal system of linear equations \newinstance.
  28979. template<typename t>
  28980. CImg<_cimg_Ttfloat> get_solve_tridiagonal(const CImg<t>& A) const {
  28981. return CImg<_cimg_Ttfloat>(*this,false).solve_tridiagonal(A);
  28982. }
  28983. //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix.
  28984. /**
  28985. \param[out] val Vector of the estimated eigenvalues, in decreasing order.
  28986. \param[out] vec Matrix of the estimated eigenvectors, sorted by columns.
  28987. **/
  28988. template<typename t>
  28989. const CImg<T>& eigen(CImg<t>& val, CImg<t> &vec) const {
  28990. if (is_empty()) { val.assign(); vec.assign(); }
  28991. else {
  28992. if (_width!=_height || _depth>1 || _spectrum>1)
  28993. throw CImgInstanceException(_cimg_instance
  28994. "eigen(): Instance is not a square matrix.",
  28995. cimg_instance);
  28996. if (val.size()<(ulongT)_width) val.assign(1,_width);
  28997. if (vec.size()<(ulongT)_width*_width) vec.assign(_width,_width);
  28998. switch (_width) {
  28999. case 1 : { val[0] = (t)(*this)[0]; vec[0] = (t)1; } break;
  29000. case 2 : {
  29001. const double a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3], e = a + d;
  29002. double f = e*e - 4*(a*d - b*c);
  29003. if (f<0) cimg::warn(_cimg_instance
  29004. "eigen(): Complex eigenvalues found.",
  29005. cimg_instance);
  29006. f = std::sqrt(f);
  29007. const double
  29008. l1 = 0.5*(e - f),
  29009. l2 = 0.5*(e + f),
  29010. b2 = b*b,
  29011. norm1 = std::sqrt(cimg::sqr(l2 - a) + b2),
  29012. norm2 = std::sqrt(cimg::sqr(l1 - a) + b2);
  29013. val[0] = (t)l2;
  29014. val[1] = (t)l1;
  29015. 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; }
  29016. 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; }
  29017. } break;
  29018. default :
  29019. throw CImgInstanceException(_cimg_instance
  29020. "eigen(): Eigenvalues computation of general matrices is limited "
  29021. "to 2x2 matrices.",
  29022. cimg_instance);
  29023. }
  29024. }
  29025. return *this;
  29026. }
  29027. //! Compute eigenvalues and eigenvectors of the instance image, viewed as a matrix.
  29028. /**
  29029. \return A list of two images <tt>[val; vec]</tt>, whose meaning is similar as in eigen(CImg<t>&,CImg<t>&) const.
  29030. **/
  29031. CImgList<Tfloat> get_eigen() const {
  29032. CImgList<Tfloat> res(2);
  29033. eigen(res[0],res[1]);
  29034. return res;
  29035. }
  29036. //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix.
  29037. /**
  29038. \param[out] val Vector of the estimated eigenvalues, in decreasing order.
  29039. \param[out] vec Matrix of the estimated eigenvectors, sorted by columns.
  29040. **/
  29041. template<typename t>
  29042. const CImg<T>& symmetric_eigen(CImg<t>& val, CImg<t>& vec) const {
  29043. if (is_empty()) { val.assign(); vec.assign(); return *this; }
  29044. if (_width!=_height || _depth>1 || _spectrum>1)
  29045. throw CImgInstanceException(_cimg_instance
  29046. "eigen(): Instance is not a square matrix.",
  29047. cimg_instance);
  29048. val.assign(1,_width);
  29049. vec.assign(_width,_width);
  29050. if (_width==1) { val[0] = cimg::abs((*this)[0]); vec[0] = 1; return *this; }
  29051. if (_width==2) {
  29052. const double
  29053. a = (*this)[0], b = (*this)[1], c = (*this)[2], d = (*this)[3],
  29054. e = a + d, f = std::sqrt(std::max(e*e - 4*(a*d - b*c),0.0)),
  29055. l1 = 0.5*(e - f), l2 = 0.5*(e + f),
  29056. n = std::sqrt(cimg::sqr(l2 - a) + b*b);
  29057. val[0] = (t)l2;
  29058. val[1] = (t)l1;
  29059. if (n>0) { vec[0] = (t)(b/n); vec[2] = (t)((l2 - a)/n); } else { vec[0] = 1; vec[2] = 0; }
  29060. vec[1] = -vec[2];
  29061. vec[3] = vec[0];
  29062. return *this;
  29063. }
  29064. #ifdef cimg_use_lapack
  29065. char JOB = 'V', UPLO = 'U';
  29066. int N = _width, LWORK = 4*N, INFO;
  29067. Tfloat
  29068. *const lapA = new Tfloat[N*N],
  29069. *const lapW = new Tfloat[N],
  29070. *const WORK = new Tfloat[LWORK];
  29071. cimg_forXY(*this,k,l) lapA[k*N + l] = (Tfloat)((*this)(k,l));
  29072. cimg::syev(JOB,UPLO,N,lapA,lapW,WORK,LWORK,INFO);
  29073. if (INFO)
  29074. cimg::warn(_cimg_instance
  29075. "symmetric_eigen(): LAPACK library function dsyev_() returned error code %d.",
  29076. cimg_instance,
  29077. INFO);
  29078. if (!INFO) {
  29079. cimg_forY(val,i) val(i) = (T)lapW[N - 1 -i];
  29080. cimg_forXY(vec,k,l) vec(k,l) = (T)(lapA[(N - 1 - k)*N + l]);
  29081. } else { val.fill(0); vec.fill(0); }
  29082. delete[] lapA; delete[] lapW; delete[] WORK;
  29083. #else
  29084. CImg<t> V(_width,_width);
  29085. Tfloat M = 0, m = (Tfloat)min_max(M), maxabs = cimg::max((Tfloat)1,cimg::abs(m),cimg::abs(M));
  29086. (CImg<Tfloat>(*this,false)/=maxabs).SVD(vec,val,V,false);
  29087. if (maxabs!=1) val*=maxabs;
  29088. bool is_ambiguous = false;
  29089. float eig = 0;
  29090. cimg_forY(val,p) { // Check for ambiguous cases
  29091. if (val[p]>eig) eig = (float)val[p];
  29092. t scal = 0;
  29093. cimg_forY(vec,y) scal+=vec(p,y)*V(p,y);
  29094. if (cimg::abs(scal)<0.9f) is_ambiguous = true;
  29095. if (scal<0) val[p] = -val[p];
  29096. }
  29097. if (is_ambiguous) {
  29098. ++(eig*=2);
  29099. SVD(vec,val,V,false,40,eig);
  29100. val-=eig;
  29101. }
  29102. CImg<intT> permutations; // Sort eigenvalues in decreasing order
  29103. CImg<t> tmp(_width);
  29104. val.sort(permutations,false);
  29105. cimg_forY(vec,k) {
  29106. cimg_forY(permutations,y) tmp(y) = vec(permutations(y),k);
  29107. std::memcpy(vec.data(0,k),tmp._data,sizeof(t)*_width);
  29108. }
  29109. #endif
  29110. return *this;
  29111. }
  29112. //! Compute eigenvalues and eigenvectors of the instance image, viewed as a symmetric matrix.
  29113. /**
  29114. \return A list of two images <tt>[val; vec]</tt>, whose meaning are similar as in
  29115. symmetric_eigen(CImg<t>&,CImg<t>&) const.
  29116. **/
  29117. CImgList<Tfloat> get_symmetric_eigen() const {
  29118. CImgList<Tfloat> res(2);
  29119. symmetric_eigen(res[0],res[1]);
  29120. return res;
  29121. }
  29122. //! Sort pixel values and get sorting permutations.
  29123. /**
  29124. \param[out] permutations Permutation map used for the sorting.
  29125. \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way.
  29126. **/
  29127. template<typename t>
  29128. CImg<T>& sort(CImg<t>& permutations, const bool is_increasing=true) {
  29129. permutations.assign(_width,_height,_depth,_spectrum);
  29130. if (is_empty()) return *this;
  29131. cimg_foroff(permutations,off) permutations[off] = (t)off;
  29132. return _quicksort(0,size() - 1,permutations,is_increasing,true);
  29133. }
  29134. //! Sort pixel values and get sorting permutations \newinstance.
  29135. template<typename t>
  29136. CImg<T> get_sort(CImg<t>& permutations, const bool is_increasing=true) const {
  29137. return (+*this).sort(permutations,is_increasing);
  29138. }
  29139. //! Sort pixel values.
  29140. /**
  29141. \param is_increasing Tells if pixel values are sorted in an increasing (\c true) or decreasing (\c false) way.
  29142. \param axis Tells if the value sorting must be done along a specific axis. Can be:
  29143. - \c 0: All pixel values are sorted, independently on their initial position.
  29144. - \c 'x': Image columns are sorted, according to the first value in each column.
  29145. - \c 'y': Image rows are sorted, according to the first value in each row.
  29146. - \c 'z': Image slices are sorted, according to the first value in each slice.
  29147. - \c 'c': Image channels are sorted, according to the first value in each channel.
  29148. **/
  29149. CImg<T>& sort(const bool is_increasing=true, const char axis=0) {
  29150. if (is_empty()) return *this;
  29151. CImg<uintT> perm;
  29152. switch (cimg::lowercase(axis)) {
  29153. case 0 :
  29154. _quicksort(0,size() - 1,perm,is_increasing,false);
  29155. break;
  29156. case 'x' : {
  29157. perm.assign(_width);
  29158. get_crop(0,0,0,0,_width - 1,0,0,0).sort(perm,is_increasing);
  29159. CImg<T> img(*this,false);
  29160. cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(perm[x],y,z,c);
  29161. } break;
  29162. case 'y' : {
  29163. perm.assign(_height);
  29164. get_crop(0,0,0,0,0,_height - 1,0,0).sort(perm,is_increasing);
  29165. CImg<T> img(*this,false);
  29166. cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,perm[y],z,c);
  29167. } break;
  29168. case 'z' : {
  29169. perm.assign(_depth);
  29170. get_crop(0,0,0,0,0,0,_depth - 1,0).sort(perm,is_increasing);
  29171. CImg<T> img(*this,false);
  29172. cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,perm[z],c);
  29173. } break;
  29174. case 'c' : {
  29175. perm.assign(_spectrum);
  29176. get_crop(0,0,0,0,0,0,0,_spectrum - 1).sort(perm,is_increasing);
  29177. CImg<T> img(*this,false);
  29178. cimg_forXYZC(*this,x,y,z,c) (*this)(x,y,z,c) = img(x,y,z,perm[c]);
  29179. } break;
  29180. default :
  29181. throw CImgArgumentException(_cimg_instance
  29182. "sort(): Invalid specified axis '%c' "
  29183. "(should be { x | y | z | c }).",
  29184. cimg_instance,axis);
  29185. }
  29186. return *this;
  29187. }
  29188. //! Sort pixel values \newinstance.
  29189. CImg<T> get_sort(const bool is_increasing=true, const char axis=0) const {
  29190. return (+*this).sort(is_increasing,axis);
  29191. }
  29192. template<typename t>
  29193. CImg<T>& _quicksort(const long indm, const long indM, CImg<t>& permutations,
  29194. const bool is_increasing, const bool is_permutations) {
  29195. if (indm<indM) {
  29196. const long mid = (indm + indM)/2;
  29197. if (is_increasing) {
  29198. if ((*this)[indm]>(*this)[mid]) {
  29199. cimg::swap((*this)[indm],(*this)[mid]);
  29200. if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
  29201. }
  29202. if ((*this)[mid]>(*this)[indM]) {
  29203. cimg::swap((*this)[indM],(*this)[mid]);
  29204. if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
  29205. }
  29206. if ((*this)[indm]>(*this)[mid]) {
  29207. cimg::swap((*this)[indm],(*this)[mid]);
  29208. if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
  29209. }
  29210. } else {
  29211. if ((*this)[indm]<(*this)[mid]) {
  29212. cimg::swap((*this)[indm],(*this)[mid]);
  29213. if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
  29214. }
  29215. if ((*this)[mid]<(*this)[indM]) {
  29216. cimg::swap((*this)[indM],(*this)[mid]);
  29217. if (is_permutations) cimg::swap(permutations[indM],permutations[mid]);
  29218. }
  29219. if ((*this)[indm]<(*this)[mid]) {
  29220. cimg::swap((*this)[indm],(*this)[mid]);
  29221. if (is_permutations) cimg::swap(permutations[indm],permutations[mid]);
  29222. }
  29223. }
  29224. if (indM - indm>=3) {
  29225. const T pivot = (*this)[mid];
  29226. long i = indm, j = indM;
  29227. if (is_increasing) {
  29228. do {
  29229. while ((*this)[i]<pivot) ++i;
  29230. while ((*this)[j]>pivot) --j;
  29231. if (i<=j) {
  29232. if (is_permutations) cimg::swap(permutations[i],permutations[j]);
  29233. cimg::swap((*this)[i++],(*this)[j--]);
  29234. }
  29235. } while (i<=j);
  29236. } else {
  29237. do {
  29238. while ((*this)[i]>pivot) ++i;
  29239. while ((*this)[j]<pivot) --j;
  29240. if (i<=j) {
  29241. if (is_permutations) cimg::swap(permutations[i],permutations[j]);
  29242. cimg::swap((*this)[i++],(*this)[j--]);
  29243. }
  29244. } while (i<=j);
  29245. }
  29246. if (indm<j) _quicksort(indm,j,permutations,is_increasing,is_permutations);
  29247. if (i<indM) _quicksort(i,indM,permutations,is_increasing,is_permutations);
  29248. }
  29249. }
  29250. return *this;
  29251. }
  29252. //! Compute the SVD of the instance image, viewed as a general matrix.
  29253. /**
  29254. Compute the SVD decomposition \c *this=U*S*V' where \c U and \c V are orthogonal matrices
  29255. and \c S is a diagonal matrix. \c V' denotes the matrix transpose of \c V.
  29256. \param[out] U First matrix of the SVD product.
  29257. \param[out] S Coefficients of the second (diagonal) matrix of the SVD product.
  29258. These coefficients are stored as a vector.
  29259. \param[out] V Third matrix of the SVD product.
  29260. \param sorting Tells if the diagonal coefficients are sorted (in decreasing order).
  29261. \param max_iteration Maximum number of iterations considered for the algorithm convergence.
  29262. \param lambda Epsilon used for the algorithm convergence.
  29263. \note The instance matrix can be computed from \c U,\c S and \c V by
  29264. \code
  29265. const CImg<> A; // Input matrix (assumed to contain some values)
  29266. CImg<> U,S,V;
  29267. A.SVD(U,S,V)
  29268. \endcode
  29269. **/
  29270. template<typename t>
  29271. const CImg<T>& SVD(CImg<t>& U, CImg<t>& S, CImg<t>& V, const bool sorting=true,
  29272. const unsigned int max_iteration=40, const float lambda=0) const {
  29273. typedef _cimg_Ttfloat Ttfloat;
  29274. const Ttfloat epsilon = (Ttfloat)1e-25;
  29275. if (is_empty()) { U.assign(); S.assign(); V.assign(); }
  29276. else if (_depth!=1 || _spectrum!=1)
  29277. throw CImgInstanceException(_cimg_instance
  29278. "SVD(): Instance has invalid dimensions (depth or channels different from 1).",
  29279. cimg_instance);
  29280. else {
  29281. U = *this;
  29282. if (lambda!=0) {
  29283. const unsigned int delta = std::min(U._width,U._height);
  29284. for (unsigned int i = 0; i<delta; ++i) U(i,i) = (t)(U(i,i) + lambda);
  29285. }
  29286. if (S.size()<_width) S.assign(1,_width);
  29287. if (V._width<_width || V._height<_height) V.assign(_width,_width);
  29288. CImg<t> rv1(_width);
  29289. Ttfloat anorm = 0, c, f, g = 0, h, s, scale = 0;
  29290. int l = 0;
  29291. cimg_forX(U,i) {
  29292. l = i + 1;
  29293. rv1[i] = scale*g;
  29294. g = s = scale = 0;
  29295. if (i<height()) {
  29296. for (int k = i; k<height(); ++k) scale+=cimg::abs(U(i,k));
  29297. if (scale) {
  29298. for (int k = i; k<height(); ++k) {
  29299. U(i,k)/=scale;
  29300. s+=U(i,k)*U(i,k);
  29301. }
  29302. f = U(i,i);
  29303. g = (Ttfloat)((f>=0?-1:1)*std::sqrt(s));
  29304. h = f*g - s;
  29305. U(i,i) = f - g;
  29306. for (int j = l; j<width(); ++j) {
  29307. s = 0;
  29308. for (int k=i; k<height(); ++k) s+=U(i,k)*U(j,k);
  29309. f = s/h;
  29310. for (int k = i; k<height(); ++k) U(j,k)+=f*U(i,k);
  29311. }
  29312. for (int k = i; k<height(); ++k) U(i,k)*=scale;
  29313. }
  29314. }
  29315. S[i] = scale*g;
  29316. g = s = scale = 0;
  29317. if (i<height() && i!=width() - 1) {
  29318. for (int k = l; k<width(); ++k) scale+=cimg::abs(U(k,i));
  29319. if (scale) {
  29320. for (int k = l; k<width(); ++k) {
  29321. U(k,i)/=scale;
  29322. s+=U(k,i)*U(k,i);
  29323. }
  29324. f = U(l,i);
  29325. g = (Ttfloat)((f>=0?-1:1)*std::sqrt(s));
  29326. h = f*g - s;
  29327. U(l,i) = f - g;
  29328. for (int k = l; k<width(); ++k) rv1[k] = U(k,i)/h;
  29329. for (int j = l; j<height(); ++j) {
  29330. s = 0;
  29331. for (int k = l; k<width(); ++k) s+=U(k,j)*U(k,i);
  29332. for (int k = l; k<width(); ++k) U(k,j)+=s*rv1[k];
  29333. }
  29334. for (int k = l; k<width(); ++k) U(k,i)*=scale;
  29335. }
  29336. }
  29337. anorm = (Ttfloat)std::max((float)anorm,(float)(cimg::abs(S[i]) + cimg::abs(rv1[i])));
  29338. }
  29339. for (int i = width() - 1; i>=0; --i) {
  29340. if (i<width() - 1) {
  29341. if (g) {
  29342. for (int j = l; j<width(); ++j) V(i,j) =(U(j,i)/U(l,i))/g;
  29343. for (int j = l; j<width(); ++j) {
  29344. s = 0;
  29345. for (int k = l; k<width(); ++k) s+=U(k,i)*V(j,k);
  29346. for (int k = l; k<width(); ++k) V(j,k)+=s*V(i,k);
  29347. }
  29348. }
  29349. for (int j = l; j<width(); ++j) V(j,i) = V(i,j) = (t)0.;
  29350. }
  29351. V(i,i) = (t)1;
  29352. g = rv1[i];
  29353. l = i;
  29354. }
  29355. for (int i = std::min(width(),height()) - 1; i>=0; --i) {
  29356. l = i + 1;
  29357. g = S[i];
  29358. for (int j = l; j<width(); ++j) U(j,i) = 0;
  29359. if (g) {
  29360. g = 1/g;
  29361. for (int j = l; j<width(); ++j) {
  29362. s = 0;
  29363. for (int k = l; k<height(); ++k) s+=U(i,k)*U(j,k);
  29364. f = (s/U(i,i))*g;
  29365. for (int k = i; k<height(); ++k) U(j,k)+=f*U(i,k);
  29366. }
  29367. for (int j = i; j<height(); ++j) U(i,j)*= g;
  29368. } else for (int j = i; j<height(); ++j) U(i,j) = 0;
  29369. ++U(i,i);
  29370. }
  29371. for (int k = width() - 1; k>=0; --k) {
  29372. int nm = 0;
  29373. for (unsigned int its = 0; its<max_iteration; ++its) {
  29374. bool flag = true;
  29375. for (l = k; l>=1; --l) {
  29376. nm = l - 1;
  29377. if ((cimg::abs(rv1[l]) + anorm)==anorm) { flag = false; break; }
  29378. if ((cimg::abs(S[nm]) + anorm)==anorm) break;
  29379. }
  29380. if (flag) {
  29381. c = 0;
  29382. s = 1;
  29383. for (int i = l; i<=k; ++i) {
  29384. f = s*rv1[i];
  29385. rv1[i] = c*rv1[i];
  29386. if ((cimg::abs(f) + anorm)==anorm) break;
  29387. g = S[i];
  29388. h = cimg::hypot(f,g);
  29389. S[i] = h;
  29390. h = 1/h;
  29391. c = g*h;
  29392. s = -f*h;
  29393. cimg_forY(U,j) {
  29394. const t y = U(nm,j), z = U(i,j);
  29395. U(nm,j) = y*c + z*s;
  29396. U(i,j) = z*c - y*s;
  29397. }
  29398. }
  29399. }
  29400. const t z = S[k];
  29401. if (l==k) { if (z<0) { S[k] = -z; cimg_forX(U,j) V(k,j) = -V(k,j); } break; }
  29402. nm = k - 1;
  29403. t x = S[l], y = S[nm];
  29404. g = rv1[nm];
  29405. h = rv1[k];
  29406. f = ((y - z)*(y + z) + (g - h)*(g + h))/std::max(epsilon,(Ttfloat)2*h*y);
  29407. g = cimg::hypot(f,(Ttfloat)1);
  29408. f = ((x - z)*(x + z) + h*((y/(f + (f>=0?g:-g))) - h))/std::max(epsilon,(Ttfloat)x);
  29409. c = s = 1;
  29410. for (int j = l; j<=nm; ++j) {
  29411. const int i = j + 1;
  29412. g = rv1[i];
  29413. h = s*g;
  29414. g = c*g;
  29415. t y1 = S[i], z1 = cimg::hypot(f,h);
  29416. rv1[j] = z1;
  29417. c = f/std::max(epsilon,(Ttfloat)z1);
  29418. s = h/std::max(epsilon,(Ttfloat)z1);
  29419. f = x*c + g*s;
  29420. g = g*c - x*s;
  29421. h = y1*s;
  29422. y1*=c;
  29423. cimg_forX(U,jj) {
  29424. const t x2 = V(j,jj), z2 = V(i,jj);
  29425. V(j,jj) = x2*c + z2*s;
  29426. V(i,jj) = z2*c - x2*s;
  29427. }
  29428. z1 = cimg::hypot(f,h);
  29429. S[j] = z1;
  29430. if (z1) {
  29431. z1 = 1/std::max(epsilon,(Ttfloat)z1);
  29432. c = f*z1;
  29433. s = h*z1;
  29434. }
  29435. f = c*g + s*y1;
  29436. x = c*y1 - s*g;
  29437. cimg_forY(U,jj) {
  29438. const t y2 = U(j,jj), z2 = U(i,jj);
  29439. U(j,jj) = y2*c + z2*s;
  29440. U(i,jj) = z2*c - y2*s;
  29441. }
  29442. }
  29443. rv1[l] = 0;
  29444. rv1[k] = f;
  29445. S[k] = x;
  29446. }
  29447. }
  29448. if (sorting) {
  29449. CImg<intT> permutations;
  29450. CImg<t> tmp(_width);
  29451. S.sort(permutations,false);
  29452. cimg_forY(U,k) {
  29453. cimg_forY(permutations,y) tmp(y) = U(permutations(y),k);
  29454. std::memcpy(U.data(0,k),tmp._data,sizeof(t)*_width);
  29455. }
  29456. cimg_forY(V,k) {
  29457. cimg_forY(permutations,y) tmp(y) = V(permutations(y),k);
  29458. std::memcpy(V.data(0,k),tmp._data,sizeof(t)*_width);
  29459. }
  29460. }
  29461. }
  29462. return *this;
  29463. }
  29464. //! Compute the SVD of the instance image, viewed as a general matrix.
  29465. /**
  29466. \return A list of three images <tt>[U; S; V]</tt>, whose meaning is similar as in
  29467. SVD(CImg<t>&,CImg<t>&,CImg<t>&,bool,unsigned int,float) const.
  29468. **/
  29469. CImgList<Tfloat> get_SVD(const bool sorting=true,
  29470. const unsigned int max_iteration=40, const float lambda=0) const {
  29471. CImgList<Tfloat> res(3);
  29472. SVD(res[0],res[1],res[2],sorting,max_iteration,lambda);
  29473. return res;
  29474. }
  29475. // [internal] Compute the LU decomposition of a permuted matrix.
  29476. template<typename t>
  29477. CImg<T>& _LU(CImg<t>& indx, bool& d) {
  29478. const int N = width();
  29479. int imax = 0;
  29480. CImg<Tfloat> vv(N);
  29481. indx.assign(N);
  29482. d = true;
  29483. bool return0 = false;
  29484. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=512))
  29485. cimg_forX(*this,i) {
  29486. Tfloat vmax = 0;
  29487. cimg_forX(*this,j) {
  29488. const Tfloat tmp = cimg::abs((*this)(j,i));
  29489. if (tmp>vmax) vmax = tmp;
  29490. }
  29491. if (vmax==0) return0 = true; else vv[i] = 1/vmax;
  29492. }
  29493. if (return0) { indx.fill(0); return fill(0); }
  29494. cimg_forX(*this,j) {
  29495. for (int i = 0; i<j; ++i) {
  29496. Tfloat sum = (*this)(j,i);
  29497. for (int k = 0; k<i; ++k) sum-=(*this)(k,i)*(*this)(j,k);
  29498. (*this)(j,i) = (T)sum;
  29499. }
  29500. Tfloat vmax = 0;
  29501. for (int i = j; i<width(); ++i) {
  29502. Tfloat sum = (*this)(j,i);
  29503. for (int k = 0; k<j; ++k) sum-=(*this)(k,i)*(*this)(j,k);
  29504. (*this)(j,i) = (T)sum;
  29505. const Tfloat tmp = vv[i]*cimg::abs(sum);
  29506. if (tmp>=vmax) { vmax = tmp; imax = i; }
  29507. }
  29508. if (j!=imax) {
  29509. cimg_forX(*this,k) cimg::swap((*this)(k,imax),(*this)(k,j));
  29510. d = !d;
  29511. vv[imax] = vv[j];
  29512. }
  29513. indx[j] = (t)imax;
  29514. if ((*this)(j,j)==0) (*this)(j,j) = (T)1e-20;
  29515. if (j<N) {
  29516. const Tfloat tmp = 1/(Tfloat)(*this)(j,j);
  29517. for (int i = j + 1; i<N; ++i) (*this)(j,i) = (T)((*this)(j,i)*tmp);
  29518. }
  29519. }
  29520. return *this;
  29521. }
  29522. //! Compute the projection of the instance matrix onto the specified dictionary.
  29523. /**
  29524. Find the best matching projection of selected matrix onto the span of an over-complete dictionary D,
  29525. using the orthogonal projection or (opt. Orthogonal) Matching Pursuit algorithm.
  29526. Instance image must a 2D-matrix in which each column represent a signal to project.
  29527. \param dictionary A matrix in which each column is an element of the dictionary D.
  29528. \param method Tell what projection method is applied. It can be:
  29529. - 0 = orthogonal projection (default).
  29530. - 1 = matching pursuit.
  29531. - 2 = matching pursuit, with a single orthogonal projection step at the end.
  29532. - >=3 = orthogonal matching pursuit where an orthogonal projection step is performed
  29533. every 'method-2' iterations.
  29534. \param max_iter Sets the max number of iterations processed for each signal.
  29535. If set to '0' (default), 'max_iter' is set to the number of dictionary columns.
  29536. (only meaningful for matching pursuit and its variants).
  29537. \param max_residual Gives a stopping criterion on signal reconstruction accuracy.
  29538. (only meaningful for matching pursuit and its variants).
  29539. \return A matrix W whose columns correspond to the sparse weights of associated to each input matrix column.
  29540. Thus, the matrix product D*W is an approximation of the input matrix.
  29541. **/
  29542. template<typename t>
  29543. CImg<T>& project_matrix(const CImg<t>& dictionary, const unsigned int method=0,
  29544. const unsigned int max_iter=0, const double max_residual=1e-6) {
  29545. return get_project_matrix(dictionary,method,max_iter,max_residual).move_to(*this);
  29546. }
  29547. template<typename t>
  29548. CImg<Tfloat> get_project_matrix(const CImg<t>& dictionary, const unsigned int method=0,
  29549. const unsigned int max_iter=0, const double max_residual=1e-6) const {
  29550. if (_depth!=1 || _spectrum!=1)
  29551. throw CImgInstanceException(_cimg_instance
  29552. "project_matrix(): Instance image is not a matrix.",
  29553. cimg_instance);
  29554. if (dictionary._height!=_height || dictionary._depth!=1 || dictionary._spectrum!=1)
  29555. throw CImgArgumentException(_cimg_instance
  29556. "project_matrix(): Specified dictionary (%u,%u,%u,%u) has an invalid size.",
  29557. cimg_instance,
  29558. dictionary._width,dictionary._height,dictionary._depth,dictionary._spectrum);
  29559. if (!method) return get_solve(dictionary);
  29560. CImg<Tfloat> W(_width,dictionary._width,1,1,0);
  29561. // Compute dictionary norm and normalize it.
  29562. CImg<Tfloat> D(dictionary,false), Dnorm(D._width);
  29563. cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=2 && _width*_height>=32))
  29564. cimg_forX(Dnorm,d) {
  29565. Tfloat norm = 0;
  29566. cimg_forY(D,y) norm+=cimg::sqr(D(d,y));
  29567. Dnorm[d] = std::max((Tfloat)1e-8,std::sqrt(norm));
  29568. }
  29569. cimg_forXY(D,d,y) D(d,y)/=Dnorm[d];
  29570. // Matching pursuit.
  29571. const unsigned int proj_step = method<3?1:method - 2;
  29572. bool is_orthoproj = false;
  29573. cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=2 && _width*_height>=32))
  29574. cimg_forX(*this,x) {
  29575. CImg<Tfloat> S = get_column(x);
  29576. const CImg<Tfloat> S0 = method<2?CImg<Tfloat>():S;
  29577. Tfloat residual = S.magnitude(2)/S._height;
  29578. const unsigned int nmax = max_iter?max_iter:D._width;
  29579. for (unsigned int n = 0; n<nmax && residual>max_residual; ++n) {
  29580. // Find best matching column in D.
  29581. int dmax = 0;
  29582. Tfloat absdotmax = 0, dotmax = 0;
  29583. cimg_pragma_openmp(parallel for cimg_openmp_if(D._width>=2 && D._width*D._height>=32))
  29584. cimg_forX(D,d) {
  29585. Tfloat _dot = 0;
  29586. cimg_forY(D,y) _dot+=S[y]*D(d,y);
  29587. Tfloat absdot = cimg::abs(_dot);
  29588. cimg_pragma_openmp(critical(get_project_matrix)) {
  29589. if (absdot>absdotmax) {
  29590. absdotmax = absdot;
  29591. dotmax = _dot;
  29592. dmax = d;
  29593. }
  29594. }
  29595. }
  29596. if (!n || method<3 || n%proj_step) {
  29597. // Matching Pursuit: Subtract component to signal.
  29598. W(x,dmax)+=dotmax;
  29599. residual = 0;
  29600. cimg_forY(S,y) {
  29601. S[y]-=dotmax*D(dmax,y);
  29602. residual+=cimg::sqr(S[y]);
  29603. }
  29604. residual = std::sqrt(residual)/S._height;
  29605. is_orthoproj = false;
  29606. } else {
  29607. // Orthogonal Matching Pursuit: Orthogonal projection step.
  29608. W(x,dmax) = 1; // Used as a marker only.
  29609. unsigned int nbW = 0;
  29610. cimg_forY(W,d) if (W(x,d)) ++nbW;
  29611. CImg<Tfloat> sD(nbW,D._height);
  29612. CImg<uintT> inds(nbW);
  29613. int sd = 0;
  29614. cimg_forY(W,d) if (W(x,d)) {
  29615. cimg_forY(sD,y) sD(sd,y) = D(d,y);
  29616. inds[sd++] = d;
  29617. }
  29618. S0.get_solve(sD).move_to(sD); // sD is now a one-column vector of weights
  29619. // Recompute residual signal.
  29620. S = S0;
  29621. cimg_forY(sD,k) {
  29622. const Tfloat weight = sD[k];
  29623. const unsigned int ind = inds[k];
  29624. W(x,ind) = weight;
  29625. cimg_forY(S,y) S[y]-=weight*D(ind,y);
  29626. }
  29627. residual = S.magnitude(2)/S._height;
  29628. is_orthoproj = true;
  29629. }
  29630. }
  29631. // Perform last orthoprojection step if needed.
  29632. if (method>=2 && !is_orthoproj) {
  29633. unsigned int nbW = 0;
  29634. cimg_forY(W,d) if (W(x,d)) ++nbW;
  29635. if (nbW) { // Avoid degenerated case where 0 coefs are used
  29636. CImg<Tfloat> sD(nbW,D._height);
  29637. CImg<uintT> inds(nbW);
  29638. int sd = 0;
  29639. cimg_forY(W,d) if (W(x,d)) {
  29640. cimg_forY(sD,y) sD(sd,y) = D(d,y);
  29641. inds[sd++] = d;
  29642. }
  29643. S0.get_solve(sD).move_to(sD);
  29644. cimg_forY(sD,k) W(x,inds[k]) = sD[k];
  29645. }
  29646. }
  29647. }
  29648. // Normalize resulting coefficients according to initial (non-normalized) dictionary.
  29649. cimg_forXY(W,x,y) W(x,y)/=Dnorm[y];
  29650. return W;
  29651. }
  29652. //! Compute minimal path in a graph, using the Dijkstra algorithm.
  29653. /**
  29654. \param distance An object having operator()(unsigned int i, unsigned int j) which returns distance
  29655. between two nodes (i,j).
  29656. \param nb_nodes Number of graph nodes.
  29657. \param starting_node Index of the starting node.
  29658. \param ending_node Index of the ending node (set to ~0U to ignore ending node).
  29659. \param previous_node Array that gives the previous node index in the path to the starting node
  29660. (optional parameter).
  29661. \return Array of distances of each node to the starting node.
  29662. **/
  29663. template<typename tf, typename t>
  29664. static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
  29665. const unsigned int starting_node, const unsigned int ending_node,
  29666. CImg<t>& previous_node) {
  29667. if (starting_node>=nb_nodes)
  29668. throw CImgArgumentException("CImg<%s>::dijkstra(): Specified index of starting node %u is higher "
  29669. "than number of nodes %u.",
  29670. pixel_type(),starting_node,nb_nodes);
  29671. CImg<T> dist(1,nb_nodes,1,1,cimg::type<T>::max());
  29672. dist(starting_node) = 0;
  29673. previous_node.assign(1,nb_nodes,1,1,(t)-1);
  29674. previous_node(starting_node) = (t)starting_node;
  29675. CImg<uintT> Q(nb_nodes);
  29676. cimg_forX(Q,u) Q(u) = (unsigned int)u;
  29677. cimg::swap(Q(starting_node),Q(0));
  29678. unsigned int sizeQ = nb_nodes;
  29679. while (sizeQ) {
  29680. // Update neighbors from minimal vertex
  29681. const unsigned int umin = Q(0);
  29682. if (umin==ending_node) sizeQ = 0;
  29683. else {
  29684. const T dmin = dist(umin);
  29685. const T infty = cimg::type<T>::max();
  29686. for (unsigned int q = 1; q<sizeQ; ++q) {
  29687. const unsigned int v = Q(q);
  29688. const T d = (T)distance(v,umin);
  29689. if (d<infty) {
  29690. const T alt = dmin + d;
  29691. if (alt<dist(v)) {
  29692. dist(v) = alt;
  29693. previous_node(v) = (t)umin;
  29694. const T distpos = dist(Q(q));
  29695. for (unsigned int pos = q, par = 0; pos && distpos<dist(Q(par=(pos + 1)/2 - 1)); pos=par)
  29696. cimg::swap(Q(pos),Q(par));
  29697. }
  29698. }
  29699. }
  29700. // Remove minimal vertex from queue
  29701. Q(0) = Q(--sizeQ);
  29702. const T distpos = dist(Q(0));
  29703. for (unsigned int pos = 0, left = 0, right = 0;
  29704. ((right=2*(pos + 1),(left=right - 1))<sizeQ && distpos>dist(Q(left))) ||
  29705. (right<sizeQ && distpos>dist(Q(right)));) {
  29706. if (right<sizeQ) {
  29707. if (dist(Q(left))<dist(Q(right))) { cimg::swap(Q(pos),Q(left)); pos = left; }
  29708. else { cimg::swap(Q(pos),Q(right)); pos = right; }
  29709. } else { cimg::swap(Q(pos),Q(left)); pos = left; }
  29710. }
  29711. }
  29712. }
  29713. return dist;
  29714. }
  29715. //! Return minimal path in a graph, using the Dijkstra algorithm.
  29716. template<typename tf, typename t>
  29717. static CImg<T> dijkstra(const tf& distance, const unsigned int nb_nodes,
  29718. const unsigned int starting_node, const unsigned int ending_node=~0U) {
  29719. CImg<uintT> foo;
  29720. return dijkstra(distance,nb_nodes,starting_node,ending_node,foo);
  29721. }
  29722. //! Return minimal path in a graph, using the Dijkstra algorithm.
  29723. /**
  29724. \param starting_node Index of the starting node.
  29725. \param ending_node Index of the ending node.
  29726. \param previous_node Array that gives the previous node index in the path to the starting node
  29727. (optional parameter).
  29728. \return Array of distances of each node to the starting node.
  29729. \note image instance corresponds to the adjacency matrix of the graph.
  29730. **/
  29731. template<typename t>
  29732. CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node,
  29733. CImg<t>& previous_node) {
  29734. return get_dijkstra(starting_node,ending_node,previous_node).move_to(*this);
  29735. }
  29736. //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance.
  29737. template<typename t>
  29738. CImg<T> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node,
  29739. CImg<t>& previous_node) const {
  29740. if (_width!=_height || _depth!=1 || _spectrum!=1)
  29741. throw CImgInstanceException(_cimg_instance
  29742. "dijkstra(): Instance is not a graph adjacency matrix.",
  29743. cimg_instance);
  29744. return dijkstra(*this,_width,starting_node,ending_node,previous_node);
  29745. }
  29746. //! Return minimal path in a graph, using the Dijkstra algorithm.
  29747. CImg<T>& dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) {
  29748. return get_dijkstra(starting_node,ending_node).move_to(*this);
  29749. }
  29750. //! Return minimal path in a graph, using the Dijkstra algorithm \newinstance.
  29751. CImg<Tfloat> get_dijkstra(const unsigned int starting_node, const unsigned int ending_node=~0U) const {
  29752. CImg<uintT> foo;
  29753. return get_dijkstra(starting_node,ending_node,foo);
  29754. }
  29755. //! Return an image containing the character codes of specified string.
  29756. /**
  29757. \param str input C-string to encode as an image.
  29758. \param is_last_zero Tells if the ending \c '0' character appear in the resulting image.
  29759. \param is_shared Return result that shares its buffer with \p str.
  29760. **/
  29761. static CImg<T> string(const char *const str, const bool is_last_zero=true, const bool is_shared=false) {
  29762. if (!str) return CImg<T>();
  29763. return CImg<T>(str,(unsigned int)std::strlen(str) + (is_last_zero?1:0),1,1,1,is_shared);
  29764. }
  29765. //! Return a \c 1x1 image containing specified value.
  29766. /**
  29767. \param a0 First vector value.
  29768. **/
  29769. static CImg<T> row_vector(const T& a0) {
  29770. return vector(a0);
  29771. }
  29772. //! Return a \c 2x1 image containing specified values.
  29773. /**
  29774. \param a0 First vector value.
  29775. \param a1 Second vector value.
  29776. **/
  29777. static CImg<T> row_vector(const T& a0, const T& a1) {
  29778. CImg<T> r(2,1);
  29779. r[0] = a0; r[1] = a1;
  29780. return r;
  29781. }
  29782. //! Return a \c 3x1 image containing specified values.
  29783. /**
  29784. \param a0 First vector value.
  29785. \param a1 Second vector value.
  29786. \param a2 Third vector value.
  29787. **/
  29788. static CImg<T> row_vector(const T& a0, const T& a1, const T& a2) {
  29789. CImg<T> r(3,1);
  29790. r[0] = a0; r[1] = a1; r[2] = a2;
  29791. return r;
  29792. }
  29793. //! Return a \c 4x1 image containing specified values.
  29794. /**
  29795. \param a0 First vector value.
  29796. \param a1 Second vector value.
  29797. \param a2 Third vector value.
  29798. \param a3 Fourth vector value.
  29799. **/
  29800. static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3) {
  29801. CImg<T> r(4,1);
  29802. r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3;
  29803. return r;
  29804. }
  29805. //! Return a \c 5x1 image containing specified values.
  29806. static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
  29807. CImg<T> r(5,1);
  29808. r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4;
  29809. return r;
  29810. }
  29811. //! Return a \c 6x1 image containing specified values.
  29812. static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
  29813. CImg<T> r(6,1);
  29814. r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5;
  29815. return r;
  29816. }
  29817. //! Return a \c 7x1 image containing specified values.
  29818. static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
  29819. const T& a4, const T& a5, const T& a6) {
  29820. CImg<T> r(7,1);
  29821. r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6;
  29822. return r;
  29823. }
  29824. //! Return a \c 8x1 image containing specified values.
  29825. static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
  29826. const T& a4, const T& a5, const T& a6, const T& a7) {
  29827. CImg<T> r(8,1);
  29828. r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7;
  29829. return r;
  29830. }
  29831. //! Return a \c 9x1 image containing specified values.
  29832. static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
  29833. const T& a4, const T& a5, const T& a6, const T& a7,
  29834. const T& a8) {
  29835. CImg<T> r(9,1);
  29836. 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;
  29837. return r;
  29838. }
  29839. //! Return a \c 10x1 image containing specified values.
  29840. static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
  29841. const T& a4, const T& a5, const T& a6, const T& a7,
  29842. const T& a8, const T& a9) {
  29843. CImg<T> r(10,1);
  29844. 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;
  29845. return r;
  29846. }
  29847. //! Return a \c 11x1 image containing specified values.
  29848. static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
  29849. const T& a4, const T& a5, const T& a6, const T& a7,
  29850. const T& a8, const T& a9, const T& a10) {
  29851. CImg<T> r(11,1);
  29852. 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;
  29853. r[10] = a10;
  29854. return r;
  29855. }
  29856. //! Return a \c 12x1 image containing specified values.
  29857. static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
  29858. const T& a4, const T& a5, const T& a6, const T& a7,
  29859. const T& a8, const T& a9, const T& a10, const T& a11) {
  29860. CImg<T> r(12,1);
  29861. 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;
  29862. r[10] = a10; r[11] = a11;
  29863. return r;
  29864. }
  29865. //! Return a \c 13x1 image containing specified values.
  29866. static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
  29867. const T& a4, const T& a5, const T& a6, const T& a7,
  29868. const T& a8, const T& a9, const T& a10, const T& a11,
  29869. const T& a12) {
  29870. CImg<T> r(13,1);
  29871. 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;
  29872. r[10] = a10; r[11] = a11; r[12] = a12;
  29873. return r;
  29874. }
  29875. //! Return a \c 14x1 image containing specified values.
  29876. static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
  29877. const T& a4, const T& a5, const T& a6, const T& a7,
  29878. const T& a8, const T& a9, const T& a10, const T& a11,
  29879. const T& a12, const T& a13) {
  29880. CImg<T> r(14,1);
  29881. 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;
  29882. r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13;
  29883. return r;
  29884. }
  29885. //! Return a \c 15x1 image containing specified values.
  29886. static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
  29887. const T& a4, const T& a5, const T& a6, const T& a7,
  29888. const T& a8, const T& a9, const T& a10, const T& a11,
  29889. const T& a12, const T& a13, const T& a14) {
  29890. CImg<T> r(15,1);
  29891. 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;
  29892. r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14;
  29893. return r;
  29894. }
  29895. //! Return a \c 16x1 image containing specified values.
  29896. static CImg<T> row_vector(const T& a0, const T& a1, const T& a2, const T& a3,
  29897. const T& a4, const T& a5, const T& a6, const T& a7,
  29898. const T& a8, const T& a9, const T& a10, const T& a11,
  29899. const T& a12, const T& a13, const T& a14, const T& a15) {
  29900. CImg<T> r(16,1);
  29901. 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;
  29902. r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15;
  29903. return r;
  29904. }
  29905. //! Return a \c 1x1 image containing specified value.
  29906. /**
  29907. \param a0 First vector value.
  29908. **/
  29909. static CImg<T> vector(const T& a0) {
  29910. CImg<T> r(1,1);
  29911. r[0] = a0;
  29912. return r;
  29913. }
  29914. //! Return a \c 1x2 image containing specified values.
  29915. /**
  29916. \param a0 First vector value.
  29917. \param a1 Second vector value.
  29918. **/
  29919. static CImg<T> vector(const T& a0, const T& a1) {
  29920. CImg<T> r(1,2);
  29921. r[0] = a0; r[1] = a1;
  29922. return r;
  29923. }
  29924. //! Return a \c 1x3 image containing specified values.
  29925. /**
  29926. \param a0 First vector value.
  29927. \param a1 Second vector value.
  29928. \param a2 Third vector value.
  29929. **/
  29930. static CImg<T> vector(const T& a0, const T& a1, const T& a2) {
  29931. CImg<T> r(1,3);
  29932. r[0] = a0; r[1] = a1; r[2] = a2;
  29933. return r;
  29934. }
  29935. //! Return a \c 1x4 image containing specified values.
  29936. /**
  29937. \param a0 First vector value.
  29938. \param a1 Second vector value.
  29939. \param a2 Third vector value.
  29940. \param a3 Fourth vector value.
  29941. **/
  29942. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3) {
  29943. CImg<T> r(1,4);
  29944. r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3;
  29945. return r;
  29946. }
  29947. //! Return a \c 1x5 image containing specified values.
  29948. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
  29949. CImg<T> r(1,5);
  29950. r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4;
  29951. return r;
  29952. }
  29953. //! Return a \c 1x6 image containing specified values.
  29954. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
  29955. CImg<T> r(1,6);
  29956. r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5;
  29957. return r;
  29958. }
  29959. //! Return a \c 1x7 image containing specified values.
  29960. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  29961. const T& a4, const T& a5, const T& a6) {
  29962. CImg<T> r(1,7);
  29963. r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6;
  29964. return r;
  29965. }
  29966. //! Return a \c 1x8 image containing specified values.
  29967. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  29968. const T& a4, const T& a5, const T& a6, const T& a7) {
  29969. CImg<T> r(1,8);
  29970. r[0] = a0; r[1] = a1; r[2] = a2; r[3] = a3; r[4] = a4; r[5] = a5; r[6] = a6; r[7] = a7;
  29971. return r;
  29972. }
  29973. //! Return a \c 1x9 image containing specified values.
  29974. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  29975. const T& a4, const T& a5, const T& a6, const T& a7,
  29976. const T& a8) {
  29977. CImg<T> r(1,9);
  29978. 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;
  29979. return r;
  29980. }
  29981. //! Return a \c 1x10 image containing specified values.
  29982. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  29983. const T& a4, const T& a5, const T& a6, const T& a7,
  29984. const T& a8, const T& a9) {
  29985. CImg<T> r(1,10);
  29986. 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;
  29987. return r;
  29988. }
  29989. //! Return a \c 1x11 image containing specified values.
  29990. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  29991. const T& a4, const T& a5, const T& a6, const T& a7,
  29992. const T& a8, const T& a9, const T& a10) {
  29993. CImg<T> r(1,11);
  29994. 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;
  29995. r[10] = a10;
  29996. return r;
  29997. }
  29998. //! Return a \c 1x12 image containing specified values.
  29999. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  30000. const T& a4, const T& a5, const T& a6, const T& a7,
  30001. const T& a8, const T& a9, const T& a10, const T& a11) {
  30002. CImg<T> r(1,12);
  30003. 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;
  30004. r[10] = a10; r[11] = a11;
  30005. return r;
  30006. }
  30007. //! Return a \c 1x13 image containing specified values.
  30008. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  30009. const T& a4, const T& a5, const T& a6, const T& a7,
  30010. const T& a8, const T& a9, const T& a10, const T& a11,
  30011. const T& a12) {
  30012. CImg<T> r(1,13);
  30013. 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;
  30014. r[10] = a10; r[11] = a11; r[12] = a12;
  30015. return r;
  30016. }
  30017. //! Return a \c 1x14 image containing specified values.
  30018. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  30019. const T& a4, const T& a5, const T& a6, const T& a7,
  30020. const T& a8, const T& a9, const T& a10, const T& a11,
  30021. const T& a12, const T& a13) {
  30022. CImg<T> r(1,14);
  30023. 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;
  30024. r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13;
  30025. return r;
  30026. }
  30027. //! Return a \c 1x15 image containing specified values.
  30028. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  30029. const T& a4, const T& a5, const T& a6, const T& a7,
  30030. const T& a8, const T& a9, const T& a10, const T& a11,
  30031. const T& a12, const T& a13, const T& a14) {
  30032. CImg<T> r(1,15);
  30033. 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;
  30034. r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14;
  30035. return r;
  30036. }
  30037. //! Return a \c 1x16 image containing specified values.
  30038. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  30039. const T& a4, const T& a5, const T& a6, const T& a7,
  30040. const T& a8, const T& a9, const T& a10, const T& a11,
  30041. const T& a12, const T& a13, const T& a14, const T& a15) {
  30042. CImg<T> r(1,16);
  30043. 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;
  30044. r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15;
  30045. return r;
  30046. }
  30047. //! Return a \c 1x17 image containing specified values.
  30048. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  30049. const T& a4, const T& a5, const T& a6, const T& a7,
  30050. const T& a8, const T& a9, const T& a10, const T& a11,
  30051. const T& a12, const T& a13, const T& a14, const T& a15,
  30052. const T& a16) {
  30053. CImg<T> r(1,17);
  30054. 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;
  30055. r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15; r[16] = a16;
  30056. return r;
  30057. }
  30058. //! Return a \c 1x18 image containing specified values.
  30059. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  30060. const T& a4, const T& a5, const T& a6, const T& a7,
  30061. const T& a8, const T& a9, const T& a10, const T& a11,
  30062. const T& a12, const T& a13, const T& a14, const T& a15,
  30063. const T& a16, const T& a17) {
  30064. CImg<T> r(1,18);
  30065. 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;
  30066. r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15; r[16] = a16; r[17] = a17;
  30067. return r;
  30068. }
  30069. //! Return a \c 1x19 image containing specified values.
  30070. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  30071. const T& a4, const T& a5, const T& a6, const T& a7,
  30072. const T& a8, const T& a9, const T& a10, const T& a11,
  30073. const T& a12, const T& a13, const T& a14, const T& a15,
  30074. const T& a16, const T& a17, const T& a18) {
  30075. CImg<T> r(1,19);
  30076. 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;
  30077. r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15; r[16] = a16; r[17] = a17;
  30078. r[18] = a18;
  30079. return r;
  30080. }
  30081. //! Return a \c 1x20 image containing specified values.
  30082. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  30083. const T& a4, const T& a5, const T& a6, const T& a7,
  30084. const T& a8, const T& a9, const T& a10, const T& a11,
  30085. const T& a12, const T& a13, const T& a14, const T& a15,
  30086. const T& a16, const T& a17, const T& a18, const T& a19) {
  30087. CImg<T> r(1,20);
  30088. 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;
  30089. r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15; r[16] = a16; r[17] = a17;
  30090. r[18] = a18; r[19] = a19;
  30091. return r;
  30092. }
  30093. //! Return a \c 1x21 image containing specified values.
  30094. static CImg<T> vector(const T& a0, const T& a1, const T& a2, const T& a3,
  30095. const T& a4, const T& a5, const T& a6, const T& a7,
  30096. const T& a8, const T& a9, const T& a10, const T& a11,
  30097. const T& a12, const T& a13, const T& a14, const T& a15,
  30098. const T& a16, const T& a17, const T& a18, const T& a19,
  30099. const T& a20) {
  30100. CImg<T> r(1,21);
  30101. 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;
  30102. r[10] = a10; r[11] = a11; r[12] = a12; r[13] = a13; r[14] = a14; r[15] = a15; r[16] = a16; r[17] = a17;
  30103. r[18] = a18; r[19] = a19; r[20] = a20;
  30104. return r;
  30105. }
  30106. //! Return a 1x1 matrix containing specified coefficients.
  30107. /**
  30108. \param a0 First matrix value.
  30109. \note Equivalent to vector(const T&).
  30110. **/
  30111. static CImg<T> matrix(const T& a0) {
  30112. return vector(a0);
  30113. }
  30114. //! Return a 2x2 matrix containing specified coefficients.
  30115. /**
  30116. \param a0 First matrix value.
  30117. \param a1 Second matrix value.
  30118. \param a2 Third matrix value.
  30119. \param a3 Fourth matrix value.
  30120. **/
  30121. static CImg<T> matrix(const T& a0, const T& a1,
  30122. const T& a2, const T& a3) {
  30123. CImg<T> r(2,2); T *ptr = r._data;
  30124. *(ptr++) = a0; *(ptr++) = a1;
  30125. *(ptr++) = a2; *(ptr++) = a3;
  30126. return r;
  30127. }
  30128. //! Return a 3x3 matrix containing specified coefficients.
  30129. /**
  30130. \param a0 First matrix value.
  30131. \param a1 Second matrix value.
  30132. \param a2 Third matrix value.
  30133. \param a3 Fourth matrix value.
  30134. \param a4 Fifth matrix value.
  30135. \param a5 Sixth matrix value.
  30136. \param a6 Seventh matrix value.
  30137. \param a7 Eighth matrix value.
  30138. \param a8 Ninth matrix value.
  30139. **/
  30140. static CImg<T> matrix(const T& a0, const T& a1, const T& a2,
  30141. const T& a3, const T& a4, const T& a5,
  30142. const T& a6, const T& a7, const T& a8) {
  30143. CImg<T> r(3,3); T *ptr = r._data;
  30144. *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2;
  30145. *(ptr++) = a3; *(ptr++) = a4; *(ptr++) = a5;
  30146. *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8;
  30147. return r;
  30148. }
  30149. //! Return a 4x4 matrix containing specified coefficients.
  30150. static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3,
  30151. const T& a4, const T& a5, const T& a6, const T& a7,
  30152. const T& a8, const T& a9, const T& a10, const T& a11,
  30153. const T& a12, const T& a13, const T& a14, const T& a15) {
  30154. CImg<T> r(4,4); T *ptr = r._data;
  30155. *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3;
  30156. *(ptr++) = a4; *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7;
  30157. *(ptr++) = a8; *(ptr++) = a9; *(ptr++) = a10; *(ptr++) = a11;
  30158. *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14; *(ptr++) = a15;
  30159. return r;
  30160. }
  30161. //! Return a 5x5 matrix containing specified coefficients.
  30162. static CImg<T> matrix(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4,
  30163. const T& a5, const T& a6, const T& a7, const T& a8, const T& a9,
  30164. const T& a10, const T& a11, const T& a12, const T& a13, const T& a14,
  30165. const T& a15, const T& a16, const T& a17, const T& a18, const T& a19,
  30166. const T& a20, const T& a21, const T& a22, const T& a23, const T& a24) {
  30167. CImg<T> r(5,5); T *ptr = r._data;
  30168. *(ptr++) = a0; *(ptr++) = a1; *(ptr++) = a2; *(ptr++) = a3; *(ptr++) = a4;
  30169. *(ptr++) = a5; *(ptr++) = a6; *(ptr++) = a7; *(ptr++) = a8; *(ptr++) = a9;
  30170. *(ptr++) = a10; *(ptr++) = a11; *(ptr++) = a12; *(ptr++) = a13; *(ptr++) = a14;
  30171. *(ptr++) = a15; *(ptr++) = a16; *(ptr++) = a17; *(ptr++) = a18; *(ptr++) = a19;
  30172. *(ptr++) = a20; *(ptr++) = a21; *(ptr++) = a22; *(ptr++) = a23; *(ptr++) = a24;
  30173. return r;
  30174. }
  30175. //! Return a 1x1 symmetric matrix containing specified coefficients.
  30176. /**
  30177. \param a0 First matrix value.
  30178. \note Equivalent to vector(const T&).
  30179. **/
  30180. static CImg<T> tensor(const T& a0) {
  30181. return matrix(a0);
  30182. }
  30183. //! Return a 2x2 symmetric matrix tensor containing specified coefficients.
  30184. static CImg<T> tensor(const T& a0, const T& a1, const T& a2) {
  30185. return matrix(a0,a1,a1,a2);
  30186. }
  30187. //! Return a 3x3 symmetric matrix containing specified coefficients.
  30188. static CImg<T> tensor(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4, const T& a5) {
  30189. return matrix(a0,a1,a2,a1,a3,a4,a2,a4,a5);
  30190. }
  30191. //! Return a 1x1 diagonal matrix containing specified coefficients.
  30192. static CImg<T> diagonal(const T& a0) {
  30193. return matrix(a0);
  30194. }
  30195. //! Return a 2x2 diagonal matrix containing specified coefficients.
  30196. static CImg<T> diagonal(const T& a0, const T& a1) {
  30197. return matrix(a0,0,0,a1);
  30198. }
  30199. //! Return a 3x3 diagonal matrix containing specified coefficients.
  30200. static CImg<T> diagonal(const T& a0, const T& a1, const T& a2) {
  30201. return matrix(a0,0,0,0,a1,0,0,0,a2);
  30202. }
  30203. //! Return a 4x4 diagonal matrix containing specified coefficients.
  30204. static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3) {
  30205. return matrix(a0,0,0,0,0,a1,0,0,0,0,a2,0,0,0,0,a3);
  30206. }
  30207. //! Return a 5x5 diagonal matrix containing specified coefficients.
  30208. static CImg<T> diagonal(const T& a0, const T& a1, const T& a2, const T& a3, const T& a4) {
  30209. 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);
  30210. }
  30211. //! Return a NxN identity matrix.
  30212. /**
  30213. \param N Dimension of the matrix.
  30214. **/
  30215. static CImg<T> identity_matrix(const unsigned int N) {
  30216. CImg<T> res(N,N,1,1,0);
  30217. cimg_forX(res,x) res(x,x) = 1;
  30218. return res;
  30219. }
  30220. //! Return a N-numbered sequence vector from \p a0 to \p a1.
  30221. /**
  30222. \param N Size of the resulting vector.
  30223. \param a0 Starting value of the sequence.
  30224. \param a1 Ending value of the sequence.
  30225. **/
  30226. static CImg<T> sequence(const unsigned int N, const T& a0, const T& a1) {
  30227. if (N) return CImg<T>(1,N).sequence(a0,a1);
  30228. return CImg<T>();
  30229. }
  30230. //! Return a 3x3 rotation matrix from an { axis + angle } or a quaternion.
  30231. /**
  30232. \param x X-coordinate of the rotation axis, or first quaternion coordinate.
  30233. \param y Y-coordinate of the rotation axis, or second quaternion coordinate.
  30234. \param z Z-coordinate of the rotation axis, or third quaternion coordinate.
  30235. \param w Angle of the rotation axis (in degree), or fourth quaternion coordinate.
  30236. \param is_quaternion Tell is the four arguments denotes a set { axis + angle } or a quaternion (x,y,z,w).
  30237. **/
  30238. static CImg<T> rotation_matrix(const float x, const float y, const float z, const float w,
  30239. const bool is_quaternion=false) {
  30240. double X, Y, Z, W, N;
  30241. if (is_quaternion) {
  30242. N = std::sqrt((double)x*x + (double)y*y + (double)z*z + (double)w*w);
  30243. if (N>0) { X = x/N; Y = y/N; Z = z/N; W = w/N; }
  30244. else { X = Y = Z = 0; W = 1; }
  30245. 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),
  30246. (T)(2*X*W + 2*Y*Z),(T)(X*X - Y*Y + Z*Z - W*W),(T)(2*Z*W - 2*X*Y),
  30247. (T)(2*Y*W - 2*X*Z),(T)(2*X*Y + 2*Z*W),(T)(X*X - Y*Y - Z*Z + W*W));
  30248. }
  30249. N = cimg::hypot((double)x,(double)y,(double)z);
  30250. if (N>0) { X = x/N; Y = y/N; Z = z/N; }
  30251. else { X = Y = 0; Z = 1; }
  30252. const double ang = w*cimg::PI/180, c = std::cos(ang), omc = 1 - c, s = std::sin(ang);
  30253. return CImg<T>::matrix((T)(X*X*omc + c),(T)(X*Y*omc - Z*s),(T)(X*Z*omc + Y*s),
  30254. (T)(X*Y*omc + Z*s),(T)(Y*Y*omc + c),(T)(Y*Z*omc - X*s),
  30255. (T)(X*Z*omc - Y*s),(T)(Y*Z*omc + X*s),(T)(Z*Z*omc + c));
  30256. }
  30257. //@}
  30258. //-----------------------------------
  30259. //
  30260. //! \name Value Manipulation
  30261. //@{
  30262. //-----------------------------------
  30263. //! Fill all pixel values with specified value.
  30264. /**
  30265. \param val Fill value.
  30266. **/
  30267. CImg<T>& fill(const T& val) {
  30268. if (is_empty()) return *this;
  30269. if (val && sizeof(T)!=1) cimg_for(*this,ptrd,T) *ptrd = val;
  30270. else std::memset(_data,(int)(ulongT)val,sizeof(T)*size()); // Double cast to allow val to be (void*)
  30271. return *this;
  30272. }
  30273. //! Fill all pixel values with specified value \newinstance.
  30274. CImg<T> get_fill(const T& val) const {
  30275. return CImg<T>(_width,_height,_depth,_spectrum).fill(val);
  30276. }
  30277. //! Fill sequentially all pixel values with specified values.
  30278. /**
  30279. \param val0 First fill value.
  30280. \param val1 Second fill value.
  30281. **/
  30282. CImg<T>& fill(const T& val0, const T& val1) {
  30283. if (is_empty()) return *this;
  30284. T *ptrd, *ptre = end() - 1;
  30285. for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; }
  30286. if (ptrd!=ptre + 1) *(ptrd++) = val0;
  30287. return *this;
  30288. }
  30289. //! Fill sequentially all pixel values with specified values \newinstance.
  30290. CImg<T> get_fill(const T& val0, const T& val1) const {
  30291. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1);
  30292. }
  30293. //! Fill sequentially all pixel values with specified values \overloading.
  30294. CImg<T>& fill(const T& val0, const T& val1, const T& val2) {
  30295. if (is_empty()) return *this;
  30296. T *ptrd, *ptre = end() - 2;
  30297. for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; }
  30298. ptre+=2;
  30299. switch (ptre - ptrd) {
  30300. case 2 : *(--ptre) = val1; // Fallthrough
  30301. case 1 : *(--ptre) = val0; // Fallthrough
  30302. }
  30303. return *this;
  30304. }
  30305. //! Fill sequentially all pixel values with specified values \newinstance.
  30306. CImg<T> get_fill(const T& val0, const T& val1, const T& val2) const {
  30307. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2);
  30308. }
  30309. //! Fill sequentially all pixel values with specified values \overloading.
  30310. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3) {
  30311. if (is_empty()) return *this;
  30312. T *ptrd, *ptre = end() - 3;
  30313. for (ptrd = _data; ptrd<ptre; ) { *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; }
  30314. ptre+=3;
  30315. switch (ptre - ptrd) {
  30316. case 3 : *(--ptre) = val2; // Fallthrough
  30317. case 2 : *(--ptre) = val1; // Fallthrough
  30318. case 1 : *(--ptre) = val0; // Fallthrough
  30319. }
  30320. return *this;
  30321. }
  30322. //! Fill sequentially all pixel values with specified values \newinstance.
  30323. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3) const {
  30324. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3);
  30325. }
  30326. //! Fill sequentially all pixel values with specified values \overloading.
  30327. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) {
  30328. if (is_empty()) return *this;
  30329. T *ptrd, *ptre = end() - 4;
  30330. for (ptrd = _data; ptrd<ptre; ) {
  30331. *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
  30332. }
  30333. ptre+=4;
  30334. switch (ptre - ptrd) {
  30335. case 4 : *(--ptre) = val3; // Fallthrough
  30336. case 3 : *(--ptre) = val2; // Fallthrough
  30337. case 2 : *(--ptre) = val1; // Fallthrough
  30338. case 1 : *(--ptre) = val0; // Fallthrough
  30339. }
  30340. return *this;
  30341. }
  30342. //! Fill sequentially all pixel values with specified values \newinstance.
  30343. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4) const {
  30344. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4);
  30345. }
  30346. //! Fill sequentially all pixel values with specified values \overloading.
  30347. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) {
  30348. if (is_empty()) return *this;
  30349. T *ptrd, *ptre = end() - 5;
  30350. for (ptrd = _data; ptrd<ptre; ) {
  30351. *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
  30352. }
  30353. ptre+=5;
  30354. switch (ptre - ptrd) {
  30355. case 5 : *(--ptre) = val4; // Fallthrough
  30356. case 4 : *(--ptre) = val3; // Fallthrough
  30357. case 3 : *(--ptre) = val2; // Fallthrough
  30358. case 2 : *(--ptre) = val1; // Fallthrough
  30359. case 1 : *(--ptre) = val0; // Fallthrough
  30360. }
  30361. return *this;
  30362. }
  30363. //! Fill sequentially all pixel values with specified values \newinstance.
  30364. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5) const {
  30365. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5);
  30366. }
  30367. //! Fill sequentially all pixel values with specified values \overloading.
  30368. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  30369. const T& val6) {
  30370. if (is_empty()) return *this;
  30371. T *ptrd, *ptre = end() - 6;
  30372. for (ptrd = _data; ptrd<ptre; ) {
  30373. *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
  30374. *(ptrd++) = val6;
  30375. }
  30376. ptre+=6;
  30377. switch (ptre - ptrd) {
  30378. case 6 : *(--ptre) = val5; // Fallthrough
  30379. case 5 : *(--ptre) = val4; // Fallthrough
  30380. case 4 : *(--ptre) = val3; // Fallthrough
  30381. case 3 : *(--ptre) = val2; // Fallthrough
  30382. case 2 : *(--ptre) = val1; // Fallthrough
  30383. case 1 : *(--ptre) = val0; // Fallthrough
  30384. }
  30385. return *this;
  30386. }
  30387. //! Fill sequentially all pixel values with specified values \newinstance.
  30388. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  30389. const T& val6) const {
  30390. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6);
  30391. }
  30392. //! Fill sequentially all pixel values with specified values \overloading.
  30393. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  30394. const T& val6, const T& val7) {
  30395. if (is_empty()) return *this;
  30396. T *ptrd, *ptre = end() - 7;
  30397. for (ptrd = _data; ptrd<ptre; ) {
  30398. *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3;
  30399. *(ptrd++) = val4; *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7;
  30400. }
  30401. ptre+=7;
  30402. switch (ptre - ptrd) {
  30403. case 7 : *(--ptre) = val6; // Fallthrough
  30404. case 6 : *(--ptre) = val5; // Fallthrough
  30405. case 5 : *(--ptre) = val4; // Fallthrough
  30406. case 4 : *(--ptre) = val3; // Fallthrough
  30407. case 3 : *(--ptre) = val2; // Fallthrough
  30408. case 2 : *(--ptre) = val1; // Fallthrough
  30409. case 1 : *(--ptre) = val0; // Fallthrough
  30410. }
  30411. return *this;
  30412. }
  30413. //! Fill sequentially all pixel values with specified values \newinstance.
  30414. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  30415. const T& val6, const T& val7) const {
  30416. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7);
  30417. }
  30418. //! Fill sequentially all pixel values with specified values \overloading.
  30419. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  30420. const T& val6, const T& val7, const T& val8) {
  30421. if (is_empty()) return *this;
  30422. T *ptrd, *ptre = end() - 8;
  30423. for (ptrd = _data; ptrd<ptre; ) {
  30424. *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2;
  30425. *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
  30426. *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8;
  30427. }
  30428. ptre+=8;
  30429. switch (ptre - ptrd) {
  30430. case 8 : *(--ptre) = val7; // Fallthrough
  30431. case 7 : *(--ptre) = val6; // Fallthrough
  30432. case 6 : *(--ptre) = val5; // Fallthrough
  30433. case 5 : *(--ptre) = val4; // Fallthrough
  30434. case 4 : *(--ptre) = val3; // Fallthrough
  30435. case 3 : *(--ptre) = val2; // Fallthrough
  30436. case 2 : *(--ptre) = val1; // Fallthrough
  30437. case 1 : *(--ptre) = val0; // Fallthrough
  30438. }
  30439. return *this;
  30440. }
  30441. //! Fill sequentially all pixel values with specified values \newinstance.
  30442. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  30443. const T& val6, const T& val7, const T& val8) const {
  30444. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8);
  30445. }
  30446. //! Fill sequentially all pixel values with specified values \overloading.
  30447. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  30448. const T& val6, const T& val7, const T& val8, const T& val9) {
  30449. if (is_empty()) return *this;
  30450. T *ptrd, *ptre = end() - 9;
  30451. for (ptrd = _data; ptrd<ptre; ) {
  30452. *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
  30453. *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
  30454. }
  30455. ptre+=9;
  30456. switch (ptre - ptrd) {
  30457. case 9 : *(--ptre) = val8; // Fallthrough
  30458. case 8 : *(--ptre) = val7; // Fallthrough
  30459. case 7 : *(--ptre) = val6; // Fallthrough
  30460. case 6 : *(--ptre) = val5; // Fallthrough
  30461. case 5 : *(--ptre) = val4; // Fallthrough
  30462. case 4 : *(--ptre) = val3; // Fallthrough
  30463. case 3 : *(--ptre) = val2; // Fallthrough
  30464. case 2 : *(--ptre) = val1; // Fallthrough
  30465. case 1 : *(--ptre) = val0; // Fallthrough
  30466. }
  30467. return *this;
  30468. }
  30469. //! Fill sequentially all pixel values with specified values \newinstance.
  30470. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  30471. const T& val6, const T& val7, const T& val8, const T& val9) const {
  30472. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9);
  30473. }
  30474. //! Fill sequentially all pixel values with specified values \overloading.
  30475. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  30476. const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) {
  30477. if (is_empty()) return *this;
  30478. T *ptrd, *ptre = end() - 10;
  30479. for (ptrd = _data; ptrd<ptre; ) {
  30480. *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4;
  30481. *(ptrd++) = val5; *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9;
  30482. *(ptrd++) = val10;
  30483. }
  30484. ptre+=10;
  30485. switch (ptre - ptrd) {
  30486. case 10 : *(--ptre) = val9; // Fallthrough
  30487. case 9 : *(--ptre) = val8; // Fallthrough
  30488. case 8 : *(--ptre) = val7; // Fallthrough
  30489. case 7 : *(--ptre) = val6; // Fallthrough
  30490. case 6 : *(--ptre) = val5; // Fallthrough
  30491. case 5 : *(--ptre) = val4; // Fallthrough
  30492. case 4 : *(--ptre) = val3; // Fallthrough
  30493. case 3 : *(--ptre) = val2; // Fallthrough
  30494. case 2 : *(--ptre) = val1; // Fallthrough
  30495. case 1 : *(--ptre) = val0; // Fallthrough
  30496. }
  30497. return *this;
  30498. }
  30499. //! Fill sequentially all pixel values with specified values \newinstance.
  30500. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  30501. const T& val6, const T& val7, const T& val8, const T& val9, const T& val10) const {
  30502. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10);
  30503. }
  30504. //! Fill sequentially all pixel values with specified values \overloading.
  30505. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  30506. const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) {
  30507. if (is_empty()) return *this;
  30508. T *ptrd, *ptre = end() - 11;
  30509. for (ptrd = _data; ptrd<ptre; ) {
  30510. *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
  30511. *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
  30512. }
  30513. ptre+=11;
  30514. switch (ptre - ptrd) {
  30515. case 11 : *(--ptre) = val10; // Fallthrough
  30516. case 10 : *(--ptre) = val9; // Fallthrough
  30517. case 9 : *(--ptre) = val8; // Fallthrough
  30518. case 8 : *(--ptre) = val7; // Fallthrough
  30519. case 7 : *(--ptre) = val6; // Fallthrough
  30520. case 6 : *(--ptre) = val5; // Fallthrough
  30521. case 5 : *(--ptre) = val4; // Fallthrough
  30522. case 4 : *(--ptre) = val3; // Fallthrough
  30523. case 3 : *(--ptre) = val2; // Fallthrough
  30524. case 2 : *(--ptre) = val1; // Fallthrough
  30525. case 1 : *(--ptre) = val0; // Fallthrough
  30526. }
  30527. return *this;
  30528. }
  30529. //! Fill sequentially all pixel values with specified values \newinstance.
  30530. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  30531. const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11) const {
  30532. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
  30533. val11);
  30534. }
  30535. //! Fill sequentially all pixel values with specified values \overloading.
  30536. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  30537. const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
  30538. const T& val12) {
  30539. if (is_empty()) return *this;
  30540. T *ptrd, *ptre = end() - 12;
  30541. for (ptrd = _data; ptrd<ptre; ) {
  30542. *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
  30543. *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
  30544. *(ptrd++) = val12;
  30545. }
  30546. ptre+=12;
  30547. switch (ptre - ptrd) {
  30548. case 12 : *(--ptre) = val11; // Fallthrough
  30549. case 11 : *(--ptre) = val10; // Fallthrough
  30550. case 10 : *(--ptre) = val9; // Fallthrough
  30551. case 9 : *(--ptre) = val8; // Fallthrough
  30552. case 8 : *(--ptre) = val7; // Fallthrough
  30553. case 7 : *(--ptre) = val6; // Fallthrough
  30554. case 6 : *(--ptre) = val5; // Fallthrough
  30555. case 5 : *(--ptre) = val4; // Fallthrough
  30556. case 4 : *(--ptre) = val3; // Fallthrough
  30557. case 3 : *(--ptre) = val2; // Fallthrough
  30558. case 2 : *(--ptre) = val1; // Fallthrough
  30559. case 1 : *(--ptre) = val0; // Fallthrough
  30560. }
  30561. return *this;
  30562. }
  30563. //! Fill sequentially all pixel values with specified values \newinstance.
  30564. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  30565. const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
  30566. const T& val12) const {
  30567. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
  30568. val11,val12);
  30569. }
  30570. //! Fill sequentially all pixel values with specified values \overloading.
  30571. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  30572. const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
  30573. const T& val12, const T& val13) {
  30574. if (is_empty()) return *this;
  30575. T *ptrd, *ptre = end() - 13;
  30576. for (ptrd = _data; ptrd<ptre; ) {
  30577. *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
  30578. *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
  30579. *(ptrd++) = val12; *(ptrd++) = val13;
  30580. }
  30581. ptre+=13;
  30582. switch (ptre - ptrd) {
  30583. case 13 : *(--ptre) = val12; // Fallthrough
  30584. case 12 : *(--ptre) = val11; // Fallthrough
  30585. case 11 : *(--ptre) = val10; // Fallthrough
  30586. case 10 : *(--ptre) = val9; // Fallthrough
  30587. case 9 : *(--ptre) = val8; // Fallthrough
  30588. case 8 : *(--ptre) = val7; // Fallthrough
  30589. case 7 : *(--ptre) = val6; // Fallthrough
  30590. case 6 : *(--ptre) = val5; // Fallthrough
  30591. case 5 : *(--ptre) = val4; // Fallthrough
  30592. case 4 : *(--ptre) = val3; // Fallthrough
  30593. case 3 : *(--ptre) = val2; // Fallthrough
  30594. case 2 : *(--ptre) = val1; // Fallthrough
  30595. case 1 : *(--ptre) = val0; // Fallthrough
  30596. }
  30597. return *this;
  30598. }
  30599. //! Fill sequentially all pixel values with specified values \newinstance.
  30600. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  30601. const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
  30602. const T& val12, const T& val13) const {
  30603. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
  30604. val11,val12,val13);
  30605. }
  30606. //! Fill sequentially all pixel values with specified values \overloading.
  30607. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  30608. const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
  30609. const T& val12, const T& val13, const T& val14) {
  30610. if (is_empty()) return *this;
  30611. T *ptrd, *ptre = end() - 14;
  30612. for (ptrd = _data; ptrd<ptre; ) {
  30613. *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
  30614. *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
  30615. *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14;
  30616. }
  30617. ptre+=14;
  30618. switch (ptre - ptrd) {
  30619. case 14 : *(--ptre) = val13; // Fallthrough
  30620. case 13 : *(--ptre) = val12; // Fallthrough
  30621. case 12 : *(--ptre) = val11; // Fallthrough
  30622. case 11 : *(--ptre) = val10; // Fallthrough
  30623. case 10 : *(--ptre) = val9; // Fallthrough
  30624. case 9 : *(--ptre) = val8; // Fallthrough
  30625. case 8 : *(--ptre) = val7; // Fallthrough
  30626. case 7 : *(--ptre) = val6; // Fallthrough
  30627. case 6 : *(--ptre) = val5; // Fallthrough
  30628. case 5 : *(--ptre) = val4; // Fallthrough
  30629. case 4 : *(--ptre) = val3; // Fallthrough
  30630. case 3 : *(--ptre) = val2; // Fallthrough
  30631. case 2 : *(--ptre) = val1; // Fallthrough
  30632. case 1 : *(--ptre) = val0; // Fallthrough
  30633. }
  30634. return *this;
  30635. }
  30636. //! Fill sequentially all pixel values with specified values \newinstance.
  30637. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  30638. const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
  30639. const T& val12, const T& val13, const T& val14) const {
  30640. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
  30641. val11,val12,val13,val14);
  30642. }
  30643. //! Fill sequentially all pixel values with specified values \overloading.
  30644. CImg<T>& fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  30645. const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
  30646. const T& val12, const T& val13, const T& val14, const T& val15) {
  30647. if (is_empty()) return *this;
  30648. T *ptrd, *ptre = end() - 15;
  30649. for (ptrd = _data; ptrd<ptre; ) {
  30650. *(ptrd++) = val0; *(ptrd++) = val1; *(ptrd++) = val2; *(ptrd++) = val3; *(ptrd++) = val4; *(ptrd++) = val5;
  30651. *(ptrd++) = val6; *(ptrd++) = val7; *(ptrd++) = val8; *(ptrd++) = val9; *(ptrd++) = val10; *(ptrd++) = val11;
  30652. *(ptrd++) = val12; *(ptrd++) = val13; *(ptrd++) = val14; *(ptrd++) = val15;
  30653. }
  30654. ptre+=15;
  30655. switch (ptre - ptrd) {
  30656. case 15 : *(--ptre) = val14; // Fallthrough
  30657. case 14 : *(--ptre) = val13; // Fallthrough
  30658. case 13 : *(--ptre) = val12; // Fallthrough
  30659. case 12 : *(--ptre) = val11; // Fallthrough
  30660. case 11 : *(--ptre) = val10; // Fallthrough
  30661. case 10 : *(--ptre) = val9; // Fallthrough
  30662. case 9 : *(--ptre) = val8; // Fallthrough
  30663. case 8 : *(--ptre) = val7; // Fallthrough
  30664. case 7 : *(--ptre) = val6; // Fallthrough
  30665. case 6 : *(--ptre) = val5; // Fallthrough
  30666. case 5 : *(--ptre) = val4; // Fallthrough
  30667. case 4 : *(--ptre) = val3; // Fallthrough
  30668. case 3 : *(--ptre) = val2; // Fallthrough
  30669. case 2 : *(--ptre) = val1; // Fallthrough
  30670. case 1 : *(--ptre) = val0; // Fallthrough
  30671. }
  30672. return *this;
  30673. }
  30674. //! Fill sequentially all pixel values with specified values \newinstance.
  30675. CImg<T> get_fill(const T& val0, const T& val1, const T& val2, const T& val3, const T& val4, const T& val5,
  30676. const T& val6, const T& val7, const T& val8, const T& val9, const T& val10, const T& val11,
  30677. const T& val12, const T& val13, const T& val14, const T& val15) const {
  30678. return CImg<T>(_width,_height,_depth,_spectrum).fill(val0,val1,val2,val3,val4,val5,val6,val7,val8,val9,val10,
  30679. val11,val12,val13,val14,val15);
  30680. }
  30681. //! Fill sequentially pixel values according to a given expression.
  30682. /**
  30683. \param expression C-string describing a math formula, or a sequence of values.
  30684. \param repeat_values In case a list of values is provided, tells if this list must be repeated for the filling.
  30685. \param allow_formula Tells that mathematical formulas are authorized for the filling.
  30686. \param list_images In case of a mathematical expression, attach a list of images to the specified expression.
  30687. **/
  30688. CImg<T>& fill(const char *const expression, const bool repeat_values, const bool allow_formula=true,
  30689. CImgList<T> *const list_images=0) {
  30690. return _fill(expression,repeat_values,allow_formula?3:1,list_images,"fill",0,0);
  30691. }
  30692. // bits of 'mode' can enable/disable these properties:
  30693. // . 1 = Allow list of values.
  30694. // . 2 = Allow formula.
  30695. // . 4 = Evaluate but does not fill image values.
  30696. CImg<T>& _fill(const char *const expression, const bool repeat_values, const unsigned int mode,
  30697. CImgList<T> *const list_images, const char *const calling_function,
  30698. const CImg<T> *provides_copy, CImg<doubleT> *const result_end) {
  30699. if (is_empty() || !expression || !*expression) return *this;
  30700. const unsigned int excmode = cimg::exception_mode();
  30701. cimg::exception_mode(0);
  30702. CImg<charT> is_error_expr;
  30703. bool is_done = false, is_value_sequence = false;
  30704. cimg_abort_init;
  30705. if (result_end) result_end->assign();
  30706. // Detect value sequence.
  30707. if (mode&1) {
  30708. double value;
  30709. char sep;
  30710. const int err = cimg_sscanf(expression,"%lf %c",&value,&sep);
  30711. if (err==1 || (err==2 && sep==',')) {
  30712. if (err==1) { if (mode&4) return *this; return fill((T)value); }
  30713. else is_value_sequence = true;
  30714. }
  30715. }
  30716. // Try to fill values according to a formula.
  30717. if (mode&2 && !is_value_sequence) {
  30718. _cimg_abort_init_openmp;
  30719. try {
  30720. CImg<T> base = provides_copy?provides_copy->get_shared():get_shared();
  30721. _cimg_math_parser mp(expression + (*expression=='>' || *expression=='<' || *expression=='+' ||
  30722. *expression=='*' || *expression==':'),
  30723. calling_function,base,this,list_images,true);
  30724. if (!provides_copy && expression &&
  30725. *expression!='>' && *expression!='<' && *expression!=':' &&
  30726. mp.need_input_copy)
  30727. base.assign().assign(*this,false); // Needs input copy
  30728. // Determine M2, smallest image dimension (used as axis for the most inner loop in parallelized iterations).
  30729. // M1 is the total number of parallelized iterations.
  30730. unsigned int M1 = 0, M2 = 0;
  30731. cimg::unused(M1,M2);
  30732. if (mp.result_dim) {
  30733. M2 = cimg::min(_width,_height,_depth);
  30734. M1 = M2==_width?_height*_depth:M2==_height?_width*_depth:_width*_height;
  30735. } else {
  30736. M2 = cimg::min(_width,_height,_depth,_spectrum);
  30737. M1 = M2==_width?_height*_depth*_spectrum:M2==_height?_width*_depth*_spectrum:
  30738. M2==_depth?_width*_height*_spectrum:_width*_height*_depth;
  30739. }
  30740. bool is_parallelizable = false;
  30741. cimg_openmp_if(*expression=='*' || *expression==':' || (mp.is_parallelizable &&
  30742. (M2>=2 || M1>=4096) && M1*M2>=32))
  30743. is_parallelizable = true;
  30744. if (mp.result_dim) { // Vector-valued expression
  30745. const unsigned int N = std::min(mp.result_dim,_spectrum);
  30746. const ulongT whd = (ulongT)_width*_height*_depth;
  30747. T *ptrd = *expression=='<'?_data + _width*_height*_depth - 1:_data;
  30748. if (*expression=='<') {
  30749. CImg<doubleT> res(1,mp.result_dim);
  30750. mp.begin_t();
  30751. cimg_rofYZ(*this,y,z) {
  30752. cimg_abort_test;
  30753. if (mode&4) cimg_rofX(*this,x) mp(x,y,z,0);
  30754. else cimg_rofX(*this,x) {
  30755. mp(x,y,z,0,res._data);
  30756. const double *ptrs = res._data;
  30757. T *_ptrd = ptrd--; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; }
  30758. }
  30759. }
  30760. mp.end_t();
  30761. } else if (*expression=='>' || *expression=='+' || !is_parallelizable) {
  30762. CImg<doubleT> res(1,mp.result_dim);
  30763. mp.begin_t();
  30764. cimg_forYZ(*this,y,z) {
  30765. cimg_abort_test;
  30766. if (mode&4) cimg_forX(*this,x) mp(x,y,z,0);
  30767. else cimg_forX(*this,x) {
  30768. mp(x,y,z,0,res._data);
  30769. const double *ptrs = res._data;
  30770. T *_ptrd = ptrd++; for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; }
  30771. }
  30772. }
  30773. mp.end_t();
  30774. } else {
  30775. #if cimg_use_openmp!=0
  30776. cimg_pragma_openmp(parallel)
  30777. {
  30778. _cimg_math_parser
  30779. *const _mp = omp_get_thread_num()?new _cimg_math_parser(mp):&mp,
  30780. &lmp = *_mp;
  30781. lmp.is_fill = true;
  30782. cimg_pragma_openmp(barrier)
  30783. lmp.begin_t();
  30784. #define _cimg_fill_openmp_vector(_YZ,_y,_z,_X,_x,_sx,_sy,_sz,_off) \
  30785. cimg_pragma_openmp(for cimg_openmp_collapse(2)) \
  30786. cimg_for##_YZ(*this,_y,_z) _cimg_abort_try_openmp { \
  30787. cimg_abort_test; \
  30788. if (mode&4) cimg_for##_X(*this,_x) lmp(x,y,z,0); \
  30789. else { \
  30790. CImg<doubleT> res(1,lmp.result_dim); \
  30791. T *__ptrd = data(_sx,_sy,_sz,0); \
  30792. const ulongT off = (ulongT)_off; \
  30793. cimg_for##_X(*this,_x) { \
  30794. lmp(x,y,z,0,res._data); \
  30795. const double *ptrs = res._data; \
  30796. T *_ptrd = __ptrd; \
  30797. for (unsigned int n = N; n>0; --n) { *_ptrd = (T)(*ptrs++); _ptrd+=whd; } \
  30798. __ptrd+=off; \
  30799. } \
  30800. } \
  30801. } _cimg_abort_catch_openmp _cimg_abort_catch_fill_openmp
  30802. if (M2==_width) { _cimg_fill_openmp_vector(YZ,y,z,X,x,0,y,z,1) }
  30803. else if (M2==_height) { _cimg_fill_openmp_vector(XZ,x,z,Y,y,x,0,z,_width) }
  30804. else { _cimg_fill_openmp_vector(XY,x,y,Z,z,x,y,0,_width*_height) }
  30805. lmp.end_t();
  30806. cimg_pragma_openmp(barrier) cimg_pragma_openmp(critical) { lmp.merge(mp); }
  30807. if (&lmp!=&mp) delete &lmp;
  30808. }
  30809. #endif
  30810. }
  30811. } else { // Scalar-valued expression
  30812. T *ptrd = *expression=='<'?end() - 1:_data;
  30813. if (*expression=='<') {
  30814. mp.begin_t();
  30815. if (mode&4) cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) mp(x,y,z,c); }
  30816. else cimg_rofYZC(*this,y,z,c) { cimg_abort_test; cimg_rofX(*this,x) *(ptrd--) = (T)mp(x,y,z,c); }
  30817. mp.end_t();
  30818. } else if (*expression=='>' || *expression=='+' || !is_parallelizable) {
  30819. mp.begin_t();
  30820. if (mode&4) cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) mp(x,y,z,c); }
  30821. else cimg_forYZC(*this,y,z,c) { cimg_abort_test; cimg_forX(*this,x) *(ptrd++) = (T)mp(x,y,z,c); }
  30822. mp.end_t();
  30823. } else {
  30824. #if cimg_use_openmp!=0
  30825. cimg_pragma_openmp(parallel)
  30826. {
  30827. _cimg_math_parser
  30828. *const _mp = omp_get_thread_num()?new _cimg_math_parser(mp):&mp,
  30829. &lmp = *_mp;
  30830. lmp.is_fill = true;
  30831. cimg_pragma_openmp(barrier)
  30832. lmp.begin_t();
  30833. #define _cimg_fill_openmp_scalar(_YZC,_y,_z,_c,_X,_x,_sx,_sy,_sz,_sc,_off) \
  30834. cimg_pragma_openmp(for cimg_openmp_collapse(3)) \
  30835. cimg_for##_YZC(*this,_y,_z,_c) _cimg_abort_try_openmp { \
  30836. cimg_abort_test; \
  30837. if (mode&4) cimg_for##_X(*this,_x) lmp(x,y,z,c); \
  30838. else { \
  30839. T *_ptrd = data(_sx,_sy,_sz,_sc); \
  30840. const ulongT off = (ulongT)_off; \
  30841. cimg_for##_X(*this,_x) { *_ptrd = (T)lmp(x,y,z,c); _ptrd+=off; } \
  30842. } \
  30843. } _cimg_abort_catch_openmp _cimg_abort_catch_fill_openmp
  30844. if (M2==_width) { _cimg_fill_openmp_scalar(YZC,y,z,c,X,x,0,y,z,c,1) }
  30845. else if (M2==_height) { _cimg_fill_openmp_scalar(XZC,x,z,c,Y,y,x,0,z,c,_width) }
  30846. else if (M2==_depth) { _cimg_fill_openmp_scalar(XYC,x,y,c,Z,z,x,y,0,c,_width*_height) }
  30847. else { _cimg_fill_openmp_scalar(XYZ,x,y,z,C,c,x,y,z,0,_width*_height*_depth) }
  30848. lmp.end_t();
  30849. cimg_pragma_openmp(barrier) cimg_pragma_openmp(critical) { lmp.merge(mp); }
  30850. if (&lmp!=&mp) delete &lmp;
  30851. }
  30852. #endif
  30853. }
  30854. }
  30855. mp.end();
  30856. if (result_end && mp.result_end) // Transfer result of the end() block if requested.
  30857. result_end->assign(mp.result_end + (mp.result_end_dim?1:0),std::max(1U,mp.result_end_dim));
  30858. is_done = true;
  30859. } catch (CImgException& e) { CImg<charT>::string(e._message).move_to(is_error_expr); }
  30860. }
  30861. // Try to fill values according to a value sequence.
  30862. if (!is_done && mode&1) is_done = !_fill_from_values(expression,repeat_values);
  30863. if (!is_done) {
  30864. cimg::exception_mode(excmode);
  30865. if (is_error_expr) throw CImgArgumentException(_cimg_instance
  30866. "%s",
  30867. cimg_instance,is_error_expr._data);
  30868. else throw CImgArgumentException(_cimg_instance
  30869. "%s(): Invalid sequence of filling values '%s'.",
  30870. cimg_instance,calling_function,expression);
  30871. }
  30872. cimg::exception_mode(excmode);
  30873. cimg_abort_test;
  30874. return *this;
  30875. }
  30876. //! Fill sequentially pixel values according to a given expression \newinstance.
  30877. CImg<T> get_fill(const char *const expression, const bool repeat_values, const bool allow_formula=true,
  30878. CImgList<T> *const list_images=0) const {
  30879. return (+*this).fill(expression,repeat_values,allow_formula?1:0,list_images);
  30880. }
  30881. //! Fill sequentially pixel values according to a value sequence, given as a string.
  30882. /**
  30883. \param values C-string describing a sequence of values.
  30884. \param repeat_values Tells if this sequence must be repeated when filling.
  30885. **/
  30886. CImg<T>& fill_from_values(const char *const values, const bool repeat_values) {
  30887. if (_fill_from_values(values,repeat_values))
  30888. throw CImgArgumentException(_cimg_instance
  30889. "Invalid sequence of filling values '%s'.",
  30890. cimg_instance,values);
  30891. return *this;
  30892. }
  30893. //! Fill sequentially pixel values according to a value sequence, given as a string \newinstance.
  30894. CImg<T> get_fill_from_values(const char *const values, const bool repeat_values) const {
  30895. return (+*this).fill_from_values(values,repeat_values);
  30896. }
  30897. // Fill image according to a value sequence, given as a string.
  30898. // Return 'true' if an error occured, 'false' otherwise.
  30899. bool _fill_from_values(const char *const values, const bool repeat_values) {
  30900. CImg<charT> item(256);
  30901. const char *nvalues = values;
  30902. const ulongT siz = size();
  30903. T *ptrd = _data;
  30904. ulongT nb = 0;
  30905. char sep = 0;
  30906. for (double val = 0; *nvalues && nb<siz; ++nb) {
  30907. sep = 0;
  30908. const int err = cimg_sscanf(nvalues,"%255[ \n\t0-9.eEinfa+-]%c",item._data,&sep);
  30909. if (err>0 && cimg_sscanf(item,"%lf",&val)==1 && (sep==',' || sep==';' || err==1)) {
  30910. nvalues+=std::strlen(item) + (err>1);
  30911. *(ptrd++) = (T)val;
  30912. } else break;
  30913. }
  30914. if (nb<siz && (sep || *nvalues)) return true;
  30915. if (repeat_values && nb && nb<siz)
  30916. for (T *ptrs = _data, *const ptre = _data + siz; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
  30917. return false;
  30918. }
  30919. //! Fill sequentially pixel values according to the values found in another image.
  30920. /**
  30921. \param values Image containing the values used for the filling.
  30922. \param repeat_values In case there are less values than necessary in \c values, tells if these values must be
  30923. repeated for the filling.
  30924. **/
  30925. template<typename t>
  30926. CImg<T>& fill(const CImg<t>& values, const bool repeat_values=true) {
  30927. if (is_empty() || !values) return *this;
  30928. T *ptrd = _data, *ptre = ptrd + size();
  30929. for (t *ptrs = values._data, *ptrs_end = ptrs + values.size(); ptrs<ptrs_end && ptrd<ptre; ++ptrs)
  30930. *(ptrd++) = (T)*ptrs;
  30931. if (repeat_values && ptrd<ptre) for (T *ptrs = _data; ptrd<ptre; ++ptrs) *(ptrd++) = *ptrs;
  30932. return *this;
  30933. }
  30934. //! Fill sequentially pixel values according to the values found in another image \newinstance.
  30935. template<typename t>
  30936. CImg<T> get_fill(const CImg<t>& values, const bool repeat_values=true) const {
  30937. return repeat_values?CImg<T>(_width,_height,_depth,_spectrum).fill(values,repeat_values):
  30938. (+*this).fill(values,repeat_values);
  30939. }
  30940. //! Fill pixel values along the X-axis at a specified pixel position.
  30941. /**
  30942. \param y Y-coordinate of the filled column.
  30943. \param z Z-coordinate of the filled column.
  30944. \param c C-coordinate of the filled column.
  30945. \param a0 First fill value.
  30946. **/
  30947. CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const int a0, ...) {
  30948. #define _cimg_fill1(x,y,z,c,off,siz,t) { \
  30949. va_list ap; va_start(ap,a0); T *ptrd = data(x,y,z,c); *ptrd = (T)a0; \
  30950. for (unsigned int k = 1; k<siz; ++k) { ptrd+=off; *ptrd = (T)va_arg(ap,t); } \
  30951. va_end(ap); }
  30952. if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,int);
  30953. return *this;
  30954. }
  30955. //! Fill pixel values along the X-axis at a specified pixel position \overloading.
  30956. CImg<T>& fillX(const unsigned int y, const unsigned int z, const unsigned int c, const double a0, ...) {
  30957. if (y<_height && z<_depth && c<_spectrum) _cimg_fill1(0,y,z,c,1,_width,double);
  30958. return *this;
  30959. }
  30960. //! Fill pixel values along the Y-axis at a specified pixel position.
  30961. /**
  30962. \param x X-coordinate of the filled row.
  30963. \param z Z-coordinate of the filled row.
  30964. \param c C-coordinate of the filled row.
  30965. \param a0 First fill value.
  30966. **/
  30967. CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const int a0, ...) {
  30968. if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,int);
  30969. return *this;
  30970. }
  30971. //! Fill pixel values along the Y-axis at a specified pixel position \overloading.
  30972. CImg<T>& fillY(const unsigned int x, const unsigned int z, const unsigned int c, const double a0, ...) {
  30973. if (x<_width && z<_depth && c<_spectrum) _cimg_fill1(x,0,z,c,_width,_height,double);
  30974. return *this;
  30975. }
  30976. //! Fill pixel values along the Z-axis at a specified pixel position.
  30977. /**
  30978. \param x X-coordinate of the filled slice.
  30979. \param y Y-coordinate of the filled slice.
  30980. \param c C-coordinate of the filled slice.
  30981. \param a0 First fill value.
  30982. **/
  30983. CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const int a0, ...) {
  30984. const ulongT wh = (ulongT)_width*_height;
  30985. if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,int);
  30986. return *this;
  30987. }
  30988. //! Fill pixel values along the Z-axis at a specified pixel position \overloading.
  30989. CImg<T>& fillZ(const unsigned int x, const unsigned int y, const unsigned int c, const double a0, ...) {
  30990. const ulongT wh = (ulongT)_width*_height;
  30991. if (x<_width && y<_height && c<_spectrum) _cimg_fill1(x,y,0,c,wh,_depth,double);
  30992. return *this;
  30993. }
  30994. //! Fill pixel values along the C-axis at a specified pixel position.
  30995. /**
  30996. \param x X-coordinate of the filled channel.
  30997. \param y Y-coordinate of the filled channel.
  30998. \param z Z-coordinate of the filled channel.
  30999. \param a0 First filling value.
  31000. **/
  31001. CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const int a0, ...) {
  31002. const ulongT whd = (ulongT)_width*_height*_depth;
  31003. if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,int);
  31004. return *this;
  31005. }
  31006. //! Fill pixel values along the C-axis at a specified pixel position \overloading.
  31007. CImg<T>& fillC(const unsigned int x, const unsigned int y, const unsigned int z, const double a0, ...) {
  31008. const ulongT whd = (ulongT)_width*_height*_depth;
  31009. if (x<_width && y<_height && z<_depth) _cimg_fill1(x,y,z,0,whd,_spectrum,double);
  31010. return *this;
  31011. }
  31012. //! Discard specified sequence of values in the image buffer, along a specific axis.
  31013. /**
  31014. \param values Sequence of values to discard.
  31015. \param axis Axis along which the values are discarded. If set to \c 0 (default value)
  31016. the method does it for all the buffer values and returns a one-column vector.
  31017. \note Discarded values will change the image geometry, so the resulting image
  31018. is returned as a one-column vector.
  31019. **/
  31020. template<typename t>
  31021. CImg<T>& discard(const CImg<t>& values, const char axis=0) {
  31022. if (is_empty() || !values) return *this;
  31023. return get_discard(values,axis).move_to(*this);
  31024. }
  31025. template<typename t>
  31026. CImg<T> get_discard(const CImg<t>& values, const char axis=0) const {
  31027. if (!values) return +*this;
  31028. CImg<T> res;
  31029. if (is_empty()) return res;
  31030. const ulongT vsiz = values.size();
  31031. const char _axis = cimg::lowercase(axis);
  31032. ulongT j = 0;
  31033. unsigned int k = 0;
  31034. int i0 = 0;
  31035. res.assign(width(),height(),depth(),spectrum());
  31036. switch (_axis) {
  31037. case 'x' : {
  31038. cimg_forX(*this,i) {
  31039. if ((*this)(i)!=(T)values[j]) {
  31040. if (j) --i;
  31041. res.draw_image(k,get_columns(i0,i));
  31042. k+=i - i0 + 1; i0 = i + 1; j = 0;
  31043. } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
  31044. }
  31045. if (i0<width()) { res.draw_image(k,get_columns(i0,width() - 1)); k+=width() - i0; }
  31046. res.resize(k,-100,-100,-100,0);
  31047. } break;
  31048. case 'y' : {
  31049. cimg_forY(*this,i) {
  31050. if ((*this)(0,i)!=(T)values[j]) {
  31051. if (j) --i;
  31052. res.draw_image(0,k,get_rows(i0,i));
  31053. k+=i - i0 + 1; i0 = i + 1; j = 0;
  31054. } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
  31055. }
  31056. if (i0<height()) { res.draw_image(0,k,get_rows(i0,height() - 1)); k+=height() - i0; }
  31057. res.resize(-100,k,-100,-100,0);
  31058. } break;
  31059. case 'z' : {
  31060. cimg_forZ(*this,i) {
  31061. if ((*this)(0,0,i)!=(T)values[j]) {
  31062. if (j) --i;
  31063. res.draw_image(0,0,k,get_slices(i0,i));
  31064. k+=i - i0 + 1; i0 = i + 1; j = 0;
  31065. } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
  31066. }
  31067. if (i0<depth()) { res.draw_image(0,0,k,get_slices(i0,height() - 1)); k+=depth() - i0; }
  31068. res.resize(-100,-100,k,-100,0);
  31069. } break;
  31070. case 'c' : {
  31071. cimg_forC(*this,i) {
  31072. if ((*this)(0,0,0,i)!=(T)values[j]) {
  31073. if (j) --i;
  31074. res.draw_image(0,0,0,k,get_channels(i0,i));
  31075. k+=i - i0 + 1; i0 = i + 1; j = 0;
  31076. } else { ++j; if (j>=vsiz) { j = 0; i0 = i + 1; } }
  31077. }
  31078. if (i0<spectrum()) { res.draw_image(0,0,k,get_channels(i0,height() - 1)); k+=spectrum() - i0; }
  31079. res.resize(-100,-100,-100,k,0);
  31080. } break;
  31081. default : {
  31082. const ulongT siz = size();
  31083. res.unroll('y');
  31084. if (vsiz==1) { // Optimized version for a single discard value
  31085. const T val = (T)values[0];
  31086. cimg_foroff(*this,i) {
  31087. const T _val = (T)_data[i];
  31088. if (_val!=val) res[k++] = _val;
  31089. }
  31090. } else { // Generic version
  31091. cimg_foroff(*this,i) {
  31092. if ((*this)[i]!=(T)values[j]) {
  31093. if (j) --i;
  31094. std::memcpy(res._data + k,_data + i0,(i - i0 + 1)*sizeof(T));
  31095. k+=i - i0 + 1; i0 = (int)i + 1; j = 0;
  31096. } else { ++j; if (j>=vsiz) { j = 0; i0 = (int)i + 1; }}
  31097. }
  31098. if ((ulongT)i0<siz) { std::memcpy(res._data + k,_data + i0,(siz - i0)*sizeof(T)); k+=siz - i0; }
  31099. }
  31100. res.resize(1,k,1,1,0);
  31101. }
  31102. }
  31103. return res;
  31104. }
  31105. //! Discard neighboring duplicates in the image buffer, along the specified axis.
  31106. CImg<T>& discard(const char axis=0) {
  31107. return get_discard(axis).move_to(*this);
  31108. }
  31109. //! Discard neighboring duplicates in the image buffer, along the specified axis \newinstance.
  31110. CImg<T> get_discard(const char axis=0) const {
  31111. CImg<T> res;
  31112. if (is_empty()) return res;
  31113. const char _axis = cimg::lowercase(axis);
  31114. T current = *_data?(T)0:(T)1;
  31115. int j = 0;
  31116. res.assign(width(),height(),depth(),spectrum());
  31117. switch (_axis) {
  31118. case 'x' : {
  31119. cimg_forX(*this,i)
  31120. if ((*this)(i)!=current) { res.draw_image(j++,get_column(i)); current = (*this)(i); }
  31121. res.resize(j,-100,-100,-100,0);
  31122. } break;
  31123. case 'y' : {
  31124. cimg_forY(*this,i)
  31125. if ((*this)(0,i)!=current) { res.draw_image(0,j++,get_row(i)); current = (*this)(0,i); }
  31126. res.resize(-100,j,-100,-100,0);
  31127. } break;
  31128. case 'z' : {
  31129. cimg_forZ(*this,i)
  31130. if ((*this)(0,0,i)!=current) { res.draw_image(0,0,j++,get_slice(i)); current = (*this)(0,0,i); }
  31131. res.resize(-100,-100,j,-100,0);
  31132. } break;
  31133. case 'c' : {
  31134. cimg_forC(*this,i)
  31135. if ((*this)(0,0,0,i)!=current) { res.draw_image(0,0,0,j++,get_channel(i)); current = (*this)(0,0,0,i); }
  31136. res.resize(-100,-100,-100,j,0);
  31137. } break;
  31138. default : {
  31139. res.unroll('y');
  31140. cimg_foroff(*this,i) {
  31141. const T val = (*this)[i];
  31142. if (val!=current) res[j++] = current = val;
  31143. }
  31144. res.resize(-100,j,-100,-100,0);
  31145. }
  31146. }
  31147. return res;
  31148. }
  31149. //! Invert endianness of all pixel values.
  31150. /**
  31151. **/
  31152. CImg<T>& invert_endianness() {
  31153. cimg::invert_endianness(_data,size());
  31154. return *this;
  31155. }
  31156. //! Invert endianness of all pixel values \newinstance.
  31157. CImg<T> get_invert_endianness() const {
  31158. return (+*this).invert_endianness();
  31159. }
  31160. //! Fill image with random values in specified range.
  31161. /**
  31162. \param val_min Minimal authorized random value.
  31163. \param val_max Maximal authorized random value.
  31164. \note Random variables are uniformly distributed in [val_min,val_max].
  31165. **/
  31166. CImg<T>& rand(const T& val_min, const T& val_max) {
  31167. const float delta = (float)val_max - (float)val_min + (cimg::type<T>::is_float()?0:1);
  31168. if (cimg::type<T>::is_float()) cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),524288)) {
  31169. cimg_uint64 rng = (cimg::_rand(),cimg::rng());
  31170. #if cimg_use_openmp!=0
  31171. rng+=omp_get_thread_num();
  31172. #endif
  31173. cimg_pragma_openmp(for)
  31174. cimg_rofoff(*this,off) _data[off] = (T)(val_min + delta*cimg::rand(1,&rng));
  31175. cimg::srand(rng);
  31176. } else cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),524288)) {
  31177. cimg_uint64 rng = (cimg::_rand(),cimg::rng());
  31178. #if cimg_use_openmp!=0
  31179. rng+=omp_get_thread_num();
  31180. #endif
  31181. cimg_pragma_openmp(for)
  31182. cimg_rofoff(*this,off) _data[off] = std::min(val_max,(T)(val_min + delta*cimg::rand(1,&rng)));
  31183. cimg::srand(rng);
  31184. }
  31185. return *this;
  31186. }
  31187. //! Fill image with random values in specified range \newinstance.
  31188. CImg<T> get_rand(const T& val_min, const T& val_max) const {
  31189. return (+*this).rand(val_min,val_max);
  31190. }
  31191. //! Round pixel values.
  31192. /**
  31193. \param y Rounding precision.
  31194. \param rounding_type Rounding type. Can be:
  31195. - \c -1: Backward.
  31196. - \c 0: Nearest.
  31197. - \c 1: Forward.
  31198. **/
  31199. CImg<T>& round(const double y=1, const int rounding_type=0) {
  31200. if (y>0) cimg_openmp_for(*this,cimg::round(*ptr,y,rounding_type),8192);
  31201. return *this;
  31202. }
  31203. //! Round pixel values \newinstance.
  31204. CImg<T> get_round(const double y=1, const unsigned int rounding_type=0) const {
  31205. return (+*this).round(y,rounding_type);
  31206. }
  31207. //! Add random noise to pixel values.
  31208. /**
  31209. \param sigma Amplitude of the random additive noise. If \p sigma<0, it stands for a percentage of the
  31210. global value range.
  31211. \param noise_type Type of additive noise (can be \p 0=gaussian, \p 1=uniform, \p 2=Salt and Pepper,
  31212. \p 3=Poisson or \p 4=Rician).
  31213. \return A reference to the modified image instance.
  31214. \note
  31215. - For Poisson noise (\p noise_type=3), parameter \p sigma is ignored, as Poisson noise only depends on
  31216. the image value itself.
  31217. - Function \p CImg<T>::get_noise() is also defined. It returns a non-shared modified copy of the image instance.
  31218. \par Example
  31219. \code
  31220. const CImg<float> img("reference.jpg"), res = img.get_noise(40);
  31221. (img,res.normalize(0,255)).display();
  31222. \endcode
  31223. \image html ref_noise.jpg
  31224. **/
  31225. CImg<T>& noise(const double sigma, const unsigned int noise_type=0) {
  31226. if (is_empty()) return *this;
  31227. const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
  31228. Tfloat nsigma = (Tfloat)sigma, m = 0, M = 0;
  31229. if (nsigma==0 && noise_type!=3) return *this;
  31230. if (nsigma<0 || noise_type==2) m = (Tfloat)min_max(M);
  31231. if (nsigma<0) nsigma = (Tfloat)(-nsigma*(M-m)/100.);
  31232. switch (noise_type) {
  31233. case 0 : { // Gaussian noise
  31234. cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
  31235. cimg_uint64 rng = (cimg::_rand(),cimg::rng());
  31236. #if cimg_use_openmp!=0
  31237. rng+=omp_get_thread_num();
  31238. #endif
  31239. cimg_pragma_openmp(for)
  31240. cimg_rofoff(*this,off) {
  31241. Tfloat val = (Tfloat)(_data[off] + nsigma*cimg::grand(&rng));
  31242. if (val>vmax) val = vmax;
  31243. if (val<vmin) val = vmin;
  31244. _data[off] = (T)val;
  31245. }
  31246. cimg::srand(rng);
  31247. }
  31248. } break;
  31249. case 1 : { // Uniform noise
  31250. cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
  31251. cimg_uint64 rng = (cimg::_rand(),cimg::rng());
  31252. #if cimg_use_openmp!=0
  31253. rng+=omp_get_thread_num();
  31254. #endif
  31255. cimg_pragma_openmp(for)
  31256. cimg_rofoff(*this,off) {
  31257. Tfloat val = (Tfloat)(_data[off] + nsigma*cimg::rand(-1,1,&rng));
  31258. if (val>vmax) val = vmax;
  31259. if (val<vmin) val = vmin;
  31260. _data[off] = (T)val;
  31261. }
  31262. cimg::srand(rng);
  31263. }
  31264. } break;
  31265. case 2 : { // Salt & Pepper noise
  31266. if (nsigma<0) nsigma = -nsigma;
  31267. if (M==m) {
  31268. if (cimg::type<T>::is_float()) { --m; ++M; }
  31269. else { m = (Tfloat)cimg::type<T>::min(); M = (Tfloat)cimg::type<T>::max(); }
  31270. }
  31271. cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
  31272. cimg_uint64 rng = (cimg::_rand(),cimg::rng());
  31273. #if cimg_use_openmp!=0
  31274. rng+=omp_get_thread_num();
  31275. #endif
  31276. cimg_pragma_openmp(for)
  31277. cimg_rofoff(*this,off) if (cimg::rand(100,&rng)<nsigma) _data[off] = (T)(cimg::rand(1,&rng)<0.5?M:m);
  31278. cimg::srand(rng);
  31279. }
  31280. } break;
  31281. case 3 : { // Poisson Noise
  31282. cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
  31283. cimg_uint64 rng = (cimg::_rand(),cimg::rng());
  31284. #if cimg_use_openmp!=0
  31285. rng+=omp_get_thread_num();
  31286. #endif
  31287. cimg_pragma_openmp(for)
  31288. cimg_rofoff(*this,off) _data[off] = (T)cimg::prand(_data[off],&rng);
  31289. cimg::srand(rng);
  31290. }
  31291. } break;
  31292. case 4 : { // Rice noise
  31293. const Tfloat sqrt2 = (Tfloat)std::sqrt(2.);
  31294. cimg_pragma_openmp(parallel cimg_openmp_if_size(size(),131072)) {
  31295. cimg_uint64 rng = (cimg::_rand(),cimg::rng());
  31296. #if cimg_use_openmp!=0
  31297. rng+=omp_get_thread_num();
  31298. #endif
  31299. cimg_pragma_openmp(for)
  31300. cimg_rofoff(*this,off) {
  31301. const Tfloat
  31302. val0 = (Tfloat)_data[off]/sqrt2,
  31303. re = (Tfloat)(val0 + nsigma*cimg::grand(&rng)),
  31304. im = (Tfloat)(val0 + nsigma*cimg::grand(&rng));
  31305. Tfloat val = cimg::hypot(re,im);
  31306. if (val>vmax) val = vmax;
  31307. if (val<vmin) val = vmin;
  31308. _data[off] = (T)val;
  31309. }
  31310. cimg::srand(rng);
  31311. }
  31312. } break;
  31313. default :
  31314. throw CImgArgumentException(_cimg_instance
  31315. "noise(): Invalid specified noise type %d "
  31316. "(should be { 0=gaussian | 1=uniform | 2=salt&Pepper | 3=poisson }).",
  31317. cimg_instance,
  31318. noise_type);
  31319. }
  31320. return *this;
  31321. }
  31322. //! Add random noise to pixel values \newinstance.
  31323. CImg<T> get_noise(const double sigma, const unsigned int noise_type=0) const {
  31324. return (+*this).noise(sigma,noise_type);
  31325. }
  31326. //! Linearly normalize pixel values.
  31327. /**
  31328. \param min_value Minimum desired value of the resulting image.
  31329. \param max_value Maximum desired value of the resulting image.
  31330. \param constant_case_ratio In case of instance image having a constant value, tell what ratio
  31331. of [min_value,max_value] is used to fill the normalized image
  31332. (=0 for min_value, =1 for max_value, =0.5 for (min_value + max_value)/2).
  31333. \par Example
  31334. \code
  31335. const CImg<float> img("reference.jpg"), res = img.get_normalize(160,220);
  31336. (img,res).display();
  31337. \endcode
  31338. \image html ref_normalize2.jpg
  31339. **/
  31340. CImg<T>& normalize(const T& min_value, const T& max_value,
  31341. const float constant_case_ratio=0) {
  31342. if (is_empty()) return *this;
  31343. const T a = min_value<max_value?min_value:max_value, b = min_value<max_value?max_value:min_value;
  31344. T m, M = max_min(m);
  31345. const Tfloat fm = (Tfloat)m, fM = (Tfloat)M;
  31346. if (m==M)
  31347. return fill(constant_case_ratio==0?a:
  31348. constant_case_ratio==1?b:
  31349. (T)((1 - constant_case_ratio)*a + constant_case_ratio*b));
  31350. if (m!=a || M!=b) cimg_rof(*this,ptrd,T) *ptrd = (T)((*ptrd - fm)/(fM - fm)*(b - a) + a);
  31351. return *this;
  31352. }
  31353. //! Linearly normalize pixel values \newinstance.
  31354. CImg<Tfloat> get_normalize(const T& min_value, const T& max_value,
  31355. const float ratio_if_constant_image=0) const {
  31356. return CImg<Tfloat>(*this,false).normalize((Tfloat)min_value,(Tfloat)max_value,ratio_if_constant_image);
  31357. }
  31358. //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm.
  31359. /**
  31360. \par Example
  31361. \code
  31362. const CImg<float> img("reference.jpg"), res = img.get_normalize();
  31363. (img,res.normalize(0,255)).display();
  31364. \endcode
  31365. \image html ref_normalize.jpg
  31366. **/
  31367. CImg<T>& normalize() {
  31368. const ulongT whd = (ulongT)_width*_height*_depth;
  31369. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
  31370. _height*_depth>=16))
  31371. cimg_forYZ(*this,y,z) {
  31372. T *ptrd = data(0,y,z,0);
  31373. cimg_forX(*this,x) {
  31374. const T *ptrs = ptrd;
  31375. float n = 0;
  31376. cimg_forC(*this,c) { n+=cimg::sqr((float)*ptrs); ptrs+=whd; }
  31377. n = (float)std::sqrt(n);
  31378. T *_ptrd = ptrd++;
  31379. if (n>0) cimg_forC(*this,c) { *_ptrd = (T)(*_ptrd/n); _ptrd+=whd; }
  31380. else cimg_forC(*this,c) { *_ptrd = (T)0; _ptrd+=whd; }
  31381. }
  31382. }
  31383. return *this;
  31384. }
  31385. //! Normalize multi-valued pixels of the image instance, with respect to their L2-norm \newinstance.
  31386. CImg<Tfloat> get_normalize() const {
  31387. return CImg<Tfloat>(*this,false).normalize();
  31388. }
  31389. //! Compute Lp-norm of each multi-valued pixel of the image instance.
  31390. /**
  31391. \param norm_type Type of computed vector norm (can be \p -1=Linf, or \p greater or equal than 0).
  31392. \par Example
  31393. \code
  31394. const CImg<float> img("reference.jpg"), res = img.get_norm();
  31395. (img,res.normalize(0,255)).display();
  31396. \endcode
  31397. \image html ref_norm.jpg
  31398. **/
  31399. CImg<T>& norm(const int norm_type=2) {
  31400. if (_spectrum==1 && norm_type) return abs();
  31401. return get_norm(norm_type).move_to(*this);
  31402. }
  31403. //! Compute L2-norm of each multi-valued pixel of the image instance \newinstance.
  31404. CImg<Tfloat> get_norm(const int norm_type=2) const {
  31405. if (is_empty()) return *this;
  31406. if (_spectrum==1 && norm_type) return get_abs();
  31407. const ulongT whd = (ulongT)_width*_height*_depth;
  31408. CImg<Tfloat> res(_width,_height,_depth);
  31409. switch (norm_type) {
  31410. case -1 : { // Linf-norm
  31411. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
  31412. _height*_depth>=16))
  31413. cimg_forYZ(*this,y,z) {
  31414. const ulongT off = (ulongT)offset(0,y,z);
  31415. const T *ptrs = _data + off;
  31416. Tfloat *ptrd = res._data + off;
  31417. cimg_forX(*this,x) {
  31418. Tfloat n = 0;
  31419. const T *_ptrs = ptrs++;
  31420. cimg_forC(*this,c) { const Tfloat val = (Tfloat)cimg::abs(*_ptrs); if (val>n) n = val; _ptrs+=whd; }
  31421. *(ptrd++) = n;
  31422. }
  31423. }
  31424. } break;
  31425. case 0 : { // L0-norm
  31426. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
  31427. _height*_depth>=16))
  31428. cimg_forYZ(*this,y,z) {
  31429. const ulongT off = (ulongT)offset(0,y,z);
  31430. const T *ptrs = _data + off;
  31431. Tfloat *ptrd = res._data + off;
  31432. cimg_forX(*this,x) {
  31433. unsigned int n = 0;
  31434. const T *_ptrs = ptrs++;
  31435. cimg_forC(*this,c) { n+=*_ptrs==0?0:1; _ptrs+=whd; }
  31436. *(ptrd++) = (Tfloat)n;
  31437. }
  31438. }
  31439. } break;
  31440. case 1 : { // L1-norm
  31441. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
  31442. _height*_depth>=16))
  31443. cimg_forYZ(*this,y,z) {
  31444. const ulongT off = (ulongT)offset(0,y,z);
  31445. const T *ptrs = _data + off;
  31446. Tfloat *ptrd = res._data + off;
  31447. cimg_forX(*this,x) {
  31448. Tfloat n = 0;
  31449. const T *_ptrs = ptrs++;
  31450. cimg_forC(*this,c) { n+=cimg::abs(*_ptrs); _ptrs+=whd; }
  31451. *(ptrd++) = n;
  31452. }
  31453. }
  31454. } break;
  31455. case 2 : { // L2-norm
  31456. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
  31457. _height*_depth>=16))
  31458. cimg_forYZ(*this,y,z) {
  31459. const ulongT off = (ulongT)offset(0,y,z);
  31460. const T *ptrs = _data + off;
  31461. Tfloat *ptrd = res._data + off;
  31462. cimg_forX(*this,x) {
  31463. Tfloat n = 0;
  31464. const T *_ptrs = ptrs++;
  31465. cimg_forC(*this,c) { n+=cimg::sqr((Tfloat)*_ptrs); _ptrs+=whd; }
  31466. *(ptrd++) = (Tfloat)std::sqrt((Tfloat)n);
  31467. }
  31468. }
  31469. } break;
  31470. default : { // Linf-norm
  31471. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
  31472. _height*_depth>=16))
  31473. cimg_forYZ(*this,y,z) {
  31474. const ulongT off = (ulongT)offset(0,y,z);
  31475. const T *ptrs = _data + off;
  31476. Tfloat *ptrd = res._data + off;
  31477. cimg_forX(*this,x) {
  31478. Tfloat n = 0;
  31479. const T *_ptrs = ptrs++;
  31480. cimg_forC(*this,c) { n+=std::pow(cimg::abs((Tfloat)*_ptrs),(Tfloat)norm_type); _ptrs+=whd; }
  31481. *(ptrd++) = (Tfloat)std::pow((Tfloat)n,1/(Tfloat)norm_type);
  31482. }
  31483. }
  31484. }
  31485. }
  31486. return res;
  31487. }
  31488. //! Cut pixel values in specified range.
  31489. /**
  31490. \param min_value Minimum desired value of the resulting image.
  31491. \param max_value Maximum desired value of the resulting image.
  31492. \par Example
  31493. \code
  31494. const CImg<float> img("reference.jpg"), res = img.get_cut(160,220);
  31495. (img,res).display();
  31496. \endcode
  31497. \image html ref_cut.jpg
  31498. **/
  31499. CImg<T>& cut(const T& min_value, const T& max_value) {
  31500. if (is_empty()) return *this;
  31501. const T a = min_value<max_value?min_value:max_value, b = min_value<max_value?max_value:min_value;
  31502. cimg_openmp_for(*this,cimg::cut(*ptr,a,b),32768);
  31503. return *this;
  31504. }
  31505. //! Cut pixel values in specified range \newinstance.
  31506. CImg<T> get_cut(const T& min_value, const T& max_value) const {
  31507. return (+*this).cut(min_value,max_value);
  31508. }
  31509. //! Uniformly quantize pixel values.
  31510. /**
  31511. \param nb_levels Number of quantization levels.
  31512. \param keep_range Tells if resulting values keep the same range as the original ones.
  31513. \par Example
  31514. \code
  31515. const CImg<float> img("reference.jpg"), res = img.get_quantize(4);
  31516. (img,res).display();
  31517. \endcode
  31518. \image html ref_quantize.jpg
  31519. **/
  31520. CImg<T>& quantize(const unsigned int nb_levels, const bool keep_range=true) {
  31521. if (!nb_levels)
  31522. throw CImgArgumentException(_cimg_instance
  31523. "quantize(): Invalid quantization request with 0 values.",
  31524. cimg_instance);
  31525. if (is_empty()) return *this;
  31526. Tfloat m, M = (Tfloat)max_min(m), range = M - m;
  31527. if (range>0) {
  31528. if (keep_range)
  31529. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768))
  31530. cimg_rofoff(*this,off) {
  31531. const unsigned int val = (unsigned int)((_data[off] - m)*nb_levels/range);
  31532. _data[off] = (T)(m + std::min(val,nb_levels - 1)*range/nb_levels);
  31533. } else
  31534. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768))
  31535. cimg_rofoff(*this,off) {
  31536. const unsigned int val = (unsigned int)((_data[off] - m)*nb_levels/range);
  31537. _data[off] = (T)std::min(val,nb_levels - 1);
  31538. }
  31539. }
  31540. return *this;
  31541. }
  31542. //! Uniformly quantize pixel values \newinstance.
  31543. CImg<T> get_quantize(const unsigned int n, const bool keep_range=true) const {
  31544. return (+*this).quantize(n,keep_range);
  31545. }
  31546. //! Return the Otsu threshold.
  31547. /**
  31548. \param nb_levels Number of histogram levels used for the estimation.
  31549. **/
  31550. T otsu(const unsigned int nb_levels=256) const {
  31551. T m,M = max_min(m);
  31552. CImg<ulongT> hist = get_histogram(nb_levels,m,M);
  31553. ulongT sum = 0, sumB = 0, wB = 0;
  31554. double best_variance = 0;
  31555. unsigned int best_t = 0;
  31556. cimg_forX(hist,t) sum+=t*hist[t];
  31557. cimg_forX(hist,t) {
  31558. wB+=hist[t];
  31559. if (wB) {
  31560. const ulongT wF = size() - wB;
  31561. if (!wF) break;
  31562. sumB+=t*hist[t];
  31563. const double
  31564. mB = (double)sumB/wB,
  31565. mF = (double)(sum - sumB)/wF,
  31566. variance = wB*wF*cimg::sqr(mB - mF);
  31567. if (variance>best_variance) { best_variance = variance; best_t = t; }
  31568. }
  31569. }
  31570. return m + best_t*(M - m)/(hist.width() - 1);
  31571. }
  31572. //! Threshold pixel values.
  31573. /**
  31574. \param value Threshold value
  31575. \param soft_threshold Tells if soft thresholding must be applied (instead of hard one).
  31576. \param strict_threshold Tells if threshold value is strict.
  31577. \par Example
  31578. \code
  31579. const CImg<float> img("reference.jpg"), res = img.get_threshold(128);
  31580. (img,res.normalize(0,255)).display();
  31581. \endcode
  31582. \image html ref_threshold.jpg
  31583. **/
  31584. CImg<T>& threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) {
  31585. if (is_empty()) return *this;
  31586. if (strict_threshold) {
  31587. if (soft_threshold)
  31588. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768))
  31589. cimg_rofoff(*this,off) {
  31590. const T v = _data[off];
  31591. _data[off] = v>value?(T)(v-value):v<-(float)value?(T)(v + value):(T)0;
  31592. }
  31593. else
  31594. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),65536))
  31595. cimg_rofoff(*this,off) _data[off] = _data[off]>value?(T)1:(T)0;
  31596. } else {
  31597. if (soft_threshold)
  31598. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32768))
  31599. cimg_rofoff(*this,off) {
  31600. const T v = _data[off];
  31601. _data[off] = v>=value?(T)(v-value):v<=-(float)value?(T)(v + value):(T)0;
  31602. }
  31603. else
  31604. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),65536))
  31605. cimg_rofoff(*this,off) _data[off] = _data[off]>=value?(T)1:(T)0;
  31606. }
  31607. return *this;
  31608. }
  31609. //! Threshold pixel values \newinstance.
  31610. CImg<T> get_threshold(const T& value, const bool soft_threshold=false, const bool strict_threshold=false) const {
  31611. return (+*this).threshold(value,soft_threshold,strict_threshold);
  31612. }
  31613. //! Compute the histogram of pixel values.
  31614. /**
  31615. \param nb_levels Number of desired histogram levels.
  31616. \param min_value Minimum pixel value considered for the histogram computation.
  31617. All pixel values lower than \p min_value will not be counted.
  31618. \param max_value Maximum pixel value considered for the histogram computation.
  31619. All pixel values higher than \p max_value will not be counted.
  31620. \note
  31621. - The histogram H of an image I is the 1D function where H(x) counts the number of occurrences of the value x
  31622. in the image I.
  31623. - The resulting histogram is always defined in 1D. Histograms of multi-valued images are not multi-dimensional.
  31624. \par Example
  31625. \code
  31626. const CImg<float> img = CImg<float>("reference.jpg").histogram(256);
  31627. img.display_graph(0,3);
  31628. \endcode
  31629. \image html ref_histogram.jpg
  31630. **/
  31631. CImg<T>& histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) {
  31632. return get_histogram(nb_levels,min_value,max_value).move_to(*this);
  31633. }
  31634. //! Compute the histogram of pixel values \overloading.
  31635. CImg<T>& histogram(const unsigned int nb_levels) {
  31636. return get_histogram(nb_levels).move_to(*this);
  31637. }
  31638. //! Compute the histogram of pixel values \newinstance.
  31639. CImg<ulongT> get_histogram(const unsigned int nb_levels, const T& min_value, const T& max_value) const {
  31640. if (!nb_levels || is_empty()) return CImg<ulongT>();
  31641. const double
  31642. vmin = (double)(min_value<max_value?min_value:max_value),
  31643. vmax = (double)(min_value<max_value?max_value:min_value);
  31644. CImg<ulongT> res(nb_levels,1,1,1,0);
  31645. cimg_rof(*this,ptrs,T) {
  31646. const T val = *ptrs;
  31647. if (val>=vmin && val<=vmax) ++res[val==vmax?nb_levels - 1:(unsigned int)((val - vmin)*nb_levels/(vmax - vmin))];
  31648. }
  31649. return res;
  31650. }
  31651. //! Compute the histogram of pixel values \newinstance.
  31652. CImg<ulongT> get_histogram(const unsigned int nb_levels) const {
  31653. if (!nb_levels || is_empty()) return CImg<ulongT>();
  31654. T vmax = 0, vmin = min_max(vmax);
  31655. return get_histogram(nb_levels,vmin,vmax);
  31656. }
  31657. //! Equalize histogram of pixel values.
  31658. /**
  31659. \param nb_levels Number of histogram levels used for the equalization.
  31660. \param min_value Minimum pixel value considered for the histogram computation.
  31661. All pixel values lower than \p min_value will not be counted.
  31662. \param max_value Maximum pixel value considered for the histogram computation.
  31663. All pixel values higher than \p max_value will not be counted.
  31664. \par Example
  31665. \code
  31666. const CImg<float> img("reference.jpg"), res = img.get_equalize(256);
  31667. (img,res).display();
  31668. \endcode
  31669. \image html ref_equalize.jpg
  31670. **/
  31671. CImg<T>& equalize(const unsigned int nb_levels, const T& min_value, const T& max_value) {
  31672. if (!nb_levels || is_empty()) return *this;
  31673. const T
  31674. vmin = min_value<max_value?min_value:max_value,
  31675. vmax = min_value<max_value?max_value:min_value;
  31676. CImg<ulongT> hist = get_histogram(nb_levels,vmin,vmax);
  31677. ulongT cumul = 0;
  31678. cimg_forX(hist,pos) { cumul+=hist[pos]; hist[pos] = cumul; }
  31679. if (!cumul) cumul = 1;
  31680. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),1048576))
  31681. cimg_rofoff(*this,off) {
  31682. const int pos = (int)((_data[off] - vmin)*(nb_levels - 1.)/(vmax - vmin));
  31683. if (pos>=0 && pos<(int)nb_levels) _data[off] = (T)(vmin + (vmax - vmin)*hist[pos]/cumul);
  31684. }
  31685. return *this;
  31686. }
  31687. //! Equalize histogram of pixel values \overloading.
  31688. CImg<T>& equalize(const unsigned int nb_levels) {
  31689. if (!nb_levels || is_empty()) return *this;
  31690. T vmax = 0, vmin = min_max(vmax);
  31691. return equalize(nb_levels,vmin,vmax);
  31692. }
  31693. //! Equalize histogram of pixel values \newinstance.
  31694. CImg<T> get_equalize(const unsigned int nblevels, const T& val_min, const T& val_max) const {
  31695. return (+*this).equalize(nblevels,val_min,val_max);
  31696. }
  31697. //! Equalize histogram of pixel values \newinstance.
  31698. CImg<T> get_equalize(const unsigned int nblevels) const {
  31699. return (+*this).equalize(nblevels);
  31700. }
  31701. //! Index multi-valued pixels regarding to a specified palette.
  31702. /**
  31703. \param colormap Multi-valued colormap used as the basis for multi-valued pixel indexing.
  31704. \param dithering Level of dithering (0=disable, 1=standard level).
  31705. \param map_indexes Tell if the values of the resulting image are the colormap indices or the colormap vectors.
  31706. \note
  31707. - \p img.index(colormap,dithering,1) is equivalent to <tt>img.index(colormap,dithering,0).map(colormap)</tt>.
  31708. \par Example
  31709. \code
  31710. const CImg<float> img("reference.jpg"), colormap(3,1,1,3, 0,128,255, 0,128,255, 0,128,255);
  31711. const CImg<float> res = img.get_index(colormap,1,true);
  31712. (img,res).display();
  31713. \endcode
  31714. \image html ref_index.jpg
  31715. **/
  31716. template<typename t>
  31717. CImg<T>& index(const CImg<t>& colormap, const float dithering=1, const bool map_indexes=false) {
  31718. return get_index(colormap,dithering,map_indexes).move_to(*this);
  31719. }
  31720. //! Index multi-valued pixels regarding to a specified colormap \newinstance.
  31721. template<typename t>
  31722. CImg<typename CImg<t>::Tuint>
  31723. get_index(const CImg<t>& colormap, const float dithering=1, const bool map_indexes=true) const {
  31724. if (colormap._spectrum!=_spectrum)
  31725. throw CImgArgumentException(_cimg_instance
  31726. "index(): Instance and specified colormap (%u,%u,%u,%u,%p) "
  31727. "have incompatible dimensions.",
  31728. cimg_instance,
  31729. colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
  31730. typedef typename CImg<t>::Tuint tuint;
  31731. if (is_empty()) return CImg<tuint>();
  31732. const ulongT
  31733. whd = (ulongT)_width*_height*_depth,
  31734. pwhd = (ulongT)colormap._width*colormap._height*colormap._depth;
  31735. CImg<tuint> res(_width,_height,_depth,map_indexes?_spectrum:1);
  31736. if (dithering>0) { // Dithered versions
  31737. tuint *ptrd = res._data;
  31738. const float ndithering = cimg::cut(dithering,0,1)/16;
  31739. Tfloat valm = 0, valM = (Tfloat)max_min(valm);
  31740. if (valm==valM && valm>=0 && valM<=255) { valm = 0; valM = 255; }
  31741. CImg<Tfloat> cache = get_crop(-1,0,0,0,_width,1,0,_spectrum - 1);
  31742. Tfloat *cache_current = cache.data(1,0,0,0), *cache_next = cache.data(1,1,0,0);
  31743. const ulongT cwhd = (ulongT)cache._width*cache._height*cache._depth;
  31744. switch (_spectrum) {
  31745. case 1 : { // Optimized for scalars
  31746. cimg_forYZ(*this,y,z) {
  31747. if (y<height() - 2) {
  31748. Tfloat *ptrc0 = cache_next; const T *ptrs0 = data(0,y + 1,z,0);
  31749. cimg_forX(*this,x) *(ptrc0++) = (Tfloat)*(ptrs0++);
  31750. }
  31751. Tfloat *ptrs0 = cache_current, *ptrsn0 = cache_next;
  31752. cimg_forX(*this,x) {
  31753. const Tfloat _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0;
  31754. Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
  31755. for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
  31756. const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
  31757. if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
  31758. }
  31759. const Tfloat err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering;
  31760. *ptrs0+=7*err0; *(ptrsn0 - 1)+=3*err0; *(ptrsn0++)+=5*err0; *ptrsn0+=err0;
  31761. if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
  31762. }
  31763. cimg::swap(cache_current,cache_next);
  31764. }
  31765. } break;
  31766. case 2 : { // Optimized for 2D vectors
  31767. tuint *ptrd1 = ptrd + whd;
  31768. cimg_forYZ(*this,y,z) {
  31769. if (y<height() - 2) {
  31770. Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd;
  31771. const T *ptrs0 = data(0,y + 1,z,0), *ptrs1 = ptrs0 + whd;
  31772. cimg_forX(*this,x) { *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); }
  31773. }
  31774. Tfloat
  31775. *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd,
  31776. *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd;
  31777. cimg_forX(*this,x) {
  31778. const Tfloat
  31779. _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
  31780. _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1;
  31781. Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
  31782. for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
  31783. const Tfloat
  31784. pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
  31785. dist = pval0*pval0 + pval1*pval1;
  31786. if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
  31787. }
  31788. const t *const ptrmin1 = ptrmin0 + pwhd;
  31789. const Tfloat
  31790. err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering,
  31791. err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)*ndithering;
  31792. *ptrs0+=7*err0; *ptrs1+=7*err1;
  31793. *(ptrsn0 - 1)+=3*err0; *(ptrsn1 - 1)+=3*err1;
  31794. *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1;
  31795. *ptrsn0+=err0; *ptrsn1+=err1;
  31796. if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; }
  31797. else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
  31798. }
  31799. cimg::swap(cache_current,cache_next);
  31800. }
  31801. } break;
  31802. case 3 : { // Optimized for 3D vectors (colors)
  31803. tuint *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
  31804. cimg_forYZ(*this,y,z) {
  31805. if (y<height() - 2) {
  31806. Tfloat *ptrc0 = cache_next, *ptrc1 = ptrc0 + cwhd, *ptrc2 = ptrc1 + cwhd;
  31807. const T *ptrs0 = data(0,y + 1,z,0), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd;
  31808. cimg_forX(*this,x) {
  31809. *(ptrc0++) = (Tfloat)*(ptrs0++); *(ptrc1++) = (Tfloat)*(ptrs1++); *(ptrc2++) = (Tfloat)*(ptrs2++);
  31810. }
  31811. }
  31812. Tfloat
  31813. *ptrs0 = cache_current, *ptrs1 = ptrs0 + cwhd, *ptrs2 = ptrs1 + cwhd,
  31814. *ptrsn0 = cache_next, *ptrsn1 = ptrsn0 + cwhd, *ptrsn2 = ptrsn1 + cwhd;
  31815. cimg_forX(*this,x) {
  31816. const Tfloat
  31817. _val0 = (Tfloat)*ptrs0, val0 = _val0<valm?valm:_val0>valM?valM:_val0,
  31818. _val1 = (Tfloat)*ptrs1, val1 = _val1<valm?valm:_val1>valM?valM:_val1,
  31819. _val2 = (Tfloat)*ptrs2, val2 = _val2<valm?valm:_val2>valM?valM:_val2;
  31820. Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
  31821. for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd,
  31822. *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
  31823. const Tfloat
  31824. pval0 = (Tfloat)*(ptrp0++) - val0,
  31825. pval1 = (Tfloat)*(ptrp1++) - val1,
  31826. pval2 = (Tfloat)*(ptrp2++) - val2,
  31827. dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
  31828. if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
  31829. }
  31830. const t *const ptrmin1 = ptrmin0 + pwhd, *const ptrmin2 = ptrmin1 + pwhd;
  31831. const Tfloat
  31832. err0 = ((*(ptrs0++)=val0) - (Tfloat)*ptrmin0)*ndithering,
  31833. err1 = ((*(ptrs1++)=val1) - (Tfloat)*ptrmin1)*ndithering,
  31834. err2 = ((*(ptrs2++)=val2) - (Tfloat)*ptrmin2)*ndithering;
  31835. *ptrs0+=7*err0; *ptrs1+=7*err1; *ptrs2+=7*err2;
  31836. *(ptrsn0 - 1)+=3*err0; *(ptrsn1 - 1)+=3*err1; *(ptrsn2 - 1)+=3*err2;
  31837. *(ptrsn0++)+=5*err0; *(ptrsn1++)+=5*err1; *(ptrsn2++)+=5*err2;
  31838. *ptrsn0+=err0; *ptrsn1+=err1; *ptrsn2+=err2;
  31839. if (map_indexes) {
  31840. *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*ptrmin1; *(ptrd2++) = (tuint)*ptrmin2;
  31841. } else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
  31842. }
  31843. cimg::swap(cache_current,cache_next);
  31844. }
  31845. } break;
  31846. default : // Generic version
  31847. cimg_forYZ(*this,y,z) {
  31848. if (y<height() - 2) {
  31849. Tfloat *ptrc = cache_next;
  31850. cimg_forC(*this,c) {
  31851. Tfloat *_ptrc = ptrc; const T *_ptrs = data(0,y + 1,z,c);
  31852. cimg_forX(*this,x) *(_ptrc++) = (Tfloat)*(_ptrs++);
  31853. ptrc+=cwhd;
  31854. }
  31855. }
  31856. Tfloat *ptrs = cache_current, *ptrsn = cache_next;
  31857. cimg_forX(*this,x) {
  31858. Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = colormap._data;
  31859. for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
  31860. Tfloat dist = 0; Tfloat *_ptrs = ptrs; const t *_ptrp = ptrp;
  31861. cimg_forC(*this,c) {
  31862. const Tfloat _val = *_ptrs, val = _val<valm?valm:_val>valM?valM:_val;
  31863. dist+=cimg::sqr((*_ptrs=val) - (Tfloat)*_ptrp); _ptrs+=cwhd; _ptrp+=pwhd;
  31864. }
  31865. if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
  31866. }
  31867. const t *_ptrmin = ptrmin; Tfloat *_ptrs = ptrs++, *_ptrsn = (ptrsn++) - 1;
  31868. cimg_forC(*this,c) {
  31869. const Tfloat err = (*(_ptrs++) - (Tfloat)*_ptrmin)*ndithering;
  31870. *_ptrs+=7*err; *(_ptrsn++)+=3*err; *(_ptrsn++)+=5*err; *_ptrsn+=err;
  31871. _ptrmin+=pwhd; _ptrs+=cwhd - 1; _ptrsn+=cwhd - 2;
  31872. }
  31873. if (map_indexes) {
  31874. tuint *_ptrd = ptrd++;
  31875. cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
  31876. }
  31877. else *(ptrd++) = (tuint)(ptrmin - colormap._data);
  31878. }
  31879. cimg::swap(cache_current,cache_next);
  31880. }
  31881. }
  31882. } else { // Non-dithered versions
  31883. switch (_spectrum) {
  31884. case 1 : { // Optimized for scalars
  31885. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 &&
  31886. _height*_depth>=16 && pwhd>=16))
  31887. cimg_forYZ(*this,y,z) {
  31888. tuint *ptrd = res.data(0,y,z);
  31889. for (const T *ptrs0 = data(0,y,z), *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
  31890. const Tfloat val0 = (Tfloat)*(ptrs0++);
  31891. Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
  31892. for (const t *ptrp0 = colormap._data, *ptrp_end = ptrp0 + pwhd; ptrp0<ptrp_end; ) {
  31893. const Tfloat pval0 = (Tfloat)*(ptrp0++) - val0, dist = pval0*pval0;
  31894. if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
  31895. }
  31896. if (map_indexes) *(ptrd++) = (tuint)*ptrmin0; else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
  31897. }
  31898. }
  31899. } break;
  31900. case 2 : { // Optimized for 2D vectors
  31901. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 &&
  31902. _height*_depth>=16 && pwhd>=16))
  31903. cimg_forYZ(*this,y,z) {
  31904. tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd;
  31905. for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
  31906. const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++);
  31907. Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
  31908. for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
  31909. const Tfloat
  31910. pval0 = (Tfloat)*(ptrp0++) - val0, pval1 = (Tfloat)*(ptrp1++) - val1,
  31911. dist = pval0*pval0 + pval1*pval1;
  31912. if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
  31913. }
  31914. if (map_indexes) { *(ptrd++) = (tuint)*ptrmin0; *(ptrd1++) = (tuint)*(ptrmin0 + pwhd); }
  31915. else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
  31916. }
  31917. }
  31918. } break;
  31919. case 3 : { // Optimized for 3D vectors (colors)
  31920. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 &&
  31921. _height*_depth>=16 && pwhd>=16))
  31922. cimg_forYZ(*this,y,z) {
  31923. tuint *ptrd = res.data(0,y,z), *ptrd1 = ptrd + whd, *ptrd2 = ptrd1 + whd;
  31924. for (const T *ptrs0 = data(0,y,z), *ptrs1 = ptrs0 + whd, *ptrs2 = ptrs1 + whd,
  31925. *ptrs_end = ptrs0 + _width; ptrs0<ptrs_end; ) {
  31926. const Tfloat val0 = (Tfloat)*(ptrs0++), val1 = (Tfloat)*(ptrs1++), val2 = (Tfloat)*(ptrs2++);
  31927. Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin0 = colormap._data;
  31928. for (const t *ptrp0 = colormap._data, *ptrp1 = ptrp0 + pwhd, *ptrp2 = ptrp1 + pwhd,
  31929. *ptrp_end = ptrp1; ptrp0<ptrp_end; ) {
  31930. const Tfloat
  31931. pval0 = (Tfloat)*(ptrp0++) - val0,
  31932. pval1 = (Tfloat)*(ptrp1++) - val1,
  31933. pval2 = (Tfloat)*(ptrp2++) - val2,
  31934. dist = pval0*pval0 + pval1*pval1 + pval2*pval2;
  31935. if (dist<distmin) { ptrmin0 = ptrp0 - 1; distmin = dist; }
  31936. }
  31937. if (map_indexes) {
  31938. *(ptrd++) = (tuint)*ptrmin0;
  31939. *(ptrd1++) = (tuint)*(ptrmin0 + pwhd);
  31940. *(ptrd2++) = (tuint)*(ptrmin0 + 2*pwhd);
  31941. } else *(ptrd++) = (tuint)(ptrmin0 - colormap._data);
  31942. }
  31943. }
  31944. } break;
  31945. default : // Generic version
  31946. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64 &&
  31947. _height*_depth>=16 && pwhd>=16))
  31948. cimg_forYZ(*this,y,z) {
  31949. tuint *ptrd = res.data(0,y,z);
  31950. for (const T *ptrs = data(0,y,z), *ptrs_end = ptrs + _width; ptrs<ptrs_end; ++ptrs) {
  31951. Tfloat distmin = cimg::type<Tfloat>::max(); const t *ptrmin = colormap._data;
  31952. for (const t *ptrp = colormap._data, *ptrp_end = ptrp + pwhd; ptrp<ptrp_end; ++ptrp) {
  31953. Tfloat dist = 0; const T *_ptrs = ptrs; const t *_ptrp = ptrp;
  31954. cimg_forC(*this,c) { dist+=cimg::sqr((Tfloat)*_ptrs - (Tfloat)*_ptrp); _ptrs+=whd; _ptrp+=pwhd; }
  31955. if (dist<distmin) { ptrmin = ptrp; distmin = dist; }
  31956. }
  31957. if (map_indexes) {
  31958. tuint *_ptrd = ptrd++;
  31959. cimg_forC(*this,c) { *_ptrd = (tuint)*ptrmin; _ptrd+=whd; ptrmin+=pwhd; }
  31960. }
  31961. else *(ptrd++) = (tuint)(ptrmin - colormap._data);
  31962. }
  31963. }
  31964. }
  31965. }
  31966. return res;
  31967. }
  31968. //! Map predefined palette on the scalar (indexed) image instance.
  31969. /**
  31970. \param palette Multi-valued palette used for mapping the indexes.
  31971. \param boundary_conditions Boundary conditions.
  31972. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
  31973. \par Example
  31974. \code
  31975. const CImg<float> img("reference.jpg"),
  31976. palette1(3,1,1,3, 0,128,255, 0,128,255, 0,128,255),
  31977. palette2(3,1,1,3, 255,0,0, 0,255,0, 0,0,255),
  31978. res = img.get_index(palette1,0).map(palette2);
  31979. (img,res).display();
  31980. \endcode
  31981. \image html ref_map.jpg
  31982. **/
  31983. template<typename t>
  31984. CImg<T>& map(const CImg<t>& palette, const unsigned int boundary_conditions=0) {
  31985. return get_map(palette,boundary_conditions).move_to(*this);
  31986. }
  31987. //! Map predefined palette on the scalar (indexed) image instance \newinstance.
  31988. template<typename t>
  31989. CImg<t> get_map(const CImg<t>& palette, const unsigned int boundary_conditions=0) const {
  31990. const ulongT
  31991. whd = (ulongT)_width*_height*_depth, siz = size(),
  31992. cwhd = (ulongT)palette._width*palette._height*palette._depth,
  31993. cwhd2 = 2*cwhd;
  31994. CImg<t> res(_width,_height,_depth,_spectrum*palette._spectrum);
  31995. switch (palette._spectrum) {
  31996. case 1 : { // Optimized for scalars
  31997. switch (boundary_conditions) {
  31998. case 3 : // Mirror
  31999. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
  32000. for (longT off = 0; off<(longT)siz; ++off) {
  32001. const ulongT ind = ((ulongT)_data[off])%cwhd2;
  32002. res[off] = palette[ind<cwhd?ind:cwhd2 - ind - 1];
  32003. }
  32004. break;
  32005. case 2 : // Periodic
  32006. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
  32007. for (longT off = 0; off<(longT)siz; ++off) {
  32008. const ulongT ind = (ulongT)_data[off];
  32009. res[off] = palette[ind%cwhd];
  32010. }
  32011. break;
  32012. case 1 : // Neumann
  32013. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
  32014. for (longT off = 0; off<(longT)siz; ++off) {
  32015. const longT ind = (longT)_data[off];
  32016. res[off] = palette[cimg::cut(ind,(longT)0,(longT)cwhd - 1)];
  32017. } break;
  32018. default : // Dirichlet
  32019. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
  32020. for (longT off = 0; off<(longT)siz; ++off) {
  32021. const ulongT ind = (ulongT)_data[off];
  32022. res[off] = ind<cwhd?palette[ind]:(t)0;
  32023. }
  32024. }
  32025. } break;
  32026. case 2 : { // Optimized for 2D vectors
  32027. const t *const ptrp0 = palette._data, *const ptrp1 = ptrp0 + cwhd;
  32028. switch (boundary_conditions) {
  32029. case 3 : // Mirror
  32030. cimg_forC(*this,c) {
  32031. t *const ptrd0 = res.data(0,0,0,2*c), *const ptrd1 = ptrd0 + whd;
  32032. const T *const ptrs = data(0,0,0,c);
  32033. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
  32034. for (longT off = 0; off<(longT)whd; ++off) {
  32035. const ulongT
  32036. _ind = ((ulongT)ptrs[off])%cwhd2,
  32037. ind = _ind<cwhd?_ind:cwhd2 - _ind - 1;
  32038. ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind];
  32039. }
  32040. } break;
  32041. case 2 : // Periodic
  32042. cimg_forC(*this,c) {
  32043. t *const ptrd0 = res.data(0,0,0,2*c), *const ptrd1 = ptrd0 + whd;
  32044. const T *const ptrs = data(0,0,0,c);
  32045. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
  32046. for (longT off = 0; off<(longT)whd; ++off) {
  32047. const ulongT ind = ((ulongT)ptrs[off])%cwhd;
  32048. ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind];
  32049. }
  32050. } break;
  32051. case 1 : // Neumann
  32052. cimg_forC(*this,c) {
  32053. t *const ptrd0 = res.data(0,0,0,2*c), *const ptrd1 = ptrd0 + whd;
  32054. const T *const ptrs = data(0,0,0,c);
  32055. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
  32056. for (longT off = 0; off<(longT)whd; ++off) {
  32057. const longT ind = cimg::cut((longT)ptrs[off],(longT)0,(longT)cwhd - 1);
  32058. ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind];
  32059. }
  32060. } break;
  32061. default : // Dirichlet
  32062. cimg_forC(*this,c) {
  32063. t *const ptrd0 = res.data(0,0,0,2*c), *const ptrd1 = ptrd0 + whd;
  32064. const T *const ptrs = data(0,0,0,c);
  32065. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
  32066. for (longT off = 0; off<(longT)whd; ++off) {
  32067. const ulongT ind = (ulongT)ptrs[off];
  32068. if (ind<cwhd) { ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; }
  32069. else ptrd0[off] = ptrd1[off] = (t)0;
  32070. }
  32071. }
  32072. }
  32073. } break;
  32074. case 3 : { // Optimized for 3D vectors (colors)
  32075. const t *const ptrp0 = palette._data, *ptrp1 = ptrp0 + cwhd, *ptrp2 = ptrp0 + 2*cwhd;
  32076. switch (boundary_conditions) {
  32077. case 3 : // Mirror
  32078. cimg_forC(*this,c) {
  32079. t *const ptrd0 = res.data(0,0,0,3*c), *const ptrd1 = ptrd0 + whd, *const ptrd2 = ptrd1 + whd;
  32080. const T *const ptrs = data(0,0,0,c);
  32081. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
  32082. for (longT off = 0; off<(longT)whd; ++off) {
  32083. const ulongT
  32084. _ind = ((ulongT)ptrs[off])%cwhd2,
  32085. ind = _ind<cwhd?_ind:cwhd2 - _ind - 1;
  32086. ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; ptrd2[off] = ptrp2[ind];
  32087. }
  32088. } break;
  32089. case 2 : // Periodic
  32090. cimg_forC(*this,c) {
  32091. t *const ptrd0 = res.data(0,0,0,3*c), *const ptrd1 = ptrd0 + whd, *const ptrd2 = ptrd1 + whd;
  32092. const T *const ptrs = data(0,0,0,c);
  32093. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
  32094. for (longT off = 0; off<(longT)whd; ++off) {
  32095. const ulongT ind = ((ulongT)ptrs[off])%cwhd;
  32096. ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; ptrd2[off] = ptrp2[ind];
  32097. }
  32098. } break;
  32099. case 1 : // Neumann
  32100. cimg_forC(*this,c) {
  32101. t *const ptrd0 = res.data(0,0,0,3*c), *const ptrd1 = ptrd0 + whd, *const ptrd2 = ptrd1 + whd;
  32102. const T *const ptrs = data(0,0,0,c);
  32103. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
  32104. for (longT off = 0; off<(longT)whd; ++off) {
  32105. const longT ind = cimg::cut((longT)ptrs[off],(longT)0,(longT)cwhd - 1);
  32106. ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; ptrd2[off] = ptrp2[ind];
  32107. }
  32108. } break;
  32109. default : // Dirichlet
  32110. cimg_forC(*this,c) {
  32111. t *const ptrd0 = res.data(0,0,0,3*c), *const ptrd1 = ptrd0 + whd, *const ptrd2 = ptrd1 + whd;
  32112. const T *const ptrs = data(0,0,0,c);
  32113. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
  32114. for (longT off = 0; off<(longT)whd; ++off) {
  32115. const ulongT ind = (ulongT)ptrs[off];
  32116. if (ind<cwhd) { ptrd0[off] = ptrp0[ind]; ptrd1[off] = ptrp1[ind]; ptrd2[off] = ptrp2[ind]; }
  32117. else ptrd0[off] = ptrd1[off] = ptrd2[off] = (t)0;
  32118. }
  32119. }
  32120. }
  32121. } break;
  32122. default : { // Generic version
  32123. switch (boundary_conditions) {
  32124. case 3 : // Mirror
  32125. cimg_forC(*this,c) {
  32126. t *const ptrd = res.data(0,0,0,palette._spectrum*c);
  32127. const T *const ptrs = data(0,0,0,c);
  32128. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
  32129. for (longT off = 0; off<(longT)whd; ++off) {
  32130. const ulongT
  32131. _ind = ((ulongT)ptrs[off])%cwhd,
  32132. ind = _ind<cwhd?_ind:cwhd2 - _ind - 1;
  32133. t *const _ptrd = ptrd + off;
  32134. const t *const ptrp = &palette[ind];
  32135. cimg_forC(palette,k) _ptrd[k*whd] = ptrp[k*cwhd];
  32136. }
  32137. } break;
  32138. case 2 : // Periodic
  32139. cimg_forC(*this,c) {
  32140. t *const ptrd = res.data(0,0,0,palette._spectrum*c);
  32141. const T *const ptrs = data(0,0,0,c);
  32142. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
  32143. for (longT off = 0; off<(longT)whd; ++off) {
  32144. const ulongT ind = ((ulongT)ptrs[off])%cwhd;
  32145. t *const _ptrd = ptrd + off;
  32146. const t *const ptrp = &palette[ind];
  32147. cimg_forC(palette,k) _ptrd[k*whd] = ptrp[k*cwhd];
  32148. }
  32149. } break;
  32150. case 1 : // Neumann
  32151. cimg_forC(*this,c) {
  32152. t *const ptrd = res.data(0,0,0,palette._spectrum*c);
  32153. const T *const ptrs = data(0,0,0,c);
  32154. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
  32155. for (longT off = 0; off<(longT)whd; ++off) {
  32156. const longT ind = cimg::cut((longT)ptrs[off],(longT)0,(longT)cwhd - 1);
  32157. t *const _ptrd = ptrd + off;
  32158. const t *const ptrp = &palette[ind];
  32159. cimg_forC(palette,k) _ptrd[k*whd] = ptrp[k*cwhd];
  32160. }
  32161. } break;
  32162. default : // Dirichlet
  32163. cimg_forC(*this,c) {
  32164. t *const ptrd = res.data(0,0,0,palette._spectrum*c);
  32165. const T *const ptrs = data(0,0,0,c);
  32166. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),256))
  32167. for (longT off = 0; off<(longT)whd; ++off) {
  32168. const ulongT ind = (ulongT)ptrs[off];
  32169. t *const _ptrd = ptrd + off;
  32170. if (ind<cwhd) {
  32171. const t *const ptrp = &palette[ind];
  32172. cimg_forC(palette,k) _ptrd[k*whd] = ptrp[k*cwhd];
  32173. } else cimg_forC(palette,k) _ptrd[k*whd] = (t)0;
  32174. }
  32175. }
  32176. }
  32177. }
  32178. }
  32179. return res;
  32180. }
  32181. //! Label connected components.
  32182. /**
  32183. \param is_high_connectivity Boolean that choose between 4(false)- or 8(true)-connectivity
  32184. in 2D case, and between 6(false)- or 26(true)-connectivity in 3D case.
  32185. \param tolerance Tolerance used to determine if two neighboring pixels belong to the same region.
  32186. \param is_L2_norm If true, tolerance is compared against L2 difference, otherwise L1 is used.
  32187. \note The algorithm of connected components computation has been primarily done
  32188. by A. Meijster, according to the publication:
  32189. 'W.H. Hesselink, A. Meijster, C. Bron, "Concurrent Determination of Connected Components.",
  32190. In: Science of Computer Programming 41 (2001), pp. 173--194'.
  32191. The submitted code has then been modified to fit CImg coding style and constraints.
  32192. **/
  32193. CImg<T>& label(const bool is_high_connectivity=false, const Tfloat tolerance=0,
  32194. const bool is_L2_norm=true) {
  32195. if (is_empty()) return *this;
  32196. return get_label(is_high_connectivity,tolerance,is_L2_norm).move_to(*this);
  32197. }
  32198. //! Label connected components \newinstance.
  32199. CImg<ulongT> get_label(const bool is_high_connectivity=false, const Tfloat tolerance=0,
  32200. const bool is_L2_norm=true) const {
  32201. if (is_empty()) return CImg<ulongT>();
  32202. // Create neighborhood tables.
  32203. int dx[13], dy[13], dz[13], nb = 0;
  32204. dx[nb] = 1; dy[nb] = 0; dz[nb++] = 0;
  32205. dx[nb] = 0; dy[nb] = 1; dz[nb++] = 0;
  32206. if (is_high_connectivity) {
  32207. dx[nb] = 1; dy[nb] = 1; dz[nb++] = 0;
  32208. dx[nb] = 1; dy[nb] = -1; dz[nb++] = 0;
  32209. }
  32210. if (_depth>1) { // 3D version
  32211. dx[nb] = 0; dy[nb] = 0; dz[nb++]=1;
  32212. if (is_high_connectivity) {
  32213. dx[nb] = 1; dy[nb] = 1; dz[nb++] = -1;
  32214. dx[nb] = 1; dy[nb] = 0; dz[nb++] = -1;
  32215. dx[nb] = 1; dy[nb] = -1; dz[nb++] = -1;
  32216. dx[nb] = 0; dy[nb] = 1; dz[nb++] = -1;
  32217. dx[nb] = 0; dy[nb] = 1; dz[nb++] = 1;
  32218. dx[nb] = 1; dy[nb] = -1; dz[nb++] = 1;
  32219. dx[nb] = 1; dy[nb] = 0; dz[nb++] = 1;
  32220. dx[nb] = 1; dy[nb] = 1; dz[nb++] = 1;
  32221. }
  32222. }
  32223. return _label(nb,dx,dy,dz,tolerance,is_L2_norm);
  32224. }
  32225. //! Label connected components \overloading.
  32226. /**
  32227. \param connectivity_mask Mask of the neighboring pixels.
  32228. \param tolerance Tolerance used to determine if two neighboring pixels belong to the same region.
  32229. \param is_L2_norm If true, tolerance is compared against L2 difference, otherwise L1 is used.
  32230. **/
  32231. template<typename t>
  32232. CImg<T>& label(const CImg<t>& connectivity_mask, const Tfloat tolerance=0,
  32233. const bool is_L2_norm=true) {
  32234. if (is_empty()) return *this;
  32235. return get_label(connectivity_mask,tolerance,is_L2_norm).move_to(*this);
  32236. }
  32237. //! Label connected components \newinstance.
  32238. template<typename t>
  32239. CImg<ulongT> get_label(const CImg<t>& connectivity_mask, const Tfloat tolerance=0,
  32240. const bool is_L2_norm=true) const {
  32241. if (is_empty()) return CImg<ulongT>();
  32242. int nb = 0;
  32243. cimg_for(connectivity_mask,ptr,t) if (*ptr) ++nb;
  32244. CImg<intT> dx(nb,1,1,1,0), dy(nb,1,1,1,0), dz(nb,1,1,1,0);
  32245. nb = 0;
  32246. cimg_forXYZ(connectivity_mask,x,y,z) if ((x || y || z) &&
  32247. connectivity_mask(x,y,z)) {
  32248. dx[nb] = x; dy[nb] = y; dz[nb++] = z;
  32249. }
  32250. return _label(nb,dx,dy,dz,tolerance,is_L2_norm);
  32251. }
  32252. CImg<ulongT> _label(const unsigned int nb, const int *const dx,
  32253. const int *const dy, const int *const dz,
  32254. const Tfloat tolerance, const bool is_L2_norm) const {
  32255. CImg<ulongT> res(_width,_height,_depth);
  32256. const Tfloat _tolerance = _spectrum>1 && is_L2_norm?cimg::sqr(tolerance):tolerance;
  32257. // Init label numbers.
  32258. ulongT *ptr = res.data();
  32259. cimg_foroff(res,p) *(ptr++) = p;
  32260. // For each neighbour-direction, label.
  32261. for (unsigned int n = 0; n<nb; ++n) {
  32262. const int _dx = dx[n], _dy = dy[n], _dz = dz[n];
  32263. if (_dx || _dy || _dz) {
  32264. const int
  32265. x0 = _dx<0?-_dx:0,
  32266. x1 = _dx<0?width():width() - _dx,
  32267. y0 = _dy<0?-_dy:0,
  32268. y1 = _dy<0?height():height() - _dy,
  32269. z0 = _dz<0?-_dz:0,
  32270. z1 = _dz<0?depth():depth() - _dz;
  32271. const longT
  32272. wh = (longT)width()*height(),
  32273. whd = (longT)width()*height()*depth(),
  32274. offset = _dz*wh + _dy*width() + _dx;
  32275. for (longT z = z0, nz = z0 + _dz, pz = z0*wh; z<z1; ++z, ++nz, pz+=wh) {
  32276. for (longT y = y0, ny = y0 + _dy, py = y0*width() + pz; y<y1; ++y, ++ny, py+=width()) {
  32277. for (longT x = x0, nx = x0 + _dx, p = x0 + py; x<x1; ++x, ++nx, ++p) {
  32278. Tfloat diff;
  32279. switch (_spectrum) {
  32280. case 1 :
  32281. diff = cimg::abs((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd));
  32282. break;
  32283. case 2 :
  32284. if (is_L2_norm)
  32285. diff = cimg::sqr((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
  32286. cimg::sqr((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd));
  32287. else
  32288. diff = cimg::abs((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
  32289. cimg::abs((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd));
  32290. break;
  32291. case 3 :
  32292. if (is_L2_norm)
  32293. diff = cimg::sqr((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
  32294. cimg::sqr((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd)) +
  32295. cimg::sqr((Tfloat)(*this)(x,y,z,2,wh,whd) - (Tfloat)(*this)(nx,ny,nz,2,wh,whd));
  32296. else
  32297. diff = cimg::abs((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
  32298. cimg::abs((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd)) +
  32299. cimg::abs((Tfloat)(*this)(x,y,z,2,wh,whd) - (Tfloat)(*this)(nx,ny,nz,2,wh,whd));
  32300. break;
  32301. case 4 :
  32302. if (is_L2_norm)
  32303. diff = cimg::sqr((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
  32304. cimg::sqr((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd)) +
  32305. cimg::sqr((Tfloat)(*this)(x,y,z,2,wh,whd) - (Tfloat)(*this)(nx,ny,nz,2,wh,whd)) +
  32306. cimg::sqr((Tfloat)(*this)(x,y,z,3,wh,whd) - (Tfloat)(*this)(nx,ny,nz,3,wh,whd));
  32307. else
  32308. diff = cimg::abs((Tfloat)(*this)(x,y,z,0,wh,whd) - (Tfloat)(*this)(nx,ny,nz,0,wh,whd)) +
  32309. cimg::abs((Tfloat)(*this)(x,y,z,1,wh,whd) - (Tfloat)(*this)(nx,ny,nz,1,wh,whd)) +
  32310. cimg::abs((Tfloat)(*this)(x,y,z,2,wh,whd) - (Tfloat)(*this)(nx,ny,nz,2,wh,whd)) +
  32311. cimg::abs((Tfloat)(*this)(x,y,z,3,wh,whd) - (Tfloat)(*this)(nx,ny,nz,3,wh,whd));
  32312. break;
  32313. default :
  32314. diff = 0;
  32315. if (is_L2_norm)
  32316. cimg_forC(*this,c)
  32317. diff+=cimg::sqr((Tfloat)(*this)(x,y,z,c,wh,whd) - (Tfloat)(*this)(nx,ny,nz,c,wh,whd));
  32318. else
  32319. cimg_forC(*this,c)
  32320. diff+=cimg::abs((Tfloat)(*this)(x,y,z,c,wh,whd) - (Tfloat)(*this)(nx,ny,nz,c,wh,whd));
  32321. }
  32322. if (diff<=_tolerance) {
  32323. const longT q = p + offset;
  32324. ulongT xk, yk;
  32325. for (xk = (ulongT)(p<q?q:p), yk = (ulongT)(p<q?p:q); xk!=yk && res[xk]!=xk; ) {
  32326. xk = res[xk]; if (xk<yk) cimg::swap(xk,yk);
  32327. }
  32328. if (xk!=yk) res[xk] = (ulongT)yk;
  32329. for (ulongT _p = (ulongT)p; _p!=yk; ) {
  32330. const ulongT h = res[_p];
  32331. res[_p] = (ulongT)yk;
  32332. _p = h;
  32333. }
  32334. for (ulongT _q = (ulongT)q; _q!=yk; ) {
  32335. const ulongT h = res[_q];
  32336. res[_q] = (ulongT)yk;
  32337. _q = h;
  32338. }
  32339. }
  32340. }
  32341. }
  32342. }
  32343. }
  32344. }
  32345. // Resolve equivalences.
  32346. ulongT counter = 0;
  32347. ptr = res.data();
  32348. cimg_foroff(res,p) { *ptr = *ptr==p?counter++:res[*ptr]; ++ptr; }
  32349. return res;
  32350. }
  32351. // [internal] Replace possibly malicious characters for commands to be called by system() by their escaped version.
  32352. CImg<T>& _system_strescape() {
  32353. #define cimg_system_strescape(c,s) case c : if (p!=ptrs) CImg<T>(ptrs,(unsigned int)(p-ptrs),1,1,1,false).\
  32354. move_to(list); \
  32355. CImg<T>(s,(unsigned int)std::strlen(s),1,1,1,false).move_to(list); ptrs = p + 1; break
  32356. CImgList<T> list;
  32357. const T *ptrs = _data;
  32358. cimg_for(*this,p,T) switch ((int)*p) {
  32359. cimg_system_strescape('\\',"\\\\");
  32360. cimg_system_strescape('\"',"\\\"");
  32361. cimg_system_strescape('!',"\"\\!\"");
  32362. cimg_system_strescape('`',"\\`");
  32363. cimg_system_strescape('$',"\\$");
  32364. }
  32365. if (ptrs<end()) CImg<T>(ptrs,(unsigned int)(end()-ptrs),1,1,1,false).move_to(list);
  32366. return (list>'x').move_to(*this);
  32367. }
  32368. //@}
  32369. //---------------------------------
  32370. //
  32371. //! \name Color Base Management
  32372. //@{
  32373. //---------------------------------
  32374. //! Return palette \e "default", containing 256 colors entries in RGB.
  32375. /**
  32376. \return The following \c 256x1x1x3 palette is returned:
  32377. \image html ref_colormap_default.jpg
  32378. **/
  32379. static const CImg<Tuchar>& default_LUT256() {
  32380. static CImg<Tuchar> palette;
  32381. cimg::mutex(8);
  32382. if (!palette) {
  32383. palette.assign(1,256,1,3);
  32384. for (unsigned int index = 0, r = 16; r<256; r+=32)
  32385. for (unsigned int g = 16; g<256; g+=32)
  32386. for (unsigned int b = 32; b<256; b+=64) {
  32387. palette(0,index,0) = (Tuchar)r;
  32388. palette(0,index,1) = (Tuchar)g;
  32389. palette(0,index++,2) = (Tuchar)b;
  32390. }
  32391. }
  32392. cimg::mutex(8,0);
  32393. return palette;
  32394. }
  32395. //! Return palette \e "HSV", containing 256 colors entries in RGB.
  32396. /**
  32397. \return The following \c 256x1x1x3 palette is returned:
  32398. \image html ref_colormap_hsv.jpg
  32399. **/
  32400. static const CImg<Tuchar>& HSV_LUT256() {
  32401. static CImg<Tuchar> palette;
  32402. cimg::mutex(8);
  32403. if (!palette) {
  32404. CImg<Tint> tmp(1,256,1,3,1);
  32405. tmp.get_shared_channel(0).sequence(0,359);
  32406. palette = tmp.HSVtoRGB();
  32407. }
  32408. cimg::mutex(8,0);
  32409. return palette;
  32410. }
  32411. //! Return palette \e "lines", containing 256 colors entries in RGB.
  32412. /**
  32413. \return The following \c 256x1x1x3 palette is returned:
  32414. \image html ref_colormap_lines.jpg
  32415. **/
  32416. static const CImg<Tuchar>& lines_LUT256() {
  32417. static const unsigned char pal[] = {
  32418. 0,255,255,0,0,28,125,125,235,210,186,182,36,0,125,255,
  32419. 53,32,255,210,89,186,65,45,125,210,210,97,130,194,0,125,
  32420. 206,53,190,89,255,146,20,190,154,73,255,36,130,215,0,138,
  32421. 101,210,61,194,206,0,77,45,255,154,174,0,190,239,89,125,
  32422. 16,36,158,223,117,0,97,69,223,255,40,239,0,0,255,0,
  32423. 97,170,93,255,138,40,117,210,0,170,53,158,186,255,0,121,
  32424. 227,121,186,40,20,190,89,255,77,57,130,142,255,73,186,85,
  32425. 210,8,32,166,243,130,210,40,255,45,61,142,223,49,121,255,
  32426. 20,162,158,73,89,255,53,138,210,190,57,235,36,73,255,49,
  32427. 210,0,210,85,57,97,255,121,85,174,40,255,162,178,0,121,
  32428. 166,125,53,146,166,255,97,121,65,89,235,231,12,170,36,190,
  32429. 85,255,166,97,198,77,20,146,109,166,255,28,40,202,121,81,
  32430. 247,0,210,255,49,0,65,255,36,166,93,77,255,85,251,0,
  32431. 170,178,0,182,255,0,162,16,154,142,162,223,223,0,0,81,
  32432. 215,4,215,162,215,125,77,206,121,36,125,231,101,16,255,121,
  32433. 0,57,190,215,65,125,89,142,255,101,73,53,146,223,125,125,
  32434. 0,255,0,255,0,206,93,138,49,255,0,202,154,85,45,219,
  32435. 251,53,0,255,40,130,219,158,16,117,186,130,202,49,65,239,
  32436. 89,202,49,28,247,134,150,0,255,117,202,4,215,81,186,57,
  32437. 202,89,73,210,40,93,45,251,206,28,223,142,40,134,162,125,
  32438. 32,247,97,170,0,255,57,134,73,247,162,0,251,40,142,142,
  32439. 8,166,206,81,154,194,93,89,125,243,28,109,227,0,190,65,
  32440. 194,186,0,255,53,45,109,186,186,0,255,130,49,170,69,210,
  32441. 154,0,109,227,45,255,125,105,81,81,255,0,219,134,170,85,
  32442. 146,28,170,89,223,97,8,210,255,158,49,40,125,174,174,125,
  32443. 0,227,166,28,219,130,0,93,239,0,85,255,81,178,125,49,
  32444. 89,255,53,206,73,113,146,255,0,150,36,219,162,0,210,125,
  32445. 69,134,255,85,40,89,235,49,215,121,0,206,36,223,174,69,
  32446. 40,182,178,130,69,45,255,210,85,77,215,0,231,146,0,194,
  32447. 125,174,0,255,40,89,121,206,57,0,206,170,231,150,81,0,
  32448. 125,255,4,174,4,190,121,255,4,166,109,130,49,239,170,93,
  32449. 16,174,210,0,255,16,105,158,93,255,0,125,0,255,158,85,
  32450. 0,255,0,0,255,170,166,61,121,28,198,215,45,243,61,97,
  32451. 255,53,81,130,109,255,8,117,235,121,40,178,174,0,182,49,
  32452. 162,121,255,69,206,0,219,125,0,101,255,239,121,32,210,130,
  32453. 36,231,32,125,81,142,215,158,4,178,255,0,40,251,125,125,
  32454. 219,89,130,0,166,255,24,65,194,125,255,125,77,125,93,125,
  32455. 202,24,138,174,178,32,255,85,194,40,85,36,174,174,125,210,
  32456. 85,255,53,16,93,206,40,130,170,202,93,255,0,24,117,255,
  32457. 97,113,105,81,255,186,194,57,69,206,57,53,223,190,4,255,
  32458. 85,97,130,255,85,0,125,223,85,219,0,215,146,77,40,239,
  32459. 89,36,142,154,227,0,255,85,162,0,162,0,235,178,45,166,
  32460. 0,247,255,20,69,210,89,142,53,255,40,146,166,255,69,0,
  32461. 174,154,142,130,162,0,215,255,0,89,40,255,166,61,146,69,
  32462. 162,40,255,32,121,255,117,178,0,186,206,0,57,215,215,81,
  32463. 158,77,166,210,77,89,210,0,24,202,150,186,0,255,20,97,
  32464. 57,170,235,251,16,73,142,251,93,0,202,0,255,121,219,4,
  32465. 73,219,8,162,206,16,219,93,117,0,255,8,130,174,223,45 };
  32466. static const CImg<Tuchar> palette(pal,1,256,1,3,false);
  32467. return palette;
  32468. }
  32469. //! Return palette \e "hot", containing 256 colors entries in RGB.
  32470. /**
  32471. \return The following \c 256x1x1x3 palette is returned:
  32472. \image html ref_colormap_hot.jpg
  32473. **/
  32474. static const CImg<Tuchar>& hot_LUT256() {
  32475. static CImg<Tuchar> palette;
  32476. cimg::mutex(8);
  32477. if (!palette) {
  32478. palette.assign(1,4,1,3,(T)0);
  32479. palette[1] = palette[2] = palette[3] = palette[6] = palette[7] = palette[11] = 255;
  32480. palette.resize(1,256,1,3,3);
  32481. }
  32482. cimg::mutex(8,0);
  32483. return palette;
  32484. }
  32485. //! Return palette \e "cool", containing 256 colors entries in RGB.
  32486. /**
  32487. \return The following \c 256x1x1x3 palette is returned:
  32488. \image html ref_colormap_cool.jpg
  32489. **/
  32490. static const CImg<Tuchar>& cool_LUT256() {
  32491. static CImg<Tuchar> palette;
  32492. cimg::mutex(8);
  32493. 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);
  32494. cimg::mutex(8,0);
  32495. return palette;
  32496. }
  32497. //! Return palette \e "jet", containing 256 colors entries in RGB.
  32498. /**
  32499. \return The following \c 256x1x1x3 palette is returned:
  32500. \image html ref_colormap_jet.jpg
  32501. **/
  32502. static const CImg<Tuchar>& jet_LUT256() {
  32503. static CImg<Tuchar> palette;
  32504. cimg::mutex(8);
  32505. if (!palette) {
  32506. palette.assign(1,4,1,3,(T)0);
  32507. palette[2] = palette[3] = palette[5] = palette[6] = palette[8] = palette[9] = 255;
  32508. palette.resize(1,256,1,3,3);
  32509. }
  32510. cimg::mutex(8,0);
  32511. return palette;
  32512. }
  32513. //! Return palette \e "flag", containing 256 colors entries in RGB.
  32514. /**
  32515. \return The following \c 256x1x1x3 colormap is returned:
  32516. \image html ref_palette_flag.jpg
  32517. **/
  32518. static const CImg<Tuchar>& flag_LUT256() {
  32519. static CImg<Tuchar> palette;
  32520. cimg::mutex(8);
  32521. if (!palette) {
  32522. palette.assign(1,4,1,3,(T)0);
  32523. palette[0] = palette[1] = palette[5] = palette[9] = palette[10] = 255;
  32524. palette.resize(1,256,1,3,0,2);
  32525. }
  32526. cimg::mutex(8,0);
  32527. return palette;
  32528. }
  32529. //! Return palette \e "cube", containing 256 colors entries in RGB.
  32530. /**
  32531. \return The following \c 256x1x1x3 palette is returned:
  32532. \image html ref_colormap_cube.jpg
  32533. **/
  32534. static const CImg<Tuchar>& cube_LUT256() {
  32535. static CImg<Tuchar> palette;
  32536. cimg::mutex(8);
  32537. if (!palette) {
  32538. palette.assign(1,8,1,3,(T)0);
  32539. palette[1] = palette[3] = palette[5] = palette[7] =
  32540. palette[10] = palette[11] = palette[12] = palette[13] =
  32541. palette[20] = palette[21] = palette[22] = palette[23] = 255;
  32542. palette.resize(1,256,1,3,3);
  32543. }
  32544. cimg::mutex(8,0);
  32545. return palette;
  32546. }
  32547. //! Convert pixel values from sRGB to RGB color spaces.
  32548. CImg<T>& sRGBtoRGB() {
  32549. if (is_empty()) return *this;
  32550. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32))
  32551. cimg_rofoff(*this,off) {
  32552. const Tfloat
  32553. sval = (Tfloat)_data[off]/255,
  32554. val = (Tfloat)(sval<=0.04045f?sval/12.92f:std::pow((sval + 0.055f)/(1.055f),2.4f));
  32555. _data[off] = (T)cimg::cut(val*255,0,255);
  32556. }
  32557. return *this;
  32558. }
  32559. //! Convert pixel values from sRGB to RGB color spaces \newinstance.
  32560. CImg<Tfloat> get_sRGBtoRGB() const {
  32561. return CImg<Tfloat>(*this,false).sRGBtoRGB();
  32562. }
  32563. //! Convert pixel values from RGB to sRGB color spaces.
  32564. CImg<T>& RGBtosRGB() {
  32565. if (is_empty()) return *this;
  32566. cimg_pragma_openmp(parallel for cimg_openmp_if_size(size(),32))
  32567. cimg_rofoff(*this,off) {
  32568. const Tfloat
  32569. val = (Tfloat)_data[off]/255,
  32570. sval = (Tfloat)(val<=0.0031308f?val*12.92f:1.055f*std::pow(val,0.416667f) - 0.055f);
  32571. _data[off] = (T)cimg::cut(sval*255,0,255);
  32572. }
  32573. return *this;
  32574. }
  32575. //! Convert pixel values from RGB to sRGB color spaces \newinstance.
  32576. CImg<Tfloat> get_RGBtosRGB() const {
  32577. return CImg<Tfloat>(*this,false).RGBtosRGB();
  32578. }
  32579. //! Convert pixel values from RGB to HSI color spaces.
  32580. CImg<T>& RGBtoHSI() {
  32581. if (_spectrum!=3)
  32582. throw CImgInstanceException(_cimg_instance
  32583. "RGBtoHSI(): Instance is not a RGB image.",
  32584. cimg_instance);
  32585. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  32586. const longT whd = (longT)width()*height()*depth();
  32587. cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
  32588. for (longT N = 0; N<whd; ++N) {
  32589. const Tfloat
  32590. R = (Tfloat)p1[N],
  32591. G = (Tfloat)p2[N],
  32592. B = (Tfloat)p3[N],
  32593. m = cimg::min(R,G,B),
  32594. M = cimg::max(R,G,B),
  32595. C = M - m,
  32596. sum = R + G + B,
  32597. H = 60*(C==0?0:M==R?cimg::mod((G - B)/C,(Tfloat)6):M==G?(B - R)/C + 2:(R - G)/C + 4),
  32598. S = sum<=0?0:1 - 3*m/sum,
  32599. I = sum/(3*255);
  32600. p1[N] = (T)H;
  32601. p2[N] = (T)S;
  32602. p3[N] = (T)I;
  32603. }
  32604. return *this;
  32605. }
  32606. //! Convert pixel values from RGB to HSI color spaces \newinstance.
  32607. CImg<Tfloat> get_RGBtoHSI() const {
  32608. return CImg<Tfloat>(*this,false).RGBtoHSI();
  32609. }
  32610. //! Convert pixel values from HSI to RGB color spaces.
  32611. CImg<T>& HSItoRGB() {
  32612. if (_spectrum!=3)
  32613. throw CImgInstanceException(_cimg_instance
  32614. "HSItoRGB(): Instance is not a HSI image.",
  32615. cimg_instance);
  32616. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  32617. const longT whd = (longT)width()*height()*depth();
  32618. cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
  32619. for (longT N = 0; N<whd; ++N) {
  32620. const Tfloat
  32621. H = cimg::mod((Tfloat)p1[N]/60,(Tfloat)6),
  32622. S = (Tfloat)p2[N],
  32623. I = (Tfloat)p3[N],
  32624. Z = 1 - cimg::abs(cimg::mod(H,(Tfloat)2) - 1),
  32625. C = I*S/(1 + Z),
  32626. X = C*Z,
  32627. m = I*(1 - S)/3;
  32628. Tfloat R, G, B;
  32629. switch ((int)H) {
  32630. case 0 : R = C; G = X; B = 0; break;
  32631. case 1 : R = X; G = C; B = 0; break;
  32632. case 2 : R = 0; G = C; B = X; break;
  32633. case 3 : R = 0; G = X; B = C; break;
  32634. case 4 : R = X; G = 0; B = C; break;
  32635. default : R = C; G = 0; B = X;
  32636. }
  32637. p1[N] = (T)((R + m)*3*255);
  32638. p2[N] = (T)((G + m)*3*255);
  32639. p3[N] = (T)((B + m)*3*255);
  32640. }
  32641. return *this;
  32642. }
  32643. //! Convert pixel values from HSI to RGB color spaces \newinstance.
  32644. CImg<Tfloat> get_HSItoRGB() const {
  32645. return CImg< Tuchar>(*this,false).HSItoRGB();
  32646. }
  32647. //! Convert pixel values from RGB to HSL color spaces.
  32648. CImg<T>& RGBtoHSL() {
  32649. if (_spectrum!=3)
  32650. throw CImgInstanceException(_cimg_instance
  32651. "RGBtoHSL(): Instance is not a RGB image.",
  32652. cimg_instance);
  32653. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  32654. const longT whd = (longT)width()*height()*depth();
  32655. cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
  32656. for (longT N = 0; N<whd; ++N) {
  32657. const Tfloat
  32658. R = (Tfloat)p1[N],
  32659. G = (Tfloat)p2[N],
  32660. B = (Tfloat)p3[N],
  32661. m = cimg::min(R,G,B),
  32662. M = cimg::max(R,G,B),
  32663. C = M - m,
  32664. H = 60*(C==0?0:M==R?cimg::mod((G - B)/C,(Tfloat)6):M==G?(B - R)/C + 2:(R - G)/C + 4),
  32665. L = 0.5f*(m + M)/255,
  32666. S = L==1 || L==0?0:C/(1 - cimg::abs(2*L - 1))/255;
  32667. p1[N] = (T)H;
  32668. p2[N] = (T)S;
  32669. p3[N] = (T)L;
  32670. }
  32671. return *this;
  32672. }
  32673. //! Convert pixel values from RGB to HSL color spaces \newinstance.
  32674. CImg<Tfloat> get_RGBtoHSL() const {
  32675. return CImg<Tfloat>(*this,false).RGBtoHSL();
  32676. }
  32677. //! Convert pixel values from HSL to RGB color spaces.
  32678. CImg<T>& HSLtoRGB() {
  32679. if (_spectrum!=3)
  32680. throw CImgInstanceException(_cimg_instance
  32681. "HSLtoRGB(): Instance is not a HSL image.",
  32682. cimg_instance);
  32683. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  32684. const longT whd = (longT)width()*height()*depth();
  32685. cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
  32686. for (longT N = 0; N<whd; ++N) {
  32687. const Tfloat
  32688. H = cimg::mod((Tfloat)p1[N]/60,(Tfloat)6),
  32689. S = (Tfloat)p2[N],
  32690. L = (Tfloat)p3[N],
  32691. C = (1 - cimg::abs(2*L - 1))*S,
  32692. X = C*(1 - cimg::abs(cimg::mod(H,(Tfloat)2) - 1)),
  32693. m = L - C/2;
  32694. Tfloat R, G, B;
  32695. switch ((int)H) {
  32696. case 0 : R = C; G = X; B = 0; break;
  32697. case 1 : R = X; G = C; B = 0; break;
  32698. case 2 : R = 0; G = C; B = X; break;
  32699. case 3 : R = 0; G = X; B = C; break;
  32700. case 4 : R = X; G = 0; B = C; break;
  32701. default : R = C; G = 0; B = X;
  32702. }
  32703. p1[N] = (T)((R + m)*255);
  32704. p2[N] = (T)((G + m)*255);
  32705. p3[N] = (T)((B + m)*255);
  32706. }
  32707. return *this;
  32708. }
  32709. //! Convert pixel values from HSL to RGB color spaces \newinstance.
  32710. CImg<Tuchar> get_HSLtoRGB() const {
  32711. return CImg<Tuchar>(*this,false).HSLtoRGB();
  32712. }
  32713. //! Convert pixel values from RGB to HSV color spaces.
  32714. CImg<T>& RGBtoHSV() {
  32715. if (_spectrum!=3)
  32716. throw CImgInstanceException(_cimg_instance
  32717. "RGBtoHSV(): Instance is not a RGB image.",
  32718. cimg_instance);
  32719. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  32720. const longT whd = (longT)width()*height()*depth();
  32721. cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
  32722. for (longT N = 0; N<whd; ++N) {
  32723. const Tfloat
  32724. R = (Tfloat)p1[N],
  32725. G = (Tfloat)p2[N],
  32726. B = (Tfloat)p3[N],
  32727. M = cimg::max(R,G,B),
  32728. C = M - cimg::min(R,G,B),
  32729. H = 60*(C==0?0:M==R?cimg::mod((G-B)/C,(Tfloat)6):M==G?(B - R)/C + 2:(R - G)/C + 4),
  32730. S = M<=0?0:C/M;
  32731. p1[N] = (T)H;
  32732. p2[N] = (T)S;
  32733. p3[N] = (T)(M/255);
  32734. }
  32735. return *this;
  32736. }
  32737. //! Convert pixel values from RGB to HSV color spaces \newinstance.
  32738. CImg<Tfloat> get_RGBtoHSV() const {
  32739. return CImg<Tfloat>(*this,false).RGBtoHSV();
  32740. }
  32741. //! Convert pixel values from HSV to RGB color spaces.
  32742. CImg<T>& HSVtoRGB() {
  32743. if (_spectrum!=3)
  32744. throw CImgInstanceException(_cimg_instance
  32745. "HSVtoRGB(): Instance is not a HSV image.",
  32746. cimg_instance);
  32747. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  32748. const longT whd = (longT)width()*height()*depth();
  32749. cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,256))
  32750. for (longT N = 0; N<whd; ++N) {
  32751. Tfloat
  32752. H = cimg::mod((Tfloat)p1[N]/60,(Tfloat)6),
  32753. S = (Tfloat)p2[N],
  32754. V = (Tfloat)p3[N],
  32755. C = V*S,
  32756. X = C*(1 - cimg::abs(cimg::mod(H,(Tfloat)2) - 1)),
  32757. m = V - C;
  32758. Tfloat R, G, B;
  32759. switch ((int)H) {
  32760. case 0 : R = C; G = X; B = 0; break;
  32761. case 1 : R = X; G = C; B = 0; break;
  32762. case 2 : R = 0; G = C; B = X; break;
  32763. case 3 : R = 0; G = X; B = C; break;
  32764. case 4 : R = X; G = 0; B = C; break;
  32765. default : R = C; G = 0; B = X;
  32766. }
  32767. p1[N] = (T)((R + m)*255);
  32768. p2[N] = (T)((G + m)*255);
  32769. p3[N] = (T)((B + m)*255);
  32770. }
  32771. return *this;
  32772. }
  32773. //! Convert pixel values from HSV to RGB color spaces \newinstance.
  32774. CImg<Tuchar> get_HSVtoRGB() const {
  32775. return CImg<Tuchar>(*this,false).HSVtoRGB();
  32776. }
  32777. //! Convert pixel values from RGB to YCbCr color spaces.
  32778. CImg<T>& RGBtoYCbCr() {
  32779. if (_spectrum!=3)
  32780. throw CImgInstanceException(_cimg_instance
  32781. "RGBtoYCbCr(): Instance is not a RGB image.",
  32782. cimg_instance);
  32783. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  32784. const longT whd = (longT)width()*height()*depth();
  32785. cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,512))
  32786. for (longT N = 0; N<whd; ++N) {
  32787. const Tfloat
  32788. R = (Tfloat)p1[N],
  32789. G = (Tfloat)p2[N],
  32790. B = (Tfloat)p3[N],
  32791. Y = (66*R + 129*G + 25*B + 128)/256 + 16,
  32792. Cb = (-38*R - 74*G + 112*B + 128)/256 + 128,
  32793. Cr = (112*R - 94*G - 18*B + 128)/256 + 128;
  32794. p1[N] = (T)cimg::cut(Y,0,255),
  32795. p2[N] = (T)cimg::cut(Cb,0,255),
  32796. p3[N] = (T)cimg::cut(Cr,0,255);
  32797. }
  32798. return *this;
  32799. }
  32800. //! Convert pixel values from RGB to YCbCr color spaces \newinstance.
  32801. CImg<Tuchar> get_RGBtoYCbCr() const {
  32802. return CImg<Tuchar>(*this,false).RGBtoYCbCr();
  32803. }
  32804. //! Convert pixel values from RGB to YCbCr color spaces.
  32805. CImg<T>& YCbCrtoRGB() {
  32806. if (_spectrum!=3)
  32807. throw CImgInstanceException(_cimg_instance
  32808. "YCbCrtoRGB(): Instance is not a YCbCr image.",
  32809. cimg_instance);
  32810. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  32811. const longT whd = (longT)width()*height()*depth();
  32812. cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,512))
  32813. for (longT N = 0; N<whd; ++N) {
  32814. const Tfloat
  32815. Y = (Tfloat)p1[N] - 16,
  32816. Cb = (Tfloat)p2[N] - 128,
  32817. Cr = (Tfloat)p3[N] - 128,
  32818. R = (298*Y + 409*Cr + 128)/256,
  32819. G = (298*Y - 100*Cb - 208*Cr + 128)/256,
  32820. B = (298*Y + 516*Cb + 128)/256;
  32821. p1[N] = (T)cimg::cut(R,0,255),
  32822. p2[N] = (T)cimg::cut(G,0,255),
  32823. p3[N] = (T)cimg::cut(B,0,255);
  32824. }
  32825. return *this;
  32826. }
  32827. //! Convert pixel values from RGB to YCbCr color spaces \newinstance.
  32828. CImg<Tuchar> get_YCbCrtoRGB() const {
  32829. return CImg<Tuchar>(*this,false).YCbCrtoRGB();
  32830. }
  32831. //! Convert pixel values from RGB to YUV color spaces.
  32832. CImg<T>& RGBtoYUV() {
  32833. if (_spectrum!=3)
  32834. throw CImgInstanceException(_cimg_instance
  32835. "RGBtoYUV(): Instance is not a RGB image.",
  32836. cimg_instance);
  32837. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  32838. const longT whd = (longT)width()*height()*depth();
  32839. cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,16384))
  32840. for (longT N = 0; N<whd; ++N) {
  32841. const Tfloat
  32842. R = (Tfloat)p1[N]/255,
  32843. G = (Tfloat)p2[N]/255,
  32844. B = (Tfloat)p3[N]/255,
  32845. Y = 0.299f*R + 0.587f*G + 0.114f*B;
  32846. p1[N] = (T)Y;
  32847. p2[N] = (T)(0.492f*(B - Y));
  32848. p3[N] = (T)(0.877*(R - Y));
  32849. }
  32850. return *this;
  32851. }
  32852. //! Convert pixel values from RGB to YUV color spaces \newinstance.
  32853. CImg<Tfloat> get_RGBtoYUV() const {
  32854. return CImg<Tfloat>(*this,false).RGBtoYUV();
  32855. }
  32856. //! Convert pixel values from YUV to RGB color spaces.
  32857. CImg<T>& YUVtoRGB() {
  32858. if (_spectrum!=3)
  32859. throw CImgInstanceException(_cimg_instance
  32860. "YUVtoRGB(): Instance is not a YUV image.",
  32861. cimg_instance);
  32862. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  32863. const longT whd = (longT)width()*height()*depth();
  32864. cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,16384))
  32865. for (longT N = 0; N<whd; ++N) {
  32866. const Tfloat
  32867. Y = (Tfloat)p1[N],
  32868. U = (Tfloat)p2[N],
  32869. V = (Tfloat)p3[N],
  32870. R = (Y + 1.140f*V)*255,
  32871. G = (Y - 0.395f*U - 0.581f*V)*255,
  32872. B = (Y + 2.032f*U)*255;
  32873. p1[N] = (T)cimg::cut(R,0,255),
  32874. p2[N] = (T)cimg::cut(G,0,255),
  32875. p3[N] = (T)cimg::cut(B,0,255);
  32876. }
  32877. return *this;
  32878. }
  32879. //! Convert pixel values from YUV to RGB color spaces \newinstance.
  32880. CImg<Tuchar> get_YUVtoRGB() const {
  32881. return CImg< Tuchar>(*this,false).YUVtoRGB();
  32882. }
  32883. //! Convert pixel values from RGB to CMY color spaces.
  32884. CImg<T>& RGBtoCMY() {
  32885. if (_spectrum!=3)
  32886. throw CImgInstanceException(_cimg_instance
  32887. "RGBtoCMY(): Instance is not a RGB image.",
  32888. cimg_instance);
  32889. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  32890. const longT whd = (longT)width()*height()*depth();
  32891. cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048))
  32892. for (longT N = 0; N<whd; ++N) {
  32893. const Tfloat
  32894. R = (Tfloat)p1[N],
  32895. G = (Tfloat)p2[N],
  32896. B = (Tfloat)p3[N],
  32897. C = 255 - R,
  32898. M = 255 - G,
  32899. Y = 255 - B;
  32900. p1[N] = (T)cimg::cut(C,0,255),
  32901. p2[N] = (T)cimg::cut(M,0,255),
  32902. p3[N] = (T)cimg::cut(Y,0,255);
  32903. }
  32904. return *this;
  32905. }
  32906. //! Convert pixel values from RGB to CMY color spaces \newinstance.
  32907. CImg<Tuchar> get_RGBtoCMY() const {
  32908. return CImg<Tfloat>(*this,false).RGBtoCMY();
  32909. }
  32910. //! Convert pixel values from CMY to RGB color spaces.
  32911. CImg<T>& CMYtoRGB() {
  32912. if (_spectrum!=3)
  32913. throw CImgInstanceException(_cimg_instance
  32914. "CMYtoRGB(): Instance is not a CMY image.",
  32915. cimg_instance);
  32916. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  32917. const longT whd = (longT)width()*height()*depth();
  32918. cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048))
  32919. for (longT N = 0; N<whd; ++N) {
  32920. const Tfloat
  32921. C = (Tfloat)p1[N],
  32922. M = (Tfloat)p2[N],
  32923. Y = (Tfloat)p3[N],
  32924. R = 255 - C,
  32925. G = 255 - M,
  32926. B = 255 - Y;
  32927. p1[N] = (T)cimg::cut(R,0,255),
  32928. p2[N] = (T)cimg::cut(G,0,255),
  32929. p3[N] = (T)cimg::cut(B,0,255);
  32930. }
  32931. return *this;
  32932. }
  32933. //! Convert pixel values from CMY to RGB color spaces \newinstance.
  32934. CImg<Tuchar> get_CMYtoRGB() const {
  32935. return CImg<Tuchar>(*this,false).CMYtoRGB();
  32936. }
  32937. //! Convert pixel values from CMY to CMYK color spaces.
  32938. CImg<T>& CMYtoCMYK() {
  32939. return get_CMYtoCMYK().move_to(*this);
  32940. }
  32941. //! Convert pixel values from CMY to CMYK color spaces \newinstance.
  32942. CImg<Tuchar> get_CMYtoCMYK() const {
  32943. if (_spectrum!=3)
  32944. throw CImgInstanceException(_cimg_instance
  32945. "CMYtoCMYK(): Instance is not a CMY image.",
  32946. cimg_instance);
  32947. CImg<Tfloat> res(_width,_height,_depth,4);
  32948. const T *ps1 = data(0,0,0,0), *ps2 = data(0,0,0,1), *ps3 = data(0,0,0,2);
  32949. 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);
  32950. const longT whd = (longT)width()*height()*depth();
  32951. cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,1024))
  32952. for (longT N = 0; N<whd; ++N) {
  32953. Tfloat
  32954. C = (Tfloat)ps1[N],
  32955. M = (Tfloat)ps2[N],
  32956. Y = (Tfloat)ps3[N],
  32957. K = cimg::min(C,M,Y);
  32958. if (K>=255) C = M = Y = 0;
  32959. else { const Tfloat K1 = 255 - K; C = 255*(C - K)/K1; M = 255*(M - K)/K1; Y = 255*(Y - K)/K1; }
  32960. pd1[N] = (Tfloat)cimg::cut(C,0,255),
  32961. pd2[N] = (Tfloat)cimg::cut(M,0,255),
  32962. pd3[N] = (Tfloat)cimg::cut(Y,0,255),
  32963. pd4[N] = (Tfloat)cimg::cut(K,0,255);
  32964. }
  32965. return res;
  32966. }
  32967. //! Convert pixel values from CMYK to CMY color spaces.
  32968. CImg<T>& CMYKtoCMY() {
  32969. return get_CMYKtoCMY().move_to(*this);
  32970. }
  32971. //! Convert pixel values from CMYK to CMY color spaces \newinstance.
  32972. CImg<Tfloat> get_CMYKtoCMY() const {
  32973. if (_spectrum!=4)
  32974. throw CImgInstanceException(_cimg_instance
  32975. "CMYKtoCMY(): Instance is not a CMYK image.",
  32976. cimg_instance);
  32977. CImg<Tfloat> res(_width,_height,_depth,3);
  32978. 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);
  32979. Tfloat *pd1 = res.data(0,0,0,0), *pd2 = res.data(0,0,0,1), *pd3 = res.data(0,0,0,2);
  32980. const longT whd = (longT)width()*height()*depth();
  32981. cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,1024))
  32982. for (longT N = 0; N<whd; ++N) {
  32983. const Tfloat
  32984. C = (Tfloat)ps1[N],
  32985. M = (Tfloat)ps2[N],
  32986. Y = (Tfloat)ps3[N],
  32987. K = (Tfloat)ps4[N],
  32988. K1 = 1 - K/255,
  32989. nC = C*K1 + K,
  32990. nM = M*K1 + K,
  32991. nY = Y*K1 + K;
  32992. pd1[N] = (Tfloat)cimg::cut(nC,0,255),
  32993. pd2[N] = (Tfloat)cimg::cut(nM,0,255),
  32994. pd3[N] = (Tfloat)cimg::cut(nY,0,255);
  32995. }
  32996. return res;
  32997. }
  32998. //! Convert pixel values from RGB to XYZ color spaces.
  32999. /**
  33000. \param use_D65 Tell to use the D65 illuminant (D50 otherwise).
  33001. **/
  33002. CImg<T>& RGBtoXYZ(const bool use_D65=true) {
  33003. if (_spectrum!=3)
  33004. throw CImgInstanceException(_cimg_instance
  33005. "RGBtoXYZ(): Instance is not a RGB image.",
  33006. cimg_instance);
  33007. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  33008. const longT whd = (longT)width()*height()*depth();
  33009. cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048))
  33010. for (longT N = 0; N<whd; ++N) {
  33011. const Tfloat
  33012. R = (Tfloat)p1[N]/255,
  33013. G = (Tfloat)p2[N]/255,
  33014. B = (Tfloat)p3[N]/255;
  33015. if (use_D65) { // D65
  33016. p1[N] = (T)(0.4124564*R + 0.3575761*G + 0.1804375*B);
  33017. p2[N] = (T)(0.2126729*R + 0.7151522*G + 0.0721750*B);
  33018. p3[N] = (T)(0.0193339*R + 0.1191920*G + 0.9503041*B);
  33019. } else { // D50
  33020. p1[N] = (T)(0.43603516*R + 0.38511658*G + 0.14305115*B);
  33021. p2[N] = (T)(0.22248840*R + 0.71690369*G + 0.06060791*B);
  33022. p3[N] = (T)(0.01391602*R + 0.09706116*G + 0.71392822*B);
  33023. }
  33024. }
  33025. return *this;
  33026. }
  33027. //! Convert pixel values from RGB to XYZ color spaces \newinstance.
  33028. CImg<Tfloat> get_RGBtoXYZ(const bool use_D65=true) const {
  33029. return CImg<Tfloat>(*this,false).RGBtoXYZ(use_D65);
  33030. }
  33031. //! Convert pixel values from XYZ to RGB color spaces.
  33032. /**
  33033. \param use_D65 Tell to use the D65 illuminant (D50 otherwise).
  33034. **/
  33035. CImg<T>& XYZtoRGB(const bool use_D65=true) {
  33036. if (_spectrum!=3)
  33037. throw CImgInstanceException(_cimg_instance
  33038. "XYZtoRGB(): Instance is not a XYZ image.",
  33039. cimg_instance);
  33040. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  33041. const longT whd = (longT)width()*height()*depth();
  33042. cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,2048))
  33043. for (longT N = 0; N<whd; ++N) {
  33044. const Tfloat
  33045. X = (Tfloat)p1[N]*255,
  33046. Y = (Tfloat)p2[N]*255,
  33047. Z = (Tfloat)p3[N]*255;
  33048. if (use_D65) {
  33049. p1[N] = (T)cimg::cut(3.2404542*X - 1.5371385*Y - 0.4985314*Z,0,255);
  33050. p2[N] = (T)cimg::cut(-0.9692660*X + 1.8760108*Y + 0.0415560*Z,0,255);
  33051. p3[N] = (T)cimg::cut(0.0556434*X - 0.2040259*Y + 1.0572252*Z,0,255);
  33052. } else {
  33053. p1[N] = (T)cimg::cut(3.134274799724*X - 1.617275708956*Y - 0.490724283042*Z,0,255);
  33054. p2[N] = (T)cimg::cut(-0.978795575994*X + 1.916161689117*Y + 0.033453331711*Z,0,255);
  33055. p3[N] = (T)cimg::cut(0.071976988401*X - 0.228984974402*Y + 1.405718224383*Z,0,255);
  33056. }
  33057. }
  33058. return *this;
  33059. }
  33060. //! Convert pixel values from XYZ to RGB color spaces \newinstance.
  33061. CImg<Tuchar> get_XYZtoRGB(const bool use_D65=true) const {
  33062. return CImg<Tuchar>(*this,false).XYZtoRGB(use_D65);
  33063. }
  33064. //! Convert pixel values from XYZ to Lab color spaces.
  33065. CImg<T>& XYZtoLab(const bool use_D65=true) {
  33066. #define _cimg_Labf(x) (24389*(x)>216?cimg::cbrt(x):(24389*(x)/27 + 16)/116)
  33067. if (_spectrum!=3)
  33068. throw CImgInstanceException(_cimg_instance
  33069. "XYZtoLab(): Instance is not a XYZ image.",
  33070. cimg_instance);
  33071. const CImg<Tfloat> white = CImg<Tfloat>(1,1,1,3,255).RGBtoXYZ(use_D65);
  33072. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  33073. const longT whd = (longT)width()*height()*depth();
  33074. cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,128))
  33075. for (longT N = 0; N<whd; ++N) {
  33076. const Tfloat
  33077. X = (Tfloat)(p1[N]/white[0]),
  33078. Y = (Tfloat)(p2[N]/white[1]),
  33079. Z = (Tfloat)(p3[N]/white[2]),
  33080. fX = (Tfloat)_cimg_Labf(X),
  33081. fY = (Tfloat)_cimg_Labf(Y),
  33082. fZ = (Tfloat)_cimg_Labf(Z);
  33083. p1[N] = (T)cimg::cut(116*fY - 16,0,100);
  33084. p2[N] = (T)(500*(fX - fY));
  33085. p3[N] = (T)(200*(fY - fZ));
  33086. }
  33087. return *this;
  33088. }
  33089. //! Convert pixel values from XYZ to Lab color spaces \newinstance.
  33090. CImg<Tfloat> get_XYZtoLab(const bool use_D65=true) const {
  33091. return CImg<Tfloat>(*this,false).XYZtoLab(use_D65);
  33092. }
  33093. //! Convert pixel values from Lab to XYZ color spaces.
  33094. CImg<T>& LabtoXYZ(const bool use_D65=true) {
  33095. if (_spectrum!=3)
  33096. throw CImgInstanceException(_cimg_instance
  33097. "LabtoXYZ(): Instance is not a Lab image.",
  33098. cimg_instance);
  33099. const CImg<Tfloat> white = CImg<Tfloat>(1,1,1,3,255).RGBtoXYZ(use_D65);
  33100. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  33101. const longT whd = (longT)width()*height()*depth();
  33102. cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,128))
  33103. for (longT N = 0; N<whd; ++N) {
  33104. const Tfloat
  33105. L = (Tfloat)p1[N],
  33106. a = (Tfloat)p2[N],
  33107. b = (Tfloat)p3[N],
  33108. cY = (L + 16)/116,
  33109. cZ = cY - b/200,
  33110. cX = a/500 + cY,
  33111. X = (Tfloat)(24389*cX>216?cX*cX*cX:(116*cX - 16)*27/24389),
  33112. Y = (Tfloat)(27*L>216?cY*cY*cY:27*L/24389),
  33113. Z = (Tfloat)(24389*cZ>216?cZ*cZ*cZ:(116*cZ - 16)*27/24389);
  33114. p1[N] = (T)(X*white[0]);
  33115. p2[N] = (T)(Y*white[1]);
  33116. p3[N] = (T)(Z*white[2]);
  33117. }
  33118. return *this;
  33119. }
  33120. //! Convert pixel values from Lab to XYZ color spaces \newinstance.
  33121. CImg<Tfloat> get_LabtoXYZ(const bool use_D65=true) const {
  33122. return CImg<Tfloat>(*this,false).LabtoXYZ(use_D65);
  33123. }
  33124. //! Convert pixel values from XYZ to xyY color spaces.
  33125. CImg<T>& XYZtoxyY() {
  33126. if (_spectrum!=3)
  33127. throw CImgInstanceException(_cimg_instance
  33128. "XYZtoxyY(): Instance is not a XYZ image.",
  33129. cimg_instance);
  33130. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  33131. const longT whd = (longT)width()*height()*depth();
  33132. cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,4096))
  33133. for (longT N = 0; N<whd; ++N) {
  33134. const Tfloat
  33135. X = (Tfloat)p1[N],
  33136. Y = (Tfloat)p2[N],
  33137. Z = (Tfloat)p3[N],
  33138. sum = X + Y + Z,
  33139. nsum = sum>0?sum:1;
  33140. p1[N] = (T)(X/nsum);
  33141. p2[N] = (T)(Y/nsum);
  33142. p3[N] = (T)Y;
  33143. }
  33144. return *this;
  33145. }
  33146. //! Convert pixel values from XYZ to xyY color spaces \newinstance.
  33147. CImg<Tfloat> get_XYZtoxyY() const {
  33148. return CImg<Tfloat>(*this,false).XYZtoxyY();
  33149. }
  33150. //! Convert pixel values from xyY pixels to XYZ color spaces.
  33151. CImg<T>& xyYtoXYZ() {
  33152. if (_spectrum!=3)
  33153. throw CImgInstanceException(_cimg_instance
  33154. "xyYtoXYZ(): Instance is not a xyY image.",
  33155. cimg_instance);
  33156. T *p1 = data(0,0,0,0), *p2 = data(0,0,0,1), *p3 = data(0,0,0,2);
  33157. const longT whd = (longT)width()*height()*depth();
  33158. cimg_pragma_openmp(parallel for cimg_openmp_if_size(whd,4096))
  33159. for (longT N = 0; N<whd; ++N) {
  33160. const Tfloat
  33161. px = (Tfloat)p1[N],
  33162. py = (Tfloat)p2[N],
  33163. Y = (Tfloat)p3[N],
  33164. ny = py>0?py:1;
  33165. p1[N] = (T)(px*Y/ny);
  33166. p2[N] = (T)Y;
  33167. p3[N] = (T)((1 - px - py)*Y/ny);
  33168. }
  33169. return *this;
  33170. }
  33171. //! Convert pixel values from xyY pixels to XYZ color spaces \newinstance.
  33172. CImg<Tfloat> get_xyYtoXYZ() const {
  33173. return CImg<Tfloat>(*this,false).xyYtoXYZ();
  33174. }
  33175. //! Convert pixel values from RGB to Lab color spaces.
  33176. CImg<T>& RGBtoLab(const bool use_D65=true) {
  33177. return RGBtoXYZ(use_D65).XYZtoLab(use_D65);
  33178. }
  33179. //! Convert pixel values from RGB to Lab color spaces \newinstance.
  33180. CImg<Tfloat> get_RGBtoLab(const bool use_D65=true) const {
  33181. return CImg<Tfloat>(*this,false).RGBtoLab(use_D65);
  33182. }
  33183. //! Convert pixel values from Lab to RGB color spaces.
  33184. CImg<T>& LabtoRGB(const bool use_D65=true) {
  33185. return LabtoXYZ().XYZtoRGB(use_D65);
  33186. }
  33187. //! Convert pixel values from Lab to RGB color spaces \newinstance.
  33188. CImg<Tuchar> get_LabtoRGB(const bool use_D65=true) const {
  33189. return CImg<Tuchar>(*this,false).LabtoRGB(use_D65);
  33190. }
  33191. //! Convert pixel values from RGB to xyY color spaces.
  33192. CImg<T>& RGBtoxyY(const bool use_D65=true) {
  33193. return RGBtoXYZ(use_D65).XYZtoxyY();
  33194. }
  33195. //! Convert pixel values from RGB to xyY color spaces \newinstance.
  33196. CImg<Tfloat> get_RGBtoxyY(const bool use_D65=true) const {
  33197. return CImg<Tfloat>(*this,false).RGBtoxyY(use_D65);
  33198. }
  33199. //! Convert pixel values from xyY to RGB color spaces.
  33200. CImg<T>& xyYtoRGB(const bool use_D65=true) {
  33201. return xyYtoXYZ().XYZtoRGB(use_D65);
  33202. }
  33203. //! Convert pixel values from xyY to RGB color spaces \newinstance.
  33204. CImg<Tuchar> get_xyYtoRGB(const bool use_D65=true) const {
  33205. return CImg<Tuchar>(*this,false).xyYtoRGB(use_D65);
  33206. }
  33207. //! Convert pixel values from RGB to CMYK color spaces.
  33208. CImg<T>& RGBtoCMYK() {
  33209. return RGBtoCMY().CMYtoCMYK();
  33210. }
  33211. //! Convert pixel values from RGB to CMYK color spaces \newinstance.
  33212. CImg<Tfloat> get_RGBtoCMYK() const {
  33213. return CImg<Tfloat>(*this,false).RGBtoCMYK();
  33214. }
  33215. //! Convert pixel values from CMYK to RGB color spaces.
  33216. CImg<T>& CMYKtoRGB() {
  33217. return CMYKtoCMY().CMYtoRGB();
  33218. }
  33219. //! Convert pixel values from CMYK to RGB color spaces \newinstance.
  33220. CImg<Tuchar> get_CMYKtoRGB() const {
  33221. return CImg<Tuchar>(*this,false).CMYKtoRGB();
  33222. }
  33223. //@}
  33224. //------------------------------------------
  33225. //
  33226. //! \name Geometric / Spatial Manipulation
  33227. //@{
  33228. //------------------------------------------
  33229. static float _cimg_lanczos(const float x) {
  33230. if (x<=-2 || x>=2) return 0;
  33231. const float a = (float)cimg::PI*x, b = 0.5f*a;
  33232. return (float)(x?std::sin(a)*std::sin(b)/(a*b):1);
  33233. }
  33234. //! Resize image to new dimensions.
  33235. /**
  33236. \param size_x Number of columns (new size along the X-axis).
  33237. \param size_y Number of rows (new size along the Y-axis).
  33238. \param size_z Number of slices (new size along the Z-axis).
  33239. \param size_c Number of vector-channels (new size along the C-axis).
  33240. \param interpolation_type Method of interpolation:
  33241. - -1 = no interpolation: raw memory resizing.
  33242. - 0 = no interpolation: additional space is filled according to \p boundary_conditions.
  33243. - 1 = nearest-neighbor interpolation.
  33244. - 2 = moving average interpolation.
  33245. - 3 = linear interpolation.
  33246. - 4 = grid interpolation.
  33247. - 5 = cubic interpolation.
  33248. - 6 = lanczos interpolation.
  33249. \param boundary_conditions Type of boundary conditions used if necessary.
  33250. \param centering_x Set centering type (only if \p interpolation_type=0).
  33251. \param centering_y Set centering type (only if \p interpolation_type=0).
  33252. \param centering_z Set centering type (only if \p interpolation_type=0).
  33253. \param centering_c Set centering type (only if \p interpolation_type=0).
  33254. \note If pd[x,y,z,v]<0, it corresponds to a percentage of the original size (the default value is -100).
  33255. **/
  33256. CImg<T>& resize(const int size_x, const int size_y=-100,
  33257. const int size_z=-100, const int size_c=-100,
  33258. const int interpolation_type=1, const unsigned int boundary_conditions=0,
  33259. const float centering_x = 0, const float centering_y = 0,
  33260. const float centering_z = 0, const float centering_c = 0) {
  33261. if (!size_x || !size_y || !size_z || !size_c) return assign();
  33262. const unsigned int
  33263. _sx = (unsigned int)(size_x<0?-size_x*width()/100:size_x),
  33264. _sy = (unsigned int)(size_y<0?-size_y*height()/100:size_y),
  33265. _sz = (unsigned int)(size_z<0?-size_z*depth()/100:size_z),
  33266. _sc = (unsigned int)(size_c<0?-size_c*spectrum()/100:size_c),
  33267. sx = _sx?_sx:1, sy = _sy?_sy:1, sz = _sz?_sz:1, sc = _sc?_sc:1;
  33268. if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return *this;
  33269. if (is_empty()) return assign(sx,sy,sz,sc,(T)0);
  33270. if (interpolation_type==-1 && sx*sy*sz*sc==size()) {
  33271. _width = sx; _height = sy; _depth = sz; _spectrum = sc;
  33272. return *this;
  33273. }
  33274. return get_resize(sx,sy,sz,sc,interpolation_type,boundary_conditions,
  33275. centering_x,centering_y,centering_z,centering_c).move_to(*this);
  33276. }
  33277. //! Resize image to new dimensions \newinstance.
  33278. CImg<T> get_resize(const int size_x, const int size_y = -100,
  33279. const int size_z = -100, const int size_c = -100,
  33280. const int interpolation_type=1, const unsigned int boundary_conditions=0,
  33281. const float centering_x = 0, const float centering_y = 0,
  33282. const float centering_z = 0, const float centering_c = 0) const {
  33283. if (centering_x<0 || centering_x>1 || centering_y<0 || centering_y>1 ||
  33284. centering_z<0 || centering_z>1 || centering_c<0 || centering_c>1)
  33285. throw CImgArgumentException(_cimg_instance
  33286. "resize(): Specified centering arguments (%g,%g,%g,%g) are outside range [0,1].",
  33287. cimg_instance,
  33288. centering_x,centering_y,centering_z,centering_c);
  33289. if (!size_x || !size_y || !size_z || !size_c) return CImg<T>();
  33290. const unsigned int
  33291. sx = std::max(1U,(unsigned int)(size_x>=0?size_x:-size_x*width()/100)),
  33292. sy = std::max(1U,(unsigned int)(size_y>=0?size_y:-size_y*height()/100)),
  33293. sz = std::max(1U,(unsigned int)(size_z>=0?size_z:-size_z*depth()/100)),
  33294. sc = std::max(1U,(unsigned int)(size_c>=0?size_c:-size_c*spectrum()/100));
  33295. if (sx==_width && sy==_height && sz==_depth && sc==_spectrum) return +*this;
  33296. if (is_empty()) return CImg<T>(sx,sy,sz,sc,(T)0);
  33297. CImg<T> res;
  33298. switch (interpolation_type) {
  33299. // Raw resizing.
  33300. //
  33301. case -1 :
  33302. std::memcpy(res.assign(sx,sy,sz,sc,(T)0)._data,_data,sizeof(T)*std::min(size(),(ulongT)sx*sy*sz*sc));
  33303. break;
  33304. // No interpolation.
  33305. //
  33306. case 0 : {
  33307. const int
  33308. xc = (int)(centering_x*((int)sx - width())),
  33309. yc = (int)(centering_y*((int)sy - height())),
  33310. zc = (int)(centering_z*((int)sz - depth())),
  33311. cc = (int)(centering_c*((int)sc - spectrum()));
  33312. switch (boundary_conditions) {
  33313. case 3 : { // Mirror
  33314. res.assign(sx,sy,sz,sc);
  33315. const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum();
  33316. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1024*1024))
  33317. cimg_forXYZC(res,x,y,z,c) {
  33318. const int
  33319. mx = cimg::mod(x - xc,w2), my = cimg::mod(y - yc,h2),
  33320. mz = cimg::mod(z - zc,d2), mc = cimg::mod(c - cc,s2);
  33321. res(x,y,z,c) = (*this)(mx<width()?mx:w2 - mx - 1,
  33322. my<height()?my:h2 - my - 1,
  33323. mz<depth()?mz:d2 - mz - 1,
  33324. mc<spectrum()?mc:s2 - mc - 1);
  33325. }
  33326. } break;
  33327. case 2 : { // Periodic
  33328. res.assign(sx,sy,sz,sc);
  33329. const int
  33330. x0 = ((int)xc%width()) - width(),
  33331. y0 = ((int)yc%height()) - height(),
  33332. z0 = ((int)zc%depth()) - depth(),
  33333. c0 = ((int)cc%spectrum()) - spectrum(),
  33334. dx = width(), dy = height(), dz = depth(), dc = spectrum();
  33335. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1024*1024))
  33336. for (int c = c0; c<(int)sc; c+=dc)
  33337. for (int z = z0; z<(int)sz; z+=dz)
  33338. for (int y = y0; y<(int)sy; y+=dy)
  33339. for (int x = x0; x<(int)sx; x+=dx)
  33340. res.draw_image(x,y,z,c,*this);
  33341. } break;
  33342. case 1 : { // Neumann
  33343. res.assign(sx,sy,sz,sc).draw_image(xc,yc,zc,cc,*this);
  33344. CImg<T> sprite;
  33345. if (xc>0) { // X-backward
  33346. res.get_crop(xc,yc,zc,cc,xc,yc + height() - 1,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
  33347. for (int x = xc - 1; x>=0; --x) res.draw_image(x,yc,zc,cc,sprite);
  33348. }
  33349. if (xc + width()<(int)sx) { // X-forward
  33350. res.get_crop(xc + width() - 1,yc,zc,cc,xc + width() - 1,yc + height() - 1,
  33351. zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
  33352. for (int x = xc + width(); x<(int)sx; ++x) res.draw_image(x,yc,zc,cc,sprite);
  33353. }
  33354. if (yc>0) { // Y-backward
  33355. res.get_crop(0,yc,zc,cc,sx - 1,yc,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
  33356. for (int y = yc - 1; y>=0; --y) res.draw_image(0,y,zc,cc,sprite);
  33357. }
  33358. if (yc + height()<(int)sy) { // Y-forward
  33359. res.get_crop(0,yc + height() - 1,zc,cc,sx - 1,yc + height() - 1,
  33360. zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
  33361. for (int y = yc + height(); y<(int)sy; ++y) res.draw_image(0,y,zc,cc,sprite);
  33362. }
  33363. if (zc>0) { // Z-backward
  33364. res.get_crop(0,0,zc,cc,sx - 1,sy - 1,zc,cc + spectrum() - 1).move_to(sprite);
  33365. for (int z = zc - 1; z>=0; --z) res.draw_image(0,0,z,cc,sprite);
  33366. }
  33367. if (zc + depth()<(int)sz) { // Z-forward
  33368. res.get_crop(0,0,zc +depth() - 1,cc,sx - 1,sy - 1,zc + depth() - 1,cc + spectrum() - 1).move_to(sprite);
  33369. for (int z = zc + depth(); z<(int)sz; ++z) res.draw_image(0,0,z,cc,sprite);
  33370. }
  33371. if (cc>0) { // C-backward
  33372. res.get_crop(0,0,0,cc,sx - 1,sy - 1,sz - 1,cc).move_to(sprite);
  33373. for (int c = cc - 1; c>=0; --c) res.draw_image(0,0,0,c,sprite);
  33374. }
  33375. if (cc + spectrum()<(int)sc) { // C-forward
  33376. res.get_crop(0,0,0,cc + spectrum() - 1,sx - 1,sy - 1,sz - 1,cc + spectrum() - 1).move_to(sprite);
  33377. for (int c = cc + spectrum(); c<(int)sc; ++c) res.draw_image(0,0,0,c,sprite);
  33378. }
  33379. } break;
  33380. default : // Dirichlet
  33381. res.assign(sx,sy,sz,sc,(T)0).draw_image(xc,yc,zc,cc,*this);
  33382. }
  33383. break;
  33384. } break;
  33385. // Nearest neighbor interpolation.
  33386. //
  33387. case 1 : {
  33388. res.assign(sx,sy,sz,sc);
  33389. CImg<ulongT> off_x(sx), off_y(sy + 1), off_z(sz + 1), off_c(sc + 1);
  33390. const ulongT
  33391. wh = (ulongT)_width*_height,
  33392. whd = (ulongT)_width*_height*_depth,
  33393. sxy = (ulongT)sx*sy,
  33394. sxyz = (ulongT)sx*sy*sz,
  33395. one = (ulongT)1;
  33396. if (sx==_width) off_x.fill(1);
  33397. else {
  33398. ulongT *poff_x = off_x._data, curr = 0;
  33399. cimg_forX(res,x) {
  33400. const ulongT old = curr;
  33401. curr = (x + one)*_width/sx;
  33402. *(poff_x++) = curr - old;
  33403. }
  33404. }
  33405. if (sy==_height) off_y.fill(_width);
  33406. else {
  33407. ulongT *poff_y = off_y._data, curr = 0;
  33408. cimg_forY(res,y) {
  33409. const ulongT old = curr;
  33410. curr = (y + one)*_height/sy;
  33411. *(poff_y++) = _width*(curr - old);
  33412. }
  33413. *poff_y = 0;
  33414. }
  33415. if (sz==_depth) off_z.fill(wh);
  33416. else {
  33417. ulongT *poff_z = off_z._data, curr = 0;
  33418. cimg_forZ(res,z) {
  33419. const ulongT old = curr;
  33420. curr = (z + one)*_depth/sz;
  33421. *(poff_z++) = wh*(curr - old);
  33422. }
  33423. *poff_z = 0;
  33424. }
  33425. if (sc==_spectrum) off_c.fill(whd);
  33426. else {
  33427. ulongT *poff_c = off_c._data, curr = 0;
  33428. cimg_forC(res,c) {
  33429. const ulongT old = curr;
  33430. curr = (c + one)*_spectrum/sc;
  33431. *(poff_c++) = whd*(curr - old);
  33432. }
  33433. *poff_c = 0;
  33434. }
  33435. T *ptrd = res._data;
  33436. const T* ptrc = _data;
  33437. const ulongT *poff_c = off_c._data;
  33438. for (unsigned int c = 0; c<sc; ) {
  33439. const T *ptrz = ptrc;
  33440. const ulongT *poff_z = off_z._data;
  33441. for (unsigned int z = 0; z<sz; ) {
  33442. const T *ptry = ptrz;
  33443. const ulongT *poff_y = off_y._data;
  33444. for (unsigned int y = 0; y<sy; ) {
  33445. const T *ptrx = ptry;
  33446. const ulongT *poff_x = off_x._data;
  33447. cimg_forX(res,x) { *(ptrd++) = *ptrx; ptrx+=*(poff_x++); }
  33448. ++y;
  33449. ulongT dy = *(poff_y++);
  33450. for ( ; !dy && y<dy; std::memcpy(ptrd,ptrd - sx,sizeof(T)*sx), ++y, ptrd+=sx, dy = *(poff_y++)) {}
  33451. ptry+=dy;
  33452. }
  33453. ++z;
  33454. ulongT dz = *(poff_z++);
  33455. for ( ; !dz && z<dz; std::memcpy(ptrd,ptrd - sxy,sizeof(T)*sxy), ++z, ptrd+=sxy, dz = *(poff_z++)) {}
  33456. ptrz+=dz;
  33457. }
  33458. ++c;
  33459. ulongT dc = *(poff_c++);
  33460. for ( ; !dc && c<dc; std::memcpy(ptrd,ptrd - sxyz,sizeof(T)*sxyz), ++c, ptrd+=sxyz, dc = *(poff_c++)) {}
  33461. ptrc+=dc;
  33462. }
  33463. } break;
  33464. // Moving average.
  33465. //
  33466. case 2 : {
  33467. bool instance_first = true;
  33468. if (sx!=_width) {
  33469. if (sx>_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(res);
  33470. else {
  33471. CImg<Tfloat> tmp(sx,_height,_depth,_spectrum,0);
  33472. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
  33473. cimg_openmp_if(sx>=256 && _height*_depth*_spectrum>=256))
  33474. cimg_forYZC(tmp,y,z,v) {
  33475. for (unsigned int a = _width*sx, b = _width, c = sx, s = 0, t = 0; a; ) {
  33476. const unsigned int d = std::min(b,c);
  33477. a-=d; b-=d; c-=d;
  33478. tmp(t,y,z,v)+=(Tfloat)(*this)(s,y,z,v)*d;
  33479. if (!b) { tmp(t++,y,z,v)/=_width; b = _width; }
  33480. if (!c) { ++s; c = sx; }
  33481. }
  33482. }
  33483. tmp.move_to(res);
  33484. }
  33485. instance_first = false;
  33486. }
  33487. if (sy!=_height) {
  33488. if (sy>_height) get_resize(sx,sy,_depth,_spectrum,1).move_to(res);
  33489. else {
  33490. CImg<Tfloat> tmp(sx,sy,_depth,_spectrum,0);
  33491. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
  33492. cimg_openmp_if(sy>=256 && _width*_depth*_spectrum>=256))
  33493. cimg_forXZC(tmp,x,z,v) {
  33494. for (unsigned int a = _height*sy, b = _height, c = sy, s = 0, t = 0; a; ) {
  33495. const unsigned int d = std::min(b,c);
  33496. a-=d; b-=d; c-=d;
  33497. if (instance_first) tmp(x,t,z,v)+=(Tfloat)(*this)(x,s,z,v)*d;
  33498. else tmp(x,t,z,v)+=(Tfloat)res(x,s,z,v)*d;
  33499. if (!b) { tmp(x,t++,z,v)/=_height; b = _height; }
  33500. if (!c) { ++s; c = sy; }
  33501. }
  33502. }
  33503. tmp.move_to(res);
  33504. }
  33505. instance_first = false;
  33506. }
  33507. if (sz!=_depth) {
  33508. if (sz>_depth) get_resize(sx,sy,sz,_spectrum,1).move_to(res);
  33509. else {
  33510. CImg<Tfloat> tmp(sx,sy,sz,_spectrum,0);
  33511. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
  33512. cimg_openmp_if(sz>=256 && _width*_height*_spectrum>=256))
  33513. cimg_forXYC(tmp,x,y,v) {
  33514. for (unsigned int a = _depth*sz, b = _depth, c = sz, s = 0, t = 0; a; ) {
  33515. const unsigned int d = std::min(b,c);
  33516. a-=d; b-=d; c-=d;
  33517. if (instance_first) tmp(x,y,t,v)+=(Tfloat)(*this)(x,y,s,v)*d;
  33518. else tmp(x,y,t,v)+=(Tfloat)res(x,y,s,v)*d;
  33519. if (!b) { tmp(x,y,t++,v)/=_depth; b = _depth; }
  33520. if (!c) { ++s; c = sz; }
  33521. }
  33522. }
  33523. tmp.move_to(res);
  33524. }
  33525. instance_first = false;
  33526. }
  33527. if (sc!=_spectrum) {
  33528. if (sc>_spectrum) get_resize(sx,sy,sz,sc,1).move_to(res);
  33529. else {
  33530. CImg<Tfloat> tmp(sx,sy,sz,sc,0);
  33531. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
  33532. cimg_openmp_if(sc>=256 && _width*_height*_depth>=256))
  33533. cimg_forXYZ(tmp,x,y,z) {
  33534. for (unsigned int a = _spectrum*sc, b = _spectrum, c = sc, s = 0, t = 0; a; ) {
  33535. const unsigned int d = std::min(b,c);
  33536. a-=d; b-=d; c-=d;
  33537. if (instance_first) tmp(x,y,z,t)+=(Tfloat)(*this)(x,y,z,s)*d;
  33538. else tmp(x,y,z,t)+=(Tfloat)res(x,y,z,s)*d;
  33539. if (!b) { tmp(x,y,z,t++)/=_spectrum; b = _spectrum; }
  33540. if (!c) { ++s; c = sc; }
  33541. }
  33542. }
  33543. tmp.move_to(res);
  33544. }
  33545. instance_first = false;
  33546. }
  33547. } break;
  33548. // Linear interpolation.
  33549. //
  33550. case 3 : {
  33551. CImg<uintT> off(cimg::max(sx,sy,sz,sc));
  33552. CImg<doubleT> foff(off._width);
  33553. CImg<T> resx, resy, resz, resc;
  33554. double curr, old;
  33555. if (sx!=_width) {
  33556. if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
  33557. else if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
  33558. else {
  33559. const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0):
  33560. (double)_width/sx;
  33561. resx.assign(sx,_height,_depth,_spectrum);
  33562. curr = old = 0;
  33563. {
  33564. unsigned int *poff = off._data;
  33565. double *pfoff = foff._data;
  33566. cimg_forX(resx,x) {
  33567. *(pfoff++) = curr - (unsigned int)curr;
  33568. old = curr;
  33569. curr = std::min(width() - 1.,curr + fx);
  33570. *(poff++) = (unsigned int)curr - (unsigned int)old;
  33571. }
  33572. }
  33573. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
  33574. cimg_openmp_if(resx._width>=256 && resx._height*resx._depth*resx._spectrum>=256))
  33575. cimg_forYZC(resx,y,z,c) {
  33576. const T *ptrs = data(0,y,z,c), *const ptrsmax = ptrs + _width - 1;
  33577. T *ptrd = resx.data(0,y,z,c);
  33578. const unsigned int *poff = off._data;
  33579. const double *pfoff = foff._data;
  33580. cimg_forX(resx,x) {
  33581. const double alpha = *(pfoff++);
  33582. const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + 1):val1;
  33583. *(ptrd++) = (T)((1 - alpha)*val1 + alpha*val2);
  33584. ptrs+=*(poff++);
  33585. }
  33586. }
  33587. }
  33588. } else resx.assign(*this,true);
  33589. if (sy!=_height) {
  33590. if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
  33591. else {
  33592. if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
  33593. else {
  33594. const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0):
  33595. (double)_height/sy;
  33596. resy.assign(sx,sy,_depth,_spectrum);
  33597. curr = old = 0;
  33598. {
  33599. unsigned int *poff = off._data;
  33600. double *pfoff = foff._data;
  33601. cimg_forY(resy,y) {
  33602. *(pfoff++) = curr - (unsigned int)curr;
  33603. old = curr;
  33604. curr = std::min(height() - 1.,curr + fy);
  33605. *(poff++) = sx*((unsigned int)curr - (unsigned int)old);
  33606. }
  33607. }
  33608. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
  33609. cimg_openmp_if(resy._height>=256 && resy._width*resy._depth*resy._spectrum>=256))
  33610. cimg_forXZC(resy,x,z,c) {
  33611. const T *ptrs = resx.data(x,0,z,c), *const ptrsmax = ptrs + (_height - 1)*sx;
  33612. T *ptrd = resy.data(x,0,z,c);
  33613. const unsigned int *poff = off._data;
  33614. const double *pfoff = foff._data;
  33615. cimg_forY(resy,y) {
  33616. const double alpha = *(pfoff++);
  33617. const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sx):val1;
  33618. *ptrd = (T)((1 - alpha)*val1 + alpha*val2);
  33619. ptrd+=sx;
  33620. ptrs+=*(poff++);
  33621. }
  33622. }
  33623. }
  33624. }
  33625. resx.assign();
  33626. } else resy.assign(resx,true);
  33627. if (sz!=_depth) {
  33628. if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
  33629. else {
  33630. if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
  33631. else {
  33632. const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0):
  33633. (double)_depth/sz;
  33634. const unsigned int sxy = sx*sy;
  33635. resz.assign(sx,sy,sz,_spectrum);
  33636. curr = old = 0;
  33637. {
  33638. unsigned int *poff = off._data;
  33639. double *pfoff = foff._data;
  33640. cimg_forZ(resz,z) {
  33641. *(pfoff++) = curr - (unsigned int)curr;
  33642. old = curr;
  33643. curr = std::min(depth() - 1.,curr + fz);
  33644. *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
  33645. }
  33646. }
  33647. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
  33648. cimg_openmp_if(resz._depth>=256 && resz._width*resz._height*resz._spectrum>=256))
  33649. cimg_forXYC(resz,x,y,c) {
  33650. const T *ptrs = resy.data(x,y,0,c), *const ptrsmax = ptrs + (_depth - 1)*sxy;
  33651. T *ptrd = resz.data(x,y,0,c);
  33652. const unsigned int *poff = off._data;
  33653. const double *pfoff = foff._data;
  33654. cimg_forZ(resz,z) {
  33655. const double alpha = *(pfoff++);
  33656. const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sxy):val1;
  33657. *ptrd = (T)((1 - alpha)*val1 + alpha*val2);
  33658. ptrd+=sxy;
  33659. ptrs+=*(poff++);
  33660. }
  33661. }
  33662. }
  33663. }
  33664. resy.assign();
  33665. } else resz.assign(resy,true);
  33666. if (sc!=_spectrum) {
  33667. if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
  33668. else {
  33669. if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
  33670. else {
  33671. const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0):
  33672. (double)_spectrum/sc;
  33673. const unsigned int sxyz = sx*sy*sz;
  33674. resc.assign(sx,sy,sz,sc);
  33675. curr = old = 0;
  33676. {
  33677. unsigned int *poff = off._data;
  33678. double *pfoff = foff._data;
  33679. cimg_forC(resc,c) {
  33680. *(pfoff++) = curr - (unsigned int)curr;
  33681. old = curr;
  33682. curr = std::min(spectrum() - 1.,curr + fc);
  33683. *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
  33684. }
  33685. }
  33686. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
  33687. cimg_openmp_if(resc._spectrum>=256 && resc._width*resc._height*resc._depth>=256))
  33688. cimg_forXYZ(resc,x,y,z) {
  33689. const T *ptrs = resz.data(x,y,z,0), *const ptrsmax = ptrs + (_spectrum - 1)*sxyz;
  33690. T *ptrd = resc.data(x,y,z,0);
  33691. const unsigned int *poff = off._data;
  33692. const double *pfoff = foff._data;
  33693. cimg_forC(resc,c) {
  33694. const double alpha = *(pfoff++);
  33695. const T val1 = *ptrs, val2 = ptrs<ptrsmax?*(ptrs + sxyz):val1;
  33696. *ptrd = (T)((1 - alpha)*val1 + alpha*val2);
  33697. ptrd+=sxyz;
  33698. ptrs+=*(poff++);
  33699. }
  33700. }
  33701. }
  33702. }
  33703. resz.assign();
  33704. } else resc.assign(resz,true);
  33705. return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
  33706. } break;
  33707. // Grid interpolation.
  33708. //
  33709. case 4 : {
  33710. CImg<T> resx, resy, resz, resc;
  33711. if (sx!=_width) {
  33712. if (sx<_width) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
  33713. else {
  33714. resx.assign(sx,_height,_depth,_spectrum,(T)0);
  33715. const int dx = (int)(2*sx), dy = 2*width();
  33716. int err = (int)(dy + centering_x*(sx*dy/width() - dy)), xs = 0;
  33717. cimg_forX(resx,x) if ((err-=dy)<=0) {
  33718. cimg_forYZC(resx,y,z,c) resx(x,y,z,c) = (*this)(xs,y,z,c);
  33719. ++xs;
  33720. err+=dx;
  33721. }
  33722. }
  33723. } else resx.assign(*this,true);
  33724. if (sy!=_height) {
  33725. if (sy<_height) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
  33726. else {
  33727. resy.assign(sx,sy,_depth,_spectrum,(T)0);
  33728. const int dx = (int)(2*sy), dy = 2*height();
  33729. int err = (int)(dy + centering_y*(sy*dy/height() - dy)), ys = 0;
  33730. cimg_forY(resy,y) if ((err-=dy)<=0) {
  33731. cimg_forXZC(resy,x,z,c) resy(x,y,z,c) = resx(x,ys,z,c);
  33732. ++ys;
  33733. err+=dx;
  33734. }
  33735. }
  33736. resx.assign();
  33737. } else resy.assign(resx,true);
  33738. if (sz!=_depth) {
  33739. if (sz<_depth) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
  33740. else {
  33741. resz.assign(sx,sy,sz,_spectrum,(T)0);
  33742. const int dx = (int)(2*sz), dy = 2*depth();
  33743. int err = (int)(dy + centering_z*(sz*dy/depth() - dy)), zs = 0;
  33744. cimg_forZ(resz,z) if ((err-=dy)<=0) {
  33745. cimg_forXYC(resz,x,y,c) resz(x,y,z,c) = resy(x,y,zs,c);
  33746. ++zs;
  33747. err+=dx;
  33748. }
  33749. }
  33750. resy.assign();
  33751. } else resz.assign(resy,true);
  33752. if (sc!=_spectrum) {
  33753. if (sc<_spectrum) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
  33754. else {
  33755. resc.assign(sx,sy,sz,sc,(T)0);
  33756. const int dx = (int)(2*sc), dy = 2*spectrum();
  33757. int err = (int)(dy + centering_c*(sc*dy/spectrum() - dy)), cs = 0;
  33758. cimg_forC(resc,c) if ((err-=dy)<=0) {
  33759. cimg_forXYZ(resc,x,y,z) resc(x,y,z,c) = resz(x,y,z,cs);
  33760. ++cs;
  33761. err+=dx;
  33762. }
  33763. }
  33764. resz.assign();
  33765. } else resc.assign(resz,true);
  33766. return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
  33767. } break;
  33768. // Cubic interpolation.
  33769. //
  33770. case 5 : {
  33771. const Tfloat vmin = (Tfloat)cimg::type<T>::min(), vmax = (Tfloat)cimg::type<T>::max();
  33772. CImg<uintT> off(cimg::max(sx,sy,sz,sc));
  33773. CImg<doubleT> foff(off._width);
  33774. CImg<T> resx, resy, resz, resc;
  33775. double curr, old;
  33776. if (sx!=_width) {
  33777. if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
  33778. else {
  33779. if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
  33780. else {
  33781. const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0):
  33782. (double)_width/sx;
  33783. resx.assign(sx,_height,_depth,_spectrum);
  33784. curr = old = 0;
  33785. {
  33786. unsigned int *poff = off._data;
  33787. double *pfoff = foff._data;
  33788. cimg_forX(resx,x) {
  33789. *(pfoff++) = curr - (unsigned int)curr;
  33790. old = curr;
  33791. curr = std::min(width() - 1.,curr + fx);
  33792. *(poff++) = (unsigned int)curr - (unsigned int)old;
  33793. }
  33794. }
  33795. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
  33796. cimg_openmp_if(resx._width>=256 && resx._height*resx._depth*resx._spectrum>=256))
  33797. cimg_forYZC(resx,y,z,c) {
  33798. const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_width - 2);
  33799. T *ptrd = resx.data(0,y,z,c);
  33800. const unsigned int *poff = off._data;
  33801. const double *pfoff = foff._data;
  33802. cimg_forX(resx,x) {
  33803. const double
  33804. t = *(pfoff++),
  33805. val1 = (double)*ptrs,
  33806. val0 = ptrs>ptrs0?(double)*(ptrs - 1):val1,
  33807. val2 = ptrs<=ptrsmax?(double)*(ptrs + 1):val1,
  33808. val3 = ptrs<ptrsmax?(double)*(ptrs + 2):val2,
  33809. val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
  33810. t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
  33811. *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val);
  33812. ptrs+=*(poff++);
  33813. }
  33814. }
  33815. }
  33816. }
  33817. } else resx.assign(*this,true);
  33818. if (sy!=_height) {
  33819. if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
  33820. else {
  33821. if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
  33822. else {
  33823. const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0):
  33824. (double)_height/sy;
  33825. resy.assign(sx,sy,_depth,_spectrum);
  33826. curr = old = 0;
  33827. {
  33828. unsigned int *poff = off._data;
  33829. double *pfoff = foff._data;
  33830. cimg_forY(resy,y) {
  33831. *(pfoff++) = curr - (unsigned int)curr;
  33832. old = curr;
  33833. curr = std::min(height() - 1.,curr + fy);
  33834. *(poff++) = sx*((unsigned int)curr - (unsigned int)old);
  33835. }
  33836. }
  33837. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
  33838. cimg_openmp_if(resy._height>=256 && resy._width*resy._depth*resy._spectrum>=256))
  33839. cimg_forXZC(resy,x,z,c) {
  33840. const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_height - 2)*sx;
  33841. T *ptrd = resy.data(x,0,z,c);
  33842. const unsigned int *poff = off._data;
  33843. const double *pfoff = foff._data;
  33844. cimg_forY(resy,y) {
  33845. const double
  33846. t = *(pfoff++),
  33847. val1 = (double)*ptrs,
  33848. val0 = ptrs>ptrs0?(double)*(ptrs - sx):val1,
  33849. val2 = ptrs<=ptrsmax?(double)*(ptrs + sx):val1,
  33850. val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sx):val2,
  33851. val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
  33852. t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
  33853. *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
  33854. ptrd+=sx;
  33855. ptrs+=*(poff++);
  33856. }
  33857. }
  33858. }
  33859. }
  33860. resx.assign();
  33861. } else resy.assign(resx,true);
  33862. if (sz!=_depth) {
  33863. if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
  33864. else {
  33865. if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
  33866. else {
  33867. const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0):
  33868. (double)_depth/sz;
  33869. const unsigned int sxy = sx*sy;
  33870. resz.assign(sx,sy,sz,_spectrum);
  33871. curr = old = 0;
  33872. {
  33873. unsigned int *poff = off._data;
  33874. double *pfoff = foff._data;
  33875. cimg_forZ(resz,z) {
  33876. *(pfoff++) = curr - (unsigned int)curr;
  33877. old = curr;
  33878. curr = std::min(depth() - 1.,curr + fz);
  33879. *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
  33880. }
  33881. }
  33882. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
  33883. cimg_openmp_if(resz._depth>=256 && resz._width*resz._height*resz._spectrum>=256))
  33884. cimg_forXYC(resz,x,y,c) {
  33885. const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmax = ptrs + (_depth - 2)*sxy;
  33886. T *ptrd = resz.data(x,y,0,c);
  33887. const unsigned int *poff = off._data;
  33888. const double *pfoff = foff._data;
  33889. cimg_forZ(resz,z) {
  33890. const double
  33891. t = *(pfoff++),
  33892. val1 = (double)*ptrs,
  33893. val0 = ptrs>ptrs0?(double)*(ptrs - sxy):val1,
  33894. val2 = ptrs<=ptrsmax?(double)*(ptrs + sxy):val1,
  33895. val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sxy):val2,
  33896. val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
  33897. t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
  33898. *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
  33899. ptrd+=sxy;
  33900. ptrs+=*(poff++);
  33901. }
  33902. }
  33903. }
  33904. }
  33905. resy.assign();
  33906. } else resz.assign(resy,true);
  33907. if (sc!=_spectrum) {
  33908. if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
  33909. else {
  33910. if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
  33911. else {
  33912. const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0):
  33913. (double)_spectrum/sc;
  33914. const unsigned int sxyz = sx*sy*sz;
  33915. resc.assign(sx,sy,sz,sc);
  33916. curr = old = 0;
  33917. {
  33918. unsigned int *poff = off._data;
  33919. double *pfoff = foff._data;
  33920. cimg_forC(resc,c) {
  33921. *(pfoff++) = curr - (unsigned int)curr;
  33922. old = curr;
  33923. curr = std::min(spectrum() - 1.,curr + fc);
  33924. *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
  33925. }
  33926. }
  33927. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
  33928. cimg_openmp_if(resc._spectrum>=256 && resc._width*resc._height*resc._depth>=256))
  33929. cimg_forXYZ(resc,x,y,z) {
  33930. const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmax = ptrs + (_spectrum - 2)*sxyz;
  33931. T *ptrd = resc.data(x,y,z,0);
  33932. const unsigned int *poff = off._data;
  33933. const double *pfoff = foff._data;
  33934. cimg_forC(resc,c) {
  33935. const double
  33936. t = *(pfoff++),
  33937. val1 = (double)*ptrs,
  33938. val0 = ptrs>ptrs0?(double)*(ptrs - sxyz):val1,
  33939. val2 = ptrs<=ptrsmax?(double)*(ptrs + sxyz):val1,
  33940. val3 = ptrs<ptrsmax?(double)*(ptrs + 2*sxyz):val2,
  33941. val = val1 + 0.5f*(t*(-val0 + val2) + t*t*(2*val0 - 5*val1 + 4*val2 - val3) +
  33942. t*t*t*(-val0 + 3*val1 - 3*val2 + val3));
  33943. *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
  33944. ptrd+=sxyz;
  33945. ptrs+=*(poff++);
  33946. }
  33947. }
  33948. }
  33949. }
  33950. resz.assign();
  33951. } else resc.assign(resz,true);
  33952. return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
  33953. } break;
  33954. // Lanczos interpolation.
  33955. //
  33956. case 6 : {
  33957. const double vmin = (double)cimg::type<T>::min(), vmax = (double)cimg::type<T>::max();
  33958. CImg<uintT> off(cimg::max(sx,sy,sz,sc));
  33959. CImg<doubleT> foff(off._width);
  33960. CImg<T> resx, resy, resz, resc;
  33961. double curr, old;
  33962. if (sx!=_width) {
  33963. if (_width==1) get_resize(sx,_height,_depth,_spectrum,1).move_to(resx);
  33964. else {
  33965. if (_width>sx) get_resize(sx,_height,_depth,_spectrum,2).move_to(resx);
  33966. else {
  33967. const double fx = (!boundary_conditions && sx>_width)?(sx>1?(_width - 1.)/(sx - 1):0):
  33968. (double)_width/sx;
  33969. resx.assign(sx,_height,_depth,_spectrum);
  33970. curr = old = 0;
  33971. {
  33972. unsigned int *poff = off._data;
  33973. double *pfoff = foff._data;
  33974. cimg_forX(resx,x) {
  33975. *(pfoff++) = curr - (unsigned int)curr;
  33976. old = curr;
  33977. curr = std::min(width() - 1.,curr + fx);
  33978. *(poff++) = (unsigned int)curr - (unsigned int)old;
  33979. }
  33980. }
  33981. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
  33982. cimg_openmp_if(resx._width>=256 && resx._height*resx._depth*resx._spectrum>=256))
  33983. cimg_forYZC(resx,y,z,c) {
  33984. const T *const ptrs0 = data(0,y,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + 1,
  33985. *const ptrsmax = ptrs0 + (_width - 2);
  33986. T *ptrd = resx.data(0,y,z,c);
  33987. const unsigned int *poff = off._data;
  33988. const double *pfoff = foff._data;
  33989. cimg_forX(resx,x) {
  33990. const double
  33991. t = *(pfoff++),
  33992. w0 = _cimg_lanczos(t + 2),
  33993. w1 = _cimg_lanczos(t + 1),
  33994. w2 = _cimg_lanczos(t),
  33995. w3 = _cimg_lanczos(t - 1),
  33996. w4 = _cimg_lanczos(t - 2),
  33997. val2 = (double)*ptrs,
  33998. val1 = ptrs>=ptrsmin?(double)*(ptrs - 1):val2,
  33999. val0 = ptrs>ptrsmin?(double)*(ptrs - 2):val1,
  34000. val3 = ptrs<=ptrsmax?(double)*(ptrs + 1):val2,
  34001. val4 = ptrs<ptrsmax?(double)*(ptrs + 2):val3,
  34002. val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
  34003. *(ptrd++) = (T)(val<vmin?vmin:val>vmax?vmax:val);
  34004. ptrs+=*(poff++);
  34005. }
  34006. }
  34007. }
  34008. }
  34009. } else resx.assign(*this,true);
  34010. if (sy!=_height) {
  34011. if (_height==1) resx.get_resize(sx,sy,_depth,_spectrum,1).move_to(resy);
  34012. else {
  34013. if (_height>sy) resx.get_resize(sx,sy,_depth,_spectrum,2).move_to(resy);
  34014. else {
  34015. const double fy = (!boundary_conditions && sy>_height)?(sy>1?(_height - 1.)/(sy - 1):0):
  34016. (double)_height/sy;
  34017. resy.assign(sx,sy,_depth,_spectrum);
  34018. curr = old = 0;
  34019. {
  34020. unsigned int *poff = off._data;
  34021. double *pfoff = foff._data;
  34022. cimg_forY(resy,y) {
  34023. *(pfoff++) = curr - (unsigned int)curr;
  34024. old = curr;
  34025. curr = std::min(height() - 1.,curr + fy);
  34026. *(poff++) = sx*((unsigned int)curr - (unsigned int)old);
  34027. }
  34028. }
  34029. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
  34030. cimg_openmp_if(resy._height>=256 && resy._width*resy._depth*resy._spectrum>=256))
  34031. cimg_forXZC(resy,x,z,c) {
  34032. const T *const ptrs0 = resx.data(x,0,z,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sx,
  34033. *const ptrsmax = ptrs0 + (_height - 2)*sx;
  34034. T *ptrd = resy.data(x,0,z,c);
  34035. const unsigned int *poff = off._data;
  34036. const double *pfoff = foff._data;
  34037. cimg_forY(resy,y) {
  34038. const double
  34039. t = *(pfoff++),
  34040. w0 = _cimg_lanczos(t + 2),
  34041. w1 = _cimg_lanczos(t + 1),
  34042. w2 = _cimg_lanczos(t),
  34043. w3 = _cimg_lanczos(t - 1),
  34044. w4 = _cimg_lanczos(t - 2),
  34045. val2 = (double)*ptrs,
  34046. val1 = ptrs>=ptrsmin?(double)*(ptrs - sx):val2,
  34047. val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sx):val1,
  34048. val3 = ptrs<=ptrsmax?(double)*(ptrs + sx):val2,
  34049. val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sx):val3,
  34050. val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
  34051. *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
  34052. ptrd+=sx;
  34053. ptrs+=*(poff++);
  34054. }
  34055. }
  34056. }
  34057. }
  34058. resx.assign();
  34059. } else resy.assign(resx,true);
  34060. if (sz!=_depth) {
  34061. if (_depth==1) resy.get_resize(sx,sy,sz,_spectrum,1).move_to(resz);
  34062. else {
  34063. if (_depth>sz) resy.get_resize(sx,sy,sz,_spectrum,2).move_to(resz);
  34064. else {
  34065. const double fz = (!boundary_conditions && sz>_depth)?(sz>1?(_depth - 1.)/(sz - 1):0):
  34066. (double)_depth/sz;
  34067. const unsigned int sxy = sx*sy;
  34068. resz.assign(sx,sy,sz,_spectrum);
  34069. curr = old = 0;
  34070. {
  34071. unsigned int *poff = off._data;
  34072. double *pfoff = foff._data;
  34073. cimg_forZ(resz,z) {
  34074. *(pfoff++) = curr - (unsigned int)curr;
  34075. old = curr;
  34076. curr = std::min(depth() - 1.,curr + fz);
  34077. *(poff++) = sxy*((unsigned int)curr - (unsigned int)old);
  34078. }
  34079. }
  34080. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
  34081. cimg_openmp_if(resz._depth>=256 && resz._width*resz._height*resz._spectrum>=256))
  34082. cimg_forXYC(resz,x,y,c) {
  34083. const T *const ptrs0 = resy.data(x,y,0,c), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxy,
  34084. *const ptrsmax = ptrs0 + (_depth - 2)*sxy;
  34085. T *ptrd = resz.data(x,y,0,c);
  34086. const unsigned int *poff = off._data;
  34087. const double *pfoff = foff._data;
  34088. cimg_forZ(resz,z) {
  34089. const double
  34090. t = *(pfoff++),
  34091. w0 = _cimg_lanczos(t + 2),
  34092. w1 = _cimg_lanczos(t + 1),
  34093. w2 = _cimg_lanczos(t),
  34094. w3 = _cimg_lanczos(t - 1),
  34095. w4 = _cimg_lanczos(t - 2),
  34096. val2 = (double)*ptrs,
  34097. val1 = ptrs>=ptrsmin?(double)*(ptrs - sxy):val2,
  34098. val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sxy):val1,
  34099. val3 = ptrs<=ptrsmax?(double)*(ptrs + sxy):val2,
  34100. val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sxy):val3,
  34101. val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
  34102. *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
  34103. ptrd+=sxy;
  34104. ptrs+=*(poff++);
  34105. }
  34106. }
  34107. }
  34108. }
  34109. resy.assign();
  34110. } else resz.assign(resy,true);
  34111. if (sc!=_spectrum) {
  34112. if (_spectrum==1) resz.get_resize(sx,sy,sz,sc,1).move_to(resc);
  34113. else {
  34114. if (_spectrum>sc) resz.get_resize(sx,sy,sz,sc,2).move_to(resc);
  34115. else {
  34116. const double fc = (!boundary_conditions && sc>_spectrum)?(sc>1?(_spectrum - 1.)/(sc - 1):0):
  34117. (double)_spectrum/sc;
  34118. const unsigned int sxyz = sx*sy*sz;
  34119. resc.assign(sx,sy,sz,sc);
  34120. curr = old = 0;
  34121. {
  34122. unsigned int *poff = off._data;
  34123. double *pfoff = foff._data;
  34124. cimg_forC(resc,c) {
  34125. *(pfoff++) = curr - (unsigned int)curr;
  34126. old = curr;
  34127. curr = std::min(spectrum() - 1.,curr + fc);
  34128. *(poff++) = sxyz*((unsigned int)curr - (unsigned int)old);
  34129. }
  34130. }
  34131. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
  34132. cimg_openmp_if(resc._spectrum>=256 && resc._width*resc._height*resc._depth>=256))
  34133. cimg_forXYZ(resc,x,y,z) {
  34134. const T *const ptrs0 = resz.data(x,y,z,0), *ptrs = ptrs0, *const ptrsmin = ptrs0 + sxyz,
  34135. *const ptrsmax = ptrs + (_spectrum - 2)*sxyz;
  34136. T *ptrd = resc.data(x,y,z,0);
  34137. const unsigned int *poff = off._data;
  34138. const double *pfoff = foff._data;
  34139. cimg_forC(resc,c) {
  34140. const double
  34141. t = *(pfoff++),
  34142. w0 = _cimg_lanczos(t + 2),
  34143. w1 = _cimg_lanczos(t + 1),
  34144. w2 = _cimg_lanczos(t),
  34145. w3 = _cimg_lanczos(t - 1),
  34146. w4 = _cimg_lanczos(t - 2),
  34147. val2 = (double)*ptrs,
  34148. val1 = ptrs>=ptrsmin?(double)*(ptrs - sxyz):val2,
  34149. val0 = ptrs>ptrsmin?(double)*(ptrs - 2*sxyz):val1,
  34150. val3 = ptrs<=ptrsmax?(double)*(ptrs + sxyz):val2,
  34151. val4 = ptrs<ptrsmax?(double)*(ptrs + 2*sxyz):val3,
  34152. val = (val0*w0 + val1*w1 + val2*w2 + val3*w3 + val4*w4)/(w1 + w2 + w3 + w4);
  34153. *ptrd = (T)(val<vmin?vmin:val>vmax?vmax:val);
  34154. ptrd+=sxyz;
  34155. ptrs+=*(poff++);
  34156. }
  34157. }
  34158. }
  34159. }
  34160. resz.assign();
  34161. } else resc.assign(resz,true);
  34162. return resc._is_shared?(resz._is_shared?(resy._is_shared?(resx._is_shared?(+(*this)):resx):resy):resz):resc;
  34163. } break;
  34164. // Unknown interpolation.
  34165. //
  34166. default :
  34167. throw CImgArgumentException(_cimg_instance
  34168. "resize(): Invalid specified interpolation %d "
  34169. "(should be { -1=raw | 0=none | 1=nearest | 2=average | 3=linear | 4=grid | "
  34170. "5=cubic | 6=lanczos }).",
  34171. cimg_instance,
  34172. interpolation_type);
  34173. }
  34174. return res;
  34175. }
  34176. //! Resize image to dimensions of another image.
  34177. /**
  34178. \param src Reference image used for dimensions.
  34179. \param interpolation_type Interpolation method.
  34180. \param boundary_conditions Boundary conditions.
  34181. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
  34182. \param centering_x Set centering type (only if \p interpolation_type=0).
  34183. \param centering_y Set centering type (only if \p interpolation_type=0).
  34184. \param centering_z Set centering type (only if \p interpolation_type=0).
  34185. \param centering_c Set centering type (only if \p interpolation_type=0).
  34186. **/
  34187. template<typename t>
  34188. CImg<T>& resize(const CImg<t>& src,
  34189. const int interpolation_type=1, const unsigned int boundary_conditions=0,
  34190. const float centering_x = 0, const float centering_y = 0,
  34191. const float centering_z = 0, const float centering_c = 0) {
  34192. return resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions,
  34193. centering_x,centering_y,centering_z,centering_c);
  34194. }
  34195. //! Resize image to dimensions of another image \newinstance.
  34196. template<typename t>
  34197. CImg<T> get_resize(const CImg<t>& src,
  34198. const int interpolation_type=1, const unsigned int boundary_conditions=0,
  34199. const float centering_x = 0, const float centering_y = 0,
  34200. const float centering_z = 0, const float centering_c = 0) const {
  34201. return get_resize(src._width,src._height,src._depth,src._spectrum,interpolation_type,boundary_conditions,
  34202. centering_x,centering_y,centering_z,centering_c);
  34203. }
  34204. //! Resize image to dimensions of a display window.
  34205. /**
  34206. \param disp Reference display window used for dimensions.
  34207. \param interpolation_type Interpolation method.
  34208. \param boundary_conditions Boundary conditions.
  34209. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
  34210. \param centering_x Set centering type (only if \p interpolation_type=0).
  34211. \param centering_y Set centering type (only if \p interpolation_type=0).
  34212. \param centering_z Set centering type (only if \p interpolation_type=0).
  34213. \param centering_c Set centering type (only if \p interpolation_type=0).
  34214. **/
  34215. CImg<T>& resize(const CImgDisplay& disp,
  34216. const int interpolation_type=1, const unsigned int boundary_conditions=0,
  34217. const float centering_x = 0, const float centering_y = 0,
  34218. const float centering_z = 0, const float centering_c = 0) {
  34219. return resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions,
  34220. centering_x,centering_y,centering_z,centering_c);
  34221. }
  34222. //! Resize image to dimensions of a display window \newinstance.
  34223. CImg<T> get_resize(const CImgDisplay& disp,
  34224. const int interpolation_type=1, const unsigned int boundary_conditions=0,
  34225. const float centering_x = 0, const float centering_y = 0,
  34226. const float centering_z = 0, const float centering_c = 0) const {
  34227. return get_resize(disp.width(),disp.height(),_depth,_spectrum,interpolation_type,boundary_conditions,
  34228. centering_x,centering_y,centering_z,centering_c);
  34229. }
  34230. //! Resize image to half-size along XY axes, using an optimized filter.
  34231. CImg<T>& resize_halfXY() {
  34232. return get_resize_halfXY().move_to(*this);
  34233. }
  34234. //! Resize image to half-size along XY axes, using an optimized filter \newinstance.
  34235. CImg<T> get_resize_halfXY() const {
  34236. if (is_empty()) return *this;
  34237. static const Tfloat kernel[9] = { 0.07842776544f, 0.1231940459f, 0.07842776544f,
  34238. 0.1231940459f, 0.1935127547f, 0.1231940459f,
  34239. 0.07842776544f, 0.1231940459f, 0.07842776544f };
  34240. CImg<T> I(9), res(_width/2,_height/2,_depth,_spectrum);
  34241. T *ptrd = res._data;
  34242. cimg_forZC(*this,z,c) cimg_for3x3(*this,x,y,z,c,I,T)
  34243. if (x%2 && y%2) *(ptrd++) = (T)
  34244. (I[0]*kernel[0] + I[1]*kernel[1] + I[2]*kernel[2] +
  34245. I[3]*kernel[3] + I[4]*kernel[4] + I[5]*kernel[5] +
  34246. I[6]*kernel[6] + I[7]*kernel[7] + I[8]*kernel[8]);
  34247. return res;
  34248. }
  34249. //! Resize image to double-size, using the Scale2X algorithm.
  34250. /**
  34251. \note Use anisotropic upscaling algorithm
  34252. <a href="http://scale2x.sourceforge.net/algorithm.html">described here</a>.
  34253. **/
  34254. CImg<T>& resize_doubleXY() {
  34255. return get_resize_doubleXY().move_to(*this);
  34256. }
  34257. //! Resize image to double-size, using the Scale2X algorithm \newinstance.
  34258. CImg<T> get_resize_doubleXY() const {
  34259. #define _cimg_gs2x_for3(bound,i) \
  34260. for (int i = 0, _p1##i = 0, \
  34261. _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
  34262. _n1##i<(int)(bound) || i==--_n1##i; \
  34263. _p1##i = i++, ++_n1##i, ptrd1+=(res)._width, ptrd2+=(res)._width)
  34264. #define _cimg_gs2x_for3x3(img,x,y,z,c,I,T) \
  34265. _cimg_gs2x_for3((img)._height,y) for (int x = 0, \
  34266. _p1##x = 0, \
  34267. _n1##x = (int)( \
  34268. (I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
  34269. (I[3] = I[4] = (T)(img)(0,y,z,c)), \
  34270. (I[7] = (T)(img)(0,_n1##y,z,c)), \
  34271. 1>=(img)._width?(img).width() - 1:1); \
  34272. (_n1##x<(img).width() && ( \
  34273. (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
  34274. (I[5] = (T)(img)(_n1##x,y,z,c)), \
  34275. (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
  34276. x==--_n1##x; \
  34277. I[1] = I[2], \
  34278. I[3] = I[4], I[4] = I[5], \
  34279. I[7] = I[8], \
  34280. _p1##x = x++, ++_n1##x)
  34281. if (is_empty()) return *this;
  34282. CImg<T> res(_width<<1,_height<<1,_depth,_spectrum);
  34283. CImg_3x3(I,T);
  34284. cimg_forZC(*this,z,c) {
  34285. T
  34286. *ptrd1 = res.data(0,0,z,c),
  34287. *ptrd2 = ptrd1 + res._width;
  34288. _cimg_gs2x_for3x3(*this,x,y,z,c,I,T) {
  34289. if (Icp!=Icn && Ipc!=Inc) {
  34290. *(ptrd1++) = Ipc==Icp?Ipc:Icc;
  34291. *(ptrd1++) = Icp==Inc?Inc:Icc;
  34292. *(ptrd2++) = Ipc==Icn?Ipc:Icc;
  34293. *(ptrd2++) = Icn==Inc?Inc:Icc;
  34294. } else { *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc; }
  34295. }
  34296. }
  34297. return res;
  34298. }
  34299. //! Resize image to triple-size, using the Scale3X algorithm.
  34300. /**
  34301. \note Use anisotropic upscaling algorithm
  34302. <a href="http://scale2x.sourceforge.net/algorithm.html">described here</a>.
  34303. **/
  34304. CImg<T>& resize_tripleXY() {
  34305. return get_resize_tripleXY().move_to(*this);
  34306. }
  34307. //! Resize image to triple-size, using the Scale3X algorithm \newinstance.
  34308. CImg<T> get_resize_tripleXY() const {
  34309. #define _cimg_gs3x_for3(bound,i) \
  34310. for (int i = 0, _p1##i = 0, \
  34311. _n1##i = 1>=(bound)?(int)(bound) - 1:1; \
  34312. _n1##i<(int)(bound) || i==--_n1##i; \
  34313. _p1##i = i++, ++_n1##i, ptrd1+=2*(res)._width, ptrd2+=2*(res)._width, ptrd3+=2*(res)._width)
  34314. #define _cimg_gs3x_for3x3(img,x,y,z,c,I,T) \
  34315. _cimg_gs3x_for3((img)._height,y) for (int x = 0, \
  34316. _p1##x = 0, \
  34317. _n1##x = (int)( \
  34318. (I[0] = I[1] = (T)(img)(_p1##x,_p1##y,z,c)), \
  34319. (I[3] = I[4] = (T)(img)(0,y,z,c)), \
  34320. (I[6] = I[7] = (T)(img)(0,_n1##y,z,c)), \
  34321. 1>=(img)._width?(img).width() - 1:1); \
  34322. (_n1##x<(img).width() && ( \
  34323. (I[2] = (T)(img)(_n1##x,_p1##y,z,c)), \
  34324. (I[5] = (T)(img)(_n1##x,y,z,c)), \
  34325. (I[8] = (T)(img)(_n1##x,_n1##y,z,c)),1)) || \
  34326. x==--_n1##x; \
  34327. I[0] = I[1], I[1] = I[2], \
  34328. I[3] = I[4], I[4] = I[5], \
  34329. I[6] = I[7], I[7] = I[8], \
  34330. _p1##x = x++, ++_n1##x)
  34331. if (is_empty()) return *this;
  34332. CImg<T> res(3*_width,3*_height,_depth,_spectrum);
  34333. CImg_3x3(I,T);
  34334. cimg_forZC(*this,z,c) {
  34335. T
  34336. *ptrd1 = res.data(0,0,z,c),
  34337. *ptrd2 = ptrd1 + res._width,
  34338. *ptrd3 = ptrd2 + res._width;
  34339. _cimg_gs3x_for3x3(*this,x,y,z,c,I,T) {
  34340. if (Icp != Icn && Ipc != Inc) {
  34341. *(ptrd1++) = Ipc==Icp?Ipc:Icc;
  34342. *(ptrd1++) = (Ipc==Icp && Icc!=Inp) || (Icp==Inc && Icc!=Ipp)?Icp:Icc;
  34343. *(ptrd1++) = Icp==Inc?Inc:Icc;
  34344. *(ptrd2++) = (Ipc==Icp && Icc!=Ipn) || (Ipc==Icn && Icc!=Ipp)?Ipc:Icc;
  34345. *(ptrd2++) = Icc;
  34346. *(ptrd2++) = (Icp==Inc && Icc!=Inn) || (Icn==Inc && Icc!=Inp)?Inc:Icc;
  34347. *(ptrd3++) = Ipc==Icn?Ipc:Icc;
  34348. *(ptrd3++) = (Ipc==Icn && Icc!=Inn) || (Icn==Inc && Icc!=Ipn)?Icn:Icc;
  34349. *(ptrd3++) = Icn==Inc?Inc:Icc;
  34350. } else {
  34351. *(ptrd1++) = Icc; *(ptrd1++) = Icc; *(ptrd1++) = Icc;
  34352. *(ptrd2++) = Icc; *(ptrd2++) = Icc; *(ptrd2++) = Icc;
  34353. *(ptrd3++) = Icc; *(ptrd3++) = Icc; *(ptrd3++) = Icc;
  34354. }
  34355. }
  34356. }
  34357. return res;
  34358. }
  34359. //! Mirror image content along specified axis.
  34360. /**
  34361. \param axis Mirror axis
  34362. **/
  34363. CImg<T>& mirror(const char axis) {
  34364. if (is_empty()) return *this;
  34365. T *pf, *pb, *buf = 0;
  34366. switch (cimg::lowercase(axis)) {
  34367. case 'x' : {
  34368. pf = _data; pb = data(_width - 1);
  34369. const unsigned int width2 = _width/2;
  34370. for (unsigned int yzv = 0; yzv<_height*_depth*_spectrum; ++yzv) {
  34371. for (unsigned int x = 0; x<width2; ++x) { const T val = *pf; *(pf++) = *pb; *(pb--) = val; }
  34372. pf+=_width - width2;
  34373. pb+=_width + width2;
  34374. }
  34375. } break;
  34376. case 'y' : {
  34377. buf = new T[_width];
  34378. pf = _data; pb = data(0,_height - 1);
  34379. const unsigned int height2 = _height/2;
  34380. for (unsigned int zv = 0; zv<_depth*_spectrum; ++zv) {
  34381. for (unsigned int y = 0; y<height2; ++y) {
  34382. std::memcpy(buf,pf,_width*sizeof(T));
  34383. std::memcpy(pf,pb,_width*sizeof(T));
  34384. std::memcpy(pb,buf,_width*sizeof(T));
  34385. pf+=_width;
  34386. pb-=_width;
  34387. }
  34388. pf+=(ulongT)_width*(_height - height2);
  34389. pb+=(ulongT)_width*(_height + height2);
  34390. }
  34391. } break;
  34392. case 'z' : {
  34393. buf = new T[(ulongT)_width*_height];
  34394. pf = _data; pb = data(0,0,_depth - 1);
  34395. const unsigned int depth2 = _depth/2;
  34396. cimg_forC(*this,c) {
  34397. for (unsigned int z = 0; z<depth2; ++z) {
  34398. std::memcpy(buf,pf,_width*_height*sizeof(T));
  34399. std::memcpy(pf,pb,_width*_height*sizeof(T));
  34400. std::memcpy(pb,buf,_width*_height*sizeof(T));
  34401. pf+=(ulongT)_width*_height;
  34402. pb-=(ulongT)_width*_height;
  34403. }
  34404. pf+=(ulongT)_width*_height*(_depth - depth2);
  34405. pb+=(ulongT)_width*_height*(_depth + depth2);
  34406. }
  34407. } break;
  34408. case 'c' : {
  34409. buf = new T[(ulongT)_width*_height*_depth];
  34410. pf = _data; pb = data(0,0,0,_spectrum - 1);
  34411. const unsigned int _spectrum2 = _spectrum/2;
  34412. for (unsigned int v = 0; v<_spectrum2; ++v) {
  34413. std::memcpy(buf,pf,_width*_height*_depth*sizeof(T));
  34414. std::memcpy(pf,pb,_width*_height*_depth*sizeof(T));
  34415. std::memcpy(pb,buf,_width*_height*_depth*sizeof(T));
  34416. pf+=(ulongT)_width*_height*_depth;
  34417. pb-=(ulongT)_width*_height*_depth;
  34418. }
  34419. } break;
  34420. default :
  34421. throw CImgArgumentException(_cimg_instance
  34422. "mirror(): Invalid specified axis '%c'.",
  34423. cimg_instance,
  34424. axis);
  34425. }
  34426. delete[] buf;
  34427. return *this;
  34428. }
  34429. //! Mirror image content along specified axis \newinstance.
  34430. CImg<T> get_mirror(const char axis) const {
  34431. return (+*this).mirror(axis);
  34432. }
  34433. //! Mirror image content along specified axes.
  34434. /**
  34435. \param axes Mirror axes, as a C-string.
  34436. \note \c axes may contains multiple characters, e.g. \c "xyz"
  34437. **/
  34438. CImg<T>& mirror(const char *const axes) {
  34439. for (const char *s = axes; *s; ++s) mirror(*s);
  34440. return *this;
  34441. }
  34442. //! Mirror image content along specified axes \newinstance.
  34443. CImg<T> get_mirror(const char *const axes) const {
  34444. return (+*this).mirror(axes);
  34445. }
  34446. //! Shift image content.
  34447. /**
  34448. \param delta_x Amount of displacement along the X-axis.
  34449. \param delta_y Amount of displacement along the Y-axis.
  34450. \param delta_z Amount of displacement along the Z-axis.
  34451. \param delta_c Amount of displacement along the C-axis.
  34452. \param boundary_conditions Boundary conditions.
  34453. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
  34454. **/
  34455. CImg<T>& shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0,
  34456. const unsigned int boundary_conditions=0) {
  34457. if (is_empty()) return *this;
  34458. if (boundary_conditions==3)
  34459. return get_crop(-delta_x,-delta_y,-delta_z,-delta_c,
  34460. width() - delta_x - 1,
  34461. height() - delta_y - 1,
  34462. depth() - delta_z - 1,
  34463. spectrum() - delta_c - 1,3).move_to(*this);
  34464. if (delta_x) // Shift along X-axis
  34465. switch (boundary_conditions) {
  34466. case 2 : { // Periodic
  34467. const int ml = cimg::mod(-delta_x,width()), ndelta_x = (ml<=width()/2)?ml:(ml-width());
  34468. if (!ndelta_x) return *this;
  34469. CImg<T> buf(cimg::abs(ndelta_x));
  34470. if (ndelta_x>0) cimg_forYZC(*this,y,z,c) {
  34471. std::memcpy(buf,data(0,y,z,c),ndelta_x*sizeof(T));
  34472. std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T));
  34473. std::memcpy(data(_width-ndelta_x,y,z,c),buf,ndelta_x*sizeof(T));
  34474. } else cimg_forYZC(*this,y,z,c) {
  34475. std::memcpy(buf,data(_width + ndelta_x,y,z,c),-ndelta_x*sizeof(T));
  34476. std::memmove(data(-ndelta_x,y,z,c),data(0,y,z,c),(_width + ndelta_x)*sizeof(T));
  34477. std::memcpy(data(0,y,z,c),buf,-ndelta_x*sizeof(T));
  34478. }
  34479. } break;
  34480. case 1 : // Neumann
  34481. if (delta_x<0) {
  34482. const int ndelta_x = (-delta_x>=width())?width() - 1:-delta_x;
  34483. if (!ndelta_x) return *this;
  34484. cimg_forYZC(*this,y,z,c) {
  34485. std::memmove(data(0,y,z,c),data(ndelta_x,y,z,c),(_width-ndelta_x)*sizeof(T));
  34486. T *ptrd = data(_width - 1,y,z,c);
  34487. const T val = *ptrd;
  34488. for (int l = 0; l<ndelta_x - 1; ++l) *(--ptrd) = val;
  34489. }
  34490. } else {
  34491. const int ndelta_x = (delta_x>=width())?width() - 1:delta_x;
  34492. if (!ndelta_x) return *this;
  34493. cimg_forYZC(*this,y,z,c) {
  34494. std::memmove(data(ndelta_x,y,z,c),data(0,y,z,c),(_width-ndelta_x)*sizeof(T));
  34495. T *ptrd = data(0,y,z,c);
  34496. const T val = *ptrd;
  34497. for (int l = 0; l<ndelta_x - 1; ++l) *(++ptrd) = val;
  34498. }
  34499. }
  34500. break;
  34501. default : // Dirichlet
  34502. if (delta_x<=-width() || delta_x>=width()) return fill((T)0);
  34503. if (delta_x<0) cimg_forYZC(*this,y,z,c) {
  34504. std::memmove(data(0,y,z,c),data(-delta_x,y,z,c),(_width + delta_x)*sizeof(T));
  34505. std::memset(data(_width + delta_x,y,z,c),0,-delta_x*sizeof(T));
  34506. } else cimg_forYZC(*this,y,z,c) {
  34507. std::memmove(data(delta_x,y,z,c),data(0,y,z,c),(_width-delta_x)*sizeof(T));
  34508. std::memset(data(0,y,z,c),0,delta_x*sizeof(T));
  34509. }
  34510. }
  34511. if (delta_y) // Shift along Y-axis
  34512. switch (boundary_conditions) {
  34513. case 2 : { // Periodic
  34514. const int ml = cimg::mod(-delta_y,height()), ndelta_y = (ml<=height()/2)?ml:(ml-height());
  34515. if (!ndelta_y) return *this;
  34516. CImg<T> buf(width(),cimg::abs(ndelta_y));
  34517. if (ndelta_y>0) cimg_forZC(*this,z,c) {
  34518. std::memcpy(buf,data(0,0,z,c),_width*ndelta_y*sizeof(T));
  34519. std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T));
  34520. std::memcpy(data(0,_height-ndelta_y,z,c),buf,_width*ndelta_y*sizeof(T));
  34521. } else cimg_forZC(*this,z,c) {
  34522. std::memcpy(buf,data(0,_height + ndelta_y,z,c),-ndelta_y*_width*sizeof(T));
  34523. std::memmove(data(0,-ndelta_y,z,c),data(0,0,z,c),_width*(_height + ndelta_y)*sizeof(T));
  34524. std::memcpy(data(0,0,z,c),buf,-ndelta_y*_width*sizeof(T));
  34525. }
  34526. } break;
  34527. case 1 : // Neumann
  34528. if (delta_y<0) {
  34529. const int ndelta_y = (-delta_y>=height())?height() - 1:-delta_y;
  34530. if (!ndelta_y) return *this;
  34531. cimg_forZC(*this,z,c) {
  34532. std::memmove(data(0,0,z,c),data(0,ndelta_y,z,c),_width*(_height-ndelta_y)*sizeof(T));
  34533. T *ptrd = data(0,_height-ndelta_y,z,c), *ptrs = data(0,_height - 1,z,c);
  34534. for (int l = 0; l<ndelta_y - 1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
  34535. }
  34536. } else {
  34537. const int ndelta_y = (delta_y>=height())?height() - 1:delta_y;
  34538. if (!ndelta_y) return *this;
  34539. cimg_forZC(*this,z,c) {
  34540. std::memmove(data(0,ndelta_y,z,c),data(0,0,z,c),_width*(_height-ndelta_y)*sizeof(T));
  34541. T *ptrd = data(0,1,z,c), *ptrs = data(0,0,z,c);
  34542. for (int l = 0; l<ndelta_y - 1; ++l) { std::memcpy(ptrd,ptrs,_width*sizeof(T)); ptrd+=_width; }
  34543. }
  34544. }
  34545. break;
  34546. default : // Dirichlet
  34547. if (delta_y<=-height() || delta_y>=height()) return fill((T)0);
  34548. if (delta_y<0) cimg_forZC(*this,z,c) {
  34549. std::memmove(data(0,0,z,c),data(0,-delta_y,z,c),_width*(_height + delta_y)*sizeof(T));
  34550. std::memset(data(0,_height + delta_y,z,c),0,-delta_y*_width*sizeof(T));
  34551. } else cimg_forZC(*this,z,c) {
  34552. std::memmove(data(0,delta_y,z,c),data(0,0,z,c),_width*(_height-delta_y)*sizeof(T));
  34553. std::memset(data(0,0,z,c),0,delta_y*_width*sizeof(T));
  34554. }
  34555. }
  34556. if (delta_z) // Shift along Z-axis
  34557. switch (boundary_conditions) {
  34558. case 2 : { // Periodic
  34559. const int ml = cimg::mod(-delta_z,depth()), ndelta_z = (ml<=depth()/2)?ml:(ml-depth());
  34560. if (!ndelta_z) return *this;
  34561. CImg<T> buf(width(),height(),cimg::abs(ndelta_z));
  34562. if (ndelta_z>0) cimg_forC(*this,c) {
  34563. std::memcpy(buf,data(0,0,0,c),_width*_height*ndelta_z*sizeof(T));
  34564. std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
  34565. std::memcpy(data(0,0,_depth-ndelta_z,c),buf,_width*_height*ndelta_z*sizeof(T));
  34566. } else cimg_forC(*this,c) {
  34567. std::memcpy(buf,data(0,0,_depth + ndelta_z,c),-ndelta_z*_width*_height*sizeof(T));
  34568. std::memmove(data(0,0,-ndelta_z,c),data(0,0,0,c),_width*_height*(_depth + ndelta_z)*sizeof(T));
  34569. std::memcpy(data(0,0,0,c),buf,-ndelta_z*_width*_height*sizeof(T));
  34570. }
  34571. } break;
  34572. case 1 : // Neumann
  34573. if (delta_z<0) {
  34574. const int ndelta_z = (-delta_z>=depth())?depth() - 1:-delta_z;
  34575. if (!ndelta_z) return *this;
  34576. cimg_forC(*this,c) {
  34577. std::memmove(data(0,0,0,c),data(0,0,ndelta_z,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
  34578. T *ptrd = data(0,0,_depth-ndelta_z,c), *ptrs = data(0,0,_depth - 1,c);
  34579. for (int l = 0; l<ndelta_z - 1; ++l) {
  34580. std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=(ulongT)_width*_height;
  34581. }
  34582. }
  34583. } else {
  34584. const int ndelta_z = (delta_z>=depth())?depth() - 1:delta_z;
  34585. if (!ndelta_z) return *this;
  34586. cimg_forC(*this,c) {
  34587. std::memmove(data(0,0,ndelta_z,c),data(0,0,0,c),_width*_height*(_depth-ndelta_z)*sizeof(T));
  34588. T *ptrd = data(0,0,1,c), *ptrs = data(0,0,0,c);
  34589. for (int l = 0; l<ndelta_z - 1; ++l) {
  34590. std::memcpy(ptrd,ptrs,_width*_height*sizeof(T)); ptrd+=(ulongT)_width*_height;
  34591. }
  34592. }
  34593. }
  34594. break;
  34595. default : // Dirichlet
  34596. if (delta_z<=-depth() || delta_z>=depth()) return fill((T)0);
  34597. if (delta_z<0) cimg_forC(*this,c) {
  34598. std::memmove(data(0,0,0,c),data(0,0,-delta_z,c),_width*_height*(_depth + delta_z)*sizeof(T));
  34599. std::memset(data(0,0,_depth + delta_z,c),0,_width*_height*(-delta_z)*sizeof(T));
  34600. } else cimg_forC(*this,c) {
  34601. std::memmove(data(0,0,delta_z,c),data(0,0,0,c),_width*_height*(_depth-delta_z)*sizeof(T));
  34602. std::memset(data(0,0,0,c),0,delta_z*_width*_height*sizeof(T));
  34603. }
  34604. }
  34605. if (delta_c) // Shift along C-axis
  34606. switch (boundary_conditions) {
  34607. case 2 : { // Periodic
  34608. const int ml = cimg::mod(-delta_c,spectrum()), ndelta_c = (ml<=spectrum()/2)?ml:(ml-spectrum());
  34609. if (!ndelta_c) return *this;
  34610. CImg<T> buf(width(),height(),depth(),cimg::abs(ndelta_c));
  34611. if (ndelta_c>0) {
  34612. std::memcpy(buf,_data,_width*_height*_depth*ndelta_c*sizeof(T));
  34613. std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
  34614. std::memcpy(data(0,0,0,_spectrum-ndelta_c),buf,_width*_height*_depth*ndelta_c*sizeof(T));
  34615. } else {
  34616. std::memcpy(buf,data(0,0,0,_spectrum + ndelta_c),-ndelta_c*_width*_height*_depth*sizeof(T));
  34617. std::memmove(data(0,0,0,-ndelta_c),_data,_width*_height*_depth*(_spectrum + ndelta_c)*sizeof(T));
  34618. std::memcpy(_data,buf,-ndelta_c*_width*_height*_depth*sizeof(T));
  34619. }
  34620. } break;
  34621. case 1 : // Neumann
  34622. if (delta_c<0) {
  34623. const int ndelta_c = (-delta_c>=spectrum())?spectrum() - 1:-delta_c;
  34624. if (!ndelta_c) return *this;
  34625. std::memmove(_data,data(0,0,0,ndelta_c),_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
  34626. T *ptrd = data(0,0,0,_spectrum-ndelta_c), *ptrs = data(0,0,0,_spectrum - 1);
  34627. for (int l = 0; l<ndelta_c - 1; ++l) {
  34628. std::memcpy(ptrd,ptrs,_width*_height*_depth*sizeof(T)); ptrd+=(ulongT)_width*_height*_depth;
  34629. }
  34630. } else {
  34631. const int ndelta_c = (delta_c>=spectrum())?spectrum() - 1:delta_c;
  34632. if (!ndelta_c) return *this;
  34633. std::memmove(data(0,0,0,ndelta_c),_data,_width*_height*_depth*(_spectrum-ndelta_c)*sizeof(T));
  34634. T *ptrd = data(0,0,0,1);
  34635. for (int l = 0; l<ndelta_c - 1; ++l) {
  34636. std::memcpy(ptrd,_data,_width*_height*_depth*sizeof(T)); ptrd+=(ulongT)_width*_height*_depth;
  34637. }
  34638. }
  34639. break;
  34640. default : // Dirichlet
  34641. if (delta_c<=-spectrum() || delta_c>=spectrum()) return fill((T)0);
  34642. if (delta_c<0) {
  34643. std::memmove(_data,data(0,0,0,-delta_c),_width*_height*_depth*(_spectrum + delta_c)*sizeof(T));
  34644. std::memset(data(0,0,0,_spectrum + delta_c),0,_width*_height*_depth*(-delta_c)*sizeof(T));
  34645. } else {
  34646. std::memmove(data(0,0,0,delta_c),_data,_width*_height*_depth*(_spectrum-delta_c)*sizeof(T));
  34647. std::memset(_data,0,delta_c*_width*_height*_depth*sizeof(T));
  34648. }
  34649. }
  34650. return *this;
  34651. }
  34652. //! Shift image content \newinstance.
  34653. CImg<T> get_shift(const int delta_x, const int delta_y=0, const int delta_z=0, const int delta_c=0,
  34654. const unsigned int boundary_conditions=0) const {
  34655. return (+*this).shift(delta_x,delta_y,delta_z,delta_c,boundary_conditions);
  34656. }
  34657. //! Permute axes order.
  34658. /**
  34659. \param axes_order Axes permutations, as a C-string of 4 characters.
  34660. This function permutes image content regarding the specified axes permutation.
  34661. **/
  34662. CImg<T>& permute_axes(const char *const axes_order) {
  34663. if (is_empty() || !axes_order) return *this;
  34664. const unsigned uicase = _permute_axes_uicase(axes_order);
  34665. if (_permute_axes_is_optim(uicase)) { // Data layout allow to do nothing but set the new dimensions
  34666. CImg<T> res(*this,true);
  34667. for (unsigned int i = 0; i<4; ++i) {
  34668. const unsigned int
  34669. axis = (uicase>>(4*(3 - i)))&15,
  34670. value = !axis?_width:axis==1?_height:axis==2?_depth:_spectrum;
  34671. if (!i) res._width = value; else if (i==1) res._height = value;
  34672. else if (i==2) res._depth = value; else res._spectrum = value;
  34673. }
  34674. _width = res._width; _height = res._height; _depth = res._depth; _spectrum = res._spectrum;
  34675. return *this;
  34676. }
  34677. return get_permute_axes(axes_order).move_to(*this);
  34678. }
  34679. //! Permute axes order \newinstance.
  34680. CImg<T> get_permute_axes(const char *const axes_order) const {
  34681. const T foo = (T)0;
  34682. return _permute_axes(axes_order,foo);
  34683. }
  34684. unsigned int _permute_axes_uicase(const char *const axes_order) const { // Convert axes to integer case number
  34685. unsigned char s_axes[4] = { 0,1,2,3 }, n_axes[4] = { };
  34686. bool is_error = false;
  34687. if (axes_order) for (unsigned int l = 0; axes_order[l]; ++l) {
  34688. int c = cimg::lowercase(axes_order[l]);
  34689. if (l>=4 || (c!='x' && c!='y' && c!='z' && c!='c')) { is_error = true; break; }
  34690. else { ++n_axes[c%=4]; s_axes[l] = (unsigned char)c; }
  34691. }
  34692. is_error|=(*n_axes>1) || (n_axes[1]>1) || (n_axes[2]>1) || (n_axes[3]>1);
  34693. if (is_error)
  34694. throw CImgArgumentException(_cimg_instance
  34695. "permute_axes(): Invalid specified axes order '%s'.",
  34696. cimg_instance,
  34697. axes_order);
  34698. return (s_axes[0]<<12) | (s_axes[1]<<8) | (s_axes[2]<<4) | (s_axes[3]);
  34699. }
  34700. bool _permute_axes_is_optim(const unsigned int uicase) const { // Determine cases where nothing has to be done
  34701. const unsigned int co = ((_width>1)<<3)|((_height>1)<<2)|((_depth>1)<<1)|(_spectrum>1);
  34702. if (co<=2 || uicase==0x0123) return true;
  34703. switch (uicase) {
  34704. case (0x0132) : if ((co>=4 && co<=6) || (co>=8 && co<=10) || (co>=12 && co<=14)) return true; break;
  34705. case (0x0213) : if ((co>=3 && co<=5) || (co>=8 && co<=13)) return true; break;
  34706. case (0x0231) : if (co==3 || co==4 || (co>=8 && co<=12)) return true; break;
  34707. case (0x0312) : if (co==4 || co==6 || co==8 || co==9 || co==10 || co==12 || co==14) return true; break;
  34708. case (0x0321) : if (co==4 || (co>=8 && co<=10) || co==12) return true; break;
  34709. case (0x1023) : if (co>=3 && co<=11) return true; break;
  34710. case (0x1032) : if ((co>=4 && co<=6) || (co>=8 && co<=10)) return true; break;
  34711. case (0x1203) : if (co>=3 && co<=9) return true; break;
  34712. case (0x1230) : if (co>=3 && co<=8) return true; break;
  34713. case (0x1302) : if ((co>=4 && co<=6) || co==8 || co==10) return true; break;
  34714. case (0x1320) : if ((co>=4 && co<=6) || co==8) return true; break;
  34715. case (0x2013) : if ((co>=3 && co<=5) || co==8 || co==9 || co==12 || co==13) return true; break;
  34716. case (0x2031) : if (co==3 || co==4 || co==8 || co==9 || co==12) return true; break;
  34717. case (0x2103) : if ((co>=3 && co<=5) || co==8 || co==9) return true; break;
  34718. case (0x2130) : if ((co>=3 && co<=5) || co==8) return true; break;
  34719. case (0x2301) : if (co==3 || co==4 || co==8 || co==12) return true; break;
  34720. case (0x2310) : if (co==3 || co==4 || co==8) return true; break;
  34721. case (0x3012) : if (co==4 || co==6 || co==8 || co==10 || co==12 || co==14) return true; break;
  34722. case (0x3021) : if (co==4 || co==8 || co==10 || co==12) return true; break;
  34723. case (0x3102) : if (co==4 || co==6 || co==8 || co==10) return true; break;
  34724. case (0x3120) : if (co==4 || co==6 || co==8) return true; break;
  34725. case (0x3201) : if (co==4 || co==8 || co==12) return true; break;
  34726. case (0x3210) : if (co==4 || co==8) return true; break;
  34727. }
  34728. return false;
  34729. }
  34730. template<typename t>
  34731. CImg<t> _permute_axes(const char *const axes_order, const t&) const {
  34732. if (is_empty() || !axes_order) return CImg<t>(*this,false);
  34733. CImg<t> res;
  34734. const unsigned uicase = _permute_axes_uicase(axes_order);
  34735. if (_permute_axes_is_optim(uicase)) { // Data layout allow to do nothing but set the new dimensions
  34736. res.assign(*this,false);
  34737. for (unsigned int i = 0; i<4; ++i) {
  34738. const unsigned int
  34739. axis = (uicase>>(4*(3 - i)))&15,
  34740. value = !axis?_width:axis==1?_height:axis==2?_depth:_spectrum;
  34741. if (!i) res._width = value; else if (i==1) res._height = value;
  34742. else if (i==2) res._depth = value; else res._spectrum = value;
  34743. }
  34744. return res;
  34745. }
  34746. const T* ptrs = _data;
  34747. ulongT wh, whd;
  34748. switch (uicase) {
  34749. case 0x0123 : // xyzc
  34750. return +*this;
  34751. case 0x0132 : // xycz
  34752. res.assign(_width,_height,_spectrum,_depth);
  34753. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  34754. cimg_forXYZC(*this,x,y,z,c) res(x,y,c,z,wh,whd) = (t)*(ptrs++);
  34755. break;
  34756. case 0x0213 : // xzyc
  34757. res.assign(_width,_depth,_height,_spectrum);
  34758. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  34759. cimg_forXYZC(*this,x,y,z,c) res(x,z,y,c,wh,whd) = (t)*(ptrs++);
  34760. break;
  34761. case 0x0231 : // xzcy
  34762. res.assign(_width,_depth,_spectrum,_height);
  34763. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  34764. cimg_forXYZC(*this,x,y,z,c) res(x,z,c,y,wh,whd) = (t)*(ptrs++);
  34765. break;
  34766. case 0x0312 : // xcyz
  34767. res.assign(_width,_spectrum,_height,_depth);
  34768. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  34769. cimg_forXYZC(*this,x,y,z,c) res(x,c,y,z,wh,whd) = (t)*(ptrs++);
  34770. break;
  34771. case 0x0321 : // xczy
  34772. res.assign(_width,_spectrum,_depth,_height);
  34773. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  34774. cimg_forXYZC(*this,x,y,z,c) res(x,c,z,y,wh,whd) = (t)*(ptrs++);
  34775. break;
  34776. case 0x1023 : // yxzc
  34777. res.assign(_height,_width,_depth,_spectrum);
  34778. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  34779. cimg_forXYZC(*this,x,y,z,c) res(y,x,z,c,wh,whd) = (t)*(ptrs++);
  34780. break;
  34781. case 0x1032 : // yxcz
  34782. res.assign(_height,_width,_spectrum,_depth);
  34783. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  34784. cimg_forXYZC(*this,x,y,z,c) res(y,x,c,z,wh,whd) = (t)*(ptrs++);
  34785. break;
  34786. case 0x1203 : // yzxc
  34787. res.assign(_height,_depth,_width,_spectrum);
  34788. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  34789. cimg_forXYZC(*this,x,y,z,c) res(y,z,x,c,wh,whd) = (t)*(ptrs++);
  34790. break;
  34791. case 0x1230 : // yzcx
  34792. res.assign(_height,_depth,_spectrum,_width);
  34793. switch (_width) {
  34794. case 1 : {
  34795. t *ptr_r = res.data(0,0,0,0);
  34796. for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
  34797. *(ptr_r++) = (t)*(ptrs++);
  34798. }
  34799. } break;
  34800. case 2 : {
  34801. t *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1);
  34802. for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
  34803. *(ptr_r++) = (t)ptrs[0];
  34804. *(ptr_g++) = (t)ptrs[1];
  34805. ptrs+=2;
  34806. }
  34807. } break;
  34808. case 3 : { // Optimization for the classical conversion from interleaved RGB to planar RGB
  34809. 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);
  34810. for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
  34811. *(ptr_r++) = (t)ptrs[0];
  34812. *(ptr_g++) = (t)ptrs[1];
  34813. *(ptr_b++) = (t)ptrs[2];
  34814. ptrs+=3;
  34815. }
  34816. } break;
  34817. case 4 : { // Optimization for the classical conversion from interleaved RGBA to planar RGBA
  34818. t
  34819. *ptr_r = res.data(0,0,0,0), *ptr_g = res.data(0,0,0,1),
  34820. *ptr_b = res.data(0,0,0,2), *ptr_a = res.data(0,0,0,3);
  34821. for (unsigned int siz = _height*_depth*_spectrum; siz; --siz) {
  34822. *(ptr_r++) = (t)ptrs[0];
  34823. *(ptr_g++) = (t)ptrs[1];
  34824. *(ptr_b++) = (t)ptrs[2];
  34825. *(ptr_a++) = (t)ptrs[3];
  34826. ptrs+=4;
  34827. }
  34828. } break;
  34829. default : {
  34830. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  34831. cimg_forXYZC(*this,x,y,z,c) res(y,z,c,x,wh,whd) = *(ptrs++);
  34832. return res;
  34833. }
  34834. }
  34835. break;
  34836. case 0x1302 : // ycxz
  34837. res.assign(_height,_spectrum,_width,_depth);
  34838. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  34839. cimg_forXYZC(*this,x,y,z,c) res(y,c,x,z,wh,whd) = (t)*(ptrs++);
  34840. break;
  34841. case 0x1320 : // yczx
  34842. res.assign(_height,_spectrum,_depth,_width);
  34843. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  34844. cimg_forXYZC(*this,x,y,z,c) res(y,c,z,x,wh,whd) = (t)*(ptrs++);
  34845. break;
  34846. case 0x2013 : // zxyc
  34847. res.assign(_depth,_width,_height,_spectrum);
  34848. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  34849. cimg_forXYZC(*this,x,y,z,c) res(z,x,y,c,wh,whd) = (t)*(ptrs++);
  34850. break;
  34851. case 0x2031 : // zxcy
  34852. res.assign(_depth,_width,_spectrum,_height);
  34853. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  34854. cimg_forXYZC(*this,x,y,z,c) res(z,x,c,y,wh,whd) = (t)*(ptrs++);
  34855. break;
  34856. case 0x2103 : // zyxc
  34857. res.assign(_depth,_height,_width,_spectrum);
  34858. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  34859. cimg_forXYZC(*this,x,y,z,c) res(z,y,x,c,wh,whd) = (t)*(ptrs++);
  34860. break;
  34861. case 0x2130 : // zycx
  34862. res.assign(_depth,_height,_spectrum,_width);
  34863. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  34864. cimg_forXYZC(*this,x,y,z,c) res(z,y,c,x,wh,whd) = (t)*(ptrs++);
  34865. break;
  34866. case 0x2301 : // zcxy
  34867. res.assign(_depth,_spectrum,_width,_height);
  34868. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  34869. cimg_forXYZC(*this,x,y,z,c) res(z,c,x,y,wh,whd) = (t)*(ptrs++);
  34870. break;
  34871. case 0x2310 : // zcyx
  34872. res.assign(_depth,_spectrum,_height,_width);
  34873. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  34874. cimg_forXYZC(*this,x,y,z,c) res(z,c,y,x,wh,whd) = (t)*(ptrs++);
  34875. break;
  34876. case 0x3012 : // cxyz
  34877. res.assign(_spectrum,_width,_height,_depth);
  34878. switch (_spectrum) {
  34879. case 1 : {
  34880. const T *ptr_r = data(0,0,0,0);
  34881. t *ptrd = res._data;
  34882. for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) *(ptrd++) = (t)*(ptr_r++);
  34883. } break;
  34884. case 2 : {
  34885. const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1);
  34886. t *ptrd = res._data;
  34887. for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) {
  34888. ptrd[0] = (t)*(ptr_r++);
  34889. ptrd[1] = (t)*(ptr_g++);
  34890. ptrd+=2;
  34891. }
  34892. } break;
  34893. case 3 : { // Optimization for the classical conversion from planar RGB to interleaved RGB
  34894. const T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
  34895. t *ptrd = res._data;
  34896. for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) {
  34897. ptrd[0] = (t)*(ptr_r++);
  34898. ptrd[1] = (t)*(ptr_g++);
  34899. ptrd[2] = (t)*(ptr_b++);
  34900. ptrd+=3;
  34901. }
  34902. } break;
  34903. case 4 : { // Optimization for the classical conversion from planar RGBA to interleaved RGBA
  34904. 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);
  34905. t *ptrd = res._data;
  34906. for (ulongT siz = (ulongT)_width*_height*_depth; siz; --siz) {
  34907. ptrd[0] = (t)*(ptr_r++);
  34908. ptrd[1] = (t)*(ptr_g++);
  34909. ptrd[2] = (t)*(ptr_b++);
  34910. ptrd[3] = (t)*(ptr_a++);
  34911. ptrd+=4;
  34912. }
  34913. } break;
  34914. default : {
  34915. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  34916. cimg_forXYZC(*this,x,y,z,c) res(c,x,y,z,wh,whd) = (t)*(ptrs++);
  34917. }
  34918. }
  34919. break;
  34920. case 0x3021 : // cxzy
  34921. res.assign(_spectrum,_width,_depth,_height);
  34922. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  34923. cimg_forXYZC(*this,x,y,z,c) res(c,x,z,y,wh,whd) = (t)*(ptrs++);
  34924. break;
  34925. case 0x3102 : // cyxz
  34926. res.assign(_spectrum,_height,_width,_depth);
  34927. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  34928. cimg_forXYZC(*this,x,y,z,c) res(c,y,x,z,wh,whd) = (t)*(ptrs++);
  34929. break;
  34930. case 0x3120 : // cyzx
  34931. res.assign(_spectrum,_height,_depth,_width);
  34932. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  34933. cimg_forXYZC(*this,x,y,z,c) res(c,y,z,x,wh,whd) = (t)*(ptrs++);
  34934. break;
  34935. case 0x3201 : // czxy
  34936. res.assign(_spectrum,_depth,_width,_height);
  34937. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  34938. cimg_forXYZC(*this,x,y,z,c) res(c,z,x,y,wh,whd) = (t)*(ptrs++);
  34939. break;
  34940. case 0x3210 : // czyx
  34941. res.assign(_spectrum,_depth,_height,_width);
  34942. wh = (ulongT)res._width*res._height; whd = wh*res._depth;
  34943. cimg_forXYZC(*this,x,y,z,c) res(c,z,y,x,wh,whd) = (t)*(ptrs++);
  34944. break;
  34945. }
  34946. return res;
  34947. }
  34948. //! Unroll pixel values along specified axis.
  34949. /**
  34950. \param axis Unroll axis (can be \c 'x', \c 'y', \c 'z' or c 'c').
  34951. **/
  34952. CImg<T>& unroll(const char axis) {
  34953. const unsigned int siz = (unsigned int)size();
  34954. if (siz) switch (cimg::lowercase(axis)) {
  34955. case 'x' : _width = siz; _height = _depth = _spectrum = 1; break;
  34956. case 'y' : _height = siz; _width = _depth = _spectrum = 1; break;
  34957. case 'z' : _depth = siz; _width = _height = _spectrum = 1; break;
  34958. case 'c' : _spectrum = siz; _width = _height = _depth = 1; break;
  34959. }
  34960. return *this;
  34961. }
  34962. //! Unroll pixel values along specified axis \newinstance.
  34963. CImg<T> get_unroll(const char axis) const {
  34964. return (+*this).unroll(axis);
  34965. }
  34966. //! Rotate image with arbitrary angle.
  34967. /**
  34968. \param angle Rotation angle, in degrees.
  34969. \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
  34970. \param boundary_conditions Boundary conditions.
  34971. Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
  34972. \note The size of the image is modified.
  34973. **/
  34974. CImg<T>& rotate(const float angle, const unsigned int interpolation=1,
  34975. const unsigned int boundary_conditions=0) {
  34976. const float nangle = cimg::mod(angle,360.f);
  34977. if (nangle==0.f) return *this;
  34978. return get_rotate(nangle,interpolation,boundary_conditions).move_to(*this);
  34979. }
  34980. //! Rotate image with arbitrary angle \newinstance.
  34981. CImg<T> get_rotate(const float angle, const unsigned int interpolation=1,
  34982. const unsigned int boundary_conditions=0) const {
  34983. if (is_empty()) return *this;
  34984. CImg<T> res;
  34985. const float nangle = cimg::mod(angle,360.f);
  34986. if (boundary_conditions!=1 && cimg::mod(nangle,90.f)==0) { // Optimized version for orthogonal angles
  34987. const int wm1 = width() - 1, hm1 = height() - 1;
  34988. const int iangle = (int)nangle/90;
  34989. switch (iangle) {
  34990. case 1 : { // 90 deg
  34991. res.assign(_height,_width,_depth,_spectrum);
  34992. T *ptrd = res._data;
  34993. cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(y,hm1 - x,z,c);
  34994. } break;
  34995. case 2 : { // 180 deg
  34996. res.assign(_width,_height,_depth,_spectrum);
  34997. T *ptrd = res._data;
  34998. cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - x,hm1 - y,z,c);
  34999. } break;
  35000. case 3 : { // 270 deg
  35001. res.assign(_height,_width,_depth,_spectrum);
  35002. T *ptrd = res._data;
  35003. cimg_forXYZC(res,x,y,z,c) *(ptrd++) = (*this)(wm1 - y,x,z,c);
  35004. } break;
  35005. default : // 0 deg
  35006. return *this;
  35007. }
  35008. } else { // Generic angle
  35009. const float
  35010. rad = (float)(nangle*cimg::PI/180.),
  35011. ca = (float)std::cos(rad), sa = (float)std::sin(rad),
  35012. ux = cimg::abs((_width - 1)*ca), uy = cimg::abs((_width - 1)*sa),
  35013. vx = cimg::abs((_height - 1)*sa), vy = cimg::abs((_height - 1)*ca),
  35014. w2 = 0.5f*(_width - 1), h2 = 0.5f*(_height - 1);
  35015. res.assign((int)cimg::round(1 + ux + vx),(int)cimg::round(1 + uy + vy),_depth,_spectrum);
  35016. const float rw2 = 0.5f*(res._width - 1), rh2 = 0.5f*(res._height - 1);
  35017. _rotate(res,nangle,interpolation,boundary_conditions,w2,h2,rw2,rh2);
  35018. }
  35019. return res;
  35020. }
  35021. //! Rotate image with arbitrary angle, around a center point.
  35022. /**
  35023. \param angle Rotation angle, in degrees.
  35024. \param cx X-coordinate of the rotation center.
  35025. \param cy Y-coordinate of the rotation center.
  35026. \param interpolation Type of interpolation, <tt>{ 0=nearest | 1=linear | 2=cubic | 3=mirror }</tt>.
  35027. \param boundary_conditions Boundary conditions, <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
  35028. **/
  35029. CImg<T>& rotate(const float angle, const float cx, const float cy,
  35030. const unsigned int interpolation, const unsigned int boundary_conditions=0) {
  35031. return get_rotate(angle,cx,cy,interpolation,boundary_conditions).move_to(*this);
  35032. }
  35033. //! Rotate image with arbitrary angle, around a center point \newinstance.
  35034. CImg<T> get_rotate(const float angle, const float cx, const float cy,
  35035. const unsigned int interpolation, const unsigned int boundary_conditions=0) const {
  35036. if (is_empty()) return *this;
  35037. CImg<T> res(_width,_height,_depth,_spectrum);
  35038. _rotate(res,angle,interpolation,boundary_conditions,cx,cy,cx,cy);
  35039. return res;
  35040. }
  35041. // [internal] Perform 2D rotation with arbitrary angle.
  35042. void _rotate(CImg<T>& res, const float angle,
  35043. const unsigned int interpolation, const unsigned int boundary_conditions,
  35044. const float w2, const float h2,
  35045. const float rw2, const float rh2) const {
  35046. const float
  35047. rad = (float)(angle*cimg::PI/180.),
  35048. ca = (float)std::cos(rad), sa = (float)std::sin(rad);
  35049. switch (boundary_conditions) {
  35050. case 3 : { // Mirror
  35051. switch (interpolation) {
  35052. case 2 : { // Cubic interpolation
  35053. const float ww = 2.f*width(), hh = 2.f*height();
  35054. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
  35055. cimg_forXYZC(res,x,y,z,c) {
  35056. const float xc = x - rw2, yc = y - rh2,
  35057. mx = cimg::mod(w2 + xc*ca + yc*sa,ww),
  35058. my = cimg::mod(h2 - xc*sa + yc*ca,hh);
  35059. res(x,y,z,c) = _cubic_atXY_c(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c);
  35060. }
  35061. } break;
  35062. case 1 : { // Linear interpolation
  35063. const float ww = 2.f*width(), hh = 2.f*height();
  35064. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
  35065. cimg_forXYZC(res,x,y,z,c) {
  35066. const float xc = x - rw2, yc = y - rh2,
  35067. mx = cimg::mod(w2 + xc*ca + yc*sa,ww),
  35068. my = cimg::mod(h2 - xc*sa + yc*ca,hh);
  35069. res(x,y,z,c) = (T)_linear_atXY(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c);
  35070. }
  35071. } break;
  35072. default : { // Nearest-neighbor interpolation
  35073. const int ww = 2*width(), hh = 2*height();
  35074. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
  35075. cimg_forXYZC(res,x,y,z,c) {
  35076. const float xc = x - rw2, yc = y - rh2,
  35077. mx = cimg::mod((int)cimg::round(w2 + xc*ca + yc*sa),ww),
  35078. my = cimg::mod((int)cimg::round(h2 - xc*sa + yc*ca),hh);
  35079. res(x,y,z,c) = (*this)(mx<width()?mx:ww - mx - 1,my<height()?my:hh - my - 1,z,c);
  35080. }
  35081. }
  35082. }
  35083. } break;
  35084. case 2 : // Periodic
  35085. switch (interpolation) {
  35086. case 2 : { // Cubic interpolation
  35087. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
  35088. cimg_forXYZC(res,x,y,z,c) {
  35089. const float xc = x - rw2, yc = y - rh2;
  35090. res(x,y,z,c) = _cubic_atXY_pc(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
  35091. }
  35092. } break;
  35093. case 1 : { // Linear interpolation
  35094. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
  35095. cimg_forXYZC(res,x,y,z,c) {
  35096. const float xc = x - rw2, yc = y - rh2;
  35097. res(x,y,z,c) = (T)_linear_atXY_p(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
  35098. }
  35099. } break;
  35100. default : { // Nearest-neighbor interpolation
  35101. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
  35102. cimg_forXYZC(res,x,y,z,c) {
  35103. const float xc = x - rw2, yc = y - rh2;
  35104. res(x,y,z,c) = (*this)(cimg::mod((int)cimg::round(w2 + xc*ca + yc*sa),(float)width()),
  35105. cimg::mod((int)cimg::round(h2 - xc*sa + yc*ca),(float)height()),z,c);
  35106. }
  35107. }
  35108. } break;
  35109. case 1 : // Neumann
  35110. switch (interpolation) {
  35111. case 2 : { // Cubic interpolation
  35112. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
  35113. cimg_forXYZC(res,x,y,z,c) {
  35114. const float xc = x - rw2, yc = y - rh2;
  35115. res(x,y,z,c) = _cubic_atXY_c(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
  35116. }
  35117. } break;
  35118. case 1 : { // Linear interpolation
  35119. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
  35120. cimg_forXYZC(res,x,y,z,c) {
  35121. const float xc = x - rw2, yc = y - rh2;
  35122. res(x,y,z,c) = (T)_linear_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c);
  35123. }
  35124. } break;
  35125. default : { // Nearest-neighbor interpolation
  35126. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
  35127. cimg_forXYZC(res,x,y,z,c) {
  35128. const float xc = x - rw2, yc = y - rh2;
  35129. res(x,y,z,c) = _atXY((int)cimg::round(w2 + xc*ca + yc*sa),
  35130. (int)cimg::round(h2 - xc*sa + yc*ca),z,c);
  35131. }
  35132. }
  35133. } break;
  35134. default : // Dirichlet
  35135. switch (interpolation) {
  35136. case 2 : { // Cubic interpolation
  35137. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
  35138. cimg_forXYZC(res,x,y,z,c) {
  35139. const float xc = x - rw2, yc = y - rh2;
  35140. res(x,y,z,c) = cubic_atXY_c(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c,(T)0);
  35141. }
  35142. } break;
  35143. case 1 : { // Linear interpolation
  35144. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
  35145. cimg_forXYZC(res,x,y,z,c) {
  35146. const float xc = x - rw2, yc = y - rh2;
  35147. res(x,y,z,c) = (T)linear_atXY(w2 + xc*ca + yc*sa,h2 - xc*sa + yc*ca,z,c,(T)0);
  35148. }
  35149. } break;
  35150. default : { // Nearest-neighbor interpolation
  35151. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),2048))
  35152. cimg_forXYZC(res,x,y,z,c) {
  35153. const float xc = x - rw2, yc = y - rh2;
  35154. res(x,y,z,c) = atXY((int)cimg::round(w2 + xc*ca + yc*sa),
  35155. (int)cimg::round(h2 - xc*sa + yc*ca),z,c,(T)0);
  35156. }
  35157. }
  35158. }
  35159. }
  35160. }
  35161. //! Rotate volumetric image with arbitrary angle and axis.
  35162. /**
  35163. \param u X-coordinate of the 3D rotation axis.
  35164. \param v Y-coordinate of the 3D rotation axis.
  35165. \param w Z-coordinate of the 3D rotation axis.
  35166. \param angle Rotation angle, in degrees.
  35167. \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
  35168. \param boundary_conditions Boundary conditions.
  35169. Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
  35170. \note Most of the time, size of the image is modified.
  35171. **/
  35172. CImg<T> rotate(const float u, const float v, const float w, const float angle,
  35173. const unsigned int interpolation, const unsigned int boundary_conditions) {
  35174. const float nangle = cimg::mod(angle,360.f);
  35175. if (nangle==0.f) return *this;
  35176. return get_rotate(u,v,w,nangle,interpolation,boundary_conditions).move_to(*this);
  35177. }
  35178. //! Rotate volumetric image with arbitrary angle and axis \newinstance.
  35179. CImg<T> get_rotate(const float u, const float v, const float w, const float angle,
  35180. const unsigned int interpolation, const unsigned int boundary_conditions) const {
  35181. if (is_empty()) return *this;
  35182. CImg<T> res;
  35183. const float
  35184. w1 = _width - 1, h1 = _height - 1, d1 = _depth -1,
  35185. w2 = 0.5f*w1, h2 = 0.5f*h1, d2 = 0.5f*d1;
  35186. CImg<floatT> R = CImg<floatT>::rotation_matrix(u,v,w,angle);
  35187. const CImg<Tfloat>
  35188. X = R*CImg<Tfloat>(8,3,1,1,
  35189. 0.f,w1,w1,0.f,0.f,w1,w1,0.f,
  35190. 0.f,0.f,h1,h1,0.f,0.f,h1,h1,
  35191. 0.f,0.f,0.f,0.f,d1,d1,d1,d1);
  35192. float
  35193. xm, xM = X.get_shared_row(0).max_min(xm),
  35194. ym, yM = X.get_shared_row(1).max_min(ym),
  35195. zm, zM = X.get_shared_row(2).max_min(zm);
  35196. const int
  35197. dx = (int)cimg::round(xM - xm),
  35198. dy = (int)cimg::round(yM - ym),
  35199. dz = (int)cimg::round(zM - zm);
  35200. R.transpose();
  35201. res.assign(1 + dx,1 + dy,1 + dz,_spectrum);
  35202. const float rw2 = 0.5f*dx, rh2 = 0.5f*dy, rd2 = 0.5f*dz;
  35203. _rotate(res,R,interpolation,boundary_conditions,w2,h2,d2,rw2,rh2,rd2);
  35204. return res;
  35205. }
  35206. //! Rotate volumetric image with arbitrary angle and axis, around a center point.
  35207. /**
  35208. \param u X-coordinate of the 3D rotation axis.
  35209. \param v Y-coordinate of the 3D rotation axis.
  35210. \param w Z-coordinate of the 3D rotation axis.
  35211. \param angle Rotation angle, in degrees.
  35212. \param cx X-coordinate of the rotation center.
  35213. \param cy Y-coordinate of the rotation center.
  35214. \param cz Z-coordinate of the rotation center.
  35215. \param interpolation Type of interpolation. Can be <tt>{ 0=nearest | 1=linear | 2=cubic | 3=mirror }</tt>.
  35216. \param boundary_conditions Boundary conditions.
  35217. Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic }</tt>.
  35218. \note Most of the time, size of the image is modified.
  35219. **/
  35220. CImg<T> rotate(const float u, const float v, const float w, const float angle,
  35221. const float cx, const float cy, const float cz,
  35222. const unsigned int interpolation=1, const unsigned int boundary_conditions=0) {
  35223. const float nangle = cimg::mod(angle,360.f);
  35224. if (nangle==0.f) return *this;
  35225. return get_rotate(u,v,w,nangle,cx,cy,cz,interpolation,boundary_conditions).move_to(*this);
  35226. }
  35227. //! Rotate volumetric image with arbitrary angle and axis, around a center point \newinstance.
  35228. CImg<T> get_rotate(const float u, const float v, const float w, const float angle,
  35229. const float cx, const float cy, const float cz,
  35230. const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const {
  35231. if (is_empty()) return *this;
  35232. CImg<T> res(_width,_height,_depth,_spectrum);
  35233. CImg<floatT> R = CImg<floatT>::rotation_matrix(u,v,w,-angle);
  35234. _rotate(res,R,interpolation,boundary_conditions,cx,cy,cz,cx,cy,cz);
  35235. return res;
  35236. }
  35237. // [internal] Perform 3D rotation with arbitrary axis and angle.
  35238. void _rotate(CImg<T>& res, const CImg<Tfloat>& R,
  35239. const unsigned int interpolation, const unsigned int boundary_conditions,
  35240. const float w2, const float h2, const float d2,
  35241. const float rw2, const float rh2, const float rd2) const {
  35242. switch (boundary_conditions) {
  35243. case 3 : // Mirror
  35244. switch (interpolation) {
  35245. case 2 : { // Cubic interpolation
  35246. const float ww = 2.f*width(), hh = 2.f*height(), dd = 2.f*depth();
  35247. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
  35248. cimg_forXYZ(res,x,y,z) {
  35249. const float
  35250. xc = x - rw2, yc = y - rh2, zc = z - rd2,
  35251. X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww),
  35252. Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh),
  35253. Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd);
  35254. cimg_forC(res,c) res(x,y,z,c) = _cubic_atXYZ_c(X<width()?X:ww - X - 1,
  35255. Y<height()?Y:hh - Y - 1,
  35256. Z<depth()?Z:dd - Z - z,c);
  35257. }
  35258. } break;
  35259. case 1 : { // Linear interpolation
  35260. const float ww = 2.f*width(), hh = 2.f*height(), dd = 2.f*depth();
  35261. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
  35262. cimg_forXYZ(res,x,y,z) {
  35263. const float
  35264. xc = x - rw2, yc = y - rh2, zc = z - rd2,
  35265. X = cimg::mod((float)(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww),
  35266. Y = cimg::mod((float)(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh),
  35267. Z = cimg::mod((float)(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd);
  35268. cimg_forC(res,c) res(x,y,z,c) = (T)_linear_atXYZ(X<width()?X:ww - X - 1,
  35269. Y<height()?Y:hh - Y - 1,
  35270. Z<depth()?Z:dd - Z - 1,c);
  35271. }
  35272. } break;
  35273. default : { // Nearest-neighbor interpolation
  35274. const int ww = 2*width(), hh = 2*height(), dd = 2*depth();
  35275. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
  35276. cimg_forXYZ(res,x,y,z) {
  35277. const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
  35278. const int
  35279. X = cimg::mod((int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),ww),
  35280. Y = cimg::mod((int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),hh),
  35281. Z = cimg::mod((int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),dd);
  35282. cimg_forC(res,c) res(x,y,z,c) = (*this)(X<width()?X:ww - X - 1,
  35283. Y<height()?Y:hh - Y - 1,
  35284. Z<depth()?Z:dd - Z - 1,c);
  35285. }
  35286. }
  35287. } break;
  35288. case 2 : // Periodic
  35289. switch (interpolation) {
  35290. case 2 : { // Cubic interpolation
  35291. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
  35292. cimg_forXYZ(res,x,y,z) {
  35293. const float
  35294. xc = x - rw2, yc = y - rh2, zc = z - rd2,
  35295. X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
  35296. Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
  35297. Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
  35298. cimg_forC(res,c) res(x,y,z,c) = _cubic_atXYZ_pc(X,Y,Z,c);
  35299. }
  35300. } break;
  35301. case 1 : { // Linear interpolation
  35302. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
  35303. cimg_forXYZ(res,x,y,z) {
  35304. const float
  35305. xc = x - rw2, yc = y - rh2, zc = z - rd2,
  35306. X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
  35307. Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
  35308. Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
  35309. cimg_forC(res,c) res(x,y,z,c) = (T)_linear_atXYZ_p(X,Y,Z,c);
  35310. }
  35311. } break;
  35312. default : { // Nearest-neighbor interpolation
  35313. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
  35314. cimg_forXYZ(res,x,y,z) {
  35315. const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
  35316. const int
  35317. X = cimg::mod((int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),width()),
  35318. Y = cimg::mod((int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),height()),
  35319. Z = cimg::mod((int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc),depth());
  35320. cimg_forC(res,c) res(x,y,z,c) = (*this)(X,Y,Z,c);
  35321. }
  35322. }
  35323. } break;
  35324. case 1 : // Neumann
  35325. switch (interpolation) {
  35326. case 2 : { // Cubic interpolation
  35327. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
  35328. cimg_forXYZ(res,x,y,z) {
  35329. const float
  35330. xc = x - rw2, yc = y - rh2, zc = z - rd2,
  35331. X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
  35332. Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
  35333. Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
  35334. cimg_forC(res,c) res(x,y,z,c) = _cubic_atXYZ_c(X,Y,Z,c);
  35335. }
  35336. } break;
  35337. case 1 : { // Linear interpolation
  35338. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
  35339. cimg_forXYZ(res,x,y,z) {
  35340. const float
  35341. xc = x - rw2, yc = y - rh2, zc = z - rd2,
  35342. X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
  35343. Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
  35344. Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
  35345. cimg_forC(res,c) res(x,y,z,c) = _linear_atXYZ(X,Y,Z,c);
  35346. }
  35347. } break;
  35348. default : { // Nearest-neighbor interpolation
  35349. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
  35350. cimg_forXYZ(res,x,y,z) {
  35351. const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
  35352. const int
  35353. X = (int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),
  35354. Y = (int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),
  35355. Z = (int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc);
  35356. cimg_forC(res,c) res(x,y,z,c) = _atXYZ(X,Y,Z,c);
  35357. }
  35358. }
  35359. } break;
  35360. default : // Dirichlet
  35361. switch (interpolation) {
  35362. case 2 : { // Cubic interpolation
  35363. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
  35364. cimg_forXYZ(res,x,y,z) {
  35365. const float
  35366. xc = x - rw2, yc = y - rh2, zc = z - rd2,
  35367. X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
  35368. Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
  35369. Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
  35370. cimg_forC(res,c) res(x,y,z,c) = cubic_atXYZ_c(X,Y,Z,c,(T)0);
  35371. }
  35372. } break;
  35373. case 1 : { // Linear interpolation
  35374. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
  35375. cimg_forXYZ(res,x,y,z) {
  35376. const float
  35377. xc = x - rw2, yc = y - rh2, zc = z - rd2,
  35378. X = w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc,
  35379. Y = h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc,
  35380. Z = d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc;
  35381. cimg_forC(res,c) res(x,y,z,c) = linear_atXYZ(X,Y,Z,c,(T)0);
  35382. }
  35383. } break;
  35384. default : { // Nearest-neighbor interpolation
  35385. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(res.size(),2048))
  35386. cimg_forXYZ(res,x,y,z) {
  35387. const float xc = x - rw2, yc = y - rh2, zc = z - rd2;
  35388. const int
  35389. X = (int)cimg::round(w2 + R(0,0)*xc + R(1,0)*yc + R(2,0)*zc),
  35390. Y = (int)cimg::round(h2 + R(0,1)*xc + R(1,1)*yc + R(2,1)*zc),
  35391. Z = (int)cimg::round(d2 + R(0,2)*xc + R(1,2)*yc + R(2,2)*zc);
  35392. cimg_forC(res,c) res(x,y,z,c) = atXYZ(X,Y,Z,c,(T)0);
  35393. }
  35394. }
  35395. } break;
  35396. }
  35397. }
  35398. //! Warp image content by a warping field.
  35399. /**
  35400. \param warp Warping field.
  35401. \param mode Can be { 0=backward-absolute | 1=backward-relative | 2=forward-absolute | 3=foward-relative }
  35402. \param interpolation Can be <tt>{ 0=nearest | 1=linear | 2=cubic }</tt>.
  35403. \param boundary_conditions Boundary conditions <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
  35404. **/
  35405. template<typename t>
  35406. CImg<T>& warp(const CImg<t>& p_warp, const unsigned int mode=0,
  35407. const unsigned int interpolation=1, const unsigned int boundary_conditions=0) {
  35408. return get_warp(p_warp,mode,interpolation,boundary_conditions).move_to(*this);
  35409. }
  35410. //! Warp image content by a warping field \newinstance
  35411. template<typename t>
  35412. CImg<T> get_warp(const CImg<t>& p_warp, const unsigned int mode=0,
  35413. const unsigned int interpolation=1, const unsigned int boundary_conditions=0) const {
  35414. if (is_empty() || !p_warp) return *this;
  35415. if (mode && !is_sameXYZ(p_warp))
  35416. throw CImgArgumentException(_cimg_instance
  35417. "warp(): Instance and specified relative warping field (%u,%u,%u,%u,%p) "
  35418. "have different XYZ dimensions.",
  35419. cimg_instance,
  35420. p_warp._width,p_warp._height,p_warp._depth,p_warp._spectrum,p_warp._data);
  35421. CImg<T> res(p_warp._width,p_warp._height,p_warp._depth,_spectrum);
  35422. if (p_warp._spectrum==1) { // 1D warping
  35423. if (mode>=3) { // Forward-relative warp
  35424. res.fill((T)0);
  35425. if (interpolation>=1) // Linear interpolation
  35426. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35427. cimg_forYZC(res,y,z,c) {
  35428. const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
  35429. cimg_forX(res,x) res.set_linear_atX(*(ptrs++),x + (float)*(ptrs0++),y,z,c);
  35430. }
  35431. else // Nearest-neighbor interpolation
  35432. cimg_forYZC(res,y,z,c) {
  35433. const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
  35434. cimg_forX(res,x) {
  35435. const int X = x + (int)cimg::round(*(ptrs0++));
  35436. if (X>=0 && X<width()) res(X,y,z,c) = *(ptrs++);
  35437. }
  35438. }
  35439. } else if (mode==2) { // Forward-absolute warp
  35440. res.fill((T)0);
  35441. if (interpolation>=1) // Linear interpolation
  35442. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35443. cimg_forYZC(res,y,z,c) {
  35444. const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
  35445. cimg_forX(res,x) res.set_linear_atX(*(ptrs++),(float)*(ptrs0++),y,z,c);
  35446. }
  35447. else // Nearest-neighbor interpolation
  35448. cimg_forYZC(res,y,z,c) {
  35449. const t *ptrs0 = p_warp.data(0,y,z); const T *ptrs = data(0,y,z,c);
  35450. cimg_forX(res,x) {
  35451. const int X = (int)cimg::round(*(ptrs0++));
  35452. if (X>=0 && X<width()) res(X,y,z,c) = *(ptrs++);
  35453. }
  35454. }
  35455. } else if (mode==1) { // Backward-relative warp
  35456. if (interpolation==2) // Cubic interpolation
  35457. switch (boundary_conditions) {
  35458. case 3 : { // Mirror
  35459. const float w2 = 2.f*width();
  35460. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35461. cimg_forYZC(res,y,z,c) {
  35462. const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  35463. cimg_forX(res,x) {
  35464. const float mx = cimg::mod(x - (float)*(ptrs0++),w2);
  35465. *(ptrd++) = _cubic_atX_c(mx<width()?mx:w2 - mx - 1,y,z,c);
  35466. }
  35467. }
  35468. } break;
  35469. case 2 : // Periodic
  35470. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35471. cimg_forYZC(res,y,z,c) {
  35472. const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  35473. cimg_forX(res,x) *(ptrd++) = _cubic_atX_pc(x - (float)*(ptrs0++),y,z,c);
  35474. }
  35475. break;
  35476. case 1 : // Neumann
  35477. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35478. cimg_forYZC(res,y,z,c) {
  35479. const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  35480. cimg_forX(res,x) *(ptrd++) = _cubic_atX_c(x - (float)*(ptrs0++),y,z,c);
  35481. }
  35482. break;
  35483. default : // Dirichlet
  35484. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35485. cimg_forYZC(res,y,z,c) {
  35486. const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  35487. cimg_forX(res,x) *(ptrd++) = cubic_atX_c(x - (float)*(ptrs0++),y,z,c,(T)0);
  35488. }
  35489. }
  35490. else if (interpolation==1) // Linear interpolation
  35491. switch (boundary_conditions) {
  35492. case 3 : { // Mirror
  35493. const float w2 = 2.f*width();
  35494. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35495. cimg_forYZC(res,y,z,c) {
  35496. const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  35497. cimg_forX(res,x) {
  35498. const float mx = cimg::mod(x - (float)*(ptrs0++),w2);
  35499. *(ptrd++) = (T)_linear_atX(mx<width()?mx:w2 - mx - 1,y,z,c);
  35500. }
  35501. }
  35502. } break;
  35503. case 2 : // Periodic
  35504. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
  35505. cimg_forYZC(res,y,z,c) {
  35506. const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  35507. cimg_forX(res,x) *(ptrd++) = (T)_linear_atX_p(x - (float)*(ptrs0++),y,z,c);
  35508. }
  35509. break;
  35510. case 1 : // Neumann
  35511. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
  35512. cimg_forYZC(res,y,z,c) {
  35513. const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  35514. cimg_forX(res,x) *(ptrd++) = (T)_linear_atX(x - (float)*(ptrs0++),y,z,c);
  35515. }
  35516. break;
  35517. default : // Dirichlet
  35518. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
  35519. cimg_forYZC(res,y,z,c) {
  35520. const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  35521. cimg_forX(res,x) *(ptrd++) = (T)linear_atX(x - (float)*(ptrs0++),y,z,c,(T)0);
  35522. }
  35523. }
  35524. else // Nearest-neighbor interpolation
  35525. switch (boundary_conditions) {
  35526. case 3 : { // Mirror
  35527. const int w2 = 2*width();
  35528. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35529. cimg_forYZC(res,y,z,c) {
  35530. const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  35531. cimg_forX(res,x) {
  35532. const int mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2);
  35533. *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,y,z,c);
  35534. }
  35535. }
  35536. } break;
  35537. case 2 : // Periodic
  35538. cimg_forYZC(res,y,z,c) {
  35539. const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  35540. cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),width()),y,z,c);
  35541. }
  35542. break;
  35543. case 1 : // Neumann
  35544. cimg_forYZC(res,y,z,c) {
  35545. const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  35546. cimg_forX(res,x) *(ptrd++) = _atX(x - (int)cimg::round(*(ptrs0++)),y,z,c);
  35547. }
  35548. break;
  35549. default : // Dirichlet
  35550. cimg_forYZC(res,y,z,c) {
  35551. const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  35552. cimg_forX(res,x) *(ptrd++) = atX(x - (int)cimg::round(*(ptrs0++)),y,z,c,(T)0);
  35553. }
  35554. }
  35555. }
  35556. else { // Backward-absolute warp
  35557. if (interpolation==2) // Cubic interpolation
  35558. switch (boundary_conditions) {
  35559. case 3 : { // Mirror
  35560. const float w2 = 2.f*width();
  35561. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35562. cimg_forYZC(res,y,z,c) {
  35563. const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  35564. cimg_forX(res,x) {
  35565. const float mx = cimg::mod((float)*(ptrs0++),w2);
  35566. *(ptrd++) = _cubic_atX_c(mx<width()?mx:w2 - mx - 1,0,0,c);
  35567. }
  35568. }
  35569. } break;
  35570. case 2 : // Periodic
  35571. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35572. cimg_forYZC(res,y,z,c) {
  35573. const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  35574. cimg_forX(res,x) *(ptrd++) = _cubic_atX_pc((float)*(ptrs0++),0,0,c);
  35575. }
  35576. break;
  35577. case 1 : // Neumann
  35578. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35579. cimg_forYZC(res,y,z,c) {
  35580. const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  35581. cimg_forX(res,x) *(ptrd++) = _cubic_atX_c((float)*(ptrs0++),0,0,c);
  35582. }
  35583. break;
  35584. default : // Dirichlet
  35585. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35586. cimg_forYZC(res,y,z,c) {
  35587. const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  35588. cimg_forX(res,x) *(ptrd++) = cubic_atX_c((float)*(ptrs0++),0,0,c,(T)0);
  35589. }
  35590. }
  35591. else if (interpolation==1) // Linear interpolation
  35592. switch (boundary_conditions) {
  35593. case 3 : { // Mirror
  35594. const float w2 = 2.f*width();
  35595. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35596. cimg_forYZC(res,y,z,c) {
  35597. const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  35598. cimg_forX(res,x) {
  35599. const float mx = cimg::mod((float)*(ptrs0++),w2);
  35600. *(ptrd++) = (T)_linear_atX(mx<width()?mx:w2 - mx - 1,0,0,c);
  35601. }
  35602. }
  35603. } break;
  35604. case 2 : // Periodic
  35605. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
  35606. cimg_forYZC(res,y,z,c) {
  35607. const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  35608. cimg_forX(res,x) *(ptrd++) = (T)_linear_atX_p((float)*(ptrs0++),0,0,c);
  35609. }
  35610. break;
  35611. case 1 : // Neumann
  35612. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
  35613. cimg_forYZC(res,y,z,c) {
  35614. const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  35615. cimg_forX(res,x) *(ptrd++) = (T)_linear_atX((float)*(ptrs0++),0,0,c);
  35616. }
  35617. break;
  35618. default : // Dirichlet
  35619. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
  35620. cimg_forYZC(res,y,z,c) {
  35621. const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  35622. cimg_forX(res,x) *(ptrd++) = (T)linear_atX((float)*(ptrs0++),0,0,c,(T)0);
  35623. }
  35624. }
  35625. else // Nearest-neighbor interpolation
  35626. switch (boundary_conditions) {
  35627. case 3 : { // Mirror
  35628. const int w2 = 2*width();
  35629. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35630. cimg_forYZC(res,y,z,c) {
  35631. const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  35632. cimg_forX(res,x) {
  35633. const int mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2);
  35634. *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,0,0,c);
  35635. }
  35636. }
  35637. } break;
  35638. case 2 : // Periodic
  35639. cimg_forYZC(res,y,z,c) {
  35640. const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  35641. cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),width()),0,0,c);
  35642. }
  35643. break;
  35644. case 1 : // Neumann
  35645. cimg_forYZC(res,y,z,c) {
  35646. const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  35647. cimg_forX(res,x) *(ptrd++) = _atX((int)cimg::round(*(ptrs0++)),0,0,c);
  35648. }
  35649. break;
  35650. default : // Dirichlet
  35651. cimg_forYZC(res,y,z,c) {
  35652. const t *ptrs0 = p_warp.data(0,y,z); T *ptrd = res.data(0,y,z,c);
  35653. cimg_forX(res,x) *(ptrd++) = atX((int)cimg::round(*(ptrs0++)),0,0,c,(T)0);
  35654. }
  35655. }
  35656. }
  35657. } else if (p_warp._spectrum==2) { // 2D warping
  35658. if (mode>=3) { // Forward-relative warp
  35659. res.fill((T)0);
  35660. if (interpolation>=1) // Linear interpolation
  35661. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35662. cimg_forYZC(res,y,z,c) {
  35663. 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);
  35664. cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++),z,c);
  35665. }
  35666. else // Nearest-neighbor interpolation
  35667. cimg_forYZC(res,y,z,c) {
  35668. 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);
  35669. cimg_forX(res,x) {
  35670. const int X = x + (int)cimg::round(*(ptrs0++)), Y = y + (int)cimg::round(*(ptrs1++));
  35671. if (X>=0 && X<width() && Y>=0 && Y<height()) res(X,Y,z,c) = *(ptrs++);
  35672. }
  35673. }
  35674. } else if (mode==2) { // Forward-absolute warp
  35675. res.fill((T)0);
  35676. if (interpolation>=1) // Linear interpolation
  35677. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35678. cimg_forYZC(res,y,z,c) {
  35679. 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);
  35680. cimg_forX(res,x) res.set_linear_atXY(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),z,c);
  35681. }
  35682. else // Nearest-neighbor interpolation
  35683. cimg_forYZC(res,y,z,c) {
  35684. 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);
  35685. cimg_forX(res,x) {
  35686. const int X = (int)cimg::round(*(ptrs0++)), Y = (int)cimg::round(*(ptrs1++));
  35687. if (X>=0 && X<width() && Y>=0 && Y<height()) res(X,Y,z,c) = *(ptrs++);
  35688. }
  35689. }
  35690. } else if (mode==1) { // Backward-relative warp
  35691. if (interpolation==2) // Cubic interpolation
  35692. switch (boundary_conditions) {
  35693. case 3 : { // Mirror
  35694. const float w2 = 2.f*width(), h2 = 2.f*height();
  35695. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35696. cimg_forYZC(res,y,z,c) {
  35697. 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);
  35698. cimg_forX(res,x) {
  35699. const float
  35700. mx = cimg::mod(x - (float)*(ptrs0++),w2),
  35701. my = cimg::mod(y - (float)*(ptrs1++),h2);
  35702. *(ptrd++) = _cubic_atXY_c(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c);
  35703. }
  35704. }
  35705. } break;
  35706. case 2 : // Periodic
  35707. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35708. cimg_forYZC(res,y,z,c) {
  35709. 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);
  35710. cimg_forX(res,x) *(ptrd++) = _cubic_atXY_pc(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
  35711. }
  35712. break;
  35713. case 1 : // Neumann
  35714. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35715. cimg_forYZC(res,y,z,c) {
  35716. 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);
  35717. cimg_forX(res,x) *(ptrd++) = _cubic_atXY_c(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
  35718. }
  35719. break;
  35720. default : // Dirichlet
  35721. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35722. cimg_forYZC(res,y,z,c) {
  35723. 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);
  35724. cimg_forX(res,x) *(ptrd++) = cubic_atXY_c(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,(T)0);
  35725. }
  35726. }
  35727. else if (interpolation==1) // Linear interpolation
  35728. switch (boundary_conditions) {
  35729. case 3 : { // Mirror
  35730. const float w2 = 2.f*width(), h2 = 2.f*height();
  35731. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35732. cimg_forYZC(res,y,z,c) {
  35733. 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);
  35734. cimg_forX(res,x) {
  35735. const float
  35736. mx = cimg::mod(x - (float)*(ptrs0++),w2),
  35737. my = cimg::mod(y - (float)*(ptrs1++),h2);
  35738. *(ptrd++) = (T)_linear_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c);
  35739. }
  35740. }
  35741. } break;
  35742. case 2 : // Periodic
  35743. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
  35744. cimg_forYZC(res,y,z,c) {
  35745. 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);
  35746. cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY_p(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
  35747. }
  35748. break;
  35749. case 1 : // Neumann
  35750. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
  35751. cimg_forYZC(res,y,z,c) {
  35752. 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);
  35753. cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c);
  35754. }
  35755. break;
  35756. default : // Dirichlet
  35757. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
  35758. cimg_forYZC(res,y,z,c) {
  35759. 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);
  35760. cimg_forX(res,x) *(ptrd++) = (T)linear_atXY(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z,c,(T)0);
  35761. }
  35762. }
  35763. else // Nearest-neighbor interpolation
  35764. switch (boundary_conditions) {
  35765. case 3 : { // Mirror
  35766. const int w2 = 2*width(), h2 = 2*height();
  35767. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35768. cimg_forYZC(res,y,z,c) {
  35769. 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);
  35770. cimg_forX(res,x) {
  35771. const int
  35772. mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2),
  35773. my = cimg::mod(y - (int)cimg::round(*(ptrs1++)),h2);
  35774. *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,z,c);
  35775. }
  35776. }
  35777. } break;
  35778. case 2 : // Periodic
  35779. cimg_forYZC(res,y,z,c) {
  35780. 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);
  35781. cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),width()),
  35782. cimg::mod(y - (int)cimg::round(*(ptrs1++)),height()),z,c);
  35783. }
  35784. break;
  35785. case 1 : // Neumann
  35786. cimg_forYZC(res,y,z,c) {
  35787. 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);
  35788. cimg_forX(res,x) *(ptrd++) = _atXY(x - (int)cimg::round(*(ptrs0++)),
  35789. y - (int)cimg::round(*(ptrs1++)),z,c);
  35790. }
  35791. break;
  35792. default : // Dirichlet
  35793. cimg_forYZC(res,y,z,c) {
  35794. 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);
  35795. cimg_forX(res,x) *(ptrd++) = atXY(x - (int)cimg::round(*(ptrs0++)),
  35796. y - (int)cimg::round(*(ptrs1++)),z,c,(T)0);
  35797. }
  35798. }
  35799. } else { // Backward-absolute warp
  35800. if (interpolation==2) // Cubic interpolation
  35801. switch (boundary_conditions) {
  35802. case 3 : { // Mirror
  35803. const float w2 = 2.f*width(), h2 = 2.f*height();
  35804. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35805. cimg_forYZC(res,y,z,c) {
  35806. 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);
  35807. cimg_forX(res,x) {
  35808. const float
  35809. mx = cimg::mod((float)*(ptrs0++),w2),
  35810. my = cimg::mod((float)*(ptrs1++),h2);
  35811. *(ptrd++) = _cubic_atXY_c(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c);
  35812. }
  35813. }
  35814. } break;
  35815. case 2 : // Periodic
  35816. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35817. cimg_forYZC(res,y,z,c) {
  35818. 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);
  35819. cimg_forX(res,x) *(ptrd++) = _cubic_atXY_pc((float)*(ptrs0++),(float)*(ptrs1++),0,c);
  35820. }
  35821. break;
  35822. case 1 : // Neumann
  35823. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35824. cimg_forYZC(res,y,z,c) {
  35825. 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);
  35826. cimg_forX(res,x) *(ptrd++) = _cubic_atXY_c((float)*(ptrs0++),(float)*(ptrs1++),0,c);
  35827. }
  35828. break;
  35829. default : // Dirichlet
  35830. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35831. cimg_forYZC(res,y,z,c) {
  35832. 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);
  35833. cimg_forX(res,x) *(ptrd++) = cubic_atXY_c((float)*(ptrs0++),(float)*(ptrs1++),0,c,(T)0);
  35834. }
  35835. }
  35836. else if (interpolation==1) // Linear interpolation
  35837. switch (boundary_conditions) {
  35838. case 3 : { // Mirror
  35839. const float w2 = 2.f*width(), h2 = 2.f*height();
  35840. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35841. cimg_forYZC(res,y,z,c) {
  35842. 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);
  35843. cimg_forX(res,x) {
  35844. const float
  35845. mx = cimg::mod((float)*(ptrs0++),w2),
  35846. my = cimg::mod((float)*(ptrs1++),h2);
  35847. *(ptrd++) = (T)_linear_atXY(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c);
  35848. }
  35849. }
  35850. } break;
  35851. case 2 : // Periodic
  35852. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
  35853. cimg_forYZC(res,y,z,c) {
  35854. 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);
  35855. cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY_p((float)*(ptrs0++),(float)*(ptrs1++),0,c);
  35856. }
  35857. break;
  35858. case 1 : // Neumann
  35859. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
  35860. cimg_forYZC(res,y,z,c) {
  35861. 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);
  35862. cimg_forX(res,x) *(ptrd++) = (T)_linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c);
  35863. }
  35864. break;
  35865. default : // Dirichlet
  35866. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
  35867. cimg_forYZC(res,y,z,c) {
  35868. 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);
  35869. cimg_forX(res,x) *(ptrd++) = (T)linear_atXY((float)*(ptrs0++),(float)*(ptrs1++),0,c,(T)0);
  35870. }
  35871. }
  35872. else // Nearest-neighbor interpolation
  35873. switch (boundary_conditions) {
  35874. case 3 : { // Mirror
  35875. const int w2 = 2*width(), h2 = 2*height();
  35876. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35877. cimg_forYZC(res,y,z,c) {
  35878. 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);
  35879. cimg_forX(res,x) {
  35880. const int
  35881. mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2),
  35882. my = cimg::mod((int)cimg::round(*(ptrs1++)),h2);
  35883. *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,my<height()?my:h2 - my - 1,0,c);
  35884. }
  35885. }
  35886. } break;
  35887. case 2 : // Periodic
  35888. cimg_forYZC(res,y,z,c) {
  35889. 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);
  35890. cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),width()),
  35891. cimg::mod((int)cimg::round(*(ptrs1++)),height()),0,c);
  35892. }
  35893. break;
  35894. case 1 : // Neumann
  35895. cimg_forYZC(res,y,z,c) {
  35896. 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);
  35897. cimg_forX(res,x) *(ptrd++) = _atXY((int)cimg::round(*(ptrs0++)),
  35898. (int)cimg::round(*(ptrs1++)),0,c);
  35899. }
  35900. break;
  35901. default : // Dirichlet
  35902. cimg_forYZC(res,y,z,c) {
  35903. 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);
  35904. cimg_forX(res,x) *(ptrd++) = atXY((int)cimg::round(*(ptrs0++)),
  35905. (int)cimg::round(*(ptrs1++)),0,c,(T)0);
  35906. }
  35907. }
  35908. }
  35909. } else { // 3D warping
  35910. if (mode>=3) { // Forward-relative warp
  35911. res.fill((T)0);
  35912. if (interpolation>=1) // Linear interpolation
  35913. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35914. cimg_forYZC(res,y,z,c) {
  35915. 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);
  35916. const T *ptrs = data(0,y,z,c);
  35917. cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),x + (float)*(ptrs0++),y + (float)*(ptrs1++),
  35918. z + (float)*(ptrs2++),c);
  35919. }
  35920. else // Nearest-neighbor interpolation
  35921. cimg_forYZC(res,y,z,c) {
  35922. 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);
  35923. const T *ptrs = data(0,y,z,c);
  35924. cimg_forX(res,x) {
  35925. const int
  35926. X = x + (int)cimg::round(*(ptrs0++)),
  35927. Y = y + (int)cimg::round(*(ptrs1++)),
  35928. Z = z + (int)cimg::round(*(ptrs2++));
  35929. if (X>=0 && X<width() && Y>=0 && Y<height() && Z>=0 && Z<depth()) res(X,Y,Z,c) = *(ptrs++);
  35930. }
  35931. }
  35932. } else if (mode==2) { // Forward-absolute warp
  35933. res.fill((T)0);
  35934. if (interpolation>=1) // Linear interpolation
  35935. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35936. cimg_forYZC(res,y,z,c) {
  35937. 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);
  35938. const T *ptrs = data(0,y,z,c);
  35939. cimg_forX(res,x) res.set_linear_atXYZ(*(ptrs++),(float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
  35940. }
  35941. else // Nearest-neighbor interpolation
  35942. cimg_forYZC(res,y,z,c) {
  35943. 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);
  35944. const T *ptrs = data(0,y,z,c);
  35945. cimg_forX(res,x) {
  35946. const int
  35947. X = (int)cimg::round(*(ptrs0++)),
  35948. Y = (int)cimg::round(*(ptrs1++)),
  35949. Z = (int)cimg::round(*(ptrs2++));
  35950. if (X>=0 && X<width() && Y>=0 && Y<height() && Z>=0 && Z<depth()) res(X,Y,Z,c) = *(ptrs++);
  35951. }
  35952. }
  35953. } else if (mode==1) { // Backward-relative warp
  35954. if (interpolation==2) // Cubic interpolation
  35955. switch (boundary_conditions) {
  35956. case 3 : { // Mirror
  35957. const float w2 = 2.f*width(), h2 = 2.f*height(), d2 = 2.f*depth();
  35958. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35959. cimg_forYZC(res,y,z,c) {
  35960. 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);
  35961. T *ptrd = res.data(0,y,z,c);
  35962. cimg_forX(res,x) {
  35963. const float
  35964. mx = cimg::mod(x - (float)*(ptrs0++),w2),
  35965. my = cimg::mod(y - (float)*(ptrs1++),h2),
  35966. mz = cimg::mod(z - (float)*(ptrs2++),d2);
  35967. *(ptrd++) = _cubic_atXYZ_c(mx<width()?mx:w2 - mx - 1,
  35968. my<height()?my:h2 - my - 1,
  35969. mz<depth()?mz:d2 - mz - 1,c);
  35970. }
  35971. }
  35972. } break;
  35973. case 2 : // Periodic
  35974. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35975. cimg_forYZC(res,y,z,c) {
  35976. 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);
  35977. T *ptrd = res.data(0,y,z,c);
  35978. cimg_forX(res,x)
  35979. *(ptrd++) = _cubic_atXYZ_pc(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
  35980. }
  35981. break;
  35982. case 1 : // Neumann
  35983. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35984. cimg_forYZC(res,y,z,c) {
  35985. 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);
  35986. T *ptrd = res.data(0,y,z,c);
  35987. cimg_forX(res,x)
  35988. *(ptrd++) = _cubic_atXYZ_c(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
  35989. }
  35990. break;
  35991. default : // Dirichlet
  35992. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  35993. cimg_forYZC(res,y,z,c) {
  35994. 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);
  35995. T *ptrd = res.data(0,y,z,c);
  35996. cimg_forX(res,x)
  35997. *(ptrd++) = cubic_atXYZ_c(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,(T)0);
  35998. }
  35999. }
  36000. else if (interpolation==1) // Linear interpolation
  36001. switch (boundary_conditions) {
  36002. case 3 : { // Mirror
  36003. const float w2 = 2.f*width(), h2 = 2.f*height(), d2 = 2.f*depth();
  36004. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  36005. cimg_forYZC(res,y,z,c) {
  36006. 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);
  36007. T *ptrd = res.data(0,y,z,c);
  36008. cimg_forX(res,x) {
  36009. const float
  36010. mx = cimg::mod(x - (float)*(ptrs0++),w2),
  36011. my = cimg::mod(y - (float)*(ptrs1++),h2),
  36012. mz = cimg::mod(z - (float)*(ptrs2++),d2);
  36013. *(ptrd++) = (T)_linear_atXYZ(mx<width()?mx:w2 - mx - 1,
  36014. my<height()?my:h2 - my - 1,
  36015. mz<depth()?mz:d2 - mz - 1,c);
  36016. }
  36017. }
  36018. } break;
  36019. case 2 : // Periodic
  36020. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
  36021. cimg_forYZC(res,y,z,c) {
  36022. 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);
  36023. T *ptrd = res.data(0,y,z,c);
  36024. cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ_p(x - (float)*(ptrs0++),y - (float)*(ptrs1++),
  36025. z - (float)*(ptrs2++),c);
  36026. }
  36027. break;
  36028. case 1 : // Neumann
  36029. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
  36030. cimg_forYZC(res,y,z,c) {
  36031. 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);
  36032. T *ptrd = res.data(0,y,z,c);
  36033. cimg_forX(res,x)
  36034. *(ptrd++) = (T)_linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c);
  36035. }
  36036. break;
  36037. default : // Dirichlet
  36038. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
  36039. cimg_forYZC(res,y,z,c) {
  36040. 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);
  36041. T *ptrd = res.data(0,y,z,c);
  36042. cimg_forX(res,x)
  36043. *(ptrd++) = (T)linear_atXYZ(x - (float)*(ptrs0++),y - (float)*(ptrs1++),z - (float)*(ptrs2++),c,(T)0);
  36044. }
  36045. }
  36046. else // Nearest neighbor interpolation
  36047. switch (boundary_conditions) {
  36048. case 3 : { // Mirror
  36049. const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth();
  36050. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  36051. cimg_forYZC(res,y,z,c) {
  36052. 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);
  36053. T *ptrd = res.data(0,y,z,c);
  36054. cimg_forX(res,x) {
  36055. const int
  36056. mx = cimg::mod(x - (int)cimg::round(*(ptrs0++)),w2),
  36057. my = cimg::mod(y - (int)cimg::round(*(ptrs1++)),h2),
  36058. mz = cimg::mod(z - (int)cimg::round(*(ptrs2++)),d2);
  36059. *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,
  36060. my<height()?my:h2 - my - 1,
  36061. mz<depth()?mz:d2 - mz - 1,c);
  36062. }
  36063. }
  36064. } break;
  36065. case 2 : // Periodic
  36066. cimg_forYZC(res,y,z,c) {
  36067. 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);
  36068. T *ptrd = res.data(0,y,z,c);
  36069. cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod(x - (int)cimg::round(*(ptrs0++)),width()),
  36070. cimg::mod(y - (int)cimg::round(*(ptrs1++)),height()),
  36071. cimg::mod(z - (int)cimg::round(*(ptrs2++)),depth()),c);
  36072. }
  36073. break;
  36074. case 1 : // Neumann
  36075. cimg_forYZC(res,y,z,c) {
  36076. 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);
  36077. T *ptrd = res.data(0,y,z,c);
  36078. cimg_forX(res,x) *(ptrd++) = _atXYZ(x - (int)cimg::round(*(ptrs0++)),
  36079. y - (int)cimg::round(*(ptrs1++)),
  36080. z - (int)cimg::round(*(ptrs2++)),c);
  36081. }
  36082. break;
  36083. default : // Dirichlet
  36084. cimg_forYZC(res,y,z,c) {
  36085. 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);
  36086. T *ptrd = res.data(0,y,z,c);
  36087. cimg_forX(res,x) *(ptrd++) = atXYZ(x - (int)cimg::round(*(ptrs0++)),
  36088. y - (int)cimg::round(*(ptrs1++)),
  36089. z - (int)cimg::round(*(ptrs2++)),c,(T)0);
  36090. }
  36091. }
  36092. } else { // Backward-absolute warp
  36093. if (interpolation==2) // Cubic interpolation
  36094. switch (boundary_conditions) {
  36095. case 3 : { // Mirror
  36096. const float w2 = 2.f*width(), h2 = 2.f*height(), d2 = 2.f*depth();
  36097. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  36098. cimg_forYZC(res,y,z,c) {
  36099. 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);
  36100. T *ptrd = res.data(0,y,z,c);
  36101. cimg_forX(res,x) {
  36102. const float
  36103. mx = cimg::mod((float)*(ptrs0++),w2),
  36104. my = cimg::mod((float)*(ptrs1++),h2),
  36105. mz = cimg::mod((float)*(ptrs2++),d2);
  36106. *(ptrd++) = _cubic_atXYZ_c(mx<width()?mx:w2 - mx - 1,
  36107. my<height()?my:h2 - my - 1,
  36108. mz<depth()?mz:d2 - mz - 1,c);
  36109. }
  36110. }
  36111. } break;
  36112. case 2 : // Periodic
  36113. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  36114. cimg_forYZC(res,y,z,c) {
  36115. 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);
  36116. T *ptrd = res.data(0,y,z,c);
  36117. cimg_forX(res,x) *(ptrd++) = _cubic_atXYZ_pc((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
  36118. }
  36119. break;
  36120. case 1 : // Neumann
  36121. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  36122. cimg_forYZC(res,y,z,c) {
  36123. 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);
  36124. T *ptrd = res.data(0,y,z,c);
  36125. cimg_forX(res,x) *(ptrd++) = _cubic_atXYZ_c((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
  36126. }
  36127. break;
  36128. default : // Dirichlet
  36129. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  36130. cimg_forYZC(res,y,z,c) {
  36131. 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);
  36132. T *ptrd = res.data(0,y,z,c);
  36133. cimg_forX(res,x) *(ptrd++) = cubic_atXYZ_c((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),
  36134. c,(T)0);
  36135. }
  36136. }
  36137. else if (interpolation==1) // Linear interpolation
  36138. switch (boundary_conditions) {
  36139. case 3 : { // Mirror
  36140. const float w2 = 2.f*width(), h2 = 2.f*height(), d2 = 2.f*depth();
  36141. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  36142. cimg_forYZC(res,y,z,c) {
  36143. 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);
  36144. T *ptrd = res.data(0,y,z,c);
  36145. cimg_forX(res,x) {
  36146. const float
  36147. mx = cimg::mod((float)*(ptrs0++),w2),
  36148. my = cimg::mod((float)*(ptrs1++),h2),
  36149. mz = cimg::mod((float)*(ptrs2++),d2);
  36150. *(ptrd++) = (T)_linear_atXYZ(mx<width()?mx:w2 - mx - 1,
  36151. my<height()?my:h2 - my - 1,
  36152. mz<depth()?mz:d2 - mz - 1,c);
  36153. }
  36154. }
  36155. } break;
  36156. case 2 :// Periodic
  36157. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
  36158. cimg_forYZC(res,y,z,c) {
  36159. 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);
  36160. T *ptrd = res.data(0,y,z,c);
  36161. cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ_p((float)*(ptrs0++),(float)*(ptrs1++),
  36162. (float)*(ptrs2++),c);
  36163. }
  36164. break;
  36165. case 1 : // Neumann
  36166. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
  36167. cimg_forYZC(res,y,z,c) {
  36168. 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);
  36169. T *ptrd = res.data(0,y,z,c);
  36170. cimg_forX(res,x) *(ptrd++) = (T)_linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),c);
  36171. }
  36172. break;
  36173. default : // Dirichlet
  36174. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),1048576))
  36175. cimg_forYZC(res,y,z,c) {
  36176. 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);
  36177. T *ptrd = res.data(0,y,z,c);
  36178. cimg_forX(res,x) *(ptrd++) = (T)linear_atXYZ((float)*(ptrs0++),(float)*(ptrs1++),(float)*(ptrs2++),
  36179. c,(T)0);
  36180. }
  36181. }
  36182. else // Nearest-neighbor interpolation
  36183. switch (boundary_conditions) {
  36184. case 3 : { // Mirror
  36185. const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth();
  36186. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(res.size(),4096))
  36187. cimg_forYZC(res,y,z,c) {
  36188. 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);
  36189. T *ptrd = res.data(0,y,z,c);
  36190. cimg_forX(res,x) {
  36191. const int
  36192. mx = cimg::mod((int)cimg::round(*(ptrs0++)),w2),
  36193. my = cimg::mod((int)cimg::round(*(ptrs1++)),h2),
  36194. mz = cimg::mod((int)cimg::round(*(ptrs2++)),d2);
  36195. *(ptrd++) = (*this)(mx<width()?mx:w2 - mx - 1,
  36196. my<height()?my:h2 - my - 1,
  36197. mz<depth()?mz:d2 - mz - 1,c);
  36198. }
  36199. }
  36200. } break;
  36201. case 2 : // Periodic
  36202. cimg_forYZC(res,y,z,c) {
  36203. 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);
  36204. T *ptrd = res.data(0,y,z,c);
  36205. cimg_forX(res,x) *(ptrd++) = (*this)(cimg::mod((int)cimg::round(*(ptrs0++)),width()),
  36206. cimg::mod((int)cimg::round(*(ptrs1++)),height()),
  36207. cimg::mod((int)cimg::round(*(ptrs2++)),depth()),c);
  36208. }
  36209. break;
  36210. case 1 : // Neumann
  36211. cimg_forYZC(res,y,z,c) {
  36212. 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);
  36213. T *ptrd = res.data(0,y,z,c);
  36214. cimg_forX(res,x) *(ptrd++) = _atXYZ((int)cimg::round(*(ptrs0++)),
  36215. (int)cimg::round(*(ptrs1++)),
  36216. (int)cimg::round(*(ptrs2++)),c);
  36217. }
  36218. break;
  36219. default : // Dirichlet
  36220. cimg_forYZC(res,y,z,c) {
  36221. 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);
  36222. T *ptrd = res.data(0,y,z,c);
  36223. cimg_forX(res,x) *(ptrd++) = atXYZ((int)cimg::round(*(ptrs0++)),
  36224. (int)cimg::round(*(ptrs1++)),
  36225. (int)cimg::round(*(ptrs2++)),c,(T)0);
  36226. }
  36227. }
  36228. }
  36229. }
  36230. return res;
  36231. }
  36232. //! Generate a 2D representation of a 3D image, with XY,XZ and YZ views.
  36233. /**
  36234. \param x0 X-coordinate of the projection point.
  36235. \param y0 Y-coordinate of the projection point.
  36236. \param z0 Z-coordinate of the projection point.
  36237. **/
  36238. CImg<T> get_projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) const {
  36239. if (is_empty() || _depth<2) return +*this;
  36240. const unsigned int
  36241. _x0 = (x0>=_width)?_width - 1:x0,
  36242. _y0 = (y0>=_height)?_height - 1:y0,
  36243. _z0 = (z0>=_depth)?_depth - 1:z0;
  36244. const CImg<T>
  36245. img_xy = get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1),
  36246. img_zy = get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1).permute_axes("xzyc").
  36247. resize(_depth,_height,1,-100,-1),
  36248. img_xz = get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1).resize(_width,_depth,1,-100,-1);
  36249. return CImg<T>(_width + _depth,_height + _depth,1,_spectrum,cimg::min(img_xy.min(),img_zy.min(),img_xz.min())).
  36250. draw_image(0,0,img_xy).draw_image(img_xy._width,0,img_zy).
  36251. draw_image(0,img_xy._height,img_xz);
  36252. }
  36253. //! Construct a 2D representation of a 3D image, with XY,XZ and YZ views \inplace.
  36254. CImg<T>& projections2d(const unsigned int x0, const unsigned int y0, const unsigned int z0) {
  36255. if (_depth<2) return *this;
  36256. return get_projections2d(x0,y0,z0).move_to(*this);
  36257. }
  36258. //! Crop image region.
  36259. /**
  36260. \param x0 = X-coordinate of the upper-left crop rectangle corner.
  36261. \param y0 = Y-coordinate of the upper-left crop rectangle corner.
  36262. \param z0 = Z-coordinate of the upper-left crop rectangle corner.
  36263. \param c0 = C-coordinate of the upper-left crop rectangle corner.
  36264. \param x1 = X-coordinate of the lower-right crop rectangle corner.
  36265. \param y1 = Y-coordinate of the lower-right crop rectangle corner.
  36266. \param z1 = Z-coordinate of the lower-right crop rectangle corner.
  36267. \param c1 = C-coordinate of the lower-right crop rectangle corner.
  36268. \param boundary_conditions = Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
  36269. **/
  36270. CImg<T>& crop(const int x0, const int y0, const int z0, const int c0,
  36271. const int x1, const int y1, const int z1, const int c1,
  36272. const unsigned int boundary_conditions=0) {
  36273. return get_crop(x0,y0,z0,c0,x1,y1,z1,c1,boundary_conditions).move_to(*this);
  36274. }
  36275. //! Crop image region \newinstance.
  36276. CImg<T> get_crop(const int x0, const int y0, const int z0, const int c0,
  36277. const int x1, const int y1, const int z1, const int c1,
  36278. const unsigned int boundary_conditions=0) const {
  36279. if (is_empty())
  36280. throw CImgInstanceException(_cimg_instance
  36281. "crop(): Empty instance.",
  36282. cimg_instance);
  36283. const int
  36284. nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
  36285. ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0,
  36286. nz0 = z0<z1?z0:z1, nz1 = z0^z1^nz0,
  36287. nc0 = c0<c1?c0:c1, nc1 = c0^c1^nc0;
  36288. const unsigned int
  36289. _boundary_conditions = nx0>=0 && nx1<width() &&
  36290. ny0>=0 && ny1<height() &&
  36291. nz0>=0 && nz1<depth() &&
  36292. nc0>=0 && nc1<spectrum()?0:boundary_conditions;
  36293. CImg<T> res(1U + nx1 - nx0,1U + ny1 - ny0,1U + nz1 - nz0,1U + nc1 - nc0);
  36294. if (nx0<0 || nx1>=width() || ny0<0 || ny1>=height() || nz0<0 || nz1>=depth() || nc0<0 || nc1>=spectrum())
  36295. switch (_boundary_conditions) {
  36296. case 3 : { // Mirror
  36297. const int w2 = 2*width(), h2 = 2*height(), d2 = 2*depth(), s2 = 2*spectrum();
  36298. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
  36299. _height*_depth*_spectrum>=4))
  36300. cimg_forXYZC(res,x,y,z,c) {
  36301. const int
  36302. mx = cimg::mod(nx0 + x,w2),
  36303. my = cimg::mod(ny0 + y,h2),
  36304. mz = cimg::mod(nz0 + z,d2),
  36305. mc = cimg::mod(nc0 + c,s2);
  36306. res(x,y,z,c) = (*this)(mx<width()?mx:w2 - mx - 1,
  36307. my<height()?my:h2 - my - 1,
  36308. mz<depth()?mz:d2 - mz - 1,
  36309. mc<spectrum()?mc:s2 - mc - 1);
  36310. }
  36311. } break;
  36312. case 2 : { // Periodic
  36313. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
  36314. _height*_depth*_spectrum>=4))
  36315. cimg_forXYZC(res,x,y,z,c) {
  36316. res(x,y,z,c) = (*this)(cimg::mod(nx0 + x,width()),cimg::mod(ny0 + y,height()),
  36317. cimg::mod(nz0 + z,depth()),cimg::mod(nc0 + c,spectrum()));
  36318. }
  36319. } break;
  36320. case 1 : // Neumann
  36321. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
  36322. _height*_depth*_spectrum>=4))
  36323. cimg_forXYZC(res,x,y,z,c) res(x,y,z,c) = _atXYZC(nx0 + x,ny0 + y,nz0 + z,nc0 + c);
  36324. break;
  36325. default : // Dirichlet
  36326. res.fill((T)0).draw_image(-nx0,-ny0,-nz0,-nc0,*this);
  36327. }
  36328. else res.draw_image(-nx0,-ny0,-nz0,-nc0,*this);
  36329. return res;
  36330. }
  36331. //! Crop image region \overloading.
  36332. CImg<T>& crop(const int x0, const int y0, const int z0,
  36333. const int x1, const int y1, const int z1,
  36334. const unsigned int boundary_conditions=0) {
  36335. return crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions);
  36336. }
  36337. //! Crop image region \newinstance.
  36338. CImg<T> get_crop(const int x0, const int y0, const int z0,
  36339. const int x1, const int y1, const int z1,
  36340. const unsigned int boundary_conditions=0) const {
  36341. return get_crop(x0,y0,z0,0,x1,y1,z1,_spectrum - 1,boundary_conditions);
  36342. }
  36343. //! Crop image region \overloading.
  36344. CImg<T>& crop(const int x0, const int y0,
  36345. const int x1, const int y1,
  36346. const unsigned int boundary_conditions=0) {
  36347. return crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions);
  36348. }
  36349. //! Crop image region \newinstance.
  36350. CImg<T> get_crop(const int x0, const int y0,
  36351. const int x1, const int y1,
  36352. const unsigned int boundary_conditions=0) const {
  36353. return get_crop(x0,y0,0,0,x1,y1,_depth - 1,_spectrum - 1,boundary_conditions);
  36354. }
  36355. //! Crop image region \overloading.
  36356. CImg<T>& crop(const int x0, const int x1, const unsigned int boundary_conditions=0) {
  36357. return crop(x0,0,0,0,x1,_height - 1,_depth - 1,_spectrum - 1,boundary_conditions);
  36358. }
  36359. //! Crop image region \newinstance.
  36360. CImg<T> get_crop(const int x0, const int x1, const unsigned int boundary_conditions=0) const {
  36361. return get_crop(x0,0,0,0,x1,_height - 1,_depth - 1,_spectrum - 1,boundary_conditions);
  36362. }
  36363. //! Autocrop image region, regarding the specified background value.
  36364. CImg<T>& autocrop(const T& value, const char *const axes="czyx") {
  36365. if (is_empty()) return *this;
  36366. for (const char *s = axes; *s; ++s) {
  36367. const char axis = cimg::lowercase(*s);
  36368. const CImg<intT> coords = _autocrop(value,axis);
  36369. if (coords[0]==-1 && coords[1]==-1) return assign(); // Image has only 'value' pixels
  36370. else switch (axis) {
  36371. case 'x' : {
  36372. const int x0 = coords[0], x1 = coords[1];
  36373. if (x0>=0 && x1>=0) crop(x0,x1);
  36374. } break;
  36375. case 'y' : {
  36376. const int y0 = coords[0], y1 = coords[1];
  36377. if (y0>=0 && y1>=0) crop(0,y0,_width - 1,y1);
  36378. } break;
  36379. case 'z' : {
  36380. const int z0 = coords[0], z1 = coords[1];
  36381. if (z0>=0 && z1>=0) crop(0,0,z0,_width - 1,_height - 1,z1);
  36382. } break;
  36383. default : {
  36384. const int c0 = coords[0], c1 = coords[1];
  36385. if (c0>=0 && c1>=0) crop(0,0,0,c0,_width - 1,_height - 1,_depth - 1,c1);
  36386. }
  36387. }
  36388. }
  36389. return *this;
  36390. }
  36391. //! Autocrop image region, regarding the specified background value \newinstance.
  36392. CImg<T> get_autocrop(const T& value, const char *const axes="czyx") const {
  36393. return (+*this).autocrop(value,axes);
  36394. }
  36395. //! Autocrop image region, regarding the specified background color.
  36396. /**
  36397. \param color Color used for the crop. If \c 0, color is guessed.
  36398. \param axes Axes used for the crop.
  36399. **/
  36400. CImg<T>& autocrop(const T *const color=0, const char *const axes="zyx") {
  36401. if (is_empty()) return *this;
  36402. if (!color) { // Guess color
  36403. const CImg<T> col1 = get_vector_at(0,0,0);
  36404. const unsigned int w = _width, h = _height, d = _depth, s = _spectrum;
  36405. autocrop(col1,axes);
  36406. if (_width==w && _height==h && _depth==d && _spectrum==s) {
  36407. const CImg<T> col2 = get_vector_at(w - 1,h - 1,d - 1);
  36408. autocrop(col2,axes);
  36409. }
  36410. return *this;
  36411. }
  36412. for (const char *s = axes; *s; ++s) {
  36413. const char axis = cimg::lowercase(*s);
  36414. switch (axis) {
  36415. case 'x' : {
  36416. int x0 = width(), x1 = -1;
  36417. cimg_forC(*this,c) {
  36418. const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'x');
  36419. const int nx0 = coords[0], nx1 = coords[1];
  36420. if (nx0>=0 && nx1>=0) { x0 = std::min(x0,nx0); x1 = std::max(x1,nx1); }
  36421. }
  36422. if (x0==width() && x1==-1) return assign(); else crop(x0,x1);
  36423. } break;
  36424. case 'y' : {
  36425. int y0 = height(), y1 = -1;
  36426. cimg_forC(*this,c) {
  36427. const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'y');
  36428. const int ny0 = coords[0], ny1 = coords[1];
  36429. if (ny0>=0 && ny1>=0) { y0 = std::min(y0,ny0); y1 = std::max(y1,ny1); }
  36430. }
  36431. if (y0==height() && y1==-1) return assign(); else crop(0,y0,_width - 1,y1);
  36432. } break;
  36433. default : {
  36434. int z0 = depth(), z1 = -1;
  36435. cimg_forC(*this,c) {
  36436. const CImg<intT> coords = get_shared_channel(c)._autocrop(color[c],'z');
  36437. const int nz0 = coords[0], nz1 = coords[1];
  36438. if (nz0>=0 && nz1>=0) { z0 = std::min(z0,nz0); z1 = std::max(z1,nz1); }
  36439. }
  36440. if (z0==depth() && z1==-1) return assign(); else crop(0,0,z0,_width - 1,_height - 1,z1);
  36441. }
  36442. }
  36443. }
  36444. return *this;
  36445. }
  36446. //! Autocrop image region, regarding the specified background color \newinstance.
  36447. CImg<T> get_autocrop(const T *const color=0, const char *const axes="zyx") const {
  36448. return (+*this).autocrop(color,axes);
  36449. }
  36450. CImg<intT> _autocrop(const T& value, const char axis) const {
  36451. CImg<intT> res;
  36452. switch (cimg::lowercase(axis)) {
  36453. case 'x' : {
  36454. int x0 = -1, x1 = -1;
  36455. cimg_forX(*this,x) cimg_forYZC(*this,y,z,c)
  36456. if ((*this)(x,y,z,c)!=value) { x0 = x; x = width(); y = height(); z = depth(); c = spectrum(); }
  36457. if (x0>=0) {
  36458. for (int x = width() - 1; x>=0; --x) cimg_forYZC(*this,y,z,c)
  36459. if ((*this)(x,y,z,c)!=value) { x1 = x; x = 0; y = height(); z = depth(); c = spectrum(); }
  36460. }
  36461. res = CImg<intT>::vector(x0,x1);
  36462. } break;
  36463. case 'y' : {
  36464. int y0 = -1, y1 = -1;
  36465. cimg_forY(*this,y) cimg_forXZC(*this,x,z,c)
  36466. if ((*this)(x,y,z,c)!=value) { y0 = y; x = width(); y = height(); z = depth(); c = spectrum(); }
  36467. if (y0>=0) {
  36468. for (int y = height() - 1; y>=0; --y) cimg_forXZC(*this,x,z,c)
  36469. if ((*this)(x,y,z,c)!=value) { y1 = y; x = width(); y = 0; z = depth(); c = spectrum(); }
  36470. }
  36471. res = CImg<intT>::vector(y0,y1);
  36472. } break;
  36473. case 'z' : {
  36474. int z0 = -1, z1 = -1;
  36475. cimg_forZ(*this,z) cimg_forXYC(*this,x,y,c)
  36476. if ((*this)(x,y,z,c)!=value) { z0 = z; x = width(); y = height(); z = depth(); c = spectrum(); }
  36477. if (z0>=0) {
  36478. for (int z = depth() - 1; z>=0; --z) cimg_forXYC(*this,x,y,c)
  36479. if ((*this)(x,y,z,c)!=value) { z1 = z; x = width(); y = height(); z = 0; c = spectrum(); }
  36480. }
  36481. res = CImg<intT>::vector(z0,z1);
  36482. } break;
  36483. default : {
  36484. int c0 = -1, c1 = -1;
  36485. cimg_forC(*this,c) cimg_forXYZ(*this,x,y,z)
  36486. if ((*this)(x,y,z,c)!=value) { c0 = c; x = width(); y = height(); z = depth(); c = spectrum(); }
  36487. if (c0>=0) {
  36488. for (int c = spectrum() - 1; c>=0; --c) cimg_forXYZ(*this,x,y,z)
  36489. if ((*this)(x,y,z,c)!=value) { c1 = c; x = width(); y = height(); z = depth(); c = 0; }
  36490. }
  36491. res = CImg<intT>::vector(c0,c1);
  36492. }
  36493. }
  36494. return res;
  36495. }
  36496. //! Return specified image column.
  36497. /**
  36498. \param x0 Image column.
  36499. **/
  36500. CImg<T> get_column(const int x0) const {
  36501. return get_columns(x0,x0);
  36502. }
  36503. //! Return specified image column \inplace.
  36504. CImg<T>& column(const int x0) {
  36505. return columns(x0,x0);
  36506. }
  36507. //! Return specified range of image columns.
  36508. /**
  36509. \param x0 Starting image column.
  36510. \param x1 Ending image column.
  36511. **/
  36512. CImg<T>& columns(const int x0, const int x1) {
  36513. return get_columns(x0,x1).move_to(*this);
  36514. }
  36515. //! Return specified range of image columns \inplace.
  36516. CImg<T> get_columns(const int x0, const int x1) const {
  36517. return get_crop(x0,0,0,0,x1,height() - 1,depth() - 1,spectrum() - 1);
  36518. }
  36519. //! Return specified image row.
  36520. CImg<T> get_row(const int y0) const {
  36521. return get_rows(y0,y0);
  36522. }
  36523. //! Return specified image row \inplace.
  36524. /**
  36525. \param y0 Image row.
  36526. **/
  36527. CImg<T>& row(const int y0) {
  36528. return rows(y0,y0);
  36529. }
  36530. //! Return specified range of image rows.
  36531. /**
  36532. \param y0 Starting image row.
  36533. \param y1 Ending image row.
  36534. **/
  36535. CImg<T> get_rows(const int y0, const int y1) const {
  36536. return get_crop(0,y0,0,0,width() - 1,y1,depth() - 1,spectrum() - 1);
  36537. }
  36538. //! Return specified range of image rows \inplace.
  36539. CImg<T>& rows(const int y0, const int y1) {
  36540. return get_rows(y0,y1).move_to(*this);
  36541. }
  36542. //! Return specified image slice.
  36543. /**
  36544. \param z0 Image slice.
  36545. **/
  36546. CImg<T> get_slice(const int z0) const {
  36547. return get_slices(z0,z0);
  36548. }
  36549. //! Return specified image slice \inplace.
  36550. CImg<T>& slice(const int z0) {
  36551. return slices(z0,z0);
  36552. }
  36553. //! Return specified range of image slices.
  36554. /**
  36555. \param z0 Starting image slice.
  36556. \param z1 Ending image slice.
  36557. **/
  36558. CImg<T> get_slices(const int z0, const int z1) const {
  36559. return get_crop(0,0,z0,0,width() - 1,height() - 1,z1,spectrum() - 1);
  36560. }
  36561. //! Return specified range of image slices \inplace.
  36562. CImg<T>& slices(const int z0, const int z1) {
  36563. return get_slices(z0,z1).move_to(*this);
  36564. }
  36565. //! Return specified image channel.
  36566. /**
  36567. \param c0 Image channel.
  36568. **/
  36569. CImg<T> get_channel(const int c0) const {
  36570. return get_channels(c0,c0);
  36571. }
  36572. //! Return specified image channel \inplace.
  36573. CImg<T>& channel(const int c0) {
  36574. return channels(c0,c0);
  36575. }
  36576. //! Return specified range of image channels.
  36577. /**
  36578. \param c0 Starting image channel.
  36579. \param c1 Ending image channel.
  36580. **/
  36581. CImg<T> get_channels(const int c0, const int c1) const {
  36582. return get_crop(0,0,0,c0,width() - 1,height() - 1,depth() - 1,c1);
  36583. }
  36584. //! Return specified range of image channels \inplace.
  36585. CImg<T>& channels(const int c0, const int c1) {
  36586. return get_channels(c0,c1).move_to(*this);
  36587. }
  36588. //! Return stream line of a 2D or 3D vector field.
  36589. CImg<floatT> get_streamline(const float x, const float y, const float z,
  36590. const float L=256, const float dl=0.1f,
  36591. const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
  36592. const bool is_oriented_only=false) const {
  36593. if (_spectrum!=2 && _spectrum!=3)
  36594. throw CImgInstanceException(_cimg_instance
  36595. "streamline(): Instance is not a 2D or 3D vector field.",
  36596. cimg_instance);
  36597. if (_spectrum==2) {
  36598. if (is_oriented_only) {
  36599. typename CImg<T>::_functor4d_streamline2d_oriented func(*this);
  36600. return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,
  36601. 0,0,0,_width - 1.f,_height - 1.f,0.f);
  36602. } else {
  36603. typename CImg<T>::_functor4d_streamline2d_directed func(*this);
  36604. return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,
  36605. 0,0,0,_width - 1.f,_height - 1.f,0.f);
  36606. }
  36607. }
  36608. if (is_oriented_only) {
  36609. typename CImg<T>::_functor4d_streamline3d_oriented func(*this);
  36610. return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,true,
  36611. 0,0,0,_width - 1.f,_height - 1.f,_depth - 1.f);
  36612. }
  36613. typename CImg<T>::_functor4d_streamline3d_directed func(*this);
  36614. return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,false,
  36615. 0,0,0,_width - 1.f,_height - 1.f,_depth - 1.f);
  36616. }
  36617. //! Return stream line of a 3D vector field.
  36618. /**
  36619. \param func Vector field function.
  36620. \param x X-coordinate of the starting point of the streamline.
  36621. \param y Y-coordinate of the starting point of the streamline.
  36622. \param z Z-coordinate of the starting point of the streamline.
  36623. \param L Streamline length.
  36624. \param dl Streamline length increment.
  36625. \param interpolation_type Type of interpolation.
  36626. Can be <tt>{ 0=nearest int | 1=linear | 2=2nd-order RK | 3=4th-order RK. }</tt>.
  36627. \param is_backward_tracking Tells if the streamline is estimated forward or backward.
  36628. \param is_oriented_only Tells if the direction of the vectors must be ignored.
  36629. \param x0 X-coordinate of the first bounding-box vertex.
  36630. \param y0 Y-coordinate of the first bounding-box vertex.
  36631. \param z0 Z-coordinate of the first bounding-box vertex.
  36632. \param x1 X-coordinate of the second bounding-box vertex.
  36633. \param y1 Y-coordinate of the second bounding-box vertex.
  36634. \param z1 Z-coordinate of the second bounding-box vertex.
  36635. **/
  36636. template<typename tfunc>
  36637. static CImg<floatT> streamline(const tfunc& func,
  36638. const float x, const float y, const float z,
  36639. const float L=256, const float dl=0.1f,
  36640. const unsigned int interpolation_type=2, const bool is_backward_tracking=false,
  36641. const bool is_oriented_only=false,
  36642. const float x0=0, const float y0=0, const float z0=0,
  36643. const float x1=0, const float y1=0, const float z1=0) {
  36644. if (dl<=0)
  36645. throw CImgArgumentException("CImg<%s>::streamline(): Invalid specified integration length %g "
  36646. "(should be >0).",
  36647. pixel_type(),
  36648. dl);
  36649. const bool is_bounded = (x0!=x1 || y0!=y1 || z0!=z1);
  36650. if (L<=0 || (is_bounded && (x<x0 || x>x1 || y<y0 || y>y1 || z<z0 || z>z1))) return CImg<floatT>();
  36651. const unsigned int size_L = (unsigned int)cimg::round(L/dl + 1);
  36652. CImg<floatT> coordinates(size_L,3);
  36653. const float dl2 = dl/2;
  36654. float
  36655. *ptr_x = coordinates.data(0,0),
  36656. *ptr_y = coordinates.data(0,1),
  36657. *ptr_z = coordinates.data(0,2),
  36658. pu = (float)(dl*func(x,y,z,0)),
  36659. pv = (float)(dl*func(x,y,z,1)),
  36660. pw = (float)(dl*func(x,y,z,2)),
  36661. X = x, Y = y, Z = z;
  36662. switch (interpolation_type) {
  36663. case 0 : { // Nearest integer interpolation
  36664. cimg_forX(coordinates,l) {
  36665. *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
  36666. const int
  36667. xi = (int)(X>0?X + 0.5f:X - 0.5f),
  36668. yi = (int)(Y>0?Y + 0.5f:Y - 0.5f),
  36669. zi = (int)(Z>0?Z + 0.5f:Z - 0.5f);
  36670. float
  36671. u = (float)(dl*func((float)xi,(float)yi,(float)zi,0)),
  36672. v = (float)(dl*func((float)xi,(float)yi,(float)zi,1)),
  36673. w = (float)(dl*func((float)xi,(float)yi,(float)zi,2));
  36674. if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
  36675. if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
  36676. if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
  36677. }
  36678. } break;
  36679. case 1 : { // First-order interpolation
  36680. cimg_forX(coordinates,l) {
  36681. *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
  36682. float
  36683. u = (float)(dl*func(X,Y,Z,0)),
  36684. v = (float)(dl*func(X,Y,Z,1)),
  36685. w = (float)(dl*func(X,Y,Z,2));
  36686. if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
  36687. if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
  36688. if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
  36689. }
  36690. } break;
  36691. case 2 : { // Second order interpolation
  36692. cimg_forX(coordinates,l) {
  36693. *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
  36694. float
  36695. u0 = (float)(dl2*func(X,Y,Z,0)),
  36696. v0 = (float)(dl2*func(X,Y,Z,1)),
  36697. w0 = (float)(dl2*func(X,Y,Z,2));
  36698. if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
  36699. float
  36700. u = (float)(dl*func(X + u0,Y + v0,Z + w0,0)),
  36701. v = (float)(dl*func(X + u0,Y + v0,Z + w0,1)),
  36702. w = (float)(dl*func(X + u0,Y + v0,Z + w0,2));
  36703. if (is_oriented_only && u*pu + v*pv + w*pw<0) { u = -u; v = -v; w = -w; }
  36704. if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
  36705. if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
  36706. }
  36707. } break;
  36708. default : { // Fourth order interpolation
  36709. cimg_forX(coordinates,k) {
  36710. *(ptr_x++) = X; *(ptr_y++) = Y; *(ptr_z++) = Z;
  36711. float
  36712. u0 = (float)(dl2*func(X,Y,Z,0)),
  36713. v0 = (float)(dl2*func(X,Y,Z,1)),
  36714. w0 = (float)(dl2*func(X,Y,Z,2));
  36715. if (is_oriented_only && u0*pu + v0*pv + w0*pw<0) { u0 = -u0; v0 = -v0; w0 = -w0; }
  36716. float
  36717. u1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,0)),
  36718. v1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,1)),
  36719. w1 = (float)(dl2*func(X + u0,Y + v0,Z + w0,2));
  36720. if (is_oriented_only && u1*pu + v1*pv + w1*pw<0) { u1 = -u1; v1 = -v1; w1 = -w1; }
  36721. float
  36722. u2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,0)),
  36723. v2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,1)),
  36724. w2 = (float)(dl2*func(X + u1,Y + v1,Z + w1,2));
  36725. if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u2 = -u2; v2 = -v2; w2 = -w2; }
  36726. float
  36727. u3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,0)),
  36728. v3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,1)),
  36729. w3 = (float)(dl2*func(X + u2,Y + v2,Z + w2,2));
  36730. if (is_oriented_only && u2*pu + v2*pv + w2*pw<0) { u3 = -u3; v3 = -v3; w3 = -w3; }
  36731. const float
  36732. u = (u0 + u3)/3 + (u1 + u2)/1.5f,
  36733. v = (v0 + v3)/3 + (v1 + v2)/1.5f,
  36734. w = (w0 + w3)/3 + (w1 + w2)/1.5f;
  36735. if (is_backward_tracking) { X-=(pu=u); Y-=(pv=v); Z-=(pw=w); } else { X+=(pu=u); Y+=(pv=v); Z+=(pw=w); }
  36736. if (is_bounded && (X<x0 || X>x1 || Y<y0 || Y>y1 || Z<z0 || Z>z1)) break;
  36737. }
  36738. }
  36739. }
  36740. if (ptr_x!=coordinates.data(0,1)) coordinates.resize((int)(ptr_x-coordinates.data()),3,1,1,0);
  36741. return coordinates;
  36742. }
  36743. //! Return stream line of a 3D vector field \overloading.
  36744. static CImg<floatT> streamline(const char *const expression,
  36745. const float x, const float y, const float z,
  36746. const float L=256, const float dl=0.1f,
  36747. const unsigned int interpolation_type=2, const bool is_backward_tracking=true,
  36748. const bool is_oriented_only=false,
  36749. const float x0=0, const float y0=0, const float z0=0,
  36750. const float x1=0, const float y1=0, const float z1=0) {
  36751. _functor4d_streamline_expr func(expression);
  36752. return streamline(func,x,y,z,L,dl,interpolation_type,is_backward_tracking,is_oriented_only,x0,y0,z0,x1,y1,z1);
  36753. }
  36754. struct _functor4d_streamline2d_directed {
  36755. const CImg<T>& ref;
  36756. _functor4d_streamline2d_directed(const CImg<T>& pref):ref(pref) {}
  36757. float operator()(const float x, const float y, const float z, const unsigned int c) const {
  36758. return c<2?(float)ref._linear_atXY(x,y,(int)z,c):0;
  36759. }
  36760. };
  36761. struct _functor4d_streamline3d_directed {
  36762. const CImg<T>& ref;
  36763. _functor4d_streamline3d_directed(const CImg<T>& pref):ref(pref) {}
  36764. float operator()(const float x, const float y, const float z, const unsigned int c) const {
  36765. return (float)ref._linear_atXYZ(x,y,z,c);
  36766. }
  36767. };
  36768. struct _functor4d_streamline2d_oriented {
  36769. const CImg<T>& ref;
  36770. CImg<floatT> *pI;
  36771. _functor4d_streamline2d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,1,2); }
  36772. ~_functor4d_streamline2d_oriented() { delete pI; }
  36773. float operator()(const float x, const float y, const float z, const unsigned int c) const {
  36774. #define _cimg_vecalign2d(i,j) \
  36775. 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); }
  36776. int
  36777. xi = (int)x - (x>=0?0:1), nxi = xi + 1,
  36778. yi = (int)y - (y>=0?0:1), nyi = yi + 1,
  36779. zi = (int)z;
  36780. const float
  36781. dx = x - xi,
  36782. dy = y - yi;
  36783. if (c==0) {
  36784. CImg<floatT>& I = *pI;
  36785. if (xi<0) xi = 0;
  36786. if (nxi<0) nxi = 0;
  36787. if (xi>=ref.width()) xi = ref.width() - 1;
  36788. if (nxi>=ref.width()) nxi = ref.width() - 1;
  36789. if (yi<0) yi = 0;
  36790. if (nyi<0) nyi = 0;
  36791. if (yi>=ref.height()) yi = ref.height() - 1;
  36792. if (nyi>=ref.height()) nyi = ref.height() - 1;
  36793. I(0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,1) = (float)ref(xi,yi,zi,1);
  36794. I(1,0,0) = (float)ref(nxi,yi,zi,0); I(1,0,1) = (float)ref(nxi,yi,zi,1);
  36795. I(1,1,0) = (float)ref(nxi,nyi,zi,0); I(1,1,1) = (float)ref(nxi,nyi,zi,1);
  36796. I(0,1,0) = (float)ref(xi,nyi,zi,0); I(0,1,1) = (float)ref(xi,nyi,zi,1);
  36797. _cimg_vecalign2d(1,0); _cimg_vecalign2d(1,1); _cimg_vecalign2d(0,1);
  36798. }
  36799. return c<2?(float)pI->_linear_atXY(dx,dy,0,c):0;
  36800. }
  36801. };
  36802. struct _functor4d_streamline3d_oriented {
  36803. const CImg<T>& ref;
  36804. CImg<floatT> *pI;
  36805. _functor4d_streamline3d_oriented(const CImg<T>& pref):ref(pref),pI(0) { pI = new CImg<floatT>(2,2,2,3); }
  36806. ~_functor4d_streamline3d_oriented() { delete pI; }
  36807. float operator()(const float x, const float y, const float z, const unsigned int c) const {
  36808. #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) { \
  36809. 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); }
  36810. int
  36811. xi = (int)x - (x>=0?0:1), nxi = xi + 1,
  36812. yi = (int)y - (y>=0?0:1), nyi = yi + 1,
  36813. zi = (int)z - (z>=0?0:1), nzi = zi + 1;
  36814. const float
  36815. dx = x - xi,
  36816. dy = y - yi,
  36817. dz = z - zi;
  36818. if (c==0) {
  36819. CImg<floatT>& I = *pI;
  36820. if (xi<0) xi = 0;
  36821. if (nxi<0) nxi = 0;
  36822. if (xi>=ref.width()) xi = ref.width() - 1;
  36823. if (nxi>=ref.width()) nxi = ref.width() - 1;
  36824. if (yi<0) yi = 0;
  36825. if (nyi<0) nyi = 0;
  36826. if (yi>=ref.height()) yi = ref.height() - 1;
  36827. if (nyi>=ref.height()) nyi = ref.height() - 1;
  36828. if (zi<0) zi = 0;
  36829. if (nzi<0) nzi = 0;
  36830. if (zi>=ref.depth()) zi = ref.depth() - 1;
  36831. if (nzi>=ref.depth()) nzi = ref.depth() - 1;
  36832. I(0,0,0,0) = (float)ref(xi,yi,zi,0); I(0,0,0,1) = (float)ref(xi,yi,zi,1);
  36833. I(0,0,0,2) = (float)ref(xi,yi,zi,2); I(1,0,0,0) = (float)ref(nxi,yi,zi,0);
  36834. I(1,0,0,1) = (float)ref(nxi,yi,zi,1); I(1,0,0,2) = (float)ref(nxi,yi,zi,2);
  36835. I(1,1,0,0) = (float)ref(nxi,nyi,zi,0); I(1,1,0,1) = (float)ref(nxi,nyi,zi,1);
  36836. I(1,1,0,2) = (float)ref(nxi,nyi,zi,2); I(0,1,0,0) = (float)ref(xi,nyi,zi,0);
  36837. I(0,1,0,1) = (float)ref(xi,nyi,zi,1); I(0,1,0,2) = (float)ref(xi,nyi,zi,2);
  36838. I(0,0,1,0) = (float)ref(xi,yi,nzi,0); I(0,0,1,1) = (float)ref(xi,yi,nzi,1);
  36839. I(0,0,1,2) = (float)ref(xi,yi,nzi,2); I(1,0,1,0) = (float)ref(nxi,yi,nzi,0);
  36840. I(1,0,1,1) = (float)ref(nxi,yi,nzi,1); I(1,0,1,2) = (float)ref(nxi,yi,nzi,2);
  36841. I(1,1,1,0) = (float)ref(nxi,nyi,nzi,0); I(1,1,1,1) = (float)ref(nxi,nyi,nzi,1);
  36842. I(1,1,1,2) = (float)ref(nxi,nyi,nzi,2); I(0,1,1,0) = (float)ref(xi,nyi,nzi,0);
  36843. I(0,1,1,1) = (float)ref(xi,nyi,nzi,1); I(0,1,1,2) = (float)ref(xi,nyi,nzi,2);
  36844. _cimg_vecalign3d(1,0,0); _cimg_vecalign3d(1,1,0); _cimg_vecalign3d(0,1,0);
  36845. _cimg_vecalign3d(0,0,1); _cimg_vecalign3d(1,0,1); _cimg_vecalign3d(1,1,1); _cimg_vecalign3d(0,1,1);
  36846. }
  36847. return (float)pI->_linear_atXYZ(dx,dy,dz,c);
  36848. }
  36849. };
  36850. struct _functor4d_streamline_expr {
  36851. _cimg_math_parser *mp;
  36852. ~_functor4d_streamline_expr() { mp->end(); delete mp; }
  36853. _functor4d_streamline_expr(const char *const expr):mp(0) {
  36854. mp = new _cimg_math_parser(expr,"streamline",CImg<T>::const_empty(),0);
  36855. }
  36856. float operator()(const float x, const float y, const float z, const unsigned int c) const {
  36857. return (float)(*mp)(x,y,z,c);
  36858. }
  36859. };
  36860. //! Return a shared-memory image referencing a range of pixels of the image instance.
  36861. /**
  36862. \param x0 X-coordinate of the starting pixel.
  36863. \param x1 X-coordinate of the ending pixel.
  36864. \param y0 Y-coordinate.
  36865. \param z0 Z-coordinate.
  36866. \param c0 C-coordinate.
  36867. **/
  36868. CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
  36869. const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) {
  36870. const ulongT
  36871. beg = (ulongT)offset(x0,y0,z0,c0),
  36872. end = (ulongT)offset(x1,y0,z0,c0);
  36873. if (beg>end || beg>=size() || end>=size())
  36874. throw CImgArgumentException(_cimg_instance
  36875. "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
  36876. cimg_instance,
  36877. x0,x1,y0,z0,c0);
  36878. return CImg<T>(_data + beg,x1 - x0 + 1,1,1,1,true);
  36879. }
  36880. //! Return a shared-memory image referencing a range of pixels of the image instance \const.
  36881. const CImg<T> get_shared_points(const unsigned int x0, const unsigned int x1,
  36882. const unsigned int y0=0, const unsigned int z0=0, const unsigned int c0=0) const {
  36883. const ulongT
  36884. beg = (ulongT)offset(x0,y0,z0,c0),
  36885. end = (ulongT)offset(x1,y0,z0,c0);
  36886. if (beg>end || beg>=size() || end>=size())
  36887. throw CImgArgumentException(_cimg_instance
  36888. "get_shared_points(): Invalid request of a shared-memory subset (%u->%u,%u,%u,%u).",
  36889. cimg_instance,
  36890. x0,x1,y0,z0,c0);
  36891. return CImg<T>(_data + beg,x1 - x0 + 1,1,1,1,true);
  36892. }
  36893. //! Return a shared-memory image referencing a range of rows of the image instance.
  36894. /**
  36895. \param y0 Y-coordinate of the starting row.
  36896. \param y1 Y-coordinate of the ending row.
  36897. \param z0 Z-coordinate.
  36898. \param c0 C-coordinate.
  36899. **/
  36900. CImg<T> get_shared_rows(const unsigned int y0, const unsigned int y1,
  36901. const unsigned int z0=0, const unsigned int c0=0) {
  36902. const ulongT
  36903. beg = (ulongT)offset(0,y0,z0,c0),
  36904. end = (ulongT)offset(0,y1,z0,c0);
  36905. if (beg>end || beg>=size() || end>=size())
  36906. throw CImgArgumentException(_cimg_instance
  36907. "get_shared_rows(): Invalid request of a shared-memory subset "
  36908. "(0->%u,%u->%u,%u,%u).",
  36909. cimg_instance,
  36910. _width - 1,y0,y1,z0,c0);
  36911. return CImg<T>(_data + beg,_width,y1 - y0 + 1,1,1,true);
  36912. }
  36913. //! Return a shared-memory image referencing a range of rows of the image instance \const.
  36914. const CImg<T> get_shared_rows(const unsigned int y0, const unsigned int y1,
  36915. const unsigned int z0=0, const unsigned int c0=0) const {
  36916. const ulongT
  36917. beg = (ulongT)offset(0,y0,z0,c0),
  36918. end = (ulongT)offset(0,y1,z0,c0);
  36919. if (beg>end || beg>=size() || end>=size())
  36920. throw CImgArgumentException(_cimg_instance
  36921. "get_shared_rows(): Invalid request of a shared-memory subset "
  36922. "(0->%u,%u->%u,%u,%u).",
  36923. cimg_instance,
  36924. _width - 1,y0,y1,z0,c0);
  36925. return CImg<T>(_data + beg,_width,y1 - y0 + 1,1,1,true);
  36926. }
  36927. //! Return a shared-memory image referencing one row of the image instance.
  36928. /**
  36929. \param y0 Y-coordinate.
  36930. \param z0 Z-coordinate.
  36931. \param c0 C-coordinate.
  36932. **/
  36933. CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) {
  36934. return get_shared_rows(y0,y0,z0,c0);
  36935. }
  36936. //! Return a shared-memory image referencing one row of the image instance \const.
  36937. const CImg<T> get_shared_row(const unsigned int y0, const unsigned int z0=0, const unsigned int c0=0) const {
  36938. return get_shared_rows(y0,y0,z0,c0);
  36939. }
  36940. //! Return a shared memory image referencing a range of slices of the image instance.
  36941. /**
  36942. \param z0 Z-coordinate of the starting slice.
  36943. \param z1 Z-coordinate of the ending slice.
  36944. \param c0 C-coordinate.
  36945. **/
  36946. CImg<T> get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) {
  36947. const ulongT
  36948. beg = (ulongT)offset(0,0,z0,c0),
  36949. end = (ulongT)offset(0,0,z1,c0);
  36950. if (beg>end || beg>=size() || end>=size())
  36951. throw CImgArgumentException(_cimg_instance
  36952. "get_shared_slices(): Invalid request of a shared-memory subset "
  36953. "(0->%u,0->%u,%u->%u,%u).",
  36954. cimg_instance,
  36955. _width - 1,_height - 1,z0,z1,c0);
  36956. return CImg<T>(_data + beg,_width,_height,z1 - z0 + 1,1,true);
  36957. }
  36958. //! Return a shared memory image referencing a range of slices of the image instance \const.
  36959. const CImg<T> get_shared_slices(const unsigned int z0, const unsigned int z1, const unsigned int c0=0) const {
  36960. const ulongT
  36961. beg = (ulongT)offset(0,0,z0,c0),
  36962. end = (ulongT)offset(0,0,z1,c0);
  36963. if (beg>end || beg>=size() || end>=size())
  36964. throw CImgArgumentException(_cimg_instance
  36965. "get_shared_slices(): Invalid request of a shared-memory subset "
  36966. "(0->%u,0->%u,%u->%u,%u).",
  36967. cimg_instance,
  36968. _width - 1,_height - 1,z0,z1,c0);
  36969. return CImg<T>(_data + beg,_width,_height,z1 - z0 + 1,1,true);
  36970. }
  36971. //! Return a shared-memory image referencing one slice of the image instance.
  36972. /**
  36973. \param z0 Z-coordinate.
  36974. \param c0 C-coordinate.
  36975. **/
  36976. CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) {
  36977. return get_shared_slices(z0,z0,c0);
  36978. }
  36979. //! Return a shared-memory image referencing one slice of the image instance \const.
  36980. const CImg<T> get_shared_slice(const unsigned int z0, const unsigned int c0=0) const {
  36981. return get_shared_slices(z0,z0,c0);
  36982. }
  36983. //! Return a shared-memory image referencing a range of channels of the image instance.
  36984. /**
  36985. \param c0 C-coordinate of the starting channel.
  36986. \param c1 C-coordinate of the ending channel.
  36987. **/
  36988. CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) {
  36989. const ulongT
  36990. beg = (ulongT)offset(0,0,0,c0),
  36991. end = (ulongT)offset(0,0,0,c1);
  36992. if (beg>end || beg>=size() || end>=size())
  36993. throw CImgArgumentException(_cimg_instance
  36994. "get_shared_channels(): Invalid request of a shared-memory subset "
  36995. "(0->%u,0->%u,0->%u,%u->%u).",
  36996. cimg_instance,
  36997. _width - 1,_height - 1,_depth - 1,c0,c1);
  36998. return CImg<T>(_data + beg,_width,_height,_depth,c1 - c0 + 1,true);
  36999. }
  37000. //! Return a shared-memory image referencing a range of channels of the image instance \const.
  37001. const CImg<T> get_shared_channels(const unsigned int c0, const unsigned int c1) const {
  37002. const ulongT
  37003. beg = (ulongT)offset(0,0,0,c0),
  37004. end = (ulongT)offset(0,0,0,c1);
  37005. if (beg>end || beg>=size() || end>=size())
  37006. throw CImgArgumentException(_cimg_instance
  37007. "get_shared_channels(): Invalid request of a shared-memory subset "
  37008. "(0->%u,0->%u,0->%u,%u->%u).",
  37009. cimg_instance,
  37010. _width - 1,_height - 1,_depth - 1,c0,c1);
  37011. return CImg<T>(_data + beg,_width,_height,_depth,c1 - c0 + 1,true);
  37012. }
  37013. //! Return a shared-memory image referencing one channel of the image instance.
  37014. /**
  37015. \param c0 C-coordinate.
  37016. **/
  37017. CImg<T> get_shared_channel(const unsigned int c0) {
  37018. return get_shared_channels(c0,c0);
  37019. }
  37020. //! Return a shared-memory image referencing one channel of the image instance \const.
  37021. const CImg<T> get_shared_channel(const unsigned int c0) const {
  37022. return get_shared_channels(c0,c0);
  37023. }
  37024. //! Return a shared-memory version of the image instance.
  37025. CImg<T> get_shared() {
  37026. return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
  37027. }
  37028. //! Return a shared-memory version of the image instance \const.
  37029. const CImg<T> get_shared() const {
  37030. return CImg<T>(_data,_width,_height,_depth,_spectrum,true);
  37031. }
  37032. //! Split image into a list along specified axis.
  37033. /**
  37034. \param axis Splitting axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  37035. \param nb Number of split parts.
  37036. \note
  37037. - If \c nb==0, instance image is split into blocs of equal values along the specified axis.
  37038. - If \c nb<=0, instance image is split into blocs of -\c nb pixel wide.
  37039. - If \c nb>0, instance image is split into \c nb blocs.
  37040. **/
  37041. CImgList<T> get_split(const char axis, const int nb=-1) const {
  37042. CImgList<T> res;
  37043. if (is_empty()) return res;
  37044. const char _axis = cimg::lowercase(axis);
  37045. if (nb<0) { // Split by block size
  37046. const unsigned int dp = (unsigned int)(nb?-nb:1);
  37047. switch (_axis) {
  37048. case 'x': {
  37049. if (_width>dp) {
  37050. res.assign(_width/dp + (_width%dp?1:0),1,1);
  37051. const unsigned int pe = _width - dp;
  37052. cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 &&
  37053. _height*_depth*_spectrum>=128))
  37054. for (int p = 0; p<(int)pe; p+=dp)
  37055. get_crop(p,0,0,0,p + dp - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res[p/dp]);
  37056. get_crop((res._width - 1)*dp,0,0,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
  37057. } else res.assign(*this);
  37058. } break;
  37059. case 'y': {
  37060. if (_height>dp) {
  37061. res.assign(_height/dp + (_height%dp?1:0),1,1);
  37062. const unsigned int pe = _height - dp;
  37063. cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 &&
  37064. _width*_depth*_spectrum>=128))
  37065. for (int p = 0; p<(int)pe; p+=dp)
  37066. get_crop(0,p,0,0,_width - 1,p + dp - 1,_depth - 1,_spectrum - 1).move_to(res[p/dp]);
  37067. get_crop(0,(res._width - 1)*dp,0,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
  37068. } else res.assign(*this);
  37069. } break;
  37070. case 'z': {
  37071. if (_depth>dp) {
  37072. res.assign(_depth/dp + (_depth%dp?1:0),1,1);
  37073. const unsigned int pe = _depth - dp;
  37074. cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 &&
  37075. _width*_height*_spectrum>=128))
  37076. for (int p = 0; p<(int)pe; p+=dp)
  37077. get_crop(0,0,p,0,_width - 1,_height - 1,p + dp - 1,_spectrum - 1).move_to(res[p/dp]);
  37078. get_crop(0,0,(res._width - 1)*dp,0,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
  37079. } else res.assign(*this);
  37080. } break;
  37081. case 'c' : {
  37082. if (_spectrum>dp) {
  37083. res.assign(_spectrum/dp + (_spectrum%dp?1:0),1,1);
  37084. const unsigned int pe = _spectrum - dp;
  37085. cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*128 &&
  37086. _width*_height*_depth>=128))
  37087. for (int p = 0; p<(int)pe; p+=dp)
  37088. get_crop(0,0,0,p,_width - 1,_height - 1,_depth - 1,p + dp - 1).move_to(res[p/dp]);
  37089. get_crop(0,0,0,(res._width - 1)*dp,_width - 1,_height - 1,_depth - 1,_spectrum - 1).move_to(res.back());
  37090. } else res.assign(*this);
  37091. }
  37092. }
  37093. } else if (nb>0) { // Split by number of (non-homogeneous) blocs
  37094. const unsigned int siz = _axis=='x'?_width:_axis=='y'?_height:_axis=='z'?_depth:_axis=='c'?_spectrum:0;
  37095. if ((unsigned int)nb>siz)
  37096. throw CImgArgumentException(_cimg_instance
  37097. "get_split(): Instance cannot be split along %c-axis into %u blocs.",
  37098. cimg_instance,
  37099. axis,nb);
  37100. if (nb==1) res.assign(*this);
  37101. else {
  37102. int err = (int)siz;
  37103. unsigned int _p = 0;
  37104. switch (_axis) {
  37105. case 'x' : {
  37106. cimg_forX(*this,p) if ((err-=nb)<=0) {
  37107. get_crop(_p,0,0,0,p,_height - 1,_depth - 1,_spectrum - 1).move_to(res);
  37108. err+=(int)siz;
  37109. _p = p + 1U;
  37110. }
  37111. } break;
  37112. case 'y' : {
  37113. cimg_forY(*this,p) if ((err-=nb)<=0) {
  37114. get_crop(0,_p,0,0,_width - 1,p,_depth - 1,_spectrum - 1).move_to(res);
  37115. err+=(int)siz;
  37116. _p = p + 1U;
  37117. }
  37118. } break;
  37119. case 'z' : {
  37120. cimg_forZ(*this,p) if ((err-=nb)<=0) {
  37121. get_crop(0,0,_p,0,_width - 1,_height - 1,p,_spectrum - 1).move_to(res);
  37122. err+=(int)siz;
  37123. _p = p + 1U;
  37124. }
  37125. } break;
  37126. case 'c' : {
  37127. cimg_forC(*this,p) if ((err-=nb)<=0) {
  37128. get_crop(0,0,0,_p,_width - 1,_height - 1,_depth - 1,p).move_to(res);
  37129. err+=(int)siz;
  37130. _p = p + 1U;
  37131. }
  37132. }
  37133. }
  37134. }
  37135. } else { // Split by equal values according to specified axis
  37136. T current = *_data;
  37137. switch (_axis) {
  37138. case 'x' : {
  37139. int i0 = 0;
  37140. cimg_forX(*this,i)
  37141. if ((*this)(i)!=current) { get_columns(i0,i - 1).move_to(res); i0 = i; current = (*this)(i); }
  37142. get_columns(i0,width() - 1).move_to(res);
  37143. } break;
  37144. case 'y' : {
  37145. int i0 = 0;
  37146. cimg_forY(*this,i)
  37147. if ((*this)(0,i)!=current) { get_rows(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,i); }
  37148. get_rows(i0,height() - 1).move_to(res);
  37149. } break;
  37150. case 'z' : {
  37151. int i0 = 0;
  37152. cimg_forZ(*this,i)
  37153. if ((*this)(0,0,i)!=current) { get_slices(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,i); }
  37154. get_slices(i0,depth() - 1).move_to(res);
  37155. } break;
  37156. case 'c' : {
  37157. int i0 = 0;
  37158. cimg_forC(*this,i)
  37159. if ((*this)(0,0,0,i)!=current) { get_channels(i0,i - 1).move_to(res); i0 = i; current = (*this)(0,0,0,i); }
  37160. get_channels(i0,spectrum() - 1).move_to(res);
  37161. } break;
  37162. default : {
  37163. longT i0 = 0;
  37164. cimg_foroff(*this,i)
  37165. if ((*this)[i]!=current) {
  37166. CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res);
  37167. i0 = (longT)i; current = (*this)[i];
  37168. }
  37169. CImg<T>(_data + i0,1,(unsigned int)(size() - i0)).move_to(res);
  37170. }
  37171. }
  37172. }
  37173. return res;
  37174. }
  37175. //! Split image into a list of sub-images, according to a specified splitting value sequence and optionally axis.
  37176. /**
  37177. \param values Splitting value sequence.
  37178. \param axis Axis along which the splitting is performed. Can be '0' to ignore axis.
  37179. \param keep_values Tells if the splitting sequence must be kept in the split blocs.
  37180. **/
  37181. template<typename t>
  37182. CImgList<T> get_split(const CImg<t>& values, const char axis=0, const bool keep_values=true) const {
  37183. typedef _cimg_Tt Tt;
  37184. CImgList<T> res;
  37185. if (is_empty()) return res;
  37186. const ulongT vsiz = values.size();
  37187. const char _axis = cimg::lowercase(axis);
  37188. if (!vsiz) return CImgList<T>(*this);
  37189. if (vsiz==1) { // Split according to a single value
  37190. const T value = (T)*values;
  37191. switch (_axis) {
  37192. case 'x' : {
  37193. unsigned int i0 = 0, i = 0;
  37194. do {
  37195. while (i<_width && (*this)(i)==value) ++i;
  37196. if (i>i0) { if (keep_values) get_columns(i0,i - 1).move_to(res); i0 = i; }
  37197. while (i<_width && (*this)(i)!=value) ++i;
  37198. if (i>i0) { get_columns(i0,i - 1).move_to(res); i0 = i; }
  37199. } while (i<_width);
  37200. } break;
  37201. case 'y' : {
  37202. unsigned int i0 = 0, i = 0;
  37203. do {
  37204. while (i<_height && (*this)(0,i)==value) ++i;
  37205. if (i>i0) { if (keep_values) get_rows(i0,i - 1).move_to(res); i0 = i; }
  37206. while (i<_height && (*this)(0,i)!=value) ++i;
  37207. if (i>i0) { get_rows(i0,i - 1).move_to(res); i0 = i; }
  37208. } while (i<_height);
  37209. } break;
  37210. case 'z' : {
  37211. unsigned int i0 = 0, i = 0;
  37212. do {
  37213. while (i<_depth && (*this)(0,0,i)==value) ++i;
  37214. if (i>i0) { if (keep_values) get_slices(i0,i - 1).move_to(res); i0 = i; }
  37215. while (i<_depth && (*this)(0,0,i)!=value) ++i;
  37216. if (i>i0) { get_slices(i0,i - 1).move_to(res); i0 = i; }
  37217. } while (i<_depth);
  37218. } break;
  37219. case 'c' : {
  37220. unsigned int i0 = 0, i = 0;
  37221. do {
  37222. while (i<_spectrum && (*this)(0,0,0,i)==value) ++i;
  37223. if (i>i0) { if (keep_values) get_channels(i0,i - 1).move_to(res); i0 = i; }
  37224. while (i<_spectrum && (*this)(0,0,0,i)!=value) ++i;
  37225. if (i>i0) { get_channels(i0,i - 1).move_to(res); i0 = i; }
  37226. } while (i<_spectrum);
  37227. } break;
  37228. default : {
  37229. const ulongT siz = size();
  37230. ulongT i0 = 0, i = 0;
  37231. do {
  37232. while (i<siz && (*this)[i]==value) ++i;
  37233. if (i>i0) {
  37234. if (keep_values) CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res);
  37235. i0 = i;
  37236. }
  37237. while (i<siz && (*this)[i]!=value) ++i;
  37238. if (i>i0) { CImg<T>(_data + i0,1,(unsigned int)(i - i0)).move_to(res); i0 = i; }
  37239. } while (i<siz);
  37240. }
  37241. }
  37242. } else { // Split according to multiple values
  37243. ulongT j = 0;
  37244. switch (_axis) {
  37245. case 'x' : {
  37246. unsigned int i0 = 0, i1 = 0, i = 0;
  37247. do {
  37248. if ((Tt)(*this)(i)==(Tt)*values) {
  37249. i1 = i; j = 0;
  37250. while (i<_width && (Tt)(*this)(i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
  37251. i-=(unsigned int)j;
  37252. if (i>i1) {
  37253. if (i1>i0) get_columns(i0,i1 - 1).move_to(res);
  37254. if (keep_values) get_columns(i1,i - 1).move_to(res);
  37255. i0 = i;
  37256. } else ++i;
  37257. } else ++i;
  37258. } while (i<_width);
  37259. if (i0<_width) get_columns(i0,width() - 1).move_to(res);
  37260. } break;
  37261. case 'y' : {
  37262. unsigned int i0 = 0, i1 = 0, i = 0;
  37263. do {
  37264. if ((Tt)(*this)(0,i)==(Tt)*values) {
  37265. i1 = i; j = 0;
  37266. while (i<_height && (Tt)(*this)(0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
  37267. i-=(unsigned int)j;
  37268. if (i>i1) {
  37269. if (i1>i0) get_rows(i0,i1 - 1).move_to(res);
  37270. if (keep_values) get_rows(i1,i - 1).move_to(res);
  37271. i0 = i;
  37272. } else ++i;
  37273. } else ++i;
  37274. } while (i<_height);
  37275. if (i0<_height) get_rows(i0,height() - 1).move_to(res);
  37276. } break;
  37277. case 'z' : {
  37278. unsigned int i0 = 0, i1 = 0, i = 0;
  37279. do {
  37280. if ((Tt)(*this)(0,0,i)==(Tt)*values) {
  37281. i1 = i; j = 0;
  37282. while (i<_depth && (Tt)(*this)(0,0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
  37283. i-=(unsigned int)j;
  37284. if (i>i1) {
  37285. if (i1>i0) get_slices(i0,i1 - 1).move_to(res);
  37286. if (keep_values) get_slices(i1,i - 1).move_to(res);
  37287. i0 = i;
  37288. } else ++i;
  37289. } else ++i;
  37290. } while (i<_depth);
  37291. if (i0<_depth) get_slices(i0,depth() - 1).move_to(res);
  37292. } break;
  37293. case 'c' : {
  37294. unsigned int i0 = 0, i1 = 0, i = 0;
  37295. do {
  37296. if ((Tt)(*this)(0,0,0,i)==(Tt)*values) {
  37297. i1 = i; j = 0;
  37298. while (i<_spectrum && (Tt)(*this)(0,0,0,i)==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
  37299. i-=(unsigned int)j;
  37300. if (i>i1) {
  37301. if (i1>i0) get_channels(i0,i1 - 1).move_to(res);
  37302. if (keep_values) get_channels(i1,i - 1).move_to(res);
  37303. i0 = i;
  37304. } else ++i;
  37305. } else ++i;
  37306. } while (i<_spectrum);
  37307. if (i0<_spectrum) get_channels(i0,spectrum() - 1).move_to(res);
  37308. } break;
  37309. default : {
  37310. const ulongT siz = size();
  37311. ulongT i0 = 0, i1 = 0, i = 0;
  37312. do {
  37313. if ((Tt)(*this)[i]==(Tt)*values) {
  37314. i1 = i; j = 0;
  37315. while (i<siz && (Tt)(*this)[i]==(Tt)values[j]) { ++i; if (++j>=vsiz) j = 0; }
  37316. i-=(unsigned int)j;
  37317. if (i>i1) {
  37318. if (i1>i0) CImg<T>(_data + i0,1,(unsigned int)(i1 - i0)).move_to(res);
  37319. if (keep_values) CImg<T>(_data + i1,1,(unsigned int)(i - i1)).move_to(res);
  37320. i0 = i;
  37321. } else ++i;
  37322. } else ++i;
  37323. } while (i<siz);
  37324. if (i0<siz) CImg<T>(_data + i0,1,(unsigned int)(siz - i0)).move_to(res);
  37325. } break;
  37326. }
  37327. }
  37328. return res;
  37329. }
  37330. //! Append two images along specified axis.
  37331. /**
  37332. \param img Image to append with instance image.
  37333. \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  37334. \param align Append alignment in \c [0,1].
  37335. **/
  37336. template<typename t>
  37337. CImg<T>& append(const CImg<t>& img, const char axis='x', const float align=0) {
  37338. if (is_empty()) return assign(img,false);
  37339. if (!img) return *this;
  37340. return CImgList<T>(*this,true).insert(img).get_append(axis,align).move_to(*this);
  37341. }
  37342. //! Append two images along specified axis \specialization.
  37343. CImg<T>& append(const CImg<T>& img, const char axis='x', const float align=0) {
  37344. if (is_empty()) return assign(img,false);
  37345. if (!img) return *this;
  37346. return CImgList<T>(*this,img,true).get_append(axis,align).move_to(*this);
  37347. }
  37348. //! Append two images along specified axis \const.
  37349. template<typename t>
  37350. CImg<_cimg_Tt> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
  37351. if (is_empty()) return +img;
  37352. if (!img) return +*this;
  37353. return CImgList<_cimg_Tt>(*this,true).insert(img).get_append(axis,align);
  37354. }
  37355. //! Append two images along specified axis \specialization.
  37356. CImg<T> get_append(const CImg<T>& img, const char axis='x', const float align=0) const {
  37357. if (is_empty()) return +img;
  37358. if (!img) return +*this;
  37359. return CImgList<T>(*this,img,true).get_append(axis,align);
  37360. }
  37361. //@}
  37362. //---------------------------------------
  37363. //
  37364. //! \name Filtering / Transforms
  37365. //@{
  37366. //---------------------------------------
  37367. //! Correlate image by a kernel.
  37368. /**
  37369. \param kernel = the correlation kernel.
  37370. \param boundary_conditions Boundary condition. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
  37371. \param is_normalized = enable local normalization.
  37372. \param channel_mode Channel processing mode.
  37373. Can be { 0=all | 1=one for one (default) | 2=partial sum | 3=full sum }.
  37374. \param xcenter X-coordinate of the kernel center (~0U>>1 means 'centered').
  37375. \param ycenter Y-coordinate of the kernel center (~0U>>1 means 'centered').
  37376. \param zcenter Z-coordinate of the kernel center (~0U>>1 means 'centered').
  37377. \param xstart Starting X-coordinate of the instance image.
  37378. \param ystart Starting Y-coordinate of the instance image.
  37379. \param zstart Starting Z-coordinate of the instance image.
  37380. \param xend Ending X-coordinate of the instance image.
  37381. \param yend Ending Y-coordinate of the instance image.
  37382. \param zend Ending Z-coordinate of the instance image.
  37383. \param xstride Stride along the X-axis.
  37384. \param ystride Stride along the Y-axis.
  37385. \param zstride Stride along the Z-axis.
  37386. \param xdilation Dilation along the X-axis.
  37387. \param ydilation Dilation along the Y-axis.
  37388. \param zdilation Dilation along the Z-axis.
  37389. \param interpolation_type Can be { false=nearest | true=linear }.
  37390. \note
  37391. - The correlation of the image instance \p *this by the kernel \p kernel is defined to be:
  37392. res(x,y,z) = sum_{i,j,k} (*this)(\alpha_x\;x + \beta_x\;(i - c_x),\alpha_y\;y + \beta_y\;(j -
  37393. c_y),\alpha_z\;z + \beta_z\;(k - c_z))*kernel(i,j,k).
  37394. **/
  37395. template<typename t>
  37396. CImg<T>& correlate(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
  37397. const bool is_normalized=false, const unsigned int channel_mode=1,
  37398. const int xcenter=(int)(~0U>>1),
  37399. const int ycenter=(int)(~0U>>1),
  37400. const int zcenter=(int)(~0U>>1),
  37401. const int xstart=0,
  37402. const int ystart=0,
  37403. const int zstart=0,
  37404. const int xend=(int)(~0U>>1),
  37405. const int yend=(int)(~0U>>1),
  37406. const int zend=(int)(~0U>>1),
  37407. const float xstride=1, const float ystride=1, const float zstride=1,
  37408. const float xdilation=1, const float ydilation=1, const float zdilation=1,
  37409. const bool interpolation_type=false) {
  37410. if (is_empty() || !kernel) return *this;
  37411. return get_correlate(kernel,boundary_conditions,is_normalized,channel_mode,
  37412. xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend,
  37413. xstride,ystride,zstride,xdilation,ydilation,zdilation,
  37414. interpolation_type).move_to(*this);
  37415. }
  37416. template<typename t>
  37417. CImg<_cimg_Ttfloat> get_correlate(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
  37418. const bool is_normalized=false, const unsigned int channel_mode=1,
  37419. const int xcenter=(int)(~0U>>1),
  37420. const int ycenter=(int)(~0U>>1),
  37421. const int zcenter=(int)(~0U>>1),
  37422. const int xstart=0,
  37423. const int ystart=0,
  37424. const int zstart=0,
  37425. const int xend=(int)(~0U>>1),
  37426. const int yend=(int)(~0U>>1),
  37427. const int zend=(int)(~0U>>1),
  37428. const float xstride=1, const float ystride=1, const float zstride=1,
  37429. const float xdilation=1, const float ydilation=1, const float zdilation=1,
  37430. const bool interpolation_type=false) const {
  37431. return _correlate(kernel,boundary_conditions,is_normalized,channel_mode,
  37432. xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend,
  37433. xstride,ystride,zstride,xdilation,ydilation,zdilation,
  37434. interpolation_type,false);
  37435. }
  37436. //! Correlate image by a kernel \newinstance.
  37437. template<typename t>
  37438. CImg<_cimg_Ttfloat> _correlate(const CImg<t>& kernel, const unsigned int boundary_conditions,
  37439. const bool is_normalized, const unsigned int channel_mode,
  37440. const int xcenter, const int ycenter, const int zcenter,
  37441. const int xstart, const int ystart, const int zstart,
  37442. const int xend, const int yend, const int zend,
  37443. const float xstride, const float ystride, const float zstride,
  37444. const float xdilation, const float ydilation, const float zdilation,
  37445. const bool interpolation_type, const bool is_convolve) const {
  37446. typedef _cimg_Ttfloat Ttfloat;
  37447. CImg<Ttfloat> res;
  37448. _cimg_abort_init_openmp;
  37449. cimg_abort_init;
  37450. if (xstart>xend || ystart>yend || zstart>zend)
  37451. throw CImgArgumentException(_cimg_instance
  37452. "%s(): Invalid xyz-start/end arguments (start = (%d,%d,%d), end = (%d,%d,%d)).",
  37453. cimg_instance,
  37454. is_convolve?"convolve":"correlate",
  37455. xstart,ystart,zstart,xend,yend,zend);
  37456. if (xstride<=0 || ystride<=0 || zstride<=0)
  37457. throw CImgArgumentException(_cimg_instance
  37458. "%s(): Invalid stride arguments (%g,%g,%g).",
  37459. cimg_instance,
  37460. is_convolve?"convolve":"correlate",
  37461. xstride,ystride,zstride);
  37462. if (is_empty() || !kernel) return *this;
  37463. int
  37464. _xcenter = xcenter==(int)(~0U>>1)?kernel.width()/2 - 1 + (kernel.width()%2):xcenter,
  37465. _ycenter = ycenter==(int)(~0U>>1)?kernel.height()/2 - 1 + (kernel.height()%2):ycenter,
  37466. _zcenter = zcenter==(int)(~0U>>1)?kernel.depth()/2 - 1 + (kernel.depth()%2):zcenter;
  37467. float _xdilation = xdilation, _ydilation = ydilation, _zdilation = zdilation;
  37468. CImg<t> _kernel;
  37469. if (is_convolve) { // If convolution, go back to correlation
  37470. if (kernel.size()/kernel.spectrum()<=27) {
  37471. _kernel = CImg<t>(kernel._data,kernel.size()/kernel._spectrum,1,1,kernel._spectrum,true).
  37472. get_mirror('x').resize(kernel,-1);
  37473. _xcenter = kernel.width() - 1 - _xcenter;
  37474. _ycenter = kernel.height() - 1 - _ycenter;
  37475. _zcenter = kernel.depth() - _zcenter - 1;
  37476. } else { _kernel = kernel.get_shared(); _xdilation*=-1; _ydilation*=-1; _zdilation*=-1; }
  37477. } else _kernel = kernel.get_shared();
  37478. const int
  37479. _xend = xend==(int)(~0U>>1)?width() - 1:xend,
  37480. _yend = yend==(int)(~0U>>1)?height() - 1:yend,
  37481. _zend = zend==(int)(~0U>>1)?depth() - 1:zend,
  37482. i_xstride = (int)cimg::round(xstride),
  37483. i_ystride = (int)cimg::round(ystride),
  37484. i_zstride = (int)cimg::round(zstride),
  37485. i_xdilation = (int)cimg::round(_xdilation),
  37486. i_ydilation = (int)cimg::round(_ydilation),
  37487. i_zdilation = (int)cimg::round(_zdilation),
  37488. res_width = _xend - xstart + 1,
  37489. res_height = _yend - ystart + 1,
  37490. res_depth = _zend - zstart + 1,
  37491. smin = std::min(spectrum(),_kernel.spectrum()),
  37492. smax = std::max(spectrum(),_kernel.spectrum()),
  37493. cend = !channel_mode?spectrum()*_kernel.spectrum():smax;
  37494. const ulongT
  37495. res_wh = (ulongT)res_width*res_height,
  37496. res_whd = res_wh*res_depth;
  37497. if (!res_whd) return CImg<Ttfloat>();
  37498. res.assign(res_width,res_height,res_depth,
  37499. !channel_mode?_spectrum*_kernel._spectrum:
  37500. channel_mode==1?smax:
  37501. channel_mode==2?(int)std::ceil((float)smax/smin):1);
  37502. if (channel_mode>=2) res.fill(0);
  37503. const ulongT res_siz = res_whd*res._spectrum;
  37504. const bool
  37505. #if cimg_use_openmp==1
  37506. is_master_thread = !omp_get_thread_num(),
  37507. #else
  37508. is_master_thread = true,
  37509. #endif
  37510. is_outer_parallel = is_master_thread &&
  37511. (res._spectrum>=cimg::nb_cpus() || (res_siz<=(cimg_openmp_sizefactor)*32768 && res._spectrum>1)),
  37512. is_inner_parallel = is_master_thread &&
  37513. (!is_outer_parallel && res_whd>=(cimg_openmp_sizefactor)*32768),
  37514. is_int_stride_dilation = xstride==i_xstride && ystride==i_ystride && zstride==i_zstride &&
  37515. _xdilation==i_xdilation && _ydilation==i_ydilation && _zdilation==i_zdilation;
  37516. cimg::unused(is_inner_parallel,is_outer_parallel);
  37517. const int
  37518. w = width(), h = height(), d = depth(),
  37519. w1 = w - 1, h1 = h - 1, d1 = d - 1,
  37520. w2 = 2*w, h2 = 2*h, d2 = 2*d;
  37521. const ulongT wh = (ulongT)w*h, whd = wh*d;
  37522. // Reshape kernel to enable optimizations for a few cases.
  37523. if (boundary_conditions==1 &&
  37524. _kernel._width>1 && _kernel._height>1 &&
  37525. ((_kernel._depth==1 && _kernel._width<=5 && _kernel._height<=5) ||
  37526. (_kernel._depth<=3 && _kernel._width<=3 && _kernel._height<=3)) &&
  37527. xstart>=0 && ystart>=0 && zstart>=0 &&
  37528. _xend<width() && _yend<height() && _zend<depth() &&
  37529. is_int_stride_dilation &&
  37530. xstride==1 && ystride==1 && zstride==1 &&
  37531. i_xdilation>=0 && i_ydilation>=0 && i_zdilation>=0) {
  37532. const unsigned int M = cimg::max(_kernel._width,_kernel._height,_kernel._depth);
  37533. _kernel.assign(_kernel.get_resize(M + 1 - (M%2),M + 1 - (M%2),_kernel._depth>1?M + 1 - (M%2):1,-100,
  37534. 0,0,
  37535. 1,1,1),false);
  37536. _xcenter = _ycenter = (int)M/2;
  37537. if (_kernel._depth>1) _ycenter = (int)M/2;
  37538. }
  37539. // Optimized version for a few particular cases (3x3, 5x5 and 3x3x3 kernels, with a few other conditions).
  37540. if (boundary_conditions==1 &&
  37541. _kernel._width==_kernel._height &&
  37542. ((_kernel._depth==1 && (_kernel._width==3 || _kernel._width==5)) ||
  37543. (_kernel._depth==_kernel._width && _kernel._width==3)) &&
  37544. _xcenter==_kernel.width()/2 && _ycenter==_kernel.height()/2 && _zcenter==_kernel.depth()/2 &&
  37545. xstart>=0 && ystart>=0 && zstart>=0 &&
  37546. _xend<width() && _yend<height() && _zend<depth() &&
  37547. is_int_stride_dilation &&
  37548. xstride==1 && ystride==1 && zstride==1 &&
  37549. i_xdilation>=0 && i_ydilation>=0 && i_zdilation>=0) {
  37550. switch (_kernel._depth) {
  37551. case 3 : { // 3x3x3 centered kernel
  37552. cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
  37553. for (int c = 0; c<cend; ++c) _cimg_abort_try_openmp2 {
  37554. cimg_abort_test2;
  37555. const CImg<T> I = get_shared_channel(c%_spectrum);
  37556. const CImg<t> K = _kernel.get_shared_channel(!channel_mode?c/_spectrum:c%_kernel._spectrum);
  37557. CImg<Ttfloat> _resu = channel_mode<=1?res.get_shared_channel(c):
  37558. CImg<Ttfloat>(res.width(),res.height(),res.depth(),1);
  37559. if (is_normalized) {
  37560. const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M;
  37561. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
  37562. cimg_forXYZ(res,X,Y,Z) {
  37563. const int
  37564. x = xstart + X, y = ystart + Y, z = zstart + Z,
  37565. px = x - i_xdilation>0?x - i_xdilation:0, nx = x + i_xdilation<w1?x + i_xdilation:w1,
  37566. py = y - i_ydilation>0?y - i_ydilation:0, ny = y + i_ydilation<h1?y + i_ydilation:h1,
  37567. pz = z - i_zdilation>0?z - i_zdilation:0, nz = z + i_zdilation<d1?z + i_zdilation:d1;
  37568. const Ttfloat N = M2*(cimg::sqr(I(px,py,pz)) + cimg::sqr(I(x,py,pz)) + cimg::sqr(I(nx,py,pz)) +
  37569. cimg::sqr(I(px,y,pz)) + cimg::sqr(I(x,y,pz)) + cimg::sqr(I(nx,y,pz)) +
  37570. cimg::sqr(I(px,ny,pz)) + cimg::sqr(I(x,ny,pz)) + cimg::sqr(I(nx,ny,pz)) +
  37571. cimg::sqr(I(px,py,z)) + cimg::sqr(I(x,py,z)) + cimg::sqr(I(nx,py,z)) +
  37572. cimg::sqr(I(px,y,z)) + cimg::sqr(I(x,y,z)) + cimg::sqr(I(nx,y,z)) +
  37573. cimg::sqr(I(px,ny,z)) + cimg::sqr(I(x,ny,z)) + cimg::sqr(I(nx,ny,z)) +
  37574. cimg::sqr(I(px,py,nz)) + cimg::sqr(I(x,py,nz)) + cimg::sqr(I(nx,py,nz)) +
  37575. cimg::sqr(I(px,y,nz)) + cimg::sqr(I(x,y,nz)) + cimg::sqr(I(nx,y,nz)) +
  37576. cimg::sqr(I(px,ny,nz)) + cimg::sqr(I(x,ny,nz)) + cimg::sqr(I(nx,ny,nz)));
  37577. _resu(X,Y,Z) = (Ttfloat)(N?(K[0]*I(px,py,pz) + K[1]*I(x,py,pz) + K[2]*I(nx,py,pz) +
  37578. K[3]*I(px,y,pz) + K[4]*I(x,y,pz) + K[5]*I(nx,y,pz) +
  37579. K[6]*I(px,ny,pz) + K[7]*I(x,ny,pz) + K[8]*I(nx,ny,pz) +
  37580. K[9]*I(px,py,z) + K[10]*I(x,py,z) + K[11]*I(nx,py,z) +
  37581. K[12]*I(px,y,z) + K[13]*I(x,y,z) + K[14]*I(nx,y,z) +
  37582. K[15]*I(px,ny,z) + K[16]*I(x,ny,z) + K[17]*I(nx,ny,z) +
  37583. K[18]*I(px,py,nz) + K[19]*I(x,py,nz) + K[20]*I(nx,py,nz) +
  37584. K[21]*I(px,y,nz) + K[22]*I(x,y,nz) + K[23]*I(nx,y,nz) +
  37585. K[24]*I(px,ny,nz) + K[25]*I(x,ny,nz) + K[26]*I(nx,ny,nz))/std::sqrt(N):0);
  37586. }
  37587. } else {
  37588. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
  37589. cimg_forXYZ(res,X,Y,Z) {
  37590. const int
  37591. x = xstart + X, y = ystart + Y, z = zstart + Z,
  37592. px = x - i_xdilation>0?x - i_xdilation:0, nx = x + i_xdilation<w1?x + i_xdilation:w1,
  37593. py = y - i_ydilation>0?y - i_ydilation:0, ny = y + i_ydilation<h1?y + i_ydilation:h1,
  37594. pz = z - i_zdilation>0?z - i_zdilation:0, nz = z + i_zdilation<d1?z + i_zdilation:d1;
  37595. _resu(X,Y,Z) = (Ttfloat)(K[0]*I(px,py,pz) + K[1]*I(x,py,pz) + K[2]*I(nx,py,pz) +
  37596. K[3]*I(px,y,pz) + K[4]*I(x,y,pz) + K[5]*I(nx,y,pz) +
  37597. K[6]*I(px,ny,pz) + K[7]*I(x,ny,pz) + K[8]*I(nx,ny,pz) +
  37598. K[9]*I(px,py,z) + K[10]*I(x,py,z) + K[11]*I(nx,py,z) +
  37599. K[12]*I(px,y,z) + K[13]*I(x,y,z) + K[14]*I(nx,y,z) +
  37600. K[15]*I(px,ny,z) + K[16]*I(x,ny,z) + K[17]*I(nx,ny,z) +
  37601. K[18]*I(px,py,nz) + K[19]*I(x,py,nz) + K[20]*I(nx,py,nz) +
  37602. K[21]*I(px,y,nz) + K[22]*I(x,y,nz) + K[23]*I(nx,y,nz) +
  37603. K[24]*I(px,ny,nz) + K[25]*I(x,ny,nz) + K[26]*I(nx,ny,nz));
  37604. }
  37605. }
  37606. if (channel_mode==2)
  37607. cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(c/smin)+=_resu;
  37608. else if (channel_mode==3)
  37609. cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(0)+=_resu;
  37610. } _cimg_abort_catch_openmp2
  37611. } break;
  37612. default :
  37613. case 1 :
  37614. switch (_kernel._width) {
  37615. case 5 : { // 5x5 centered kernel
  37616. cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
  37617. for (int c = 0; c<cend; ++c) _cimg_abort_try_openmp2 {
  37618. cimg_abort_test2;
  37619. const CImg<T> I = get_shared_channel(c%_spectrum);
  37620. const CImg<t> K = _kernel.get_shared_channel(!channel_mode?c/_spectrum:c%_kernel._spectrum);
  37621. CImg<Ttfloat> _resu = channel_mode<=1?res.get_shared_channel(c):
  37622. CImg<Ttfloat>(res.width(),res.height(),res.depth(),1);
  37623. if (is_normalized) {
  37624. const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M;
  37625. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
  37626. cimg_forXYZ(res,X,Y,z) {
  37627. const int
  37628. x = xstart + X, y = ystart + Y,
  37629. px = x - i_xdilation>0?x - i_xdilation:0, bx = px - i_xdilation>0?px - i_xdilation:0,
  37630. nx = x + i_xdilation<w1?x + i_xdilation:w1, ax = nx + i_xdilation<w1?nx + i_xdilation:w1,
  37631. py = y - i_ydilation>0?y - i_ydilation:0, by = py - i_ydilation>0?py - i_ydilation:0,
  37632. ny = y + i_ydilation<h1?y + i_ydilation:h1, ay = ny + i_ydilation<h1?ny + i_ydilation:h1;
  37633. const Ttfloat N = M2*(cimg::sqr(I(bx,by,z)) + cimg::sqr(I(px,by,z)) + cimg::sqr(I(x,by,z)) +
  37634. cimg::sqr(I(nx,by,z)) + cimg::sqr(I(ax,by,z)) +
  37635. cimg::sqr(I(bx,py,z)) + cimg::sqr(I(px,py,z)) + cimg::sqr(I(x,py,z)) +
  37636. cimg::sqr(I(nx,py,z)) + cimg::sqr(I(ax,py,z)) +
  37637. cimg::sqr(I(bx,y,z)) + cimg::sqr(I(px,y,z)) + cimg::sqr(I(x,y,z)) +
  37638. cimg::sqr(I(nx,y,z)) + cimg::sqr(I(ax,y,z)) +
  37639. cimg::sqr(I(bx,ny,z)) + cimg::sqr(I(px,ny,z)) + cimg::sqr(I(x,ny,z)) +
  37640. cimg::sqr(I(nx,ny,z)) + cimg::sqr(I(ax,ny,z)) +
  37641. cimg::sqr(I(bx,ay,z)) + cimg::sqr(I(px,ay,z)) + cimg::sqr(I(x,ay,z)) +
  37642. cimg::sqr(I(nx,ay,z)) + cimg::sqr(I(ax,ay,z)));
  37643. _resu(X,Y,z) = (Ttfloat)(N?(K[0]*I(bx,by,z) + K[1]*I(px,by,z) + K[2]*I(x,by,z) +
  37644. K[3]*I(nx,by,z) + K[4]*I(ax,by,z) +
  37645. K[5]*I(bx,py,z) + K[6]*I(px,py,z) + K[7]*I(x,py,z) +
  37646. K[8]*I(nx,py,z) + K[9]*I(ax,py,z) +
  37647. K[10]*I(bx,y,z) + K[11]*I(px,y,z) + K[12]*I(x,y,z) +
  37648. K[13]*I(nx,y,z) + K[14]*I(ax,y,z) +
  37649. K[15]*I(bx,ny,z) + K[16]*I(px,ny,z) + K[17]*I(x,ny,z) +
  37650. K[18]*I(nx,ny,z) + K[19]*I(ax,ny,z) +
  37651. K[20]*I(bx,ay,z) + K[21]*I(px,ay,z) + K[22]*I(x,ay,z) +
  37652. K[23]*I(nx,ay,z) + K[24]*I(ax,ay,z))/std::sqrt(N):0);
  37653. }
  37654. } else {
  37655. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
  37656. cimg_forXYZ(res,X,Y,z) {
  37657. const int
  37658. x = xstart + X, y = ystart + Y,
  37659. px = x - i_xdilation>0?x - i_xdilation:0, bx = px - i_xdilation>0?px - i_xdilation:0,
  37660. nx = x + i_xdilation<w1?x + i_xdilation:w1, ax = nx + i_xdilation<w1?nx + i_xdilation:w1,
  37661. py = y - i_ydilation>0?y - i_ydilation:0, by = py - i_ydilation>0?py - i_ydilation:0,
  37662. ny = y + i_ydilation<h1?y + i_ydilation:h1, ay = ny + i_ydilation<h1?ny + i_ydilation:h1;
  37663. _resu(X,Y,z) = (Ttfloat)(K[0]*I(bx,by,z) + K[1]*I(px,by,z) + K[2]*I(x,by,z) +
  37664. K[3]*I(nx,by,z) + K[4]*I(ax,by,z) +
  37665. K[5]*I(bx,py,z) + K[6]*I(px,py,z) + K[7]*I(x,py,z) +
  37666. K[8]*I(nx,py,z) + K[9]*I(ax,py,z) +
  37667. K[10]*I(bx,y,z) + K[11]*I(px,y,z) + K[12]*I(x,y,z) +
  37668. K[13]*I(nx,y,z) + K[14]*I(ax,y,z) +
  37669. K[15]*I(bx,ny,z) + K[16]*I(px,ny,z) + K[17]*I(x,ny,z) +
  37670. K[18]*I(nx,ny,z) + K[19]*I(ax,ny,z) +
  37671. K[20]*I(bx,ay,z) + K[21]*I(px,ay,z) + K[22]*I(x,ay,z) +
  37672. K[23]*I(nx,ay,z) + K[24]*I(ax,ay,z));
  37673. }
  37674. }
  37675. if (channel_mode==2)
  37676. cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(c/smin)+=_resu;
  37677. else if (channel_mode==3)
  37678. cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(0)+=_resu;
  37679. } _cimg_abort_catch_openmp2
  37680. } break;
  37681. case 3 : { // 3x3 centered kernel
  37682. cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
  37683. for (int c = 0; c<cend; ++c) _cimg_abort_try_openmp2 {
  37684. cimg_abort_test2;
  37685. const CImg<T> I = get_shared_channel(c%_spectrum);
  37686. const CImg<t> K = _kernel.get_shared_channel(!channel_mode?c/_spectrum:c%_kernel._spectrum);
  37687. CImg<Ttfloat> _resu = channel_mode<=1?res.get_shared_channel(c):
  37688. CImg<Ttfloat>(res.width(),res.height(),res.depth(),1);
  37689. if (is_normalized) {
  37690. const Ttfloat M = (Ttfloat)K.magnitude(2), M2 = M*M;
  37691. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
  37692. cimg_forXYZ(res,X,Y,z) {
  37693. const int
  37694. x = xstart + X, y = ystart + Y,
  37695. px = x - i_xdilation>0?x - i_xdilation:0, nx = x + i_xdilation<w1?x + i_xdilation:w1,
  37696. py = y - i_ydilation>0?y - i_ydilation:0, ny = y + i_ydilation<h1?y + i_ydilation:h1;
  37697. const Ttfloat N = M2*(cimg::sqr(I(px,py,z)) + cimg::sqr(I(x,py,z)) + cimg::sqr(I(nx,py,z)) +
  37698. cimg::sqr(I(px,y,z)) + cimg::sqr(I(x,y,z)) + cimg::sqr(I(nx,y,z)) +
  37699. cimg::sqr(I(px,ny,z)) + cimg::sqr(I(x,ny,z)) + cimg::sqr(I(nx,ny,z)));
  37700. _resu(X,Y,z) = (Ttfloat)(N?(K[0]*I(px,py,z) + K[1]*I(x,py,z) + K[2]*I(nx,py,z) +
  37701. K[3]*I(px,y,z) + K[4]*I(x,y,z) + K[5]*I(nx,y,z) +
  37702. K[6]*I(px,ny,z) + K[7]*I(x,ny,z) + K[8]*I(nx,ny,z))/std::sqrt(N):0);
  37703. }
  37704. } else {
  37705. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
  37706. cimg_forXYZ(res,X,Y,z) {
  37707. const int
  37708. x = xstart + X, y = ystart + Y,
  37709. px = x - i_xdilation>0?x - i_xdilation:0, nx = x + i_xdilation<w1?x + i_xdilation:w1,
  37710. py = y - i_ydilation>0?y - i_ydilation:0, ny = y + i_ydilation<h1?y + i_ydilation:h1;
  37711. _resu(X,Y,z) = (Ttfloat)(K[0]*I(px,py,z) + K[1]*I(x,py,z) + K[2]*I(nx,py,z) +
  37712. K[3]*I(px,y,z) + K[4]*I(x,y,z) + K[5]*I(nx,y,z) +
  37713. K[6]*I(px,ny,z) + K[7]*I(x,ny,z) + K[8]*I(nx,ny,z));
  37714. }
  37715. }
  37716. if (channel_mode==2)
  37717. cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(c/smin)+=_resu;
  37718. else if (channel_mode==3)
  37719. cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(0)+=_resu;
  37720. } _cimg_abort_catch_openmp2
  37721. } break;
  37722. }
  37723. }
  37724. } else if (_kernel._width==1 && _kernel._height==1 && _kernel._depth==1 &&
  37725. !_xcenter && !_ycenter && !_zcenter &&
  37726. xstart>=0 && ystart>=0 && zstart>=0 &&
  37727. _xend<width() && _yend<height() && _zend<depth() &&
  37728. xstride==1 && ystride==1 && zstride==1) {
  37729. // Special optimization for 1x1 kernel.
  37730. cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
  37731. for (int c = 0; c<cend; ++c) {
  37732. const t valK = _kernel[!channel_mode?c/_spectrum:c%_kernel._spectrum];
  37733. CImg<T> I = get_crop(xstart,ystart,zstart,c%_spectrum,_xend,_yend,_zend,c%_spectrum);
  37734. if (valK!=1) I*=valK;
  37735. if (is_normalized) I.sign();
  37736. switch (channel_mode) {
  37737. case 0 : // All
  37738. case 1 : // One for one
  37739. res.get_shared_channel(c) = I;
  37740. break;
  37741. case 2 : // Partial sum
  37742. cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(c/smin)+=I;
  37743. break;
  37744. case 3 : // Full sum
  37745. cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(0)+=I;
  37746. break;
  37747. }
  37748. }
  37749. } else { // Generic version
  37750. cimg_pragma_openmp(parallel for cimg_openmp_if(is_outer_parallel))
  37751. for (int c = 0; c<cend; ++c) _cimg_abort_try_openmp2 {
  37752. cimg_abort_test2;
  37753. const CImg<T> I = get_shared_channel(c%_spectrum);
  37754. const CImg<t> K = _kernel.get_shared_channel(!channel_mode?c/_spectrum:c%_kernel._spectrum);
  37755. CImg<Ttfloat> _resu = channel_mode<=1?res.get_shared_channel(c):
  37756. CImg<Ttfloat>(res.width(),res.height(),res.depth(),1);
  37757. Ttfloat M = 0, M2 = 0;
  37758. if (is_normalized) { M = (Ttfloat)K.magnitude(2); M2 = cimg::sqr(M); }
  37759. #define _cimg_correlate_x_int const int ix = xstart + i_xstride*x + i_xdilation*(p - _xcenter)
  37760. #define _cimg_correlate_y_int const int iy = ystart + i_ystride*y + i_ydilation*(q - _ycenter)
  37761. #define _cimg_correlate_z_int const int iz = zstart + i_zstride*z + i_zdilation*(r - _zcenter)
  37762. #define _cimg_correlate_x_float const float ix = xstart + xstride*x + _xdilation*(p - _xcenter)
  37763. #define _cimg_correlate_y_float const float iy = ystart + ystride*y + _ydilation*(q - _ycenter)
  37764. #define _cimg_correlate_z_float const float iz = zstart + zstride*z + _zdilation*(r - _zcenter)
  37765. #define _cimg_correlate_x_int_dirichlet const bool is_in_x = ix>=0 && ix<w
  37766. #define _cimg_correlate_y_int_dirichlet const bool is_in_y = iy>=0 && iy<h
  37767. #define _cimg_correlate_z_int_dirichlet const bool is_in_z = iz>=0 && iz<d
  37768. #define _cimg_correlate_x_float_dirichlet _cimg_correlate_x_int_dirichlet
  37769. #define _cimg_correlate_y_float_dirichlet _cimg_correlate_y_int_dirichlet
  37770. #define _cimg_correlate_z_float_dirichlet _cimg_correlate_z_int_dirichlet
  37771. #define _cimg_correlate_x_int_neumann const int nix = cimg::cut(ix,0,w1)
  37772. #define _cimg_correlate_y_int_neumann const int niy = cimg::cut(iy,0,h1)
  37773. #define _cimg_correlate_z_int_neumann const int niz = cimg::cut(iz,0,d1)
  37774. #define _cimg_correlate_x_float_neumann const float nix = cimg::cut(ix,0,w1)
  37775. #define _cimg_correlate_y_float_neumann const float niy = cimg::cut(iy,0,h1)
  37776. #define _cimg_correlate_z_float_neumann const float niz = cimg::cut(iz,0,d1)
  37777. #define _cimg_correlate_x_int_periodic const int nix = cimg::mod(ix,w)
  37778. #define _cimg_correlate_y_int_periodic const int niy = cimg::mod(iy,h)
  37779. #define _cimg_correlate_z_int_periodic const int niz = cimg::mod(iz,d)
  37780. #define _cimg_correlate_x_float_periodic const float nix = cimg::mod(ix,w)
  37781. #define _cimg_correlate_y_float_periodic const float niy = cimg::mod(iy,h)
  37782. #define _cimg_correlate_z_float_periodic const float niz = cimg::mod(iz,d)
  37783. #define _cimg_correlate_x_int_mirror const int mx = cimg::mod(ix,w2), nix = mx<w?mx:w2 - mx - 1
  37784. #define _cimg_correlate_y_int_mirror const int my = cimg::mod(iy,h2), niy = my<h?my:h2 - my - 1
  37785. #define _cimg_correlate_z_int_mirror const int mz = cimg::mod(iz,d2), niz = mz<d?mz:d2 - mz - 1
  37786. #define _cimg_correlate_x_float_mirror const float mx = cimg::mod(ix,w2), nix = mx<w?mx:w2 - mx - 1
  37787. #define _cimg_correlate_y_float_mirror const float my = cimg::mod(iy,h2), niy = my<h?my:h2 - my - 1
  37788. #define _cimg_correlate_z_float_mirror const float mz = cimg::mod(iz,d2), niz = mz<d?mz:d2 - mz - 1
  37789. #define _cimg_correlate(type,boundary,access) \
  37790. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) \
  37791. cimg_forXYZ(res,x,y,z) { \
  37792. Ttfloat val = 0; \
  37793. const t *pK = K._data; \
  37794. cimg_forZ(_kernel,r) { _cimg_correlate_z_##type; _cimg_correlate_z_##type##_##boundary; \
  37795. cimg_forY(_kernel,q) { _cimg_correlate_y_##type; _cimg_correlate_y_##type##_##boundary; \
  37796. cimg_forX(_kernel,p) { _cimg_correlate_x_##type; _cimg_correlate_x_##type##_##boundary; \
  37797. val+=*(pK++)*(access); \
  37798. } \
  37799. } \
  37800. } \
  37801. _resu(x,y,z,0,res_wh,res_whd) = val; \
  37802. }
  37803. #define _cimg_correlate_n(type,boundary,access) \
  37804. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel)) \
  37805. cimg_forXYZ(res,x,y,z) { \
  37806. Ttfloat val = 0, N = 0; \
  37807. const t *pK = K._data; \
  37808. cimg_forZ(_kernel,r) { _cimg_correlate_z_##type; _cimg_correlate_z_##type##_##boundary; \
  37809. cimg_forY(_kernel,q) { _cimg_correlate_y_##type; _cimg_correlate_y_##type##_##boundary; \
  37810. cimg_forX(_kernel,p) { _cimg_correlate_x_##type; _cimg_correlate_x_##type##_##boundary; \
  37811. Ttfloat _val = access; \
  37812. val+=*(pK++)*_val; \
  37813. _val*=_val; N+=_val; \
  37814. } \
  37815. } \
  37816. } \
  37817. N*=M2; _resu(x,y,z,0,res_wh,res_whd) = N?val/std::sqrt(N):0; \
  37818. }
  37819. if (is_normalized) { // Normalized convolution/correlation
  37820. if (is_int_stride_dilation) // Integer stride and dilation
  37821. switch (boundary_conditions) {
  37822. case 0 : // Dirichlet
  37823. _cimg_correlate_n(int,dirichlet,is_in_x && is_in_y && is_in_z?I(ix,iy,iz,0,wh,whd):(T)0);
  37824. break;
  37825. case 1 : // Neumann
  37826. _cimg_correlate_n(int,neumann,I(nix,niy,niz,0,wh,whd));
  37827. break;
  37828. case 2 : // Periodic
  37829. _cimg_correlate_n(int,periodic,I(nix,niy,niz,0,wh,whd));
  37830. break;
  37831. case 3 : // Mirror
  37832. _cimg_correlate_n(int,mirror,I(nix,niy,niz,0,wh,whd));
  37833. break;
  37834. }
  37835. else if (interpolation_type) // Non-integer stride or dilation, linear interpolation
  37836. switch (boundary_conditions) {
  37837. case 0 : // Dirichlet
  37838. _cimg_correlate_n(float,dirichlet,is_in_x && is_in_y && is_in_z?I.linear_atXYZ(ix,iy,iz,0,0):0);
  37839. break;
  37840. case 1 : // Neumann
  37841. _cimg_correlate_n(float,neumann,I._linear_atXYZ(nix,niy,niz,0));
  37842. break;
  37843. case 2 : // Periodic
  37844. _cimg_correlate_n(float,periodic,I._linear_atXYZ(nix,niy,niz,0));
  37845. break;
  37846. case 3 : // Mirror
  37847. _cimg_correlate_n(float,mirror,I._linear_atXYZ(nix,niy,niz,0));
  37848. break;
  37849. }
  37850. else // Non-integer stride or dilation, nearest-neighbor interpolation
  37851. switch (boundary_conditions) {
  37852. case 0 : // Dirichlet
  37853. _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);
  37854. break;
  37855. case 1 : // Neumann
  37856. _cimg_correlate_n(float,neumann,I((int)nix,(int)niy,(int)niz,0));
  37857. break;
  37858. case 2 : // Periodic
  37859. _cimg_correlate_n(float,periodic,I((int)nix,(int)niy,(int)niz,0));
  37860. break;
  37861. case 3 : // Mirror
  37862. _cimg_correlate_n(float,mirror,I((int)nix,(int)niy,(int)niz,0));
  37863. break;
  37864. }
  37865. } else { // Standard convolution/correlation
  37866. if (is_int_stride_dilation) // Integer stride and dilation
  37867. switch (boundary_conditions) {
  37868. case 0 : // Dirichlet
  37869. _cimg_correlate(int,dirichlet,is_in_x && is_in_y && is_in_z?I(ix,iy,iz,0,wh,whd):(T)0);
  37870. break;
  37871. case 1 : // Neumann
  37872. _cimg_correlate(int,neumann,I(nix,niy,niz,0,wh,whd));
  37873. break;
  37874. case 2 : // Periodic
  37875. _cimg_correlate(int,periodic,I(nix,niy,niz,0,wh,whd));
  37876. break;
  37877. case 3 : // Mirror
  37878. _cimg_correlate(int,mirror,I(nix,niy,niz,0,wh,whd));
  37879. break;
  37880. }
  37881. else if (interpolation_type) // Non-integer stride or dilation, linear interpolation
  37882. switch (boundary_conditions) {
  37883. case 0 : // Dirichlet
  37884. _cimg_correlate(float,dirichlet,is_in_x && is_in_y && is_in_z?I.linear_atXYZ(ix,iy,iz,0,0):0);
  37885. break;
  37886. case 1 : // Neumann
  37887. _cimg_correlate(float,neumann,I._linear_atXYZ(nix,niy,niz,0));
  37888. break;
  37889. case 2 : // Periodic
  37890. _cimg_correlate(float,periodic,I._linear_atXYZ(nix,niy,niz,0));
  37891. break;
  37892. case 3 : // Mirror
  37893. _cimg_correlate(float,mirror,I._linear_atXYZ(nix,niy,niz,0));
  37894. break;
  37895. }
  37896. else // Non-integer stride or dilation, nearest-neighbor interpolation
  37897. switch (boundary_conditions) {
  37898. case 0 : // Dirichlet
  37899. _cimg_correlate(float,dirichlet,is_in_x && is_in_y && is_in_z?I((int)ix,(int)iy,(int)iz,0,0):(T)0);
  37900. break;
  37901. case 1 : // Neumann
  37902. _cimg_correlate(float,neumann,I((int)nix,(int)niy,(int)niz,0));
  37903. break;
  37904. case 2 : // Periodic
  37905. _cimg_correlate(float,periodic,I((int)nix,(int)niy,(int)niz,0));
  37906. break;
  37907. case 3 : // Mirror
  37908. _cimg_correlate(float,mirror,I((int)nix,(int)niy,(int)niz,0));
  37909. break;
  37910. }
  37911. }
  37912. if (channel_mode==2)
  37913. cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(c/smin)+=_resu;
  37914. else if (channel_mode==3)
  37915. cimg_pragma_openmp(critical(_correlate)) res.get_shared_channel(0)+=_resu;
  37916. } _cimg_abort_catch_openmp2
  37917. }
  37918. cimg_abort_test;
  37919. return res;
  37920. }
  37921. //! Convolve image by a kernel.
  37922. /**
  37923. \param kernel = the correlation kernel.
  37924. \param boundary_conditions Boundary condition. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
  37925. \param is_normalized = enable local normalization.
  37926. \param channel_mode Channel processing mode.
  37927. Can be { 0=all | 1=one for one (default) | 2=partial sum | 3=full sum }.
  37928. \param xcenter X-coordinate of the kernel center (~0U means 'centered').
  37929. \param ycenter Y-coordinate of the kernel center (~0U means 'centered').
  37930. \param zcenter Z-coordinate of the kernel center (~0U means 'centered').
  37931. \param xstart Starting X-coordinate of the instance image.
  37932. \param ystart Starting Y-coordinate of the instance image.
  37933. \param zstart Starting Z-coordinate of the instance image.
  37934. \param xend Ending X-coordinate of the instance image.
  37935. \param yend Ending Y-coordinate of the instance image.
  37936. \param zend Ending Z-coordinate of the instance image.
  37937. \param xstride Stride along the X-axis.
  37938. \param ystride Stride along the Y-axis.
  37939. \param zstride Stride along the Z-axis.
  37940. \param xdilation Dilation along the X-axis.
  37941. \param ydilation Dilation along the Y-axis.
  37942. \param zdilation Dilation along the Z-axis.
  37943. \param interpolation_type Can be { false=nearest | true=linear }.
  37944. \note
  37945. - The convolution of the image instance \p *this by the kernel \p kernel is defined to be:
  37946. res(x,y,z) = sum_{i,j,k} (*this)(\alpha_x\;x - \beta_x\;(i - c_x),\alpha_y\;y
  37947. - \beta_y\;(j - c_y),\alpha_z\;z - \beta_z\;(k - c_z))*kernel(i,j,k).
  37948. **/
  37949. template<typename t>
  37950. CImg<T>& convolve(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
  37951. const bool is_normalized=false, const unsigned int channel_mode=1,
  37952. const int xcenter=(int)(~0U>>1),
  37953. const int ycenter=(int)(~0U>>1),
  37954. const int zcenter=(int)(~0U>>1),
  37955. const int xstart=0,
  37956. const int ystart=0,
  37957. const int zstart=0,
  37958. const int xend=(int)(~0U>>1),
  37959. const int yend=(int)(~0U>>1),
  37960. const int zend=(int)(~0U>>1),
  37961. const float xstride=1, const float ystride=1, const float zstride=1,
  37962. const float xdilation=1, const float ydilation=1, const float zdilation=1,
  37963. const bool interpolation_type=false) {
  37964. if (is_empty() || !kernel) return *this;
  37965. return get_convolve(kernel,boundary_conditions,is_normalized,channel_mode,
  37966. xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend,
  37967. xstride,ystride,zstride,xdilation,ydilation,zdilation,
  37968. interpolation_type).move_to(*this);
  37969. }
  37970. //! Convolve image by a kernel \newinstance.
  37971. template<typename t>
  37972. CImg<_cimg_Ttfloat> get_convolve(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
  37973. const bool is_normalized=false, const unsigned int channel_mode=1,
  37974. const int xcenter=(int)(~0U>>1),
  37975. const int ycenter=(int)(~0U>>1),
  37976. const int zcenter=(int)(~0U>>1),
  37977. const int xstart=0,
  37978. const int ystart=0,
  37979. const int zstart=0,
  37980. const int xend=(int)(~0U>>1),
  37981. const int yend=(int)(~0U>>1),
  37982. const int zend=(int)(~0U>>1),
  37983. const float xstride=1, const float ystride=1, const float zstride=1,
  37984. const float xdilation=1, const float ydilation=1, const float zdilation=1,
  37985. const bool interpolation_type=false) const {
  37986. return _correlate(kernel,boundary_conditions,is_normalized,channel_mode,
  37987. xcenter,ycenter,zcenter,xstart,ystart,zstart,xend,yend,zend,
  37988. xstride,ystride,zstride,xdilation,ydilation,zdilation,
  37989. interpolation_type,true);
  37990. }
  37991. //! Cumulate image values, optionally along specified axis.
  37992. /**
  37993. \param axis Cumulation axis. Set it to 0 to cumulate all values globally without taking axes into account.
  37994. **/
  37995. CImg<T>& cumulate(const char axis=0) {
  37996. switch (cimg::lowercase(axis)) {
  37997. case 'x' :
  37998. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
  37999. _height*_depth*_spectrum>=16))
  38000. cimg_forYZC(*this,y,z,c) {
  38001. T *ptrd = data(0,y,z,c);
  38002. Tlong cumul = (Tlong)0;
  38003. cimg_forX(*this,x) { cumul+=(Tlong)*ptrd; *(ptrd++) = (T)cumul; }
  38004. }
  38005. break;
  38006. case 'y' : {
  38007. const ulongT w = (ulongT)_width;
  38008. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*512 &&
  38009. _width*_depth*_spectrum>=16))
  38010. cimg_forXZC(*this,x,z,c) {
  38011. T *ptrd = data(x,0,z,c);
  38012. Tlong cumul = (Tlong)0;
  38013. cimg_forY(*this,y) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=w; }
  38014. }
  38015. } break;
  38016. case 'z' : {
  38017. const ulongT wh = (ulongT)_width*_height;
  38018. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_depth>=(cimg_openmp_sizefactor)*512 &&
  38019. _width*_depth*_spectrum>=16))
  38020. cimg_forXYC(*this,x,y,c) {
  38021. T *ptrd = data(x,y,0,c);
  38022. Tlong cumul = (Tlong)0;
  38023. cimg_forZ(*this,z) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=wh; }
  38024. }
  38025. } break;
  38026. case 'c' : {
  38027. const ulongT whd = (ulongT)_width*_height*_depth;
  38028. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
  38029. cimg_openmp_if(_spectrum>=(cimg_openmp_sizefactor)*512 && _width*_height*_depth>=16))
  38030. cimg_forXYZ(*this,x,y,z) {
  38031. T *ptrd = data(x,y,z,0);
  38032. Tlong cumul = (Tlong)0;
  38033. cimg_forC(*this,c) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; ptrd+=whd; }
  38034. }
  38035. } break;
  38036. default : { // Global cumulation
  38037. Tlong cumul = (Tlong)0;
  38038. cimg_for(*this,ptrd,T) { cumul+=(Tlong)*ptrd; *ptrd = (T)cumul; }
  38039. }
  38040. }
  38041. return *this;
  38042. }
  38043. //! Cumulate image values, optionally along specified axis \newinstance.
  38044. CImg<Tlong> get_cumulate(const char axis=0) const {
  38045. return CImg<Tlong>(*this,false).cumulate(axis);
  38046. }
  38047. //! Cumulate image values, along specified axes.
  38048. /**
  38049. \param axes Cumulation axes, as a C-string.
  38050. \note \c axes may contains multiple characters, e.g. \c "xyz"
  38051. **/
  38052. CImg<T>& cumulate(const char *const axes) {
  38053. for (const char *s = axes; *s; ++s) cumulate(*s);
  38054. return *this;
  38055. }
  38056. //! Cumulate image values, along specified axes \newinstance.
  38057. CImg<Tlong> get_cumulate(const char *const axes) const {
  38058. return CImg<Tlong>(*this,false).cumulate(axes);
  38059. }
  38060. //! Erode image by a structuring element.
  38061. /**
  38062. \param kernel Structuring element.
  38063. \param boundary_conditions Boundary conditions.
  38064. Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
  38065. \param is_real Do the erosion in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false).
  38066. **/
  38067. template<typename t>
  38068. CImg<T>& erode(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
  38069. const bool is_real=false) {
  38070. if (is_empty() || !kernel) return *this;
  38071. return get_erode(kernel,boundary_conditions,is_real).move_to(*this);
  38072. }
  38073. //! Erode image by a structuring element \newinstance.
  38074. template<typename t>
  38075. CImg<_cimg_Tt> get_erode(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
  38076. const bool is_real=false) const {
  38077. if (is_empty() || !kernel) return *this;
  38078. if (!is_real && kernel==0) return CImg<T>(width(),height(),depth(),spectrum(),0);
  38079. typedef _cimg_Tt Tt;
  38080. CImg<Tt> res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum));
  38081. const int
  38082. mx2 = kernel.width()/2, my2 = kernel.height()/2, mz2 = kernel.depth()/2,
  38083. mx1 = kernel.width() - mx2 - 1, my1 = kernel.height() - my2 - 1, mz1 = kernel.depth() - mz2 - 1,
  38084. mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2,
  38085. w2 = 2*width(), h2 = 2*height(), d2 = 2*depth();
  38086. const bool
  38087. is_inner_parallel = _width*_height*_depth>=(cimg_openmp_sizefactor)*32768,
  38088. is_outer_parallel = res.size()>=(cimg_openmp_sizefactor)*32768;
  38089. cimg::unused(is_inner_parallel,is_outer_parallel);
  38090. _cimg_abort_init_openmp;
  38091. cimg_abort_init;
  38092. cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel))
  38093. cimg_forC(res,c) _cimg_abort_try_openmp {
  38094. cimg_abort_test;
  38095. const CImg<T> img = get_shared_channel(c%_spectrum);
  38096. const CImg<t> K = kernel.get_shared_channel(c%kernel._spectrum);
  38097. if (is_real) { // Real erosion
  38098. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
  38099. for (int z = mz1; z<mze; ++z)
  38100. for (int y = my1; y<mye; ++y)
  38101. for (int x = mx1; x<mxe; ++x) _cimg_abort_try_openmp2 {
  38102. cimg_abort_test2;
  38103. Tt min_val = cimg::type<Tt>::max();
  38104. for (int zm = -mz1; zm<=mz2; ++zm)
  38105. for (int ym = -my1; ym<=my2; ++ym)
  38106. for (int xm = -mx1; xm<=mx2; ++xm) {
  38107. const t mval = K(mx1 + xm,my1 + ym,mz1 + zm);
  38108. const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) - mval);
  38109. if (cval<min_val) min_val = cval;
  38110. }
  38111. res(x,y,z,c) = min_val;
  38112. } _cimg_abort_catch_openmp2
  38113. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
  38114. cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
  38115. cimg_abort_test2;
  38116. for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
  38117. Tt min_val = cimg::type<Tt>::max();
  38118. for (int zm = -mz1; zm<=mz2; ++zm)
  38119. for (int ym = -my1; ym<=my2; ++ym)
  38120. for (int xm = -mx1; xm<=mx2; ++xm) {
  38121. const t mval = K(mx1 + xm,my1 + ym,mz1 + zm);
  38122. Tt cval;
  38123. switch (boundary_conditions) {
  38124. case 0 : cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) - mval); break;
  38125. case 1 : cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) - mval); break;
  38126. case 2 : {
  38127. const int
  38128. nx = cimg::mod(x + xm,width()),
  38129. ny = cimg::mod(y + ym,height()),
  38130. nz = cimg::mod(z + zm,depth());
  38131. cval = img(nx,ny,nz) - mval;
  38132. } break;
  38133. default : {
  38134. const int
  38135. tx = cimg::mod(x + xm,w2),
  38136. ty = cimg::mod(y + ym,h2),
  38137. tz = cimg::mod(z + zm,d2),
  38138. nx = tx<width()?tx:w2 - tx - 1,
  38139. ny = ty<height()?ty:h2 - ty - 1,
  38140. nz = tz<depth()?tz:d2 - tz - 1;
  38141. cval = img(nx,ny,nz) - mval;
  38142. }
  38143. }
  38144. if (cval<min_val) min_val = cval;
  38145. }
  38146. res(x,y,z,c) = min_val;
  38147. }
  38148. } _cimg_abort_catch_openmp2
  38149. } else { // Binary erosion
  38150. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
  38151. for (int z = mz1; z<mze; ++z)
  38152. for (int y = my1; y<mye; ++y)
  38153. for (int x = mx1; x<mxe; ++x) _cimg_abort_try_openmp2 {
  38154. cimg_abort_test2;
  38155. Tt min_val = cimg::type<Tt>::max();
  38156. for (int zm = -mz1; zm<=mz2; ++zm)
  38157. for (int ym = -my1; ym<=my2; ++ym)
  38158. for (int xm = -mx1; xm<=mx2; ++xm)
  38159. if (K(mx1 + xm,my1 + ym,mz1 + zm)) {
  38160. const Tt cval = (Tt)img(x + xm,y + ym,z + zm);
  38161. if (cval<min_val) min_val = cval;
  38162. }
  38163. res(x,y,z,c) = min_val;
  38164. } _cimg_abort_catch_openmp2
  38165. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
  38166. cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
  38167. cimg_abort_test2;
  38168. for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
  38169. Tt min_val = cimg::type<Tt>::max();
  38170. for (int zm = -mz1; zm<=mz2; ++zm)
  38171. for (int ym = -my1; ym<=my2; ++ym)
  38172. for (int xm = -mx1; xm<=mx2; ++xm) {
  38173. if (K(mx1 + xm,my1 + ym,mz1 + zm)) {
  38174. Tt cval;
  38175. switch (boundary_conditions) {
  38176. case 0 : cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); break;
  38177. case 1 : cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm); break;
  38178. case 2 : {
  38179. const int
  38180. nx = cimg::mod(x + xm,width()),
  38181. ny = cimg::mod(y + ym,height()),
  38182. nz = cimg::mod(z + zm,depth());
  38183. cval = img(nx,ny,nz);
  38184. } break;
  38185. default : {
  38186. const int
  38187. tx = cimg::mod(x + xm,w2),
  38188. ty = cimg::mod(y + ym,h2),
  38189. tz = cimg::mod(z + zm,d2),
  38190. nx = tx<width()?tx:w2 - tx - 1,
  38191. ny = ty<height()?ty:h2 - ty - 1,
  38192. nz = tz<depth()?tz:d2 - tz - 1;
  38193. cval = img(nx,ny,nz);
  38194. }
  38195. }
  38196. if (cval<min_val) min_val = cval;
  38197. }
  38198. }
  38199. res(x,y,z,c) = min_val;
  38200. }
  38201. } _cimg_abort_catch_openmp2
  38202. }
  38203. } _cimg_abort_catch_openmp
  38204. cimg_abort_test;
  38205. return res;
  38206. }
  38207. //! Erode image by a rectangular structuring element of specified size.
  38208. /**
  38209. \param sx Width of the structuring element.
  38210. \param sy Height of the structuring element.
  38211. \param sz Depth of the structuring element.
  38212. **/
  38213. CImg<T>& erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
  38214. if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this;
  38215. if (sx>1 && _width>1) { // Along X-axis
  38216. 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;
  38217. CImg<T> buf(L);
  38218. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
  38219. cimg_forYZC(*this,y,z,c) {
  38220. T *const ptrdb = buf._data, *ptrd = buf._data, *const ptrde = buf._data + L - 1;
  38221. const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + (ulongT)L*off - off;
  38222. T cur = *ptrs; ptrs+=off; bool is_first = true;
  38223. for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
  38224. const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }}
  38225. *(ptrd++) = cur;
  38226. if (ptrs>=ptrse) {
  38227. T *pd = data(0,y,z,c); cur = std::min(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
  38228. } else {
  38229. for (int p = s1; p>0 && ptrd<=ptrde; --p) {
  38230. const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
  38231. *(ptrd++) = cur;
  38232. }
  38233. for (int p = L - s - 1; p>0; --p) {
  38234. const T val = *ptrs; ptrs+=off;
  38235. if (is_first) {
  38236. const T *nptrs = ptrs - off; cur = val;
  38237. for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
  38238. nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
  38239. } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
  38240. *(ptrd++) = cur;
  38241. }
  38242. ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
  38243. for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
  38244. const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
  38245. }
  38246. *(ptrd--) = cur;
  38247. for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
  38248. const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
  38249. }
  38250. T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
  38251. }
  38252. }
  38253. }
  38254. if (sy>1 && _height>1) { // Along Y-axis
  38255. const int L = height(), off = width(), s = (int)sy, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1,
  38256. s2 = _s2>L?L:_s2;
  38257. CImg<T> buf(L);
  38258. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
  38259. cimg_forXZC(*this,x,z,c) {
  38260. T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
  38261. const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + (ulongT)L*off - off;
  38262. T cur = *ptrs; ptrs+=off; bool is_first = true;
  38263. for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
  38264. const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
  38265. }
  38266. *(ptrd++) = cur;
  38267. if (ptrs>=ptrse) {
  38268. T *pd = data(x,0,z,c); cur = std::min(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
  38269. } else {
  38270. for (int p = s1; p>0 && ptrd<=ptrde; --p) {
  38271. const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
  38272. *(ptrd++) = cur;
  38273. }
  38274. for (int p = L - s - 1; p>0; --p) {
  38275. const T val = *ptrs; ptrs+=off;
  38276. if (is_first) {
  38277. const T *nptrs = ptrs - off; cur = val;
  38278. for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
  38279. nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
  38280. } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
  38281. *(ptrd++) = cur;
  38282. }
  38283. ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
  38284. for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
  38285. const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
  38286. }
  38287. *(ptrd--) = cur;
  38288. for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
  38289. const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
  38290. }
  38291. T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
  38292. }
  38293. }
  38294. }
  38295. if (sz>1 && _depth>1) { // Along Z-axis
  38296. const int L = depth(), off = width()*height(), s = (int)sz, _s2 = s/2 + 1, _s1 = s - _s2, s1 = _s1>L?L:_s1,
  38297. s2 = _s2>L?L:_s2;
  38298. CImg<T> buf(L);
  38299. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
  38300. cimg_forXYC(*this,x,y,c) {
  38301. T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
  38302. const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + (ulongT)L*off - off;
  38303. T cur = *ptrs; ptrs+=off; bool is_first = true;
  38304. for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
  38305. const T val = *ptrs; ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
  38306. }
  38307. *(ptrd++) = cur;
  38308. if (ptrs>=ptrse) {
  38309. T *pd = data(x,y,0,c); cur = std::min(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
  38310. } else {
  38311. for (int p = s1; p>0 && ptrd<=ptrde; --p) {
  38312. const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val<=cur) { cur = val; is_first = false; }
  38313. *(ptrd++) = cur;
  38314. }
  38315. for (int p = L - s - 1; p>0; --p) {
  38316. const T val = *ptrs; ptrs+=off;
  38317. if (is_first) {
  38318. const T *nptrs = ptrs - off; cur = val;
  38319. for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval<cur) cur = nval; }
  38320. nptrs-=off; const T nval = *nptrs; if (nval<cur) { cur = nval; is_first = true; } else is_first = false;
  38321. } else { if (val<=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
  38322. *(ptrd++) = cur;
  38323. }
  38324. ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
  38325. for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
  38326. const T val = *ptrs; ptrs-=off; if (val<cur) cur = val;
  38327. }
  38328. *(ptrd--) = cur;
  38329. for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
  38330. const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val<cur) cur = val; *(ptrd--) = cur;
  38331. }
  38332. T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
  38333. }
  38334. }
  38335. }
  38336. return *this;
  38337. }
  38338. //! Erode image by a rectangular structuring element of specified size \newinstance.
  38339. CImg<T> get_erode(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
  38340. return (+*this).erode(sx,sy,sz);
  38341. }
  38342. //! Erode the image by a square structuring element of specified size.
  38343. /**
  38344. \param s Size of the structuring element.
  38345. **/
  38346. CImg<T>& erode(const unsigned int s) {
  38347. return erode(s,s,s);
  38348. }
  38349. //! Erode the image by a square structuring element of specified size \newinstance.
  38350. CImg<T> get_erode(const unsigned int s) const {
  38351. return (+*this).erode(s);
  38352. }
  38353. //! Dilate image by a structuring element.
  38354. /**
  38355. \param kernel Structuring element.
  38356. \param boundary_conditions Boundary conditions.
  38357. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
  38358. \param is_real Do the dilation in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false).
  38359. **/
  38360. template<typename t>
  38361. CImg<T>& dilate(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
  38362. const bool is_real=false) {
  38363. if (is_empty() || !kernel) return *this;
  38364. return get_dilate(kernel,boundary_conditions,is_real).move_to(*this);
  38365. }
  38366. //! Dilate image by a structuring element \newinstance.
  38367. template<typename t>
  38368. CImg<_cimg_Tt> get_dilate(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
  38369. const bool is_real=false) const {
  38370. if (is_empty() || !kernel || (!is_real && kernel==0)) return *this;
  38371. typedef _cimg_Tt Tt;
  38372. CImg<Tt> res(_width,_height,_depth,std::max(_spectrum,kernel._spectrum));
  38373. const int
  38374. mx1 = kernel.width()/2, my1 = kernel.height()/2, mz1 = kernel.depth()/2,
  38375. mx2 = kernel.width() - mx1 - 1, my2 = kernel.height() - my1 - 1, mz2 = kernel.depth() - mz1 - 1,
  38376. mxe = width() - mx2, mye = height() - my2, mze = depth() - mz2,
  38377. w2 = 2*width(), h2 = 2*height(), d2 = 2*depth();
  38378. const bool
  38379. is_inner_parallel = _width*_height*_depth>=(cimg_openmp_sizefactor)*32768,
  38380. is_outer_parallel = res.size()>=(cimg_openmp_sizefactor)*32768;
  38381. cimg::unused(is_inner_parallel,is_outer_parallel);
  38382. _cimg_abort_init_openmp;
  38383. cimg_abort_init;
  38384. cimg_pragma_openmp(parallel for cimg_openmp_if(!is_inner_parallel && is_outer_parallel))
  38385. cimg_forC(res,c) _cimg_abort_try_openmp {
  38386. cimg_abort_test;
  38387. const CImg<T> img = get_shared_channel(c%_spectrum);
  38388. const CImg<t> K = kernel.get_shared_channel(c%kernel._spectrum);
  38389. if (is_real) { // Real dilation
  38390. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
  38391. for (int z = mz1; z<mze; ++z)
  38392. for (int y = my1; y<mye; ++y)
  38393. for (int x = mx1; x<mxe; ++x) _cimg_abort_try_openmp2 {
  38394. cimg_abort_test2;
  38395. Tt max_val = cimg::type<Tt>::min();
  38396. for (int zm = -mz1; zm<=mz2; ++zm)
  38397. for (int ym = -my1; ym<=my2; ++ym)
  38398. for (int xm = -mx1; xm<=mx2; ++xm) {
  38399. const t mval = K(mx2 - xm,my2 - ym,mz2 - zm);
  38400. const Tt cval = (Tt)(img(x + xm,y + ym,z + zm) + mval);
  38401. if (cval>max_val) max_val = cval;
  38402. }
  38403. res(x,y,z,c) = max_val;
  38404. } _cimg_abort_catch_openmp2
  38405. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
  38406. cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
  38407. cimg_abort_test2;
  38408. for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
  38409. Tt max_val = cimg::type<Tt>::min();
  38410. for (int zm = -mz1; zm<=mz2; ++zm)
  38411. for (int ym = -my1; ym<=my2; ++ym)
  38412. for (int xm = -mx1; xm<=mx2; ++xm) {
  38413. const t mval = K(mx2 - xm,my2 - ym,mz2 - zm);
  38414. Tt cval;
  38415. switch (boundary_conditions) {
  38416. case 0 : cval = (Tt)(img.atXYZ(x + xm,y + ym,z + zm,0,(T)0) + mval); break;
  38417. case 1 : cval = (Tt)(img._atXYZ(x + xm,y + ym,z + zm) + mval); break;
  38418. case 2 : {
  38419. const int
  38420. nx = cimg::mod(x + xm,width()),
  38421. ny = cimg::mod(y + ym,height()),
  38422. nz = cimg::mod(z + zm,depth());
  38423. cval = img(nx,ny,nz) + mval;
  38424. } break;
  38425. default : {
  38426. const int
  38427. tx = cimg::mod(x + xm,w2),
  38428. ty = cimg::mod(y + ym,h2),
  38429. tz = cimg::mod(z + zm,d2),
  38430. nx = tx<width()?tx:w2 - tx - 1,
  38431. ny = ty<height()?ty:h2 - ty - 1,
  38432. nz = tz<depth()?tz:d2 - tz - 1;
  38433. cval = img(nx,ny,nz) + mval;
  38434. }
  38435. }
  38436. if (cval>max_val) max_val = cval;
  38437. }
  38438. res(x,y,z,c) = max_val;
  38439. }
  38440. } _cimg_abort_catch_openmp2
  38441. } else { // Binary dilation
  38442. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(is_inner_parallel))
  38443. for (int z = mz1; z<mze; ++z)
  38444. for (int y = my1; y<mye; ++y)
  38445. for (int x = mx1; x<mxe; ++x) _cimg_abort_try_openmp2 {
  38446. cimg_abort_test2;
  38447. Tt max_val = cimg::type<Tt>::min();
  38448. for (int zm = -mz1; zm<=mz2; ++zm)
  38449. for (int ym = -my1; ym<=my2; ++ym)
  38450. for (int xm = -mx1; xm<=mx2; ++xm)
  38451. if (K(mx2 - xm,my2 - ym,mz2 - zm)) {
  38452. const Tt cval = (Tt)img(x + xm,y + ym,z + zm);
  38453. if (cval>max_val) max_val = cval;
  38454. }
  38455. res(x,y,z,c) = max_val;
  38456. } _cimg_abort_catch_openmp2
  38457. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(is_inner_parallel))
  38458. cimg_forYZ(res,y,z) _cimg_abort_try_openmp2 {
  38459. cimg_abort_test2;
  38460. for (int x = 0; x<width(); (y<my1 || y>=mye || z<mz1 || z>=mze)?++x:((x<mx1 - 1 || x>=mxe)?++x:(x=mxe))) {
  38461. Tt max_val = cimg::type<Tt>::min();
  38462. for (int zm = -mz1; zm<=mz2; ++zm)
  38463. for (int ym = -my1; ym<=my2; ++ym)
  38464. for (int xm = -mx1; xm<=mx2; ++xm) {
  38465. if (K(mx2 - xm,my2 - ym,mz2 - zm)) {
  38466. Tt cval;
  38467. switch (boundary_conditions) {
  38468. case 0 : cval = (Tt)img.atXYZ(x + xm,y + ym,z + zm,0,(T)0); break;
  38469. case 1 : cval = (Tt)img._atXYZ(x + xm,y + ym,z + zm); break;
  38470. case 2 : {
  38471. const int
  38472. nx = cimg::mod(x + xm,width()),
  38473. ny = cimg::mod(y + ym,height()),
  38474. nz = cimg::mod(z + zm,depth());
  38475. cval = img(nx,ny,nz);
  38476. } break;
  38477. default : {
  38478. const int
  38479. tx = cimg::mod(x + xm,w2),
  38480. ty = cimg::mod(y + ym,h2),
  38481. tz = cimg::mod(z + zm,d2),
  38482. nx = tx<width()?tx:w2 - tx - 1,
  38483. ny = ty<height()?ty:h2 - ty - 1,
  38484. nz = tz<depth()?tz:d2 - tz - 1;
  38485. cval = img(nx,ny,nz);
  38486. }
  38487. }
  38488. if (cval>max_val) max_val = cval;
  38489. }
  38490. }
  38491. res(x,y,z,c) = max_val;
  38492. }
  38493. } _cimg_abort_catch_openmp2
  38494. }
  38495. } _cimg_abort_catch_openmp
  38496. cimg_abort_test;
  38497. return res;
  38498. }
  38499. //! Dilate image by a rectangular structuring element of specified size.
  38500. /**
  38501. \param sx Width of the structuring element.
  38502. \param sy Height of the structuring element.
  38503. \param sz Depth of the structuring element.
  38504. **/
  38505. CImg<T>& dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
  38506. if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this;
  38507. if (sx>1 && _width>1) { // Along X-axis
  38508. 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;
  38509. CImg<T> buf(L);
  38510. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
  38511. cimg_forYZC(*this,y,z,c) {
  38512. T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
  38513. const T *const ptrsb = data(0,y,z,c), *ptrs = ptrsb, *const ptrse = ptrs + (ulongT)L*off - off;
  38514. T cur = *ptrs; ptrs+=off; bool is_first = true;
  38515. for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
  38516. const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
  38517. }
  38518. *(ptrd++) = cur;
  38519. if (ptrs>=ptrse) {
  38520. T *pd = data(0,y,z,c); cur = std::max(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
  38521. } else {
  38522. for (int p = s1; p>0 && ptrd<=ptrde; --p) {
  38523. const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
  38524. *(ptrd++) = cur;
  38525. }
  38526. for (int p = L - s - 1; p>0; --p) {
  38527. const T val = *ptrs; ptrs+=off;
  38528. if (is_first) {
  38529. const T *nptrs = ptrs - off; cur = val;
  38530. for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
  38531. nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
  38532. } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
  38533. *(ptrd++) = cur;
  38534. }
  38535. ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
  38536. for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
  38537. const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
  38538. }
  38539. *(ptrd--) = cur;
  38540. for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
  38541. const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
  38542. }
  38543. T *pd = data(0,y,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
  38544. }
  38545. }
  38546. }
  38547. if (sy>1 && _height>1) { // Along Y-axis
  38548. const int L = height(), off = width(), s = (int)sy, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1,
  38549. s2 = _s2>L?L:_s2;
  38550. CImg<T> buf(L);
  38551. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
  38552. cimg_forXZC(*this,x,z,c) {
  38553. T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
  38554. const T *const ptrsb = data(x,0,z,c), *ptrs = ptrsb, *const ptrse = ptrs + (ulongT)L*off - off;
  38555. T cur = *ptrs; ptrs+=off; bool is_first = true;
  38556. for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
  38557. const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
  38558. }
  38559. *(ptrd++) = cur;
  38560. if (ptrs>=ptrse) {
  38561. T *pd = data(x,0,z,c); cur = std::max(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
  38562. } else {
  38563. for (int p = s1; p>0 && ptrd<=ptrde; --p) {
  38564. const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
  38565. *(ptrd++) = cur;
  38566. }
  38567. for (int p = L - s - 1; p>0; --p) {
  38568. const T val = *ptrs; ptrs+=off;
  38569. if (is_first) {
  38570. const T *nptrs = ptrs - off; cur = val;
  38571. for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
  38572. nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
  38573. } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
  38574. *(ptrd++) = cur;
  38575. }
  38576. ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
  38577. for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
  38578. const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
  38579. }
  38580. *(ptrd--) = cur;
  38581. for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
  38582. const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
  38583. }
  38584. T *pd = data(x,0,z,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
  38585. }
  38586. }
  38587. }
  38588. if (sz>1 && _depth>1) { // Along Z-axis
  38589. const int L = depth(), off = width()*height(), s = (int)sz, _s1 = s/2, _s2 = s - _s1, s1 = _s1>L?L:_s1,
  38590. s2 = _s2>L?L:_s2;
  38591. CImg<T> buf(L);
  38592. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) firstprivate(buf) if (size()>524288))
  38593. cimg_forXYC(*this,x,y,c) {
  38594. T *const ptrdb = buf._data, *ptrd = ptrdb, *const ptrde = buf._data + L - 1;
  38595. const T *const ptrsb = data(x,y,0,c), *ptrs = ptrsb, *const ptrse = ptrs + (ulongT)L*off - off;
  38596. T cur = *ptrs; ptrs+=off; bool is_first = true;
  38597. for (int p = s2 - 1; p>0 && ptrs<=ptrse; --p) {
  38598. const T val = *ptrs; ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
  38599. }
  38600. *(ptrd++) = cur;
  38601. if (ptrs>=ptrse) {
  38602. T *pd = data(x,y,0,c); cur = std::max(cur,*ptrse); cimg_forX(buf,k) { *pd = cur; pd+=off; }
  38603. } else {
  38604. for (int p = s1; p>0 && ptrd<=ptrde; --p) {
  38605. const T val = *ptrs; if (ptrs<ptrse) ptrs+=off; if (val>=cur) { cur = val; is_first = false; }
  38606. *(ptrd++) = cur;
  38607. }
  38608. for (int p = L - s - 1; p>0; --p) {
  38609. const T val = *ptrs; ptrs+=off;
  38610. if (is_first) {
  38611. const T *nptrs = ptrs - off; cur = val;
  38612. for (int q = s - 2; q>0; --q) { nptrs-=off; const T nval = *nptrs; if (nval>cur) cur = nval; }
  38613. nptrs-=off; const T nval = *nptrs; if (nval>cur) { cur = nval; is_first = true; } else is_first = false;
  38614. } else { if (val>=cur) cur = val; else if (cur==*(ptrs-s*off)) is_first = true; }
  38615. *(ptrd++) = cur;
  38616. }
  38617. ptrd = ptrde; ptrs = ptrse; cur = *ptrs; ptrs-=off;
  38618. for (int p = s1; p>0 && ptrs>=ptrsb; --p) {
  38619. const T val = *ptrs; ptrs-=off; if (val>cur) cur = val;
  38620. }
  38621. *(ptrd--) = cur;
  38622. for (int p = s2 - 1; p>0 && ptrd>=ptrdb; --p) {
  38623. const T val = *ptrs; if (ptrs>ptrsb) ptrs-=off; if (val>cur) cur = val; *(ptrd--) = cur;
  38624. }
  38625. T *pd = data(x,y,0,c); cimg_for(buf,ps,T) { *pd = *ps; pd+=off; }
  38626. }
  38627. }
  38628. }
  38629. return *this;
  38630. }
  38631. //! Dilate image by a rectangular structuring element of specified size \newinstance.
  38632. CImg<T> get_dilate(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
  38633. return (+*this).dilate(sx,sy,sz);
  38634. }
  38635. //! Dilate image by a square structuring element of specified size.
  38636. /**
  38637. \param s Size of the structuring element.
  38638. **/
  38639. CImg<T>& dilate(const unsigned int s) {
  38640. return dilate(s,s,s);
  38641. }
  38642. //! Dilate image by a square structuring element of specified size \newinstance.
  38643. CImg<T> get_dilate(const unsigned int s) const {
  38644. return (+*this).dilate(s);
  38645. }
  38646. //! Apply morphological closing by a structuring element.
  38647. /**
  38648. \param kernel Structuring element.
  38649. \param boundary_conditions Boundary conditions.
  38650. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
  38651. \param is_real Do the closing in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false).
  38652. **/
  38653. template<typename t>
  38654. CImg<T>& closing(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
  38655. const bool is_real=false) {
  38656. const int sx = kernel.width(), sy = kernel.height(), sz = kernel.depth();
  38657. if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this;
  38658. return get_closing(kernel,boundary_conditions,is_real).move_to(*this);
  38659. }
  38660. //! Apply morphological closing by a structuring element \newinstance.
  38661. template<typename t>
  38662. CImg<T> get_closing(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
  38663. const bool is_real=false) const {
  38664. const int sx = kernel.width(), sy = kernel.height(), sz = kernel.depth();
  38665. if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this;
  38666. const int sx1 = (int)(sx - 1)/2, sy1 = (int)(sy - 1)/2, sz1 = (int)(sz - 1)/2;
  38667. CImg<T> res;
  38668. if (_depth>1) { // 3D
  38669. get_resize(width() + sx + 1,height() + sy + 1,depth() + sz + 1,spectrum(),0,boundary_conditions,0.5,0.5,0.5).
  38670. dilate(kernel,1,is_real).erode(kernel,1,is_real).
  38671. crop(sx1 + 1,sy1 + 1,sz1 + 1,sx1 + width(),sy1 + height(),sz1 + depth()).move_to(res);
  38672. } else if (_height>1) { // 2D
  38673. get_resize(width() + sx + 1,height() + sy + 1,1,spectrum(),0,boundary_conditions,0.5,0.5).
  38674. dilate(kernel,1,is_real).erode(kernel,1,is_real).
  38675. crop(sx1 + 1,sy1 + 1,sx1 + width(),sy1 + height()).move_to(res);
  38676. } else if (_width>1) { // 1D
  38677. get_resize(width() + sx + 1,1,1,spectrum(),0,boundary_conditions,0.5).
  38678. dilate(kernel,1,is_real).erode(kernel,1,is_real).
  38679. crop(sx1 + 1,sx1 + width()).move_to(res);
  38680. }
  38681. return res;
  38682. }
  38683. //! Apply morphological closing by a rectangular structuring element of specified size.
  38684. CImg<T>& closing(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
  38685. if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this;
  38686. return get_closing(sx,sy,sz).move_to(*this);
  38687. }
  38688. //! Apply morphological closing by a rectangular structuring element of specified size \newinstance.
  38689. CImg<T> get_closing(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
  38690. if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this;
  38691. const int sx1 = (int)(sx - 1)/2, sy1 = (int)(sy - 1)/2, sz1 = (int)(sz - 1)/2;
  38692. CImg<T> res;
  38693. if (_depth>1) { // 3D
  38694. get_resize(width() + sx + 1,height() + sy + 1,depth() + sz + 1,spectrum(),0,1,0.5,0.5,0.5).
  38695. dilate(sx,sy,sz).erode(sx,sy,sz).
  38696. crop(sx1 + 1,sy1 + 1,sz1 + 1,sx1 + width(),sy1 + height(),sz1 + depth()).move_to(res);
  38697. } else if (_height>1) { // 2D
  38698. get_resize(width() + sx + 1,height() + sy + 1,1,spectrum(),0,1,0.5,0.5).
  38699. dilate(sx,sy).erode(sx,sy).
  38700. crop(sx1 + 1,sy1 + 1,sx1 + width(),sy1 + height()).move_to(res);
  38701. } else if (_width>1) { // 1D
  38702. get_resize(width() + sx + 1,1,1,spectrum(),0,1,0.5).
  38703. dilate(sx,1).erode(sx,1).
  38704. crop(sx1 + 1,sx1 + width()).move_to(res);
  38705. }
  38706. return res;
  38707. }
  38708. //! Apply morphological closing by a square structuring element of specified size.
  38709. /**
  38710. \param s Size of the structuring element.
  38711. **/
  38712. CImg<T>& closing(const unsigned int s) {
  38713. return closing(s,s,s);
  38714. }
  38715. //! Apply morphological closing by a square structuring element of specified size \newinstance.
  38716. CImg<T> get_closing(const unsigned int s) const {
  38717. return (+*this).closing(s);
  38718. }
  38719. //! Apply morphological opening by a structuring element.
  38720. /**
  38721. \param kernel Structuring element.
  38722. \param boundary_conditions Boundary conditions.
  38723. Can be { 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }.
  38724. \param is_real Do the opening in real (a.k.a 'non-flat') mode (\c true) rather than binary mode (\c false).
  38725. **/
  38726. template<typename t>
  38727. CImg<T>& opening(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
  38728. const bool is_real=false) {
  38729. const int sx = kernel.width(), sy = kernel.height(), sz = kernel.depth();
  38730. if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this;
  38731. return get_opening(kernel,boundary_conditions,is_real).move_to(*this);
  38732. }
  38733. //! Apply morphological opening by a structuring element \newinstance.
  38734. template<typename t>
  38735. CImg<T> get_opening(const CImg<t>& kernel, const unsigned int boundary_conditions=1,
  38736. const bool is_real=false) const {
  38737. const int sx = kernel.width(), sy = kernel.height(), sz = kernel.depth();
  38738. if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this;
  38739. const int sx1 = (int)(sx - 1)/2, sy1 = (int)(sy - 1)/2, sz1 = (int)(sz - 1)/2;
  38740. CImg<T> res;
  38741. if (_depth>1) { // 3D
  38742. get_resize(width() + sx + 1,height() + sy + 1,depth() + sz + 1,spectrum(),0,boundary_conditions,0.5,0.5,0.5).
  38743. erode(kernel,1,is_real).dilate(kernel,1,is_real).
  38744. crop(sx1 + 1,sy1 + 1,sz1 + 1,sx1 + width(),sy1 + height(),sz1 + depth()).move_to(res);
  38745. } else if (_height>1) { // 2D
  38746. get_resize(width() + sx + 1,height() + sy + 1,1,spectrum(),0,boundary_conditions,0.5,0.5).
  38747. erode(kernel,1,is_real).dilate(kernel,1,is_real).
  38748. crop(sx1 + 1,sy1 + 1,sx1 + width(),sy1 + height()).move_to(res);
  38749. } else if (_width>1) { // 1D
  38750. get_resize(width() + sx + 1,1,1,spectrum(),0,boundary_conditions,0.5).
  38751. erode(kernel,1,is_real).dilate(kernel,1,is_real).
  38752. crop(sx1 + 1,sx1 + width()).move_to(res);
  38753. }
  38754. return res;
  38755. }
  38756. //! Apply morphological opening by a rectangular structuring element of specified size.
  38757. CImg<T>& opening(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) {
  38758. if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this;
  38759. return get_opening(sx,sy,sz).move_to(*this);
  38760. }
  38761. //! Apply morphological opening by a rectangular structuring element of specified size \newinstance.
  38762. CImg<T> get_opening(const unsigned int sx, const unsigned int sy, const unsigned int sz=1) const {
  38763. if (is_empty() || (sx<=1 && sy<=1 && sz<=1)) return *this;
  38764. const int sx1 = (int)(sx - 1)/2, sy1 = (int)(sy - 1)/2, sz1 = (int)(sz - 1)/2;
  38765. CImg<T> res;
  38766. if (_depth>1) { // 3D
  38767. get_resize(width() + sx + 1,height() + sy + 1,depth() + sz + 1,spectrum(),0,1,0.5,0.5,0.5).
  38768. erode(sx,sy,sz).dilate(sx,sy,sz).
  38769. crop(sx1 + 1,sy1 + 1,sz1 + 1,sx1 + width(),sy1 + height(),sz1 + depth()).move_to(res);
  38770. } else if (_height>1) { // 2D
  38771. get_resize(width() + sx + 1,height() + sy + 1,1,spectrum(),0,1,0.5,0.5).
  38772. erode(sx,sy).dilate(sx,sy).
  38773. crop(sx1 + 1,sy1 + 1,sx1 + width(),sy1 + height()).move_to(res);
  38774. } else if (_width>1) { // 1D
  38775. get_resize(width() + sx + 1,1,1,spectrum(),0,1,0.5).
  38776. erode(sx,1).dilate(sx,1).
  38777. crop(sx1 + 1,sx1 + width()).move_to(res);
  38778. }
  38779. return res;
  38780. }
  38781. //! Apply morphological opening by a square structuring element of specified size.
  38782. /**
  38783. \param s Size of the structuring element.
  38784. **/
  38785. CImg<T>& opening(const unsigned int s) {
  38786. return opening(s,s,s);
  38787. }
  38788. //! Apply morphological opening by a square structuring element of specified size \newinstance.
  38789. CImg<T> get_opening(const unsigned int s) const {
  38790. return (+*this).opening(s);
  38791. }
  38792. //! Compute watershed transform.
  38793. /**
  38794. \param priority Priority map.
  38795. \param is_high_connectivity Boolean that choose between 4(false)- or 8(true)-connectivity
  38796. in 2D case, and between 6(false)- or 26(true)-connectivity in 3D case.
  38797. \note Non-zero values of the instance instance are propagated to zero-valued ones according to
  38798. specified the priority map.
  38799. **/
  38800. template<typename t>
  38801. CImg<T>& watershed(const CImg<t>& priority, const bool is_high_connectivity=false) {
  38802. #define _cimg_watershed_init(cond,X,Y,Z) \
  38803. if (cond && !(*this)(X,Y,Z)) Q._priority_queue_insert(labels,sizeQ,priority(X,Y,Z),X,Y,Z,nb_seeds)
  38804. #define _cimg_watershed_propagate(cond,X,Y,Z) \
  38805. if (cond) { \
  38806. if ((*this)(X,Y,Z)) { \
  38807. ns = labels(X,Y,Z) - 1; xs = seeds(ns,0); ys = seeds(ns,1); zs = seeds(ns,2); \
  38808. d = cimg::sqr((float)x - xs) + cimg::sqr((float)y - ys) + cimg::sqr((float)z - zs); \
  38809. if (d<dmin) { dmin = d; nmin = ns; nlabel = (*this)(xs,ys,zs); } \
  38810. } else Q._priority_queue_insert(labels,sizeQ,priority(X,Y,Z),X,Y,Z,n); \
  38811. }
  38812. if (is_empty()) return *this;
  38813. if (!is_sameXYZ(priority))
  38814. throw CImgArgumentException(_cimg_instance
  38815. "watershed(): image instance and specified priority (%u,%u,%u,%u,%p) "
  38816. "have different dimensions.",
  38817. cimg_instance,
  38818. priority._width,priority._height,priority._depth,priority._spectrum,priority._data);
  38819. if (_spectrum!=1) {
  38820. cimg_forC(*this,c)
  38821. get_shared_channel(c).watershed(priority.get_shared_channel(c%priority._spectrum));
  38822. return *this;
  38823. }
  38824. CImg<uintT> labels(_width,_height,_depth,1,0), seeds(64,3);
  38825. CImg<typename cimg::superset2<T,t,int>::type> Q;
  38826. unsigned int sizeQ = 0;
  38827. int px, nx, py, ny, pz, nz;
  38828. bool is_px, is_nx, is_py, is_ny, is_pz, is_nz;
  38829. const bool is_3d = _depth>1;
  38830. // Find seed points and insert them in priority queue.
  38831. unsigned int nb_seeds = 0;
  38832. const T *ptrs = _data;
  38833. cimg_forXYZ(*this,x,y,z) if (*(ptrs++)) { // 3D version
  38834. if (nb_seeds>=seeds._width) seeds.resize(2*seeds._width,3,1,1,0);
  38835. seeds(nb_seeds,0) = x; seeds(nb_seeds,1) = y; seeds(nb_seeds++,2) = z;
  38836. px = x - 1; nx = x + 1;
  38837. py = y - 1; ny = y + 1;
  38838. pz = z - 1; nz = z + 1;
  38839. is_px = px>=0; is_nx = nx<width();
  38840. is_py = py>=0; is_ny = ny<height();
  38841. is_pz = pz>=0; is_nz = nz<depth();
  38842. _cimg_watershed_init(is_px,px,y,z);
  38843. _cimg_watershed_init(is_nx,nx,y,z);
  38844. _cimg_watershed_init(is_py,x,py,z);
  38845. _cimg_watershed_init(is_ny,x,ny,z);
  38846. if (is_3d) {
  38847. _cimg_watershed_init(is_pz,x,y,pz);
  38848. _cimg_watershed_init(is_nz,x,y,nz);
  38849. }
  38850. if (is_high_connectivity) {
  38851. _cimg_watershed_init(is_px && is_py,px,py,z);
  38852. _cimg_watershed_init(is_nx && is_py,nx,py,z);
  38853. _cimg_watershed_init(is_px && is_ny,px,ny,z);
  38854. _cimg_watershed_init(is_nx && is_ny,nx,ny,z);
  38855. if (is_3d) {
  38856. _cimg_watershed_init(is_px && is_pz,px,y,pz);
  38857. _cimg_watershed_init(is_nx && is_pz,nx,y,pz);
  38858. _cimg_watershed_init(is_px && is_nz,px,y,nz);
  38859. _cimg_watershed_init(is_nx && is_nz,nx,y,nz);
  38860. _cimg_watershed_init(is_py && is_pz,x,py,pz);
  38861. _cimg_watershed_init(is_ny && is_pz,x,ny,pz);
  38862. _cimg_watershed_init(is_py && is_nz,x,py,nz);
  38863. _cimg_watershed_init(is_ny && is_nz,x,ny,nz);
  38864. _cimg_watershed_init(is_px && is_py && is_pz,px,py,pz);
  38865. _cimg_watershed_init(is_nx && is_py && is_pz,nx,py,pz);
  38866. _cimg_watershed_init(is_px && is_ny && is_pz,px,ny,pz);
  38867. _cimg_watershed_init(is_nx && is_ny && is_pz,nx,ny,pz);
  38868. _cimg_watershed_init(is_px && is_py && is_nz,px,py,nz);
  38869. _cimg_watershed_init(is_nx && is_py && is_nz,nx,py,nz);
  38870. _cimg_watershed_init(is_px && is_ny && is_nz,px,ny,nz);
  38871. _cimg_watershed_init(is_nx && is_ny && is_nz,nx,ny,nz);
  38872. }
  38873. }
  38874. labels(x,y,z) = nb_seeds;
  38875. }
  38876. // Start watershed computation.
  38877. while (sizeQ) {
  38878. // Get and remove point with maximal priority from the queue.
  38879. const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
  38880. const unsigned int n = labels(x,y,z);
  38881. px = x - 1; nx = x + 1;
  38882. py = y - 1; ny = y + 1;
  38883. pz = z - 1; nz = z + 1;
  38884. is_px = px>=0; is_nx = nx<width();
  38885. is_py = py>=0; is_ny = ny<height();
  38886. is_pz = pz>=0; is_nz = nz<depth();
  38887. // Check labels of the neighbors.
  38888. Q._priority_queue_remove(sizeQ);
  38889. unsigned int xs, ys, zs, ns, nmin = 0;
  38890. float d, dmin = cimg::type<float>::inf();
  38891. T nlabel = (T)0;
  38892. _cimg_watershed_propagate(is_px,px,y,z);
  38893. _cimg_watershed_propagate(is_nx,nx,y,z);
  38894. _cimg_watershed_propagate(is_py,x,py,z);
  38895. _cimg_watershed_propagate(is_ny,x,ny,z);
  38896. if (is_3d) {
  38897. _cimg_watershed_propagate(is_pz,x,y,pz);
  38898. _cimg_watershed_propagate(is_nz,x,y,nz);
  38899. }
  38900. if (is_high_connectivity) {
  38901. _cimg_watershed_propagate(is_px && is_py,px,py,z);
  38902. _cimg_watershed_propagate(is_nx && is_py,nx,py,z);
  38903. _cimg_watershed_propagate(is_px && is_ny,px,ny,z);
  38904. _cimg_watershed_propagate(is_nx && is_ny,nx,ny,z);
  38905. if (is_3d) {
  38906. _cimg_watershed_propagate(is_px && is_pz,px,y,pz);
  38907. _cimg_watershed_propagate(is_nx && is_pz,nx,y,pz);
  38908. _cimg_watershed_propagate(is_px && is_nz,px,y,nz);
  38909. _cimg_watershed_propagate(is_nx && is_nz,nx,y,nz);
  38910. _cimg_watershed_propagate(is_py && is_pz,x,py,pz);
  38911. _cimg_watershed_propagate(is_ny && is_pz,x,ny,pz);
  38912. _cimg_watershed_propagate(is_py && is_nz,x,py,nz);
  38913. _cimg_watershed_propagate(is_ny && is_nz,x,ny,nz);
  38914. _cimg_watershed_propagate(is_px && is_py && is_pz,px,py,pz);
  38915. _cimg_watershed_propagate(is_nx && is_py && is_pz,nx,py,pz);
  38916. _cimg_watershed_propagate(is_px && is_ny && is_pz,px,ny,pz);
  38917. _cimg_watershed_propagate(is_nx && is_ny && is_pz,nx,ny,pz);
  38918. _cimg_watershed_propagate(is_px && is_py && is_nz,px,py,nz);
  38919. _cimg_watershed_propagate(is_nx && is_py && is_nz,nx,py,nz);
  38920. _cimg_watershed_propagate(is_px && is_ny && is_nz,px,ny,nz);
  38921. _cimg_watershed_propagate(is_nx && is_ny && is_nz,nx,ny,nz);
  38922. }
  38923. }
  38924. (*this)(x,y,z) = nlabel;
  38925. labels(x,y,z) = ++nmin;
  38926. }
  38927. return *this;
  38928. }
  38929. //! Compute watershed transform \newinstance.
  38930. template<typename t>
  38931. CImg<T> get_watershed(const CImg<t>& priority, const bool is_high_connectivity=false) const {
  38932. return (+*this).watershed(priority,is_high_connectivity);
  38933. }
  38934. // [internal] Insert/Remove items in priority queue, for watershed/distance transforms.
  38935. template<typename tq, typename tv>
  38936. bool _priority_queue_insert(CImg<tq>& is_queued, unsigned int& siz, const tv value,
  38937. const unsigned int x, const unsigned int y, const unsigned int z,
  38938. const unsigned int n=1) {
  38939. if (is_queued(x,y,z)) return false;
  38940. is_queued(x,y,z) = (tq)n;
  38941. if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); }
  38942. (*this)(siz - 1,0) = (T)value;
  38943. (*this)(siz - 1,1) = (T)x;
  38944. (*this)(siz - 1,2) = (T)y;
  38945. (*this)(siz - 1,3) = (T)z;
  38946. for (unsigned int pos = siz - 1, par = 0; pos && value>(tv)(*this)(par=(pos + 1)/2 - 1,0); pos = par) {
  38947. cimg::swap((*this)(pos,0),(*this)(par,0));
  38948. cimg::swap((*this)(pos,1),(*this)(par,1));
  38949. cimg::swap((*this)(pos,2),(*this)(par,2));
  38950. cimg::swap((*this)(pos,3),(*this)(par,3));
  38951. }
  38952. return true;
  38953. }
  38954. CImg<T>& _priority_queue_remove(unsigned int& siz) {
  38955. (*this)(0,0) = (*this)(--siz,0);
  38956. (*this)(0,1) = (*this)(siz,1);
  38957. (*this)(0,2) = (*this)(siz,2);
  38958. (*this)(0,3) = (*this)(siz,3);
  38959. const float value = (*this)(0,0);
  38960. unsigned int pos = 0, swap = 0;
  38961. do {
  38962. const unsigned int left = 2*pos + 1, right = left + 1;
  38963. if (right<siz && value<(*this)(right,0)) swap = (*this)(left,0)>(*this)(right,0)?left:right;
  38964. else if (left<siz && value<(*this)(left,0)) swap = left;
  38965. else break;
  38966. cimg::swap((*this)(pos,0),(*this)(swap,0));
  38967. cimg::swap((*this)(pos,1),(*this)(swap,1));
  38968. cimg::swap((*this)(pos,2),(*this)(swap,2));
  38969. cimg::swap((*this)(pos,3),(*this)(swap,3));
  38970. pos = swap;
  38971. } while (true);
  38972. return *this;
  38973. }
  38974. //! Apply recursive Deriche filter.
  38975. /**
  38976. \param sigma Standard deviation of the filter.
  38977. \param order Order of the filter. Can be <tt>{ 0=smooth-filter | 1=1st-derivative | 2=2nd-derivative }</tt>.
  38978. \param axis Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  38979. \param boundary_conditions Boundary conditions.
  38980. Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
  38981. **/
  38982. CImg<T>& deriche(const float sigma, const unsigned int order=0, const char axis='x',
  38983. const unsigned int boundary_conditions=1) {
  38984. #define _cimg_deriche_apply \
  38985. CImg<doubleT> Y(N); \
  38986. double *ptrY = Y._data, yb = 0, yp = 0; \
  38987. T xp = (T)0; \
  38988. if (boundary_conditions) { xp = *ptrX; yb = yp = (double)(coefp*xp); } \
  38989. for (int m = 0; m<N; ++m) { \
  38990. const T xc = *ptrX; ptrX+=off; \
  38991. const double yc = *(ptrY++) = (double)(a0*xc + a1*xp - b1*yp - b2*yb); \
  38992. xp = xc; yb = yp; yp = yc; \
  38993. } \
  38994. T xn = (T)0, xa = (T)0; \
  38995. double yn = 0, ya = 0; \
  38996. if (boundary_conditions) { xn = xa = *(ptrX - off); yn = ya = (double)coefn*xn; } \
  38997. for (int n = N - 1; n>=0; --n) { \
  38998. const T xc = *(ptrX-=off); \
  38999. const double yc = (double)(a2*xn + a3*xa - b1*yn - b2*ya); \
  39000. xa = xn; xn = xc; ya = yn; yn = yc; \
  39001. *ptrX = (T)(*(--ptrY)+yc); \
  39002. }
  39003. if (order>2)
  39004. throw CImgArgumentException(_cimg_instance
  39005. "deriche(): Invalid specified order '%d' "
  39006. "('order' can be { 0=smoothing | 1=1st-derivative | 2=2nd-derivative }).",
  39007. cimg_instance,
  39008. order);
  39009. const char naxis = cimg::lowercase(axis);
  39010. if (naxis!='x' && naxis!='y' && naxis!='z' && naxis!='c')
  39011. throw CImgArgumentException(_cimg_instance
  39012. "deriche(): Invalid specified axis '%c'.",
  39013. cimg_instance,
  39014. axis);
  39015. const double
  39016. nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:
  39017. naxis=='y'?_height:
  39018. naxis=='z'?_depth:_spectrum)/100,
  39019. nnsigma = nsigma<0.1f?0.1f:nsigma;
  39020. if (is_empty() || (nsigma<0.1f && !order)) return *this;
  39021. if (boundary_conditions>1) {
  39022. const int w = width(), h = height(), d = depth(), s = spectrum(), border = (int)cimg::round(1 + 3*nnsigma);
  39023. switch (naxis) {
  39024. case 'x' :
  39025. return draw_image(get_resize(w + 2*border,h,d,s,0,boundary_conditions,0.5).
  39026. deriche(nnsigma,order,naxis,1).columns(border,w - 1 + border));
  39027. case 'y' :
  39028. return draw_image(get_resize(w,h + 2*border,d,s,0,boundary_conditions,0,0.5).
  39029. deriche(nnsigma,order,naxis,1).rows(border,h - 1 + border));
  39030. case 'z' :
  39031. return draw_image(get_resize(w,h,d + 2*border,s,0,boundary_conditions,0,0,0.5).
  39032. deriche(nnsigma,order,naxis,1).slices(border,d - 1 + border));
  39033. default :
  39034. return draw_image(get_resize(w,h,d,s + 2*border,0,boundary_conditions,0,0,0,0.5).
  39035. deriche(nnsigma,order,naxis,1).channels(border,d - 1 + border));
  39036. }
  39037. }
  39038. const double
  39039. alpha = 1.695f/nnsigma,
  39040. ema = std::exp(-alpha),
  39041. ema2 = std::exp(-2*alpha),
  39042. b1 = -2*ema,
  39043. b2 = ema2;
  39044. double a0 = 0, a1 = 0, a2 = 0, a3 = 0, coefp = 0, coefn = 0;
  39045. switch (order) {
  39046. case 0 : {
  39047. const double k = (1-ema)*(1-ema)/(1 + 2*alpha*ema-ema2);
  39048. a0 = k;
  39049. a1 = k*(alpha - 1)*ema;
  39050. a2 = k*(alpha + 1)*ema;
  39051. a3 = -k*ema2;
  39052. } break;
  39053. case 1 : {
  39054. const double k = -(1-ema)*(1-ema)*(1-ema)/(2*(ema + 1)*ema);
  39055. a0 = a3 = 0;
  39056. a1 = k*ema;
  39057. a2 = -a1;
  39058. } break;
  39059. default : {
  39060. const double
  39061. ea = std::exp(-alpha),
  39062. k = -(ema2 - 1)/(2*alpha*ema),
  39063. kn = (-2*(-1 + 3*ea - 3*ea*ea + ea*ea*ea)/(3*ea + 1 + 3*ea*ea + ea*ea*ea));
  39064. a0 = kn;
  39065. a1 = -kn*(1 + k*alpha)*ema;
  39066. a2 = kn*(1 - k*alpha)*ema;
  39067. a3 = -kn*ema2;
  39068. } break;
  39069. }
  39070. coefp = (a0 + a1)/(1 + b1 + b2);
  39071. coefn = (a2 + a3)/(1 + b1 + b2);
  39072. switch (naxis) {
  39073. case 'x' : {
  39074. const int N = width();
  39075. const ulongT off = 1U;
  39076. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
  39077. _height*_depth*_spectrum>=16))
  39078. cimg_forYZC(*this,y,z,c) { T *ptrX = data(0,y,z,c); _cimg_deriche_apply; }
  39079. } break;
  39080. case 'y' : {
  39081. const int N = height();
  39082. const ulongT off = (ulongT)_width;
  39083. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
  39084. _height*_depth*_spectrum>=16))
  39085. cimg_forXZC(*this,x,z,c) { T *ptrX = data(x,0,z,c); _cimg_deriche_apply; }
  39086. } break;
  39087. case 'z' : {
  39088. const int N = depth();
  39089. const ulongT off = (ulongT)_width*_height;
  39090. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
  39091. _height*_depth*_spectrum>=16))
  39092. cimg_forXYC(*this,x,y,c) { T *ptrX = data(x,y,0,c); _cimg_deriche_apply; }
  39093. } break;
  39094. default : {
  39095. const int N = spectrum();
  39096. const ulongT off = (ulongT)_width*_height*_depth;
  39097. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
  39098. _height*_depth*_spectrum>=16))
  39099. cimg_forXYZ(*this,x,y,z) { T *ptrX = data(x,y,z,0); _cimg_deriche_apply; }
  39100. }
  39101. }
  39102. return *this;
  39103. }
  39104. //! Apply recursive Deriche filter \newinstance.
  39105. CImg<Tfloat> get_deriche(const float sigma, const unsigned int order=0, const char axis='x',
  39106. const unsigned int boundary_conditions=1) const {
  39107. return CImg<Tfloat>(*this,false).deriche(sigma,order,axis,boundary_conditions);
  39108. }
  39109. // [internal] Apply a recursive filter (used by CImg<T>::vanvliet()).
  39110. /*
  39111. \param ptr the pointer of the data
  39112. \param filter the coefficient of the filter in the following order [n,n - 1,n - 2,n - 3].
  39113. \param N size of the data
  39114. \param off the offset between two data point
  39115. \param order the order of the filter 0 (smoothing), 1st derivative, 2nd derivative, 3rd derivative
  39116. \param boundary_conditions Boundary conditions.
  39117. Can be <tt>{ 0=dirichlet | 1=neumann }</tt>.
  39118. \note Boundary condition using B. Triggs method (IEEE trans on Sig Proc 2005).
  39119. */
  39120. static void _cimg_recursive_apply(T *data, const double filter[], const int N, const ulongT off,
  39121. const unsigned int order, const bool boundary_conditions) {
  39122. double val[4] = {}; // res[n,n - 1,n - 2,n - 3,..] or res[n,n + 1,n + 2,n + 3,..]
  39123. const double
  39124. sumsq = filter[0], sum = sumsq * sumsq,
  39125. a1 = filter[1], a2 = filter[2], a3 = filter[3],
  39126. scaleM = 1. / ( (1. + a1 - a2 + a3) * (1. - a1 - a2 - a3) * (1. + a2 + (a1 - a3) * a3) );
  39127. double M[9]; // Triggs matrix
  39128. M[0] = scaleM * (-a3 * a1 + 1. - a3 * a3 - a2);
  39129. M[1] = scaleM * (a3 + a1) * (a2 + a3 * a1);
  39130. M[2] = scaleM * a3 * (a1 + a3 * a2);
  39131. M[3] = scaleM * (a1 + a3 * a2);
  39132. M[4] = -scaleM * (a2 - 1.) * (a2 + a3 * a1);
  39133. M[5] = -scaleM * a3 * (a3 * a1 + a3 * a3 + a2 - 1.);
  39134. M[6] = scaleM * (a3 * a1 + a2 + a1 * a1 - a2 * a2);
  39135. M[7] = scaleM * (a1 * a2 + a3 * a2 * a2 - a1 * a3 * a3 - a3 * a3 * a3 - a3 * a2 + a3);
  39136. M[8] = scaleM * a3 * (a1 + a3 * a2);
  39137. switch (order) {
  39138. case 0 : {
  39139. const double iplus = (boundary_conditions?data[(N - 1)*off]:(T)0);
  39140. for (int pass = 0; pass<2; ++pass) {
  39141. if (!pass) {
  39142. for (int k = 1; k<4; ++k) val[k] = (boundary_conditions?*data/sumsq:0);
  39143. } else {
  39144. // Apply Triggs boundary conditions
  39145. const double
  39146. uplus = iplus/(1. - a1 - a2 - a3), vplus = uplus/(1. - a1 - a2 - a3),
  39147. unp = val[1] - uplus, unp1 = val[2] - uplus, unp2 = val[3] - uplus;
  39148. val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2 + vplus) * sum;
  39149. val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2 + vplus) * sum;
  39150. val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2 + vplus) * sum;
  39151. *data = (T)val[0];
  39152. data -= off;
  39153. for (int k = 3; k>0; --k) val[k] = val[k - 1];
  39154. }
  39155. for (int n = pass; n<N; ++n) {
  39156. val[0] = (*data);
  39157. if (pass) val[0] *= sum;
  39158. for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k];
  39159. *data = (T)val[0];
  39160. if (!pass) data += off; else data -= off;
  39161. for (int k = 3; k>0; --k) val[k] = val[k - 1];
  39162. }
  39163. if (!pass) data -= off;
  39164. }
  39165. } break;
  39166. case 1 : {
  39167. double x[3]; // [front,center,back]
  39168. for (int pass = 0; pass<2; ++pass) {
  39169. if (!pass) {
  39170. for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0);
  39171. for (int k = 0; k<4; ++k) val[k] = 0;
  39172. } else {
  39173. // Apply Triggs boundary conditions
  39174. const double
  39175. unp = val[1], unp1 = val[2], unp2 = val[3];
  39176. val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum;
  39177. val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum;
  39178. val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum;
  39179. *data = (T)val[0];
  39180. data -= off;
  39181. for (int k = 3; k>0; --k) val[k] = val[k - 1];
  39182. }
  39183. for (int n = pass; n<N - 1; ++n) {
  39184. if (!pass) {
  39185. x[0] = *(data + off);
  39186. val[0] = 0.5f * (x[0] - x[2]);
  39187. } else val[0] = (*data) * sum;
  39188. for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k];
  39189. *data = (T)val[0];
  39190. if (!pass) {
  39191. data += off;
  39192. for (int k = 2; k>0; --k) x[k] = x[k - 1];
  39193. } else { data-=off;}
  39194. for (int k = 3; k>0; --k) val[k] = val[k - 1];
  39195. }
  39196. *data = (T)0;
  39197. }
  39198. } break;
  39199. case 2: {
  39200. double x[3]; // [front,center,back]
  39201. for (int pass = 0; pass<2; ++pass) {
  39202. if (!pass) {
  39203. for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0);
  39204. for (int k = 0; k<4; ++k) val[k] = 0;
  39205. } else {
  39206. // Apply Triggs boundary conditions
  39207. const double
  39208. unp = val[1], unp1 = val[2], unp2 = val[3];
  39209. val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum;
  39210. val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum;
  39211. val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum;
  39212. *data = (T)val[0];
  39213. data -= off;
  39214. for (int k = 3; k>0; --k) val[k] = val[k - 1];
  39215. }
  39216. for (int n = pass; n<N - 1; ++n) {
  39217. if (!pass) { x[0] = *(data + off); val[0] = (x[1] - x[2]); }
  39218. else { x[0] = *(data - off); val[0] = (x[2] - x[1]) * sum; }
  39219. for (int k = 1; k<4; ++k) val[0] += val[k]*filter[k];
  39220. *data = (T)val[0];
  39221. if (!pass) data += off; else data -= off;
  39222. for (int k = 2; k>0; --k) x[k] = x[k - 1];
  39223. for (int k = 3; k>0; --k) val[k] = val[k - 1];
  39224. }
  39225. *data = (T)0;
  39226. }
  39227. } break;
  39228. case 3: {
  39229. double x[3]; // [front,center,back]
  39230. for (int pass = 0; pass<2; ++pass) {
  39231. if (!pass) {
  39232. for (int k = 0; k<3; ++k) x[k] = (boundary_conditions?*data:(T)0);
  39233. for (int k = 0; k<4; ++k) val[k] = 0;
  39234. } else {
  39235. // Apply Triggs boundary conditions
  39236. const double
  39237. unp = val[1], unp1 = val[2], unp2 = val[3];
  39238. val[0] = (M[0] * unp + M[1] * unp1 + M[2] * unp2) * sum;
  39239. val[1] = (M[3] * unp + M[4] * unp1 + M[5] * unp2) * sum;
  39240. val[2] = (M[6] * unp + M[7] * unp1 + M[8] * unp2) * sum;
  39241. *data = (T)val[0];
  39242. data -= off;
  39243. for (int k = 3; k>0; --k) val[k] = val[k - 1];
  39244. }
  39245. for (int n = pass; n<N - 1; ++n) {
  39246. if (!pass) { x[0] = *(data + off); val[0] = (x[0] - 2*x[1] + x[2]); }
  39247. else { x[0] = *(data - off); val[0] = 0.5f * (x[2] - x[0]) * sum; }
  39248. for (int k = 1; k<4; ++k) val[0] += val[k] * filter[k];
  39249. *data = (T)val[0];
  39250. if (!pass) data += off; else data -= off;
  39251. for (int k = 2; k>0; --k) x[k] = x[k - 1];
  39252. for (int k = 3; k>0; --k) val[k] = val[k - 1];
  39253. }
  39254. *data = (T)0;
  39255. }
  39256. } break;
  39257. }
  39258. }
  39259. //! Van Vliet recursive Gaussian filter.
  39260. /**
  39261. \param sigma standard deviation of the Gaussian filter
  39262. \param order the order of the filter 0,1,2,3
  39263. \param axis Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  39264. \param boundary_conditions Boundary conditions.
  39265. Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
  39266. \note dirichlet boundary condition has a strange behavior
  39267. I.T. Young, L.J. van Vliet, M. van Ginkel, Recursive Gabor filtering.
  39268. IEEE Trans. Sig. Proc., vol. 50, pp. 2799-2805, 2002.
  39269. (this is an improvement over Young-Van Vliet, Sig. Proc. 44, 1995)
  39270. Boundary conditions (only for order 0) using Triggs matrix, from
  39271. B. Triggs and M. Sdika. Boundary conditions for Young-van Vliet
  39272. recursive filtering. IEEE Trans. Signal Processing,
  39273. vol. 54, pp. 2365-2367, 2006.
  39274. **/
  39275. CImg<T>& vanvliet(const float sigma, const unsigned int order, const char axis='x',
  39276. const unsigned int boundary_conditions=1) {
  39277. if (order>2)
  39278. throw CImgArgumentException(_cimg_instance
  39279. "deriche(): Invalid specified order '%d' "
  39280. "('order' can be { 0=smoothing | 1=1st-derivative | 2=2nd-derivative }).",
  39281. cimg_instance,
  39282. order);
  39283. const char naxis = cimg::lowercase(axis);
  39284. if (naxis!='x' && naxis!='y' && naxis!='z' && naxis!='c')
  39285. throw CImgArgumentException(_cimg_instance
  39286. "deriche(): Invalid specified axis '%c'.",
  39287. cimg_instance,
  39288. axis);
  39289. const double
  39290. nsigma = sigma>=0?sigma:-sigma*(naxis=='x'?_width:
  39291. naxis=='y'?_height:
  39292. naxis=='z'?_depth:_spectrum)/100,
  39293. nnsigma = nsigma<0.5f?0.5f:nsigma;
  39294. if (is_empty() || (nsigma<0.1f && !order)) return *this;
  39295. if (nsigma<0.5f) return deriche(nsigma,order,axis,boundary_conditions);
  39296. if (!cimg::type<T>::is_float())
  39297. return CImg<Tfloat>(*this,false).vanvliet(sigma,order,axis,boundary_conditions).move_to(*this);
  39298. if (boundary_conditions>1) {
  39299. const int w = width(), h = height(), d = depth(), s = spectrum(), border = (int)cimg::round(1 + 3*nnsigma);
  39300. switch (naxis) {
  39301. case 'x' :
  39302. return draw_image(get_resize(w + 2*border,h,d,s,0,boundary_conditions,0.5).
  39303. vanvliet(nnsigma,order,naxis,1).columns(border,w - 1 + border));
  39304. case 'y' :
  39305. return draw_image(get_resize(w,h + 2*border,d,s,0,boundary_conditions,0,0.5).
  39306. vanvliet(nnsigma,order,naxis,1).rows(border,h - 1 + border));
  39307. case 'z' :
  39308. return draw_image(get_resize(w,h,d + 2*border,s,0,boundary_conditions,0,0,0.5).
  39309. vanvliet(nnsigma,order,naxis,1).slices(border,d - 1 + border));
  39310. default :
  39311. return draw_image(get_resize(w,h,d,s + 2*border,0,boundary_conditions,0,0,0,0.5).
  39312. vanvliet(nnsigma,order,naxis,1).channels(border,d - 1 + border));
  39313. }
  39314. }
  39315. const double
  39316. m0 = 1.16680, m1 = 1.10783, m2 = 1.40586,
  39317. m1sq = m1 * m1, m2sq = m2 * m2,
  39318. q = (nnsigma<3.556?-0.2568 + 0.5784*nnsigma + 0.0561*nnsigma*nnsigma:2.5091 + 0.9804*(nnsigma - 3.556)),
  39319. qsq = q * q,
  39320. scale = (m0 + q) * (m1sq + m2sq + 2 * m1 * q + qsq),
  39321. b1 = -q * (2 * m0 * m1 + m1sq + m2sq + (2 * m0 + 4 * m1) * q + 3 * qsq) / scale,
  39322. b2 = qsq * (m0 + 2 * m1 + 3 * q) / scale,
  39323. b3 = -qsq * q / scale,
  39324. B = ( m0 * (m1sq + m2sq) ) / scale;
  39325. double filter[4];
  39326. filter[0] = B; filter[1] = -b1; filter[2] = -b2; filter[3] = -b3;
  39327. switch (naxis) {
  39328. case 'x' : {
  39329. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
  39330. _height*_depth*_spectrum>=16))
  39331. cimg_forYZC(*this,y,z,c)
  39332. _cimg_recursive_apply(data(0,y,z,c),filter,_width,1U,order,boundary_conditions);
  39333. } break;
  39334. case 'y' : {
  39335. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
  39336. _height*_depth*_spectrum>=16))
  39337. cimg_forXZC(*this,x,z,c)
  39338. _cimg_recursive_apply(data(x,0,z,c),filter,_height,(ulongT)_width,order,boundary_conditions);
  39339. } break;
  39340. case 'z' : {
  39341. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
  39342. _height*_depth*_spectrum>=16))
  39343. cimg_forXYC(*this,x,y,c)
  39344. _cimg_recursive_apply(data(x,y,0,c),filter,_depth,(ulongT)_width*_height,
  39345. order,boundary_conditions);
  39346. } break;
  39347. default : {
  39348. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
  39349. _height*_depth*_spectrum>=16))
  39350. cimg_forXYZ(*this,x,y,z)
  39351. _cimg_recursive_apply(data(x,y,z,0),filter,_spectrum,(ulongT)_width*_height*_depth,
  39352. order,boundary_conditions);
  39353. }
  39354. }
  39355. return *this;
  39356. }
  39357. //! Blur image using Van Vliet recursive Gaussian filter. \newinstance.
  39358. CImg<Tfloat> get_vanvliet(const float sigma, const unsigned int order, const char axis='x',
  39359. const unsigned int boundary_conditions=1) const {
  39360. return CImg<Tfloat>(*this,false).vanvliet(sigma,order,axis,boundary_conditions);
  39361. }
  39362. //! Blur image.
  39363. /**
  39364. \param sigma_x Standard deviation of the blur, along the X-axis.
  39365. \param sigma_y Standard deviation of the blur, along the Y-axis.
  39366. \param sigma_z Standard deviation of the blur, along the Z-axis.
  39367. \param boundary_conditions Boundary conditions.
  39368. Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
  39369. \param is_gaussian Tells if the blur uses a gaussian (\c true) or quasi-gaussian (\c false) kernel.
  39370. \note
  39371. - The blur is computed as a 0-order Vanvliet (gaussian) or Deriche filter (quasi-gaussian).
  39372. - This is a recursive algorithm, not depending on the values of the standard deviations.
  39373. \see deriche(), vanvliet().
  39374. **/
  39375. CImg<T>& blur(const float sigma_x, const float sigma_y, const float sigma_z,
  39376. const unsigned int boundary_conditions=1, const bool is_gaussian=true) {
  39377. if (is_empty()) return *this;
  39378. if (is_gaussian) {
  39379. if (_width>1) vanvliet(sigma_x,0,'x',boundary_conditions);
  39380. if (_height>1) vanvliet(sigma_y,0,'y',boundary_conditions);
  39381. if (_depth>1) vanvliet(sigma_z,0,'z',boundary_conditions);
  39382. } else {
  39383. if (_width>1) deriche(sigma_x,0,'x',boundary_conditions);
  39384. if (_height>1) deriche(sigma_y,0,'y',boundary_conditions);
  39385. if (_depth>1) deriche(sigma_z,0,'z',boundary_conditions);
  39386. }
  39387. return *this;
  39388. }
  39389. //! Blur image \newinstance.
  39390. CImg<Tfloat> get_blur(const float sigma_x, const float sigma_y, const float sigma_z,
  39391. const unsigned int boundary_conditions=1, const bool is_gaussian=true) const {
  39392. return CImg<Tfloat>(*this,false).blur(sigma_x,sigma_y,sigma_z,boundary_conditions,is_gaussian);
  39393. }
  39394. //! Blur image isotropically.
  39395. /**
  39396. \param sigma Standard deviation of the blur.
  39397. \param boundary_conditions Boundary conditions.
  39398. Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.a
  39399. \param is_gaussian Use a gaussian kernel (VanVliet) is set, a quasi-gaussian (Deriche) otherwise.
  39400. \see deriche(), vanvliet().
  39401. **/
  39402. CImg<T>& blur(const float sigma, const unsigned int boundary_conditions=1, const bool is_gaussian=true) {
  39403. const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100;
  39404. return blur(nsigma,nsigma,nsigma,boundary_conditions,is_gaussian);
  39405. }
  39406. //! Blur image isotropically \newinstance.
  39407. CImg<Tfloat> get_blur(const float sigma, const unsigned int boundary_conditions=1,
  39408. const bool is_gaussian=true) const {
  39409. return CImg<Tfloat>(*this,false).blur(sigma,boundary_conditions,is_gaussian);
  39410. }
  39411. //! Blur image anisotropically, directed by a field of diffusion tensors.
  39412. /**
  39413. \param G Field of square roots of diffusion tensors/vectors used to drive the smoothing.
  39414. \param amplitude Amplitude of the smoothing.
  39415. \param dl Spatial discretization.
  39416. \param da Angular discretization.
  39417. \param gauss_prec Precision of the diffusion process.
  39418. \param interpolation_type Interpolation scheme.
  39419. Can be <tt>{ 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }</tt>.
  39420. \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
  39421. **/
  39422. template<typename t>
  39423. CImg<T>& blur_anisotropic(const CImg<t>& G,
  39424. const float amplitude=60, const float dl=0.8f, const float da=30,
  39425. const float gauss_prec=2, const unsigned int interpolation_type=0,
  39426. const bool is_fast_approx=1) {
  39427. // Check arguments and init variables
  39428. if (!is_sameXYZ(G) || (G._spectrum!=3 && G._spectrum!=6))
  39429. throw CImgArgumentException(_cimg_instance
  39430. "blur_anisotropic(): Invalid specified diffusion tensor field (%u,%u,%u,%u,%p).",
  39431. cimg_instance,
  39432. G._width,G._height,G._depth,G._spectrum,G._data);
  39433. if (is_empty() || dl<0) return *this;
  39434. const float namplitude = amplitude>=0?amplitude:-amplitude*cimg::max(_width,_height,_depth)/100;
  39435. unsigned int iamplitude = cimg::round(namplitude);
  39436. const bool is_3d = (G._spectrum==6);
  39437. T val_min, val_max = max_min(val_min);
  39438. _cimg_abort_init_openmp;
  39439. cimg_abort_init;
  39440. if (da<=0) { // Iterated oriented Laplacians
  39441. CImg<Tfloat> velocity(_width,_height,_depth,_spectrum);
  39442. for (unsigned int iteration = 0; iteration<iamplitude; ++iteration) {
  39443. Tfloat *ptrd = velocity._data, veloc_max = 0;
  39444. if (is_3d) // 3D version
  39445. cimg_forC(*this,c) {
  39446. cimg_abort_test;
  39447. CImg_3x3x3(I,Tfloat);
  39448. cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
  39449. const Tfloat
  39450. ixx = Incc + Ipcc - 2*Iccc,
  39451. ixy = (Innc + Ippc - Inpc - Ipnc)/4,
  39452. ixz = (Incn + Ipcp - Incp - Ipcn)/4,
  39453. iyy = Icnc + Icpc - 2*Iccc,
  39454. iyz = (Icnn + Icpp - Icnp - Icpn)/4,
  39455. izz = Iccn + Iccp - 2*Iccc,
  39456. veloc = (Tfloat)(G(x,y,z,0)*ixx + 2*G(x,y,z,1)*ixy + 2*G(x,y,z,2)*ixz +
  39457. G(x,y,z,3)*iyy + 2*G(x,y,z,4)*iyz + G(x,y,z,5)*izz);
  39458. *(ptrd++) = veloc;
  39459. if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
  39460. }
  39461. }
  39462. else // 2D version
  39463. cimg_forZC(*this,z,c) {
  39464. cimg_abort_test;
  39465. CImg_3x3(I,Tfloat);
  39466. cimg_for3x3(*this,x,y,z,c,I,Tfloat) {
  39467. const Tfloat
  39468. ixx = Inc + Ipc - 2*Icc,
  39469. ixy = (Inn + Ipp - Inp - Ipn)/4,
  39470. iyy = Icn + Icp - 2*Icc,
  39471. veloc = (Tfloat)(G(x,y,0,0)*ixx + 2*G(x,y,0,1)*ixy + G(x,y,0,2)*iyy);
  39472. *(ptrd++) = veloc;
  39473. if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
  39474. }
  39475. }
  39476. if (veloc_max>0) *this+=(velocity*=dl/veloc_max);
  39477. }
  39478. } else { // LIC-based smoothing
  39479. const ulongT whd = (ulongT)_width*_height*_depth;
  39480. const float sqrt2amplitude = (float)std::sqrt(2*namplitude);
  39481. const int dx1 = width() - 1, dy1 = height() - 1, dz1 = depth() - 1;
  39482. CImg<Tfloat> res(_width,_height,_depth,_spectrum,0), W(_width,_height,_depth,is_3d?4:3), val(_spectrum,1,1,1,0);
  39483. int N = 0;
  39484. if (is_3d) { // 3D version
  39485. for (float phi = cimg::mod(180.f,da)/2.f; phi<=180; phi+=da) {
  39486. const float phir = (float)(phi*cimg::PI/180), datmp = (float)(da/std::cos(phir)),
  39487. da2 = datmp<1?360.f:datmp;
  39488. for (float theta = 0; theta<360; (theta+=da2),++N) {
  39489. const float
  39490. thetar = (float)(theta*cimg::PI/180),
  39491. vx = (float)(std::cos(thetar)*std::cos(phir)),
  39492. vy = (float)(std::sin(thetar)*std::cos(phir)),
  39493. vz = (float)std::sin(phir);
  39494. const t
  39495. *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2),
  39496. *pd = G.data(0,0,0,3), *pe = G.data(0,0,0,4), *pf = G.data(0,0,0,5);
  39497. 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);
  39498. cimg_forXYZ(G,xg,yg,zg) {
  39499. const t a = *(pa++), b = *(pb++), c = *(pc++), d = *(pd++), e = *(pe++), f = *(pf++);
  39500. const float
  39501. u = (float)(a*vx + b*vy + c*vz),
  39502. v = (float)(b*vx + d*vy + e*vz),
  39503. w = (float)(c*vx + e*vy + f*vz),
  39504. n = 1e-5f + cimg::hypot(u,v,w),
  39505. dln = dl/n;
  39506. *(pd0++) = (Tfloat)(u*dln);
  39507. *(pd1++) = (Tfloat)(v*dln);
  39508. *(pd2++) = (Tfloat)(w*dln);
  39509. *(pd3++) = (Tfloat)n;
  39510. }
  39511. cimg_abort_test;
  39512. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
  39513. cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && _height*_depth>=2)
  39514. firstprivate(val))
  39515. cimg_forYZ(*this,y,z) _cimg_abort_try_openmp2 {
  39516. cimg_abort_test2;
  39517. cimg_forX(*this,x) {
  39518. val.fill(0);
  39519. const float
  39520. n = (float)W(x,y,z,3),
  39521. fsigma = (float)(n*sqrt2amplitude),
  39522. fsigma2 = 2*fsigma*fsigma,
  39523. length = gauss_prec*fsigma;
  39524. float
  39525. S = 0,
  39526. X = (float)x,
  39527. Y = (float)y,
  39528. Z = (float)z;
  39529. switch (interpolation_type) {
  39530. case 0 : { // Nearest neighbor
  39531. for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
  39532. const int
  39533. cx = (int)(X + 0.5f),
  39534. cy = (int)(Y + 0.5f),
  39535. cz = (int)(Z + 0.5f);
  39536. const float
  39537. u = (float)W(cx,cy,cz,0),
  39538. v = (float)W(cx,cy,cz,1),
  39539. w = (float)W(cx,cy,cz,2);
  39540. if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,cz,c); ++S; }
  39541. else {
  39542. const float coef = (float)std::exp(-l*l/fsigma2);
  39543. cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,cz,c));
  39544. S+=coef;
  39545. }
  39546. X+=u; Y+=v; Z+=w;
  39547. }
  39548. } break;
  39549. case 1 : { // Linear interpolation
  39550. for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
  39551. const float
  39552. u = (float)(W._linear_atXYZ(X,Y,Z,0)),
  39553. v = (float)(W._linear_atXYZ(X,Y,Z,1)),
  39554. w = (float)(W._linear_atXYZ(X,Y,Z,2));
  39555. if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
  39556. else {
  39557. const float coef = (float)std::exp(-l*l/fsigma2);
  39558. cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
  39559. S+=coef;
  39560. }
  39561. X+=u; Y+=v; Z+=w;
  39562. }
  39563. } break;
  39564. default : { // 2nd order Runge Kutta
  39565. for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1 && Z>=0 && Z<=dz1; l+=dl) {
  39566. const float
  39567. u0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,0)),
  39568. v0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,1)),
  39569. w0 = (float)(0.5f*W._linear_atXYZ(X,Y,Z,2)),
  39570. u = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,0)),
  39571. v = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,1)),
  39572. w = (float)(W._linear_atXYZ(X + u0,Y + v0,Z + w0,2));
  39573. if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXYZ(X,Y,Z,c); ++S; }
  39574. else {
  39575. const float coef = (float)std::exp(-l*l/fsigma2);
  39576. cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXYZ(X,Y,Z,c));
  39577. S+=coef;
  39578. }
  39579. X+=u; Y+=v; Z+=w;
  39580. }
  39581. } break;
  39582. }
  39583. Tfloat *ptrd = res.data(x,y,z);
  39584. if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; }
  39585. else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,z,c)); ptrd+=whd; }
  39586. }
  39587. } _cimg_abort_catch_openmp2
  39588. }
  39589. }
  39590. } else { // 2D LIC algorithm
  39591. for (float theta = cimg::mod(360.f,da)/2.f; theta<360; (theta+=da),++N) {
  39592. const float thetar = (float)(theta*cimg::PI/180),
  39593. vx = (float)(std::cos(thetar)), vy = (float)(std::sin(thetar));
  39594. const t *pa = G.data(0,0,0,0), *pb = G.data(0,0,0,1), *pc = G.data(0,0,0,2);
  39595. Tfloat *pd0 = W.data(0,0,0,0), *pd1 = W.data(0,0,0,1), *pd2 = W.data(0,0,0,2);
  39596. cimg_forXY(G,xg,yg) {
  39597. const t a = *(pa++), b = *(pb++), c = *(pc++);
  39598. const float
  39599. u = (float)(a*vx + b*vy),
  39600. v = (float)(b*vx + c*vy),
  39601. n = std::max(1e-5f,cimg::hypot(u,v)),
  39602. dln = dl/n;
  39603. *(pd0++) = (Tfloat)(u*dln);
  39604. *(pd1++) = (Tfloat)(v*dln);
  39605. *(pd2++) = (Tfloat)n;
  39606. }
  39607. cimg_abort_test;
  39608. cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 && _height>=2)
  39609. firstprivate(val))
  39610. cimg_forY(*this,y) _cimg_abort_try_openmp2 {
  39611. cimg_abort_test2;
  39612. cimg_forX(*this,x) {
  39613. val.fill(0);
  39614. const float
  39615. n = (float)W(x,y,0,2),
  39616. fsigma = (float)(n*sqrt2amplitude),
  39617. fsigma2 = 2*fsigma*fsigma,
  39618. length = gauss_prec*fsigma;
  39619. float
  39620. S = 0,
  39621. X = (float)x,
  39622. Y = (float)y;
  39623. switch (interpolation_type) {
  39624. case 0 : { // Nearest-neighbor
  39625. for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
  39626. const int
  39627. cx = (int)(X + 0.5f),
  39628. cy = (int)(Y + 0.5f);
  39629. const float
  39630. u = (float)W(cx,cy,0,0),
  39631. v = (float)W(cx,cy,0,1);
  39632. if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)(*this)(cx,cy,0,c); ++S; }
  39633. else {
  39634. const float coef = (float)std::exp(-l*l/fsigma2);
  39635. cimg_forC(*this,c) val[c]+=(Tfloat)(coef*(*this)(cx,cy,0,c));
  39636. S+=coef;
  39637. }
  39638. X+=u; Y+=v;
  39639. }
  39640. } break;
  39641. case 1 : { // Linear interpolation
  39642. for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
  39643. const float
  39644. u = (float)(W._linear_atXY(X,Y,0,0)),
  39645. v = (float)(W._linear_atXY(X,Y,0,1));
  39646. if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
  39647. else {
  39648. const float coef = (float)std::exp(-l*l/fsigma2);
  39649. cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
  39650. S+=coef;
  39651. }
  39652. X+=u; Y+=v;
  39653. }
  39654. } break;
  39655. default : { // 2nd-order Runge-kutta interpolation
  39656. for (float l = 0; l<length && X>=0 && X<=dx1 && Y>=0 && Y<=dy1; l+=dl) {
  39657. const float
  39658. u0 = (float)(0.5f*W._linear_atXY(X,Y,0,0)),
  39659. v0 = (float)(0.5f*W._linear_atXY(X,Y,0,1)),
  39660. u = (float)(W._linear_atXY(X + u0,Y + v0,0,0)),
  39661. v = (float)(W._linear_atXY(X + u0,Y + v0,0,1));
  39662. if (is_fast_approx) { cimg_forC(*this,c) val[c]+=(Tfloat)_linear_atXY(X,Y,0,c); ++S; }
  39663. else {
  39664. const float coef = (float)std::exp(-l*l/fsigma2);
  39665. cimg_forC(*this,c) val[c]+=(Tfloat)(coef*_linear_atXY(X,Y,0,c));
  39666. S+=coef;
  39667. }
  39668. X+=u; Y+=v;
  39669. }
  39670. }
  39671. }
  39672. Tfloat *ptrd = res.data(x,y);
  39673. if (S>0) cimg_forC(res,c) { *ptrd+=val[c]/S; ptrd+=whd; }
  39674. else cimg_forC(res,c) { *ptrd+=(Tfloat)((*this)(x,y,0,c)); ptrd+=whd; }
  39675. }
  39676. } _cimg_abort_catch_openmp2
  39677. }
  39678. }
  39679. const Tfloat *ptrs = res._data;
  39680. cimg_for(*this,ptrd,T) {
  39681. const Tfloat _val = *(ptrs++)/N;
  39682. *ptrd = _val<val_min?val_min:(_val>val_max?val_max:(T)_val);
  39683. }
  39684. }
  39685. cimg_abort_test;
  39686. return *this;
  39687. }
  39688. //! Blur image anisotropically, directed by a field of diffusion tensors \newinstance.
  39689. template<typename t>
  39690. CImg<Tfloat> get_blur_anisotropic(const CImg<t>& G,
  39691. const float amplitude=60, const float dl=0.8f, const float da=30,
  39692. const float gauss_prec=2, const unsigned int interpolation_type=0,
  39693. const bool is_fast_approx=true) const {
  39694. return CImg<Tfloat>(*this,false).blur_anisotropic(G,amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx);
  39695. }
  39696. //! Blur image anisotropically, in an edge-preserving way.
  39697. /**
  39698. \param amplitude Amplitude of the smoothing.
  39699. \param sharpness Sharpness.
  39700. \param anisotropy Anisotropy.
  39701. \param alpha Standard deviation of the gradient blur.
  39702. \param sigma Standard deviation of the structure tensor blur.
  39703. \param dl Spatial discretization.
  39704. \param da Angular discretization.
  39705. \param gauss_prec Precision of the diffusion process.
  39706. \param interpolation_type Interpolation scheme.
  39707. Can be <tt>{ 0=nearest-neighbor | 1=linear | 2=Runge-Kutta }</tt>.
  39708. \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
  39709. **/
  39710. CImg<T>& blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
  39711. const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f, const float da=30,
  39712. const float gauss_prec=2, const unsigned int interpolation_type=0,
  39713. const bool is_fast_approx=true) {
  39714. const float nalpha = alpha>=0?alpha:-alpha*cimg::max(_width,_height,_depth)/100;
  39715. const float nsigma = sigma>=0?sigma:-sigma*cimg::max(_width,_height,_depth)/100;
  39716. return blur_anisotropic(get_diffusion_tensors(sharpness,anisotropy,nalpha,nsigma,interpolation_type!=3),
  39717. amplitude,dl,da,gauss_prec,interpolation_type,is_fast_approx);
  39718. }
  39719. //! Blur image anisotropically, in an edge-preserving way \newinstance.
  39720. CImg<Tfloat> get_blur_anisotropic(const float amplitude, const float sharpness=0.7f, const float anisotropy=0.6f,
  39721. const float alpha=0.6f, const float sigma=1.1f, const float dl=0.8f,
  39722. const float da=30, const float gauss_prec=2,
  39723. const unsigned int interpolation_type=0,
  39724. const bool is_fast_approx=true) const {
  39725. return CImg<Tfloat>(*this,false).blur_anisotropic(amplitude,sharpness,anisotropy,alpha,sigma,dl,da,gauss_prec,
  39726. interpolation_type,is_fast_approx);
  39727. }
  39728. //! Blur image, with the joint bilateral filter.
  39729. /**
  39730. \param guide Image used to model the smoothing weights.
  39731. \param sigma_x Amount of blur along the X-axis.
  39732. \param sigma_y Amount of blur along the Y-axis.
  39733. \param sigma_z Amount of blur along the Z-axis.
  39734. \param sigma_r Amount of blur along the value axis.
  39735. \param sampling_x Amount of downsampling along the X-axis used for the approximation.
  39736. Defaults (0) to sigma_x.
  39737. \param sampling_y Amount of downsampling along the Y-axis used for the approximation.
  39738. Defaults (0) to sigma_y.
  39739. \param sampling_z Amount of downsampling along the Z-axis used for the approximation.
  39740. Defaults (0) to sigma_z.
  39741. \param sampling_r Amount of downsampling along the value axis used for the approximation.
  39742. Defaults (0) to sigma_r.
  39743. \note This algorithm uses the optimisation technique proposed by S. Paris and F. Durand, in ECCV'2006
  39744. (extended for 3D volumetric images).
  39745. It is based on the reference implementation http://people.csail.mit.edu/jiawen/software/bilateralFilter.m
  39746. **/
  39747. template<typename t>
  39748. CImg<T>& blur_bilateral(const CImg<t>& guide,
  39749. const float sigma_x, const float sigma_y,
  39750. const float sigma_z, const float sigma_r,
  39751. const float sampling_x, const float sampling_y,
  39752. const float sampling_z, const float sampling_r) {
  39753. if (!is_sameXYZ(guide))
  39754. throw CImgArgumentException(_cimg_instance
  39755. "blur_bilateral(): Invalid size for specified guide image (%u,%u,%u,%u,%p).",
  39756. cimg_instance,
  39757. guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
  39758. if (is_empty() || (!sigma_x && !sigma_y && !sigma_z)) return *this;
  39759. T edge_min, edge_max = guide.max_min(edge_min);
  39760. if (edge_min==edge_max) return blur(sigma_x,sigma_y,sigma_z);
  39761. const float
  39762. edge_delta = (float)(edge_max - edge_min),
  39763. _sigma_x = sigma_x>=0?sigma_x:-sigma_x*_width/100,
  39764. _sigma_y = sigma_y>=0?sigma_y:-sigma_y*_height/100,
  39765. _sigma_z = sigma_z>=0?sigma_z:-sigma_z*_depth/100,
  39766. _sigma_r = sigma_r>=0?sigma_r:-sigma_r*edge_delta/100,
  39767. _sampling_x = sampling_x?sampling_x:std::max(_sigma_x,1.f),
  39768. _sampling_y = sampling_y?sampling_y:std::max(_sigma_y,1.f),
  39769. _sampling_z = sampling_z?sampling_z:std::max(_sigma_z,1.f),
  39770. _sampling_r = sampling_r?sampling_r:std::max(_sigma_r,edge_delta/256),
  39771. derived_sigma_x = _sigma_x / _sampling_x,
  39772. derived_sigma_y = _sigma_y / _sampling_y,
  39773. derived_sigma_z = _sigma_z / _sampling_z,
  39774. derived_sigma_r = _sigma_r / _sampling_r;
  39775. const int
  39776. padding_x = (int)(2*derived_sigma_x) + 1,
  39777. padding_y = (int)(2*derived_sigma_y) + 1,
  39778. padding_z = (int)(2*derived_sigma_z) + 1,
  39779. padding_r = (int)(2*derived_sigma_r) + 1;
  39780. const unsigned int
  39781. bx = (unsigned int)((_width - 1)/_sampling_x + 1 + 2*padding_x),
  39782. by = (unsigned int)((_height - 1)/_sampling_y + 1 + 2*padding_y),
  39783. bz = (unsigned int)((_depth - 1)/_sampling_z + 1 + 2*padding_z),
  39784. br = (unsigned int)(edge_delta/_sampling_r + 1 + 2*padding_r);
  39785. if (bx>0 || by>0 || bz>0 || br>0) {
  39786. const bool is_3d = (_depth>1);
  39787. if (is_3d) { // 3D version of the algorithm
  39788. CImg<floatT> bgrid(bx,by,bz,br), bgridw(bx,by,bz,br);
  39789. cimg_forC(*this,c) {
  39790. const CImg<t> _guide = guide.get_shared_channel(c%guide._spectrum);
  39791. bgrid.fill(0); bgridw.fill(0);
  39792. cimg_forXYZ(*this,x,y,z) {
  39793. const T val = (*this)(x,y,z,c);
  39794. const float edge = (float)_guide(x,y,z);
  39795. const int
  39796. X = (int)cimg::round(x/_sampling_x) + padding_x,
  39797. Y = (int)cimg::round(y/_sampling_y) + padding_y,
  39798. Z = (int)cimg::round(z/_sampling_z) + padding_z,
  39799. R = (int)cimg::round((edge - edge_min)/_sampling_r) + padding_r;
  39800. bgrid(X,Y,Z,R)+=(float)val;
  39801. bgridw(X,Y,Z,R)+=1;
  39802. }
  39803. bgrid.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false);
  39804. bgridw.blur(derived_sigma_x,derived_sigma_y,derived_sigma_z,true).deriche(derived_sigma_r,0,'c',false);
  39805. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),4096))
  39806. cimg_forXYZ(*this,x,y,z) {
  39807. const float edge = (float)_guide(x,y,z);
  39808. const float
  39809. X = x/_sampling_x + padding_x,
  39810. Y = y/_sampling_y + padding_y,
  39811. Z = z/_sampling_z + padding_z,
  39812. R = (edge - edge_min)/_sampling_r + padding_r;
  39813. const float bval0 = bgrid._linear_atXYZC(X,Y,Z,R), bval1 = bgridw._linear_atXYZC(X,Y,Z,R);
  39814. (*this)(x,y,z,c) = (T)(bval0/bval1);
  39815. }
  39816. }
  39817. } else { // 2D version of the algorithm
  39818. CImg<floatT> bgrid(bx,by,br,2);
  39819. cimg_forC(*this,c) {
  39820. const CImg<t> _guide = guide.get_shared_channel(c%guide._spectrum);
  39821. bgrid.fill(0);
  39822. cimg_forXY(*this,x,y) {
  39823. const T val = (*this)(x,y,c);
  39824. const float edge = (float)_guide(x,y);
  39825. const int
  39826. X = (int)cimg::round(x/_sampling_x) + padding_x,
  39827. Y = (int)cimg::round(y/_sampling_y) + padding_y,
  39828. R = (int)cimg::round((edge - edge_min)/_sampling_r) + padding_r;
  39829. bgrid(X,Y,R,0)+=(float)val;
  39830. bgrid(X,Y,R,1)+=1;
  39831. }
  39832. bgrid.blur(derived_sigma_x,derived_sigma_y,0,true).blur(0,0,derived_sigma_r,false);
  39833. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(size(),4096))
  39834. cimg_forXY(*this,x,y) {
  39835. const float edge = (float)_guide(x,y);
  39836. const float
  39837. X = x/_sampling_x + padding_x,
  39838. Y = y/_sampling_y + padding_y,
  39839. R = (edge - edge_min)/_sampling_r + padding_r;
  39840. const float bval0 = bgrid._linear_atXYZ(X,Y,R,0), bval1 = bgrid._linear_atXYZ(X,Y,R,1);
  39841. (*this)(x,y,c) = (T)(bval0/bval1);
  39842. }
  39843. }
  39844. }
  39845. }
  39846. return *this;
  39847. }
  39848. //! Blur image, with the joint bilateral filter \newinstance.
  39849. template<typename t>
  39850. CImg<Tfloat> get_blur_bilateral(const CImg<t>& guide,
  39851. const float sigma_x, const float sigma_y,
  39852. const float sigma_z, const float sigma_r,
  39853. const float sampling_x, const float sampling_y,
  39854. const float sampling_z, const float sampling_r) const {
  39855. return CImg<Tfloat>(*this,false).blur_bilateral(guide,sigma_x,sigma_y,sigma_z,sigma_r,
  39856. sampling_x,sampling_y,sampling_z,sampling_r);
  39857. }
  39858. //! Blur image using the joint bilateral filter.
  39859. /**
  39860. \param guide Image used to model the smoothing weights.
  39861. \param sigma_s Amount of blur along the XYZ-axes.
  39862. \param sigma_r Amount of blur along the value axis.
  39863. \param sampling_s Amount of downsampling along the XYZ-axes used for the approximation. Defaults to sigma_s.
  39864. \param sampling_r Amount of downsampling along the value axis used for the approximation. Defaults to sigma_r.
  39865. **/
  39866. template<typename t>
  39867. CImg<T>& blur_bilateral(const CImg<t>& guide,
  39868. const float sigma_s, const float sigma_r,
  39869. const float sampling_s=0, const float sampling_r=0) {
  39870. const float _sigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100;
  39871. return blur_bilateral(guide,_sigma_s,_sigma_s,_sigma_s,sigma_r,sampling_s,sampling_s,sampling_s,sampling_r);
  39872. }
  39873. //! Blur image using the bilateral filter \newinstance.
  39874. template<typename t>
  39875. CImg<Tfloat> get_blur_bilateral(const CImg<t>& guide,
  39876. const float sigma_s, const float sigma_r,
  39877. const float sampling_s=0, const float sampling_r=0) const {
  39878. return CImg<Tfloat>(*this,false).blur_bilateral(guide,sigma_s,sigma_r,sampling_s,sampling_r);
  39879. }
  39880. // [internal] Apply a box filter (used by CImg<T>::boxfilter() and CImg<T>::blur_box()).
  39881. /*
  39882. \param ptr the pointer of the data
  39883. \param N size of the data
  39884. \param boxsize Size of the box filter (can be subpixel).
  39885. \param off the offset between two data point
  39886. \param order the order of the filter 0 (smoothing), 1st derivative and 2nd derivative.
  39887. \param boundary_conditions Boundary conditions.
  39888. Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
  39889. */
  39890. static void _cimg_blur_box_apply(T *ptr, const float boxsize, const int N, const ulongT off,
  39891. const int order, const unsigned int boundary_conditions,
  39892. const unsigned int nb_iter) {
  39893. const int nboundary_conditions = boundary_conditions>1 && boxsize<=3?1:boundary_conditions;
  39894. // Smooth.
  39895. if (boxsize>1 && nb_iter) {
  39896. const int w2 = (int)(boxsize - 1)/2;
  39897. const unsigned int winsize = 2*w2 + 1U;
  39898. const double frac = (boxsize - winsize)/2.;
  39899. CImg<T> win(winsize);
  39900. for (unsigned int iter = 0; iter<nb_iter; ++iter) {
  39901. Tdouble sum = 0; // window sum
  39902. for (int x = -w2; x<=w2; ++x) {
  39903. win[x + w2] = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,x);
  39904. sum+=win[x + w2];
  39905. }
  39906. int ifirst = 0, ilast = 2*w2;
  39907. T
  39908. prev = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,-w2 - 1),
  39909. next = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,w2 + 1);
  39910. for (int x = 0; x < N - 1; ++x) {
  39911. const double sum2 = sum + frac * (prev + next);
  39912. ptr[x*off] = (T)(sum2/boxsize);
  39913. prev = win[ifirst];
  39914. sum-=prev;
  39915. ifirst = (int)((ifirst + 1)%winsize);
  39916. ilast = (int)((ilast + 1)%winsize);
  39917. win[ilast] = next;
  39918. sum+=next;
  39919. next = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,x + w2 + 2);
  39920. }
  39921. const double sum2 = sum + frac * (prev + next);
  39922. ptr[(N - 1)*off] = (T)(sum2/boxsize);
  39923. }
  39924. }
  39925. // Derive.
  39926. switch (order) {
  39927. case 0 :
  39928. break;
  39929. case 1 : {
  39930. Tfloat
  39931. p = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,-1),
  39932. c = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,0),
  39933. n = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,1);
  39934. for (int x = 0; x<N - 1; ++x) {
  39935. ptr[x*off] = (T)((n-p)/2.);
  39936. p = c;
  39937. c = n;
  39938. n = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,x + 2);
  39939. }
  39940. ptr[(N - 1)*off] = (T)((n-p)/2.);
  39941. } break;
  39942. case 2: {
  39943. Tfloat
  39944. p = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,-1),
  39945. c = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,0),
  39946. n = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,1);
  39947. for (int x = 0; x<N - 1; ++x) {
  39948. ptr[x*off] = (T)(n - 2*c + p);
  39949. p = c;
  39950. c = n;
  39951. n = __cimg_blur_box_apply(ptr,N,off,nboundary_conditions,x + 2);
  39952. }
  39953. ptr[(N - 1)*off] = (T)(n - 2*c + p);
  39954. } break;
  39955. }
  39956. }
  39957. static T __cimg_blur_box_apply(T *ptr, const int N, const ulongT off,
  39958. const unsigned int boundary_conditions, const int x) {
  39959. switch (boundary_conditions) {
  39960. case 0 : // Dirichlet
  39961. return x<0 || x>=N?(T)0:ptr[x*off];
  39962. case 1 : { // Neumann
  39963. const int nx = x<0?0:x>=N?N - 1:x;
  39964. return ptr[nx*off];
  39965. }
  39966. case 2 : { // Periodic
  39967. const int nx = cimg::mod(x,N);
  39968. return ptr[nx*off];
  39969. }
  39970. default : { // Mirror
  39971. const int
  39972. N2 = 2*N,
  39973. tx = cimg::mod(x,N2),
  39974. nx = tx<N?tx:N2 - tx - 1;
  39975. return ptr[nx*off];
  39976. }
  39977. }
  39978. return (T)0;
  39979. }
  39980. // Apply box filter of order 0,1,2.
  39981. /**
  39982. \param boxsize Size of the box window (can be subpixel)
  39983. \param order the order of the filter 0,1 or 2.
  39984. \param axis Axis along which the filter is computed. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  39985. \param boundary_conditions Boundary conditions.
  39986. Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.
  39987. \param nb_iter Number of filter iterations.
  39988. **/
  39989. CImg<T>& boxfilter(const float boxsize, const int order, const char axis='x',
  39990. const unsigned int boundary_conditions=1,
  39991. const unsigned int nb_iter=1) {
  39992. const char naxis = cimg::lowercase(axis);
  39993. const float nboxsize = boxsize>=0?boxsize:-boxsize*
  39994. (naxis=='x'?_width:naxis=='y'?_height:naxis=='z'?_depth:_spectrum)/100;
  39995. if (is_empty() || !nboxsize || (nboxsize<=1 && !order)) return *this;
  39996. switch (naxis) {
  39997. case 'x' : {
  39998. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
  39999. _height*_depth*_spectrum>=16))
  40000. cimg_forYZC(*this,y,z,c)
  40001. _cimg_blur_box_apply(data(0,y,z,c),nboxsize,_width,1U,order,boundary_conditions,nb_iter);
  40002. } break;
  40003. case 'y' : {
  40004. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
  40005. _height*_depth*_spectrum>=16))
  40006. cimg_forXZC(*this,x,z,c)
  40007. _cimg_blur_box_apply(data(x,0,z,c),nboxsize,_height,(ulongT)_width,order,boundary_conditions,nb_iter);
  40008. } break;
  40009. case 'z' : {
  40010. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
  40011. _height*_depth*_spectrum>=16))
  40012. cimg_forXYC(*this,x,y,c)
  40013. _cimg_blur_box_apply(data(x,y,0,c),nboxsize,_depth,(ulongT)_width*_height,order,boundary_conditions,nb_iter);
  40014. } break;
  40015. default : {
  40016. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
  40017. _height*_depth*_spectrum>=16))
  40018. cimg_forXYZ(*this,x,y,z)
  40019. _cimg_blur_box_apply(data(x,y,z,0),nboxsize,_spectrum,(ulongT)_width*_height*_depth,
  40020. order,boundary_conditions,nb_iter);
  40021. }
  40022. }
  40023. return *this;
  40024. }
  40025. // Apply box filter of order 0,1 or 2 \newinstance.
  40026. CImg<Tfloat> get_boxfilter(const float boxsize, const int order, const char axis='x',
  40027. const unsigned int boundary_conditions=1,
  40028. const unsigned int nb_iter=1) const {
  40029. return CImg<Tfloat>(*this,false).boxfilter(boxsize,order,axis,boundary_conditions,nb_iter);
  40030. }
  40031. //! Blur image with a box filter.
  40032. /**
  40033. \param boxsize_x Size of the box window, along the X-axis (can be subpixel).
  40034. \param boxsize_y Size of the box window, along the Y-axis (can be subpixel).
  40035. \param boxsize_z Size of the box window, along the Z-axis (can be subpixel).
  40036. \param boundary_conditions Boundary conditions.
  40037. Can be <tt>{ false=dirichlet | true=neumann | 2=periodic | 3=mirror }</tt>.
  40038. \param nb_iter Number of filter iterations.
  40039. \note
  40040. - This is a recursive algorithm, not depending on the values of the box kernel size.
  40041. \see blur().
  40042. **/
  40043. CImg<T>& blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z,
  40044. const unsigned int boundary_conditions=1,
  40045. const unsigned int nb_iter=1) {
  40046. if (is_empty()) return *this;
  40047. if (_width>1) boxfilter(boxsize_x,0,'x',boundary_conditions,nb_iter);
  40048. if (_height>1) boxfilter(boxsize_y,0,'y',boundary_conditions,nb_iter);
  40049. if (_depth>1) boxfilter(boxsize_z,0,'z',boundary_conditions,nb_iter);
  40050. return *this;
  40051. }
  40052. //! Blur image with a box filter \newinstance.
  40053. CImg<Tfloat> get_blur_box(const float boxsize_x, const float boxsize_y, const float boxsize_z,
  40054. const unsigned int boundary_conditions=1) const {
  40055. return CImg<Tfloat>(*this,false).blur_box(boxsize_x,boxsize_y,boxsize_z,boundary_conditions);
  40056. }
  40057. //! Blur image with a box filter.
  40058. /**
  40059. \param boxsize Size of the box window (can be subpixel).
  40060. \param boundary_conditions Boundary conditions.
  40061. Can be <tt>{ 0=dirichlet | 1=neumann | 2=periodic | 3=mirror }</tt>.a
  40062. \see deriche(), vanvliet().
  40063. **/
  40064. CImg<T>& blur_box(const float boxsize, const unsigned int boundary_conditions=1) {
  40065. const float nboxsize = boxsize>=0?boxsize:-boxsize*cimg::max(_width,_height,_depth)/100;
  40066. return blur_box(nboxsize,nboxsize,nboxsize,boundary_conditions);
  40067. }
  40068. //! Blur image with a box filter \newinstance.
  40069. CImg<Tfloat> get_blur_box(const float boxsize, const unsigned int boundary_conditions=1) const {
  40070. return CImg<Tfloat>(*this,false).blur_box(boxsize,boundary_conditions);
  40071. }
  40072. //! Blur image, with the image guided filter.
  40073. /**
  40074. \param guide Image used to guide the smoothing process.
  40075. \param radius Spatial radius. If negative, it is expressed as a percentage of the largest image size.
  40076. \param regularization Regularization parameter.
  40077. If negative, it is expressed as a percentage of the guide value range.
  40078. \note This method implements the filtering algorithm described in:
  40079. He, Kaiming; Sun, Jian; Tang, Xiaoou, "Guided Image Filtering," Pattern Analysis and Machine Intelligence,
  40080. IEEE Transactions on , vol.35, no.6, pp.1397,1409, June 2013
  40081. **/
  40082. template<typename t>
  40083. CImg<T>& blur_guided(const CImg<t>& guide, const float radius, const float regularization) {
  40084. return get_blur_guided(guide,radius,regularization).move_to(*this);
  40085. }
  40086. //! Blur image, with the image guided filter \newinstance.
  40087. template<typename t>
  40088. CImg<Tfloat> get_blur_guided(const CImg<t>& guide, const float radius, const float regularization) const {
  40089. if (!is_sameXYZ(guide))
  40090. throw CImgArgumentException(_cimg_instance
  40091. "blur_guided(): Invalid size for specified guide image (%u,%u,%u,%u,%p).",
  40092. cimg_instance,
  40093. guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
  40094. if (is_empty() || !radius) return *this;
  40095. const int _radius = radius>=0?(int)radius:(int)(-radius*cimg::max(_width,_height,_depth)/100);
  40096. float _regularization = regularization;
  40097. if (regularization<0) {
  40098. T edge_min, edge_max = guide.max_min(edge_min);
  40099. if (edge_min==edge_max) return *this;
  40100. _regularization = -regularization*(edge_max - edge_min)/100;
  40101. }
  40102. _regularization = std::max(_regularization,0.01f);
  40103. const unsigned int psize = (unsigned int)(1 + 2*_radius);
  40104. CImg<Tfloat>
  40105. mean_p = get_blur_box(psize,true),
  40106. mean_I = guide.get_blur_box(psize,true).resize(mean_p),
  40107. cov_Ip = get_mul(guide).blur_box(psize,true)-=mean_p.get_mul(mean_I),
  40108. var_I = guide.get_sqr().blur_box(psize,true)-=mean_I.get_sqr(),
  40109. &a = cov_Ip.div(var_I+=_regularization),
  40110. &b = mean_p-=a.get_mul(mean_I);
  40111. a.blur_box(psize,true);
  40112. b.blur_box(psize,true);
  40113. return a.mul(guide)+=b;
  40114. }
  40115. //! Blur image using patch-based space.
  40116. /**
  40117. \param guide Image used to model the smoothing weights.
  40118. \param sigma_s Amount of blur along the XYZ-axes.
  40119. \param sigma_r Amount of blur along the value axis.
  40120. \param patch_size Size of the patches.
  40121. \param lookup_size Size of the window to search similar patches.
  40122. \param smoothness Smoothness for the patch comparison.
  40123. \param is_fast_approx Tells if a fast approximation of the gaussian function is used or not.
  40124. **/
  40125. template<typename t>
  40126. CImg<T>& blur_patch(const CImg<t>& guide,
  40127. const float sigma_s, const float sigma_r, const unsigned int patch_size=3,
  40128. const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) {
  40129. if (is_empty() || !patch_size || !lookup_size) return *this;
  40130. return get_blur_patch(guide,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx).move_to(*this);
  40131. }
  40132. //! Blur image using patch-based space \newinstance.
  40133. template<typename t>
  40134. CImg<Tfloat> get_blur_patch(const CImg<t>& guide,
  40135. const float sigma_s, const float sigma_r, const unsigned int patch_size=3,
  40136. const unsigned int lookup_size=4, const float smoothness=0,
  40137. const bool is_fast_approx=true) const {
  40138. #define _cimg_blur_patch3d_fast(N) { \
  40139. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) \
  40140. cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) \
  40141. firstprivate(P,Q)) \
  40142. cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp2 { \
  40143. cimg_abort_test2; \
  40144. cimg_def##N##x##N##x##N(res,x,y,z); \
  40145. tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,x,y,z,c,pP,tfloat); pP+=N3; } \
  40146. const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \
  40147. x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \
  40148. tfloat sum_weights = 0; \
  40149. cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) \
  40150. if (cimg::abs(_guide(x,y,z,0) - _guide(p,q,r,0))<sigma_r3) { \
  40151. tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,p,q,r,c,pQ,tfloat); pQ+=N3; } \
  40152. tfloat distance2 = 0; \
  40153. pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \
  40154. distance2/=Pnorm; \
  40155. const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, dz = (tfloat)r - z, \
  40156. alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = alldist>3?0:1; \
  40157. sum_weights+=weight; \
  40158. cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c); \
  40159. } \
  40160. if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; \
  40161. else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \
  40162. } _cimg_abort_catch_openmp2 }
  40163. #define _cimg_blur_patch3d(N) { \
  40164. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) \
  40165. cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4) \
  40166. firstprivate(P,Q)) \
  40167. cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp2 { \
  40168. cimg_abort_test2; \
  40169. cimg_def##N##x##N##x##N(res,x,y,z); \
  40170. tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,x,y,z,c,pP,tfloat); pP+=N3; } \
  40171. const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1, \
  40172. x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2; \
  40173. tfloat sum_weights = 0, weight_max = 0; \
  40174. cimg_for_in##N##XYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) { \
  40175. tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N##x##N(_guide,p,q,r,c,pQ,tfloat); pQ+=N3; } \
  40176. tfloat distance2 = 0; \
  40177. pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \
  40178. distance2/=Pnorm; \
  40179. const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, dz = (tfloat)r - z, \
  40180. alldist = distance2 + (dx*dx + dy*dy + dz*dz)/sigma_s2, weight = std::exp(-alldist); \
  40181. if (weight>weight_max) weight_max = weight; \
  40182. sum_weights+=weight; \
  40183. cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c); \
  40184. } \
  40185. sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight_max*(*this)(x,y,z,c); \
  40186. if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights; \
  40187. else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c)); \
  40188. } _cimg_abort_catch_openmp2 }
  40189. #define _cimg_blur_patch2d_fast(N) { \
  40190. cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) \
  40191. firstprivate(P,Q)) \
  40192. cimg_forXY(res,x,y) _cimg_abort_try_openmp2 { \
  40193. cimg_abort_test2; \
  40194. cimg_def##N##x##N(res,x,y); \
  40195. tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,x,y,0,c,pP,tfloat); pP+=N2; } \
  40196. const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \
  40197. tfloat sum_weights = 0; \
  40198. cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) \
  40199. if (cimg::abs(_guide(x,y,0,0) - _guide(p,q,0,0))<sigma_r3) { \
  40200. tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,p,q,0,c,pQ,tfloat); pQ+=N2; } \
  40201. tfloat distance2 = 0; \
  40202. pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \
  40203. distance2/=Pnorm; \
  40204. const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, \
  40205. alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = alldist>3?0:1; \
  40206. sum_weights+=weight; \
  40207. cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c); \
  40208. } \
  40209. if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; \
  40210. else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \
  40211. } _cimg_abort_catch_openmp2 }
  40212. #define _cimg_blur_patch2d(N) { \
  40213. cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4) \
  40214. firstprivate(P,Q)) \
  40215. cimg_forXY(res,x,y) _cimg_abort_try_openmp2 { \
  40216. cimg_abort_test2; \
  40217. cimg_def##N##x##N(res,x,y); \
  40218. tfloat *pP = P._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,x,y,0,c,pP,tfloat); pP+=N2; } \
  40219. const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2; \
  40220. tfloat sum_weights = 0, weight_max = 0; \
  40221. cimg_for_in##N##XY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) { \
  40222. tfloat *pQ = Q._data; cimg_forC(_guide,c) { cimg_get##N##x##N(_guide,p,q,0,c,pQ,tfloat); pQ+=N2; } \
  40223. tfloat distance2 = 0; \
  40224. pQ = Q._data; cimg_for(P,_pP,tfloat) { const tfloat dI = *_pP - *(pQ++); distance2+=dI*dI; } \
  40225. distance2/=Pnorm; \
  40226. const tfloat dx = (tfloat)p - x, dy = (tfloat)q - y, \
  40227. alldist = distance2 + (dx*dx+dy*dy)/sigma_s2, weight = std::exp(-alldist); \
  40228. if (weight>weight_max) weight_max = weight; \
  40229. sum_weights+=weight; \
  40230. cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c); \
  40231. } \
  40232. sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight_max*(*this)(x,y,c); \
  40233. if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights; \
  40234. else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c)); \
  40235. } _cimg_abort_catch_openmp2 }
  40236. typedef _cimg_tfloat tfloat;
  40237. if (!is_sameXYZ(guide))
  40238. throw CImgArgumentException(_cimg_instance
  40239. "blur_patch(): Invalid size for specified guide image (%u,%u,%u,%u,%p).",
  40240. cimg_instance,
  40241. guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
  40242. if (is_empty() || !patch_size || !lookup_size) return +*this;
  40243. Tfloat val_min, val_max = (Tfloat)max_min(val_min);
  40244. _cimg_abort_init_openmp;
  40245. cimg_abort_init;
  40246. CImg<Tfloat> res(_width,_height,_depth,_spectrum,0);
  40247. const CImg<tfloat>
  40248. __guide = guide?CImg<tfloat>(guide,guide.pixel_type()==cimg::type<tfloat>::string()):
  40249. CImg<tfloat>(*this,pixel_type()==cimg::type<tfloat>::string()),
  40250. _guide = smoothness>0?__guide.get_blur(smoothness):__guide.get_shared();
  40251. CImg<tfloat> P(_guide._spectrum*patch_size*patch_size*(_depth>1?patch_size:1)), Q(P);
  40252. t guide_min = (t)0, guide_max = (t)0;
  40253. if (sigma_r<0) guide_max = guide.max_min(guide_min);
  40254. const float
  40255. guide_delta = (float)(guide_max - guide_min),
  40256. _sigma_s = sigma_s>=0?sigma_s:-sigma_s*cimg::max(_width,_height,_depth)/100,
  40257. _sigma_r = sigma_r>=0?sigma_r:-sigma_r*guide_delta/100,
  40258. sigma_s2 = _sigma_s*_sigma_s,
  40259. sigma_r2 = _sigma_r*_sigma_r,
  40260. sigma_r3 = 3*_sigma_r,
  40261. Pnorm = P.size()*sigma_r2;
  40262. const int rsize2 = (int)lookup_size/2, rsize1 = (int)lookup_size - rsize2 - 1;
  40263. const unsigned int N2 = patch_size*patch_size, N3 = N2*patch_size;
  40264. cimg::unused(N2,N3);
  40265. if (_depth>1) switch (patch_size) { // 3D
  40266. case 2 : if (is_fast_approx) _cimg_blur_patch3d_fast(2) else _cimg_blur_patch3d(2) break;
  40267. case 3 : if (is_fast_approx) _cimg_blur_patch3d_fast(3) else _cimg_blur_patch3d(3) break;
  40268. default : {
  40269. const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1;
  40270. if (is_fast_approx) {
  40271. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
  40272. cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4)
  40273. firstprivate(P,Q))
  40274. cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp2 { // Fast
  40275. cimg_abort_test2;
  40276. P = _guide.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true);
  40277. const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1,
  40278. x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2;
  40279. tfloat sum_weights = 0;
  40280. cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r)
  40281. if (cimg::abs(_guide(x,y,z,0) - _guide(p,q,r,0))<sigma_r3) {
  40282. (Q = _guide.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P;
  40283. const tfloat
  40284. dx = (tfloat)x - p, dy = (tfloat)y - q, dz = (tfloat)z - r,
  40285. distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2),
  40286. weight = distance2>3?0:1;
  40287. sum_weights+=weight;
  40288. cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c);
  40289. }
  40290. if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights;
  40291. else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c));
  40292. } _cimg_abort_catch_openmp2
  40293. } else {
  40294. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
  40295. cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height*res._depth>=4)
  40296. firstprivate(P,Q))
  40297. cimg_forXYZ(res,x,y,z) _cimg_abort_try_openmp2 { // Exact
  40298. cimg_abort_test2;
  40299. P = _guide.get_crop(x - psize1,y - psize1,z - psize1,x + psize2,y + psize2,z + psize2,true);
  40300. const int x0 = x - rsize1, y0 = y - rsize1, z0 = z - rsize1,
  40301. x1 = x + rsize2, y1 = y + rsize2, z1 = z + rsize2;
  40302. tfloat sum_weights = 0, weight_max = 0;
  40303. cimg_for_inXYZ(res,x0,y0,z0,x1,y1,z1,p,q,r) if (p!=x || q!=y || r!=z) {
  40304. (Q = _guide.get_crop(p - psize1,q - psize1,r - psize1,p + psize2,q + psize2,r + psize2,true))-=P;
  40305. const tfloat
  40306. dx = (tfloat)x - p, dy = (tfloat)y - q, dz = (tfloat)z - r,
  40307. distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy + dz*dz)/sigma_s2),
  40308. weight = std::exp(-distance2);
  40309. if (weight>weight_max) weight_max = weight;
  40310. sum_weights+=weight;
  40311. cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight*(*this)(p,q,r,c);
  40312. }
  40313. sum_weights+=weight_max; cimg_forC(res,c) res(x,y,z,c)+=(Tfloat)weight_max*(*this)(x,y,z,c);
  40314. if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,z,c)/=(Tfloat)sum_weights;
  40315. else cimg_forC(res,c) res(x,y,z,c) = (Tfloat)((*this)(x,y,z,c));
  40316. } _cimg_abort_catch_openmp2
  40317. }
  40318. }
  40319. } else switch (patch_size) { // 2D
  40320. case 2 : if (is_fast_approx) _cimg_blur_patch2d_fast(2) else _cimg_blur_patch2d(2) break;
  40321. case 3 : if (is_fast_approx) _cimg_blur_patch2d_fast(3) else _cimg_blur_patch2d(3) break;
  40322. case 4 : if (is_fast_approx) _cimg_blur_patch2d_fast(4) else _cimg_blur_patch2d(4) break;
  40323. case 5 : if (is_fast_approx) _cimg_blur_patch2d_fast(5) else _cimg_blur_patch2d(5) break;
  40324. case 6 : if (is_fast_approx) _cimg_blur_patch2d_fast(6) else _cimg_blur_patch2d(6) break;
  40325. case 7 : if (is_fast_approx) _cimg_blur_patch2d_fast(7) else _cimg_blur_patch2d(7) break;
  40326. case 8 : if (is_fast_approx) _cimg_blur_patch2d_fast(8) else _cimg_blur_patch2d(8) break;
  40327. case 9 : if (is_fast_approx) _cimg_blur_patch2d_fast(9) else _cimg_blur_patch2d(9) break;
  40328. default : { // Fast
  40329. const int psize2 = (int)patch_size/2, psize1 = (int)patch_size - psize2 - 1;
  40330. if (is_fast_approx) {
  40331. cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4)
  40332. firstprivate(P,Q))
  40333. cimg_forXY(res,x,y) _cimg_abort_try_openmp2 { // Fast
  40334. cimg_abort_test2;
  40335. P = _guide.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true);
  40336. const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2;
  40337. tfloat sum_weights = 0;
  40338. cimg_for_inXY(res,x0,y0,x1,y1,p,q)
  40339. if (cimg::abs(_guide(x,y,0) - _guide(p,q,0))<sigma_r3) {
  40340. (Q = _guide.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P;
  40341. const tfloat
  40342. dx = (tfloat)x - p, dy = (tfloat)y - q,
  40343. distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2),
  40344. weight = distance2>3?0:1;
  40345. sum_weights+=weight;
  40346. cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c);
  40347. }
  40348. if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights;
  40349. else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c));
  40350. } _cimg_abort_catch_openmp2
  40351. } else {
  40352. cimg_pragma_openmp(parallel for cimg_openmp_if(res._width>=(cimg_openmp_sizefactor)*32 && res._height>=4)
  40353. firstprivate(P,Q))
  40354. cimg_forXY(res,x,y) _cimg_abort_try_openmp2 { // Exact
  40355. cimg_abort_test2;
  40356. P = _guide.get_crop(x - psize1,y - psize1,x + psize2,y + psize2,true);
  40357. const int x0 = x - rsize1, y0 = y - rsize1, x1 = x + rsize2, y1 = y + rsize2;
  40358. tfloat sum_weights = 0, weight_max = 0;
  40359. cimg_for_inXY(res,x0,y0,x1,y1,p,q) if (p!=x || q!=y) {
  40360. (Q = _guide.get_crop(p - psize1,q - psize1,p + psize2,q + psize2,true))-=P;
  40361. const tfloat
  40362. dx = (tfloat)x - p, dy = (tfloat)y - q,
  40363. distance2 = (tfloat)(Q.pow(2).sum()/Pnorm + (dx*dx + dy*dy)/sigma_s2),
  40364. weight = std::exp(-distance2);
  40365. if (weight>weight_max) weight_max = weight;
  40366. sum_weights+=weight;
  40367. cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight*(*this)(p,q,c);
  40368. }
  40369. sum_weights+=weight_max; cimg_forC(res,c) res(x,y,c)+=(Tfloat)weight_max*(*this)(x,y,c);
  40370. if (sum_weights>1e-10) cimg_forC(res,c) res(x,y,c)/=(Tfloat)sum_weights;
  40371. else cimg_forC(res,c) res(x,y,c) = (Tfloat)((*this)(x,y,c));
  40372. } _cimg_abort_catch_openmp2
  40373. }
  40374. }
  40375. }
  40376. cimg_abort_test;
  40377. return res.cut(val_min,val_max);
  40378. }
  40379. //! Blur image using patch-based space \simplification.
  40380. CImg<T>& blur_patch(const float sigma_s, const float sigma_r, const unsigned int patch_size=3,
  40381. const unsigned int lookup_size=4, const float smoothness=0, const bool is_fast_approx=true) {
  40382. return blur_patch(*this,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx);
  40383. }
  40384. //! Blur image using patch-based space \simplification \newinstance.
  40385. CImg<Tfloat> get_blur_patch(const float sigma_s, const float sigma_r, const unsigned int patch_size=3,
  40386. const unsigned int lookup_size=4, const float smoothness=0,
  40387. const bool is_fast_approx=true) const {
  40388. return get_blur_patch(*this,sigma_s,sigma_r,patch_size,lookup_size,smoothness,is_fast_approx);
  40389. }
  40390. //! Blur image with the median filter.
  40391. /**
  40392. \param n Size of the median filter.
  40393. \param threshold Threshold used to discard pixels too far from the current pixel value in the median computation.
  40394. **/
  40395. CImg<T>& blur_median(const unsigned int n, const float threshold=0) {
  40396. if (!n) return *this;
  40397. return get_blur_median(n,threshold).move_to(*this);
  40398. }
  40399. //! Blur image with the median filter \newinstance.
  40400. CImg<T> get_blur_median(const unsigned int n, const float threshold=0) const {
  40401. if (is_empty() || n<=1) return +*this;
  40402. CImg<T> res(_width,_height,_depth,_spectrum);
  40403. T *ptrd = res._data;
  40404. cimg::unused(ptrd);
  40405. const int hr = (int)n/2, hl = n - hr - 1;
  40406. if (res._depth!=1) { // 3D
  40407. if (threshold>0)
  40408. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
  40409. _height*_depth*_spectrum>=4))
  40410. cimg_forXYZC(*this,x,y,z,c) { // With threshold
  40411. const int
  40412. x0 = x - hl, y0 = y - hl, z0 = z - hl, x1 = x + hr, y1 = y + hr, z1 = z + hr,
  40413. nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0,
  40414. nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1, nz1 = z1>=depth()?depth() - 1:z1;
  40415. const Tfloat val0 = (Tfloat)(*this)(x,y,z,c);
  40416. CImg<T> values(n*n*n);
  40417. unsigned int nb_values = 0;
  40418. T *_ptrd = values.data();
  40419. cimg_for_inXYZ(*this,nx0,ny0,nz0,nx1,ny1,nz1,p,q,r)
  40420. if (cimg::abs((*this)(p,q,r,c) - val0)<=threshold) { *(_ptrd++) = (*this)(p,q,r,c); ++nb_values; }
  40421. res(x,y,z,c) = nb_values?values.get_shared_points(0,nb_values - 1).median():(*this)(x,y,z,c);
  40422. }
  40423. else
  40424. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
  40425. _height*_depth*_spectrum>=4))
  40426. cimg_forXYZC(*this,x,y,z,c) { // Without threshold
  40427. const int
  40428. x0 = x - hl, y0 = y - hl, z0 = z - hl, x1 = x + hr, y1 = y + hr, z1 = z + hr,
  40429. nx0 = x0<0?0:x0, ny0 = y0<0?0:y0, nz0 = z0<0?0:z0,
  40430. nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1, nz1 = z1>=depth()?depth() - 1:z1;
  40431. res(x,y,z,c) = get_crop(nx0,ny0,nz0,c,nx1,ny1,nz1,c).median();
  40432. }
  40433. } else {
  40434. if (threshold>0)
  40435. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 &&
  40436. _height*_spectrum>=4))
  40437. cimg_forXYC(*this,x,y,c) { // With threshold
  40438. const int
  40439. x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr,
  40440. nx0 = x0<0?0:x0, ny0 = y0<0?0:y0,
  40441. nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1;
  40442. const Tfloat val0 = (Tfloat)(*this)(x,y,c);
  40443. CImg<T> values(n*n);
  40444. unsigned int nb_values = 0;
  40445. T *_ptrd = values.data();
  40446. cimg_for_inXY(*this,nx0,ny0,nx1,ny1,p,q)
  40447. if (cimg::abs((*this)(p,q,c) - val0)<=threshold) { *(_ptrd++) = (*this)(p,q,c); ++nb_values; }
  40448. res(x,y,c) = nb_values?values.get_shared_points(0,nb_values - 1).median():(*this)(x,y,c);
  40449. }
  40450. else {
  40451. const int
  40452. w1 = width() - 1, h1 = height() - 1,
  40453. w2 = width() - 2, h2 = height() - 2,
  40454. w3 = width() - 3, h3 = height() - 3,
  40455. w4 = width() - 4, h4 = height() - 4;
  40456. switch (n) { // Without threshold
  40457. case 3 : {
  40458. cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
  40459. cimg_forC(*this,c) {
  40460. CImg<T> I(9);
  40461. cimg_for_in3x3(*this,1,1,w2,h2,x,y,0,c,I,T)
  40462. res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6],I[7],I[8]);
  40463. cimg_for_borderXY(*this,x,y,1)
  40464. res(x,y,c) = get_crop(std::max(0,x - 1),std::max(0,y - 1),0,c,
  40465. std::min(w1,x + 1),std::min(h1,y + 1),0,c).median();
  40466. }
  40467. } break;
  40468. case 5 : {
  40469. cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
  40470. cimg_forC(*this,c) {
  40471. CImg<T> I(25);
  40472. cimg_for_in5x5(*this,2,2,w3,h3,x,y,0,c,I,T)
  40473. res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],
  40474. I[5],I[6],I[7],I[8],I[9],
  40475. I[10],I[11],I[12],I[13],I[14],
  40476. I[15],I[16],I[17],I[18],I[19],
  40477. I[20],I[21],I[22],I[23],I[24]);
  40478. cimg_for_borderXY(*this,x,y,2)
  40479. res(x,y,c) = get_crop(std::max(0,x - 2),std::max(0,y - 2),0,c,
  40480. std::min(w1,x + 2),std::min(h1,y + 2),0,c).median();
  40481. }
  40482. } break;
  40483. case 7 : {
  40484. cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
  40485. cimg_forC(*this,c) {
  40486. CImg<T> I(49);
  40487. cimg_for_in7x7(*this,3,3,w4,h4,x,y,0,c,I,T)
  40488. res(x,y,c) = cimg::median(I[0],I[1],I[2],I[3],I[4],I[5],I[6],
  40489. I[7],I[8],I[9],I[10],I[11],I[12],I[13],
  40490. I[14],I[15],I[16],I[17],I[18],I[19],I[20],
  40491. I[21],I[22],I[23],I[24],I[25],I[26],I[27],
  40492. I[28],I[29],I[30],I[31],I[32],I[33],I[34],
  40493. I[35],I[36],I[37],I[38],I[39],I[40],I[41],
  40494. I[42],I[43],I[44],I[45],I[46],I[47],I[48]);
  40495. cimg_for_borderXY(*this,x,y,3)
  40496. res(x,y,c) = get_crop(std::max(0,x - 3),std::max(0,y - 3),0,c,
  40497. std::min(w1,x + 3),std::min(h1,y + 3),0,c).median();
  40498. }
  40499. } break;
  40500. default : {
  40501. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
  40502. cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*16 && _height*_spectrum>=4))
  40503. cimg_forXYC(*this,x,y,c) {
  40504. const int
  40505. x0 = x - hl, y0 = y - hl, x1 = x + hr, y1 = y + hr,
  40506. nx0 = x0<0?0:x0, ny0 = y0<0?0:y0,
  40507. nx1 = x1>=width()?width() - 1:x1, ny1 = y1>=height()?height() - 1:y1;
  40508. res(x,y,c) = get_crop(nx0,ny0,0,c,nx1,ny1,0,c).median();
  40509. }
  40510. }
  40511. }
  40512. }
  40513. }
  40514. return res;
  40515. }
  40516. //! Sharpen image.
  40517. /**
  40518. \param amplitude Sharpening amplitude
  40519. \param sharpen_type Select sharpening method. Can be <tt>{ false=inverse diffusion | true=shock filters }</tt>.
  40520. \param edge Edge threshold (shock filters only).
  40521. \param alpha Gradient smoothness (shock filters only).
  40522. \param sigma Tensor smoothness (shock filters only).
  40523. **/
  40524. CImg<T>& sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1,
  40525. const float alpha=0, const float sigma=0) {
  40526. if (is_empty()) return *this;
  40527. T val_min, val_max = max_min(val_min);
  40528. const float nedge = edge/2;
  40529. CImg<Tfloat> velocity(_width,_height,_depth,_spectrum), _veloc_max(_spectrum);
  40530. if (_depth>1) { // 3D
  40531. if (sharpen_type) { // Shock filters
  40532. CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors());
  40533. if (sigma>0) G.blur(sigma);
  40534. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*32 &&
  40535. _height*_depth>=16))
  40536. cimg_forYZ(G,y,z) {
  40537. Tfloat *ptrG0 = G.data(0,y,z,0), *ptrG1 = G.data(0,y,z,1),
  40538. *ptrG2 = G.data(0,y,z,2), *ptrG3 = G.data(0,y,z,3);
  40539. CImg<Tfloat> val, vec;
  40540. cimg_forX(G,x) {
  40541. G.get_tensor_at(x,y,z).symmetric_eigen(val,vec);
  40542. if (val[0]<0) val[0] = 0;
  40543. if (val[1]<0) val[1] = 0;
  40544. if (val[2]<0) val[2] = 0;
  40545. *(ptrG0++) = vec(0,0);
  40546. *(ptrG1++) = vec(0,1);
  40547. *(ptrG2++) = vec(0,2);
  40548. *(ptrG3++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1] + val[2],-(Tfloat)nedge);
  40549. }
  40550. }
  40551. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*512 &&
  40552. _spectrum>=2))
  40553. cimg_forC(*this,c) {
  40554. Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
  40555. CImg_3x3x3(I,Tfloat);
  40556. cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
  40557. const Tfloat
  40558. u = G(x,y,z,0),
  40559. v = G(x,y,z,1),
  40560. w = G(x,y,z,2),
  40561. amp = G(x,y,z,3),
  40562. ixx = Incc + Ipcc - 2*Iccc,
  40563. ixy = (Innc + Ippc - Inpc - Ipnc)/4,
  40564. ixz = (Incn + Ipcp - Incp - Ipcn)/4,
  40565. iyy = Icnc + Icpc - 2*Iccc,
  40566. iyz = (Icnn + Icpp - Icnp - Icpn)/4,
  40567. izz = Iccn + Iccp - 2*Iccc,
  40568. ixf = Incc - Iccc,
  40569. ixb = Iccc - Ipcc,
  40570. iyf = Icnc - Iccc,
  40571. iyb = Iccc - Icpc,
  40572. izf = Iccn - Iccc,
  40573. izb = Iccc - Iccp,
  40574. itt = u*u*ixx + v*v*iyy + w*w*izz + 2*u*v*ixy + 2*u*w*ixz + 2*v*w*iyz,
  40575. it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb) + w*cimg::minmod(izf,izb),
  40576. veloc = -amp*cimg::sign(itt)*cimg::abs(it);
  40577. *(ptrd++) = veloc;
  40578. if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
  40579. }
  40580. _veloc_max[c] = veloc_max;
  40581. }
  40582. } else // Inverse diffusion
  40583. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*512 &&
  40584. _spectrum>=2))
  40585. cimg_forC(*this,c) {
  40586. Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
  40587. CImg_3x3x3(I,Tfloat);
  40588. cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
  40589. const Tfloat veloc = -Ipcc - Incc - Icpc - Icnc - Iccp - Iccn + 6*Iccc;
  40590. *(ptrd++) = veloc;
  40591. if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
  40592. }
  40593. _veloc_max[c] = veloc_max;
  40594. }
  40595. } else { // 2D
  40596. if (sharpen_type) { // Shock filters
  40597. CImg<Tfloat> G = (alpha>0?get_blur(alpha).get_structure_tensors():get_structure_tensors());
  40598. if (sigma>0) G.blur(sigma);
  40599. cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*32 &&
  40600. _height>=(cimg_openmp_sizefactor)*16))
  40601. cimg_forY(G,y) {
  40602. CImg<Tfloat> val, vec;
  40603. Tfloat *ptrG0 = G.data(0,y,0,0), *ptrG1 = G.data(0,y,0,1), *ptrG2 = G.data(0,y,0,2);
  40604. cimg_forX(G,x) {
  40605. G.get_tensor_at(x,y).symmetric_eigen(val,vec);
  40606. if (val[0]<0) val[0] = 0;
  40607. if (val[1]<0) val[1] = 0;
  40608. *(ptrG0++) = vec(0,0);
  40609. *(ptrG1++) = vec(0,1);
  40610. *(ptrG2++) = 1 - (Tfloat)std::pow(1 + val[0] + val[1],-(Tfloat)nedge);
  40611. }
  40612. }
  40613. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*512 &&
  40614. _spectrum>=2))
  40615. cimg_forC(*this,c) {
  40616. Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
  40617. CImg_3x3(I,Tfloat);
  40618. cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
  40619. const Tfloat
  40620. u = G(x,y,0),
  40621. v = G(x,y,1),
  40622. amp = G(x,y,2),
  40623. ixx = Inc + Ipc - 2*Icc,
  40624. ixy = (Inn + Ipp - Inp - Ipn)/4,
  40625. iyy = Icn + Icp - 2*Icc,
  40626. ixf = Inc - Icc,
  40627. ixb = Icc - Ipc,
  40628. iyf = Icn - Icc,
  40629. iyb = Icc - Icp,
  40630. itt = u*u*ixx + v*v*iyy + 2*u*v*ixy,
  40631. it = u*cimg::minmod(ixf,ixb) + v*cimg::minmod(iyf,iyb),
  40632. veloc = -amp*cimg::sign(itt)*cimg::abs(it);
  40633. *(ptrd++) = veloc;
  40634. if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
  40635. }
  40636. _veloc_max[c] = veloc_max;
  40637. }
  40638. } else // Inverse diffusion
  40639. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*512 &&
  40640. _spectrum>=2))
  40641. cimg_forC(*this,c) {
  40642. Tfloat *ptrd = velocity.data(0,0,0,c), veloc_max = 0;
  40643. CImg_3x3(I,Tfloat);
  40644. cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
  40645. const Tfloat veloc = -Ipc - Inc - Icp - Icn + 4*Icc;
  40646. *(ptrd++) = veloc;
  40647. if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
  40648. }
  40649. _veloc_max[c] = veloc_max;
  40650. }
  40651. }
  40652. const Tfloat veloc_max = _veloc_max.max();
  40653. if (veloc_max<=0) return *this;
  40654. return ((velocity*=amplitude/veloc_max)+=*this).cut(val_min,val_max).move_to(*this);
  40655. }
  40656. //! Sharpen image \newinstance.
  40657. CImg<T> get_sharpen(const float amplitude, const bool sharpen_type=false, const float edge=1,
  40658. const float alpha=0, const float sigma=0) const {
  40659. return (+*this).sharpen(amplitude,sharpen_type,edge,alpha,sigma);
  40660. }
  40661. //! Return image gradient.
  40662. /**
  40663. \param axes Axes considered for the gradient computation, as a C-string (e.g "xy").
  40664. \param scheme = Numerical scheme used for the gradient computation:
  40665. - -1 = Backward finite differences
  40666. - 0 = Centered finite differences (default)
  40667. - 1 = Forward finite differences
  40668. - 2 = Using Sobel kernels
  40669. - 3 = Using rotation invariant kernels
  40670. - 4 = Using Deriche recursive filter.
  40671. - 5 = Using Van Vliet recursive filter.
  40672. **/
  40673. CImgList<Tfloat> get_gradient(const char *const axes=0, const int scheme=0) const {
  40674. CImgList<Tfloat> res;
  40675. char __axes[4] = {};
  40676. const char *_axes = axes?axes:__axes;
  40677. if (!axes) {
  40678. unsigned int k = 0;
  40679. if (_width>1) __axes[k++] = 'x';
  40680. if (_height>1) __axes[k++] = 'y';
  40681. if (_depth>1) __axes[k++] = 'z';
  40682. }
  40683. CImg<Tfloat> grad;
  40684. while (*_axes) {
  40685. const char axis = cimg::lowercase(*(_axes++));
  40686. if (axis!='x' && axis!='y' && axis!='z')
  40687. throw CImgArgumentException(_cimg_instance
  40688. "get_gradient(): Invalid specified axes '%s'.",
  40689. cimg_instance,
  40690. axes);
  40691. const longT off = axis=='x'?1:axis=='y'?_width:_width*_height;
  40692. if ((axis=='x' && _width==1) || (axis=='y' && _height==1) || (axis=='z' && _depth==1)) {
  40693. grad.assign(_width,_height,_depth,_spectrum,0).move_to(res);
  40694. continue;
  40695. }
  40696. const int _scheme = axis=='z' && (scheme==2 || scheme==3)?0:scheme;
  40697. switch (_scheme) {
  40698. case -1 : { // Backward finite differences
  40699. grad.assign(_width,_height,_depth,_spectrum);
  40700. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384))
  40701. cimg_forXYZC(*this,x,y,z,c) {
  40702. const ulongT pos = offset(x,y,z,c);
  40703. if ((axis=='x' && !x) || (axis=='y' && !y) || (axis=='z' && !z))
  40704. grad[pos] = 0;
  40705. else
  40706. grad[pos] = (Tfloat)_data[pos] - _data[pos - off];
  40707. }
  40708. grad.move_to(res);
  40709. } break;
  40710. case 1 : { // Forward finite differences
  40711. grad.assign(_width,_height,_depth,_spectrum);
  40712. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384))
  40713. cimg_forXYZC(*this,x,y,z,c) {
  40714. const ulongT pos = offset(x,y,z,c);
  40715. if ((axis=='x' && x==width() - 1) || (axis=='y' && y==height() - 1) || (axis=='z' && z==depth() - 1))
  40716. grad[pos] = 0;
  40717. else
  40718. grad[pos] = (Tfloat)_data[pos + off] - _data[pos];
  40719. }
  40720. grad.move_to(res);
  40721. } break;
  40722. case 2 : { // Sobel scheme
  40723. grad.assign(_width,_height,_depth,_spectrum);
  40724. if (axis=='x') // X-axis
  40725. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
  40726. cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
  40727. _depth*_spectrum>=2))
  40728. cimg_forZC(*this,z,c) {
  40729. CImg_3x3(I,Tfloat);
  40730. cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = - Ipp + Inp - 2*Ipc + 2*Inc - Ipn + Inn;
  40731. }
  40732. else // Y-axis
  40733. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
  40734. cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
  40735. _depth*_spectrum>=2))
  40736. cimg_forZC(*this,z,c) {
  40737. CImg_3x3(I,Tfloat);
  40738. cimg_for3x3(*this,x,y,z,c,I,Tfloat) grad(x,y,z,c) = - Ipp - 2*Icp - Inp + Ipn + 2*Icn + Inn;
  40739. }
  40740. grad.move_to(res);
  40741. } break;
  40742. case 3 : { // Rotation invariant scheme
  40743. const Tfloat a = (Tfloat)(0.25f*(2 - std::sqrt(2.f))), b = (Tfloat)(0.5f*(std::sqrt(2.f) - 1));
  40744. grad.assign(_width,_height,_depth,_spectrum);
  40745. if (axis=='x') // X-axis
  40746. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
  40747. cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
  40748. _depth*_spectrum>=2))
  40749. cimg_forZC(*this,z,c) {
  40750. CImg_3x3(I,Tfloat);
  40751. 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;
  40752. }
  40753. else // Y-axis
  40754. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
  40755. cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
  40756. _depth*_spectrum>=2))
  40757. cimg_forZC(*this,z,c) {
  40758. CImg_3x3(I,Tfloat);
  40759. 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;
  40760. }
  40761. grad.move_to(res);
  40762. } break;
  40763. case 4 : // Deriche filter
  40764. get_deriche(0,1,axis).move_to(res);
  40765. break;
  40766. case 5 : // Van Vliet filter
  40767. get_vanvliet(0,1,axis).move_to(res);
  40768. break;
  40769. default : { // Central finite differences
  40770. grad.assign(_width,_height,_depth,_spectrum);
  40771. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384))
  40772. cimg_forXYZC(*this,x,y,z,c) {
  40773. const ulongT pos = offset(x,y,z,c);
  40774. if ((axis=='x' && !x) || (axis=='y' && !y) || (axis=='z' && !z))
  40775. grad[pos] = ((Tfloat)_data[pos + off] - _data[pos])/2;
  40776. else if ((axis=='x' && x==width() - 1) || (axis=='y' && y==height() - 1) || (axis=='z' && z==depth() - 1))
  40777. grad[pos] = ((Tfloat)_data[pos] - _data[pos - off])/2;
  40778. else
  40779. grad[pos] = ((Tfloat)_data[pos + off] - _data[pos - off])/2;
  40780. }
  40781. grad.move_to(res);
  40782. } break;
  40783. }
  40784. }
  40785. return res;
  40786. }
  40787. //! Return image hessian.
  40788. /**
  40789. \param axes Axes considered for the hessian computation, as a C-string (e.g "xy").
  40790. **/
  40791. CImgList<Tfloat> get_hessian(const char *const axes=0) const {
  40792. CImgList<Tfloat> res;
  40793. char __axes[12] = {};
  40794. const char *_axes = axes?axes:__axes;
  40795. if (!axes) {
  40796. unsigned int k = 0;
  40797. if (_width>1) { __axes[k++] = 'x'; __axes[k++] = 'x'; }
  40798. if (_width>1 && _height>1) { __axes[k++] = 'x'; __axes[k++] = 'y'; }
  40799. if (_width>1 && _depth>1) { __axes[k++] = 'x'; __axes[k++] = 'z'; }
  40800. if (_height>1) { __axes[k++] = 'y'; __axes[k++] = 'y'; }
  40801. if (_height>1 && _depth>1) { __axes[k++] = 'y'; __axes[k++] = 'z'; }
  40802. if (_depth>1) { __axes[k++] = 'z'; __axes[k++] = 'z'; }
  40803. }
  40804. const unsigned int len = (unsigned int)std::strlen(_axes);
  40805. if (len%2)
  40806. throw CImgArgumentException(_cimg_instance
  40807. "get_hessian(): Invalid specified axes '%s'.",
  40808. cimg_instance,
  40809. axes);
  40810. CImg<Tfloat> hess;
  40811. for (unsigned int k = 0; k<len; k+=2) {
  40812. const char
  40813. _axis1 = cimg::lowercase(_axes[k]),
  40814. _axis2 = cimg::lowercase(_axes[k + 1]),
  40815. axis1 = std::min(_axis1,_axis2),
  40816. axis2 = std::max(_axis2,_axis2);
  40817. if (axis1!='x' && axis1!='y' && axis1!='z' &&
  40818. axis2!='x' && axis2!='y' && axis2!='z')
  40819. throw CImgArgumentException(_cimg_instance
  40820. "get_hessian(): Invalid specified axes '%s'.",
  40821. cimg_instance,
  40822. axes);
  40823. const longT off = axis1=='x'?1:axis1=='y'?_width:_width*_height;
  40824. hess.assign(_width,_height,_depth,_spectrum);
  40825. if (((axis1=='x' || axis2=='x') && _width==1) ||
  40826. ((axis1=='y' || axis2=='y') && _height==1) ||
  40827. ((axis1=='z' || axis2=='z') && _depth==1)) {
  40828. hess.fill(0).move_to(res);
  40829. continue;
  40830. }
  40831. if (axis1==axis2) // Ixx, Iyy, Izz
  40832. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3) cimg_openmp_if_size(size(),16384))
  40833. cimg_forXYZC(*this,x,y,z,c) {
  40834. const ulongT pos = offset(x,y,z,c);
  40835. if ((axis1=='x' && !x) || (axis1=='y' && !y) || (axis1=='z' && !z))
  40836. hess[pos] = (Tfloat)_data[pos + off] - _data[pos];
  40837. else if ((axis1=='x' && x==width() - 1) ||
  40838. (axis1=='y' && y==height() - 1) ||
  40839. (axis1=='z' && z==depth() - 1))
  40840. hess[pos] = (Tfloat)_data[pos - off] - _data[pos];
  40841. else
  40842. hess[pos] = (Tfloat)_data[pos + off] + _data[pos - off] - 2*_data[pos];
  40843. }
  40844. else if (axis1=='x' && axis2=='y') // Ixy
  40845. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
  40846. cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*16384 &&
  40847. _depth*_spectrum>=2))
  40848. cimg_forZC(*this,z,c) {
  40849. CImg_3x3(I,Tfloat);
  40850. cimg_for3x3(*this,x,y,z,c,I,Tfloat) hess(x,y,z,c) = (Inn + Ipp - Inp - Ipn)/4;
  40851. }
  40852. else if (axis1=='x' && axis2=='z') // Ixz
  40853. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*16384 &&
  40854. _spectrum>=2))
  40855. cimg_forC(*this,c) {
  40856. CImg_3x3x3(I,Tfloat);
  40857. cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) hess(x,y,z,c) = (Incn + Ipcp - Incp - Ipcn)/4;
  40858. }
  40859. else // Iyz
  40860. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*16384 &&
  40861. _spectrum>=2))
  40862. cimg_forC(*this,c) {
  40863. CImg_3x3x3(I,Tfloat);
  40864. cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) hess(x,y,z,c) = (Icnn + Icpp - Icnp - Icpn)/4;
  40865. }
  40866. hess.move_to(res);
  40867. }
  40868. return res;
  40869. }
  40870. //! Compute image Laplacian.
  40871. CImg<T>& laplacian() {
  40872. return get_laplacian().move_to(*this);
  40873. }
  40874. //! Compute image Laplacian \newinstance.
  40875. CImg<Tfloat> get_laplacian() const {
  40876. if (is_empty()) return CImg<Tfloat>();
  40877. CImg<Tfloat> res(_width,_height,_depth,_spectrum);
  40878. if (_depth>1) { // 3D
  40879. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 &&
  40880. _spectrum>=2))
  40881. cimg_forC(*this,c) {
  40882. Tfloat *ptrd = res.data(0,0,0,c);
  40883. CImg_3x3x3(I,Tfloat);
  40884. cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) *(ptrd++) = Incc + Ipcc + Icnc + Icpc + Iccn + Iccp - 6*Iccc;
  40885. }
  40886. } else if (_height>1) { // 2D
  40887. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 &&
  40888. _depth*_spectrum>=2))
  40889. cimg_forC(*this,c) {
  40890. Tfloat *ptrd = res.data(0,0,0,c);
  40891. CImg_3x3(I,Tfloat);
  40892. cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc + Icn + Icp - 4*Icc;
  40893. }
  40894. } else { // 1D
  40895. cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*1048576 &&
  40896. _height*_depth*_spectrum>=2))
  40897. cimg_forC(*this,c) {
  40898. Tfloat *ptrd = res.data(0,0,0,c);
  40899. CImg_3x3(I,Tfloat);
  40900. cimg_for3x3(*this,x,y,0,c,I,Tfloat) *(ptrd++) = Inc + Ipc - 2*Icc;
  40901. }
  40902. }
  40903. return res;
  40904. }
  40905. //! Compute the structure tensor field of an image.
  40906. /**
  40907. \param is_fwbw_scheme scheme. Can be <tt>{ false=centered | true=forward-backward }</tt>
  40908. **/
  40909. CImg<T>& structure_tensors(const bool is_fwbw_scheme=false) {
  40910. return get_structure_tensors(is_fwbw_scheme).move_to(*this);
  40911. }
  40912. //! Compute the structure tensor field of an image \newinstance.
  40913. CImg<Tfloat> get_structure_tensors(const bool is_fwbw_scheme=false) const {
  40914. if (is_empty()) return *this;
  40915. CImg<Tfloat> res;
  40916. if (_depth>1) { // 3D
  40917. res.assign(_width,_height,_depth,6,0);
  40918. if (!is_fwbw_scheme) { // Classical central finite differences
  40919. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 &&
  40920. _spectrum>=2))
  40921. cimg_forC(*this,c) {
  40922. Tfloat
  40923. *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
  40924. *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
  40925. CImg_3x3x3(I,Tfloat);
  40926. cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
  40927. const Tfloat
  40928. ix = (Incc - Ipcc)/2,
  40929. iy = (Icnc - Icpc)/2,
  40930. iz = (Iccn - Iccp)/2;
  40931. cimg_pragma_openmp(atomic) *(ptrd0++)+=ix*ix;
  40932. cimg_pragma_openmp(atomic) *(ptrd1++)+=ix*iy;
  40933. cimg_pragma_openmp(atomic) *(ptrd2++)+=ix*iz;
  40934. cimg_pragma_openmp(atomic) *(ptrd3++)+=iy*iy;
  40935. cimg_pragma_openmp(atomic) *(ptrd4++)+=iy*iz;
  40936. cimg_pragma_openmp(atomic) *(ptrd5++)+=iz*iz;
  40937. }
  40938. }
  40939. } else { // Forward/backward finite differences
  40940. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1048576 &&
  40941. _spectrum>=2))
  40942. cimg_forC(*this,c) {
  40943. Tfloat
  40944. *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2),
  40945. *ptrd3 = res.data(0,0,0,3), *ptrd4 = res.data(0,0,0,4), *ptrd5 = res.data(0,0,0,5);
  40946. CImg_3x3x3(I,Tfloat);
  40947. cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) {
  40948. const Tfloat
  40949. ixf = Incc - Iccc, ixb = Iccc - Ipcc, ixc = (Incc - Ipcc)/2,
  40950. iyf = Icnc - Iccc, iyb = Iccc - Icpc, iyc = (Icnc - Icpc)/2,
  40951. izf = Iccn - Iccc, izb = Iccc - Iccp, izc = (Iccn - Iccp)/2;
  40952. cimg_pragma_openmp(atomic) *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2;
  40953. cimg_pragma_openmp(atomic) *(ptrd1++)+=ixc*iyc;
  40954. cimg_pragma_openmp(atomic) *(ptrd2++)+=ixc*izc;
  40955. cimg_pragma_openmp(atomic) *(ptrd3++)+=(iyf*iyf + iyb*iyb)/2;
  40956. cimg_pragma_openmp(atomic) *(ptrd4++)+=iyc*izc;
  40957. cimg_pragma_openmp(atomic) *(ptrd5++)+=(izf*izf + izb*izb)/2;
  40958. }
  40959. }
  40960. }
  40961. } else { // 2D
  40962. res.assign(_width,_height,_depth,3,0);
  40963. if (!is_fwbw_scheme) { // Classical central finite differences
  40964. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 &&
  40965. _depth*_spectrum>=2))
  40966. cimg_forC(*this,c) {
  40967. Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
  40968. CImg_3x3(I,Tfloat);
  40969. cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
  40970. const Tfloat
  40971. ix = (Inc - Ipc)/2,
  40972. iy = (Icn - Icp)/2;
  40973. cimg_pragma_openmp(atomic) *(ptrd0++)+=ix*ix;
  40974. cimg_pragma_openmp(atomic) *(ptrd1++)+=ix*iy;
  40975. cimg_pragma_openmp(atomic) *(ptrd2++)+=iy*iy;
  40976. }
  40977. }
  40978. } else { // Forward/backward finite differences (version 2)
  40979. cimg_pragma_openmp(parallel for cimg_openmp_if(_width*_height>=(cimg_openmp_sizefactor)*1048576 &&
  40980. _depth*_spectrum>=2))
  40981. cimg_forC(*this,c) {
  40982. Tfloat *ptrd0 = res.data(0,0,0,0), *ptrd1 = res.data(0,0,0,1), *ptrd2 = res.data(0,0,0,2);
  40983. CImg_3x3(I,Tfloat);
  40984. cimg_for3x3(*this,x,y,0,c,I,Tfloat) {
  40985. const Tfloat
  40986. ixf = Inc - Icc, ixb = Icc - Ipc, ixc = (Inc - Ipc)/2,
  40987. iyf = Icn - Icc, iyb = Icc - Icp, iyc = (Icn - Icp)/2;
  40988. cimg_pragma_openmp(atomic) *(ptrd0++)+=(ixf*ixf + ixb*ixb)/2;
  40989. cimg_pragma_openmp(atomic) *(ptrd1++)+=ixc*iyc;
  40990. cimg_pragma_openmp(atomic) *(ptrd2++)+=(iyf*iyf + iyb*iyb)/2;
  40991. }
  40992. }
  40993. }
  40994. }
  40995. return res;
  40996. }
  40997. //! Compute field of diffusion tensors for edge-preserving smoothing.
  40998. /**
  40999. \param sharpness Sharpness
  41000. \param anisotropy Anisotropy
  41001. \param alpha Standard deviation of the gradient blur.
  41002. \param sigma Standard deviation of the structure tensor blur.
  41003. \param is_sqrt Tells if the square root of the tensor field is computed instead.
  41004. **/
  41005. CImg<T>& diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f,
  41006. const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) {
  41007. CImg<Tfloat> res;
  41008. const float
  41009. nsharpness = std::max(sharpness,1e-5f),
  41010. power1 = (is_sqrt?0.5f:1)*nsharpness,
  41011. power2 = power1/(1e-7f + 1 - anisotropy);
  41012. blur(alpha).normalize(0,(T)255);
  41013. if (_depth>1) { // 3D
  41014. get_structure_tensors().move_to(res).blur(sigma);
  41015. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
  41016. _height*_depth>=(cimg_openmp_sizefactor)*256))
  41017. cimg_forYZ(*this,y,z) {
  41018. Tfloat
  41019. *ptrd0 = res.data(0,y,z,0), *ptrd1 = res.data(0,y,z,1), *ptrd2 = res.data(0,y,z,2),
  41020. *ptrd3 = res.data(0,y,z,3), *ptrd4 = res.data(0,y,z,4), *ptrd5 = res.data(0,y,z,5);
  41021. CImg<floatT> val(3), vec(3,3);
  41022. cimg_forX(*this,x) {
  41023. res.get_tensor_at(x,y,z).symmetric_eigen(val,vec);
  41024. const float
  41025. _l1 = val[2], _l2 = val[1], _l3 = val[0],
  41026. l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0, l3 = _l3>0?_l3:0,
  41027. ux = vec(0,0), uy = vec(0,1), uz = vec(0,2),
  41028. vx = vec(1,0), vy = vec(1,1), vz = vec(1,2),
  41029. wx = vec(2,0), wy = vec(2,1), wz = vec(2,2),
  41030. n1 = (float)std::pow(1 + l1 + l2 + l3,-power1),
  41031. n2 = (float)std::pow(1 + l1 + l2 + l3,-power2);
  41032. *(ptrd0++) = n1*(ux*ux + vx*vx) + n2*wx*wx;
  41033. *(ptrd1++) = n1*(ux*uy + vx*vy) + n2*wx*wy;
  41034. *(ptrd2++) = n1*(ux*uz + vx*vz) + n2*wx*wz;
  41035. *(ptrd3++) = n1*(uy*uy + vy*vy) + n2*wy*wy;
  41036. *(ptrd4++) = n1*(uy*uz + vy*vz) + n2*wy*wz;
  41037. *(ptrd5++) = n1*(uz*uz + vz*vz) + n2*wz*wz;
  41038. }
  41039. }
  41040. } else { // for 2D images
  41041. get_structure_tensors().move_to(res).blur(sigma);
  41042. cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*256 &&
  41043. _height>=(cimg_openmp_sizefactor)*256))
  41044. cimg_forY(*this,y) {
  41045. Tfloat *ptrd0 = res.data(0,y,0,0), *ptrd1 = res.data(0,y,0,1), *ptrd2 = res.data(0,y,0,2);
  41046. CImg<floatT> val(2), vec(2,2);
  41047. cimg_forX(*this,x) {
  41048. res.get_tensor_at(x,y).symmetric_eigen(val,vec);
  41049. const float
  41050. _l1 = val[1], _l2 = val[0],
  41051. l1 = _l1>0?_l1:0, l2 = _l2>0?_l2:0,
  41052. ux = vec(1,0), uy = vec(1,1),
  41053. vx = vec(0,0), vy = vec(0,1),
  41054. n1 = (float)std::pow(1 + l1 + l2,-power1),
  41055. n2 = (float)std::pow(1 + l1 + l2,-power2);
  41056. *(ptrd0++) = n1*ux*ux + n2*vx*vx;
  41057. *(ptrd1++) = n1*ux*uy + n2*vx*vy;
  41058. *(ptrd2++) = n1*uy*uy + n2*vy*vy;
  41059. }
  41060. }
  41061. }
  41062. return res.move_to(*this);
  41063. }
  41064. //! Compute field of diffusion tensors for edge-preserving smoothing \newinstance.
  41065. CImg<Tfloat> get_diffusion_tensors(const float sharpness=0.7f, const float anisotropy=0.6f,
  41066. const float alpha=0.6f, const float sigma=1.1f, const bool is_sqrt=false) const {
  41067. return CImg<Tfloat>(*this,false).diffusion_tensors(sharpness,anisotropy,alpha,sigma,is_sqrt);
  41068. }
  41069. //! Estimate displacement field between two images.
  41070. /**
  41071. \param source Reference image.
  41072. \param smoothness Smoothness of estimated displacement field.
  41073. \param precision Precision required for algorithm convergence.
  41074. \param nb_scales Number of scales used to estimate the displacement field.
  41075. \param iteration_max Maximum number of iterations allowed for one scale.
  41076. \param is_backward If false, match I2(X + U(X)) = I1(X), else match I2(X) = I1(X - U(X)).
  41077. \param guide Image used as the initial correspondence estimate for the algorithm.
  41078. 'guide' may have a last channel with boolean values (0=false | other=true) that
  41079. tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask).
  41080. **/
  41081. CImg<T>& displacement(const CImg<T>& source, const float smoothness=0.1f, const float precision=5.f,
  41082. const unsigned int nb_scales=0, const unsigned int iteration_max=10000,
  41083. const bool is_backward=false,
  41084. const CImg<floatT>& guide=CImg<floatT>::const_empty()) {
  41085. return get_displacement(source,smoothness,precision,nb_scales,iteration_max,is_backward,guide).
  41086. move_to(*this);
  41087. }
  41088. //! Estimate displacement field between two images \newinstance.
  41089. CImg<floatT> get_displacement(const CImg<T>& source,
  41090. const float smoothness=0.1f, const float precision=5.f,
  41091. const unsigned int nb_scales=0, const unsigned int iteration_max=10000,
  41092. const bool is_backward=false,
  41093. const CImg<floatT>& guide=CImg<floatT>::const_empty()) const {
  41094. if (is_empty() || !source) return +*this;
  41095. if (!is_sameXYZC(source))
  41096. throw CImgArgumentException(_cimg_instance
  41097. "displacement(): Instance and source image (%u,%u,%u,%u,%p) have "
  41098. "different dimensions.",
  41099. cimg_instance,
  41100. source._width,source._height,source._depth,source._spectrum,source._data);
  41101. if (precision<0)
  41102. throw CImgArgumentException(_cimg_instance
  41103. "displacement(): Invalid specified precision %g "
  41104. "(should be >=0)",
  41105. cimg_instance,
  41106. precision);
  41107. const bool is_3d = source._depth>1;
  41108. const unsigned int constraint = is_3d?3:2;
  41109. if (guide &&
  41110. (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum<constraint))
  41111. throw CImgArgumentException(_cimg_instance
  41112. "displacement(): Specified guide (%u,%u,%u,%u,%p) "
  41113. "has invalid dimensions.",
  41114. cimg_instance,
  41115. guide._width,guide._height,guide._depth,guide._spectrum,guide._data);
  41116. const unsigned int
  41117. mins = is_3d?cimg::min(_width,_height,_depth):std::min(_width,_height),
  41118. _nb_scales = nb_scales>0?nb_scales:
  41119. (unsigned int)cimg::round(std::log(mins/8.)/std::log(1.5),1,1);
  41120. const float _precision = (float)std::pow(10.,-(double)precision);
  41121. float sm, sM = source.max_min(sm), tm, tM = max_min(tm);
  41122. const float sdelta = sm==sM?1:(sM - sm), tdelta = tm==tM?1:(tM - tm);
  41123. CImg<floatT> U, V;
  41124. floatT bound = 0;
  41125. for (int scale = (int)_nb_scales - 1; scale>=0; --scale) {
  41126. const float factor = (float)std::pow(1.5,(double)scale);
  41127. const unsigned int
  41128. _sw = (unsigned int)(_width/factor), sw = _sw?_sw:1,
  41129. _sh = (unsigned int)(_height/factor), sh = _sh?_sh:1,
  41130. _sd = (unsigned int)(_depth/factor), sd = _sd?_sd:1;
  41131. if (sw<5 && sh<5 && (!is_3d || sd<5)) continue; // Skip too small scales
  41132. const CImg<Tfloat>
  41133. I1 = (source.get_resize(sw,sh,sd,-100,2)-=sm)/=sdelta,
  41134. I2 = (get_resize(I1,2)-=tm)/=tdelta;
  41135. if (guide._spectrum>constraint) guide.get_resize(I2._width,I2._height,I2._depth,-100,1).move_to(V);
  41136. if (U) (U*=1.5f).resize(I2._width,I2._height,I2._depth,-100,3);
  41137. else {
  41138. if (guide)
  41139. guide.get_shared_channels(0,is_3d?2:1).get_resize(I2._width,I2._height,I2._depth,-100,2).move_to(U);
  41140. else U.assign(I2._width,I2._height,I2._depth,is_3d?3:2,0);
  41141. }
  41142. float dt = 2, energy = cimg::type<float>::max();
  41143. const CImgList<Tfloat> dI = is_backward?I1.get_gradient():I2.get_gradient();
  41144. cimg_abort_init;
  41145. for (unsigned int iteration = 0; iteration<iteration_max; ++iteration) {
  41146. cimg_abort_test;
  41147. float _energy = 0;
  41148. if (is_3d) { // 3D version
  41149. if (smoothness>=0) // Isotropic regularization
  41150. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
  41151. cimg_openmp_if(_height*_depth>=(cimg_openmp_sizefactor)*8 &&
  41152. _width>=(cimg_openmp_sizefactor)*16)
  41153. reduction(+:_energy))
  41154. cimg_forYZ(U,y,z) {
  41155. const int
  41156. _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y,
  41157. _p1z = z?z - 1:0, _n1z = z<U.depth() - 1?z + 1:z;
  41158. cimg_for3X(U,x) {
  41159. const float
  41160. X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0),
  41161. Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1),
  41162. Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2);
  41163. float delta_I = 0, _energy_regul = 0;
  41164. if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c));
  41165. else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,z,c) - I2._linear_atXYZ(X,Y,Z,c));
  41166. cimg_forC(U,c) {
  41167. const float
  41168. Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)),
  41169. Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)),
  41170. Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)),
  41171. Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c),
  41172. Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c),
  41173. Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c);
  41174. U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(delta_I*dI[c]._linear_atXYZ(X,Y,Z) +
  41175. smoothness* ( Uxx + Uyy + Uzz)))/(1 + 6*smoothness*dt);
  41176. _energy_regul+=Ux*Ux + Uy*Uy + Uz*Uz;
  41177. }
  41178. if (is_backward) { // Constraint displacement vectors to stay in image
  41179. if (U(x,y,z,0)>x) U(x,y,z,0) = (float)x;
  41180. if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y;
  41181. if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z;
  41182. bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound;
  41183. bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound;
  41184. bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound;
  41185. } else {
  41186. if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x;
  41187. if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y;
  41188. if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z;
  41189. bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound;
  41190. bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound;
  41191. bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound;
  41192. }
  41193. _energy+=delta_I*delta_I + smoothness*_energy_regul;
  41194. }
  41195. if (V) cimg_forXYZ(V,_x,_y,_z) if (V(_x,_y,_z,3)) { // Apply constraints
  41196. U(_x,_y,_z,0) = V(_x,_y,_z,0)/factor;
  41197. U(_x,_y,_z,1) = V(_x,_y,_z,1)/factor;
  41198. U(_x,_y,_z,2) = V(_x,_y,_z,2)/factor;
  41199. }
  41200. } else { // Anisotropic regularization
  41201. const float nsmoothness = -smoothness;
  41202. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
  41203. cimg_openmp_if(_height*_depth>=(cimg_openmp_sizefactor)*8 &&
  41204. _width>=(cimg_openmp_sizefactor)*16)
  41205. reduction(+:_energy))
  41206. cimg_forYZ(U,y,z) {
  41207. const int
  41208. _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y,
  41209. _p1z = z?z - 1:0, _n1z = z<U.depth() - 1?z + 1:z;
  41210. cimg_for3X(U,x) {
  41211. const float
  41212. X = is_backward?x - U(x,y,z,0):x + U(x,y,z,0),
  41213. Y = is_backward?y - U(x,y,z,1):y + U(x,y,z,1),
  41214. Z = is_backward?z - U(x,y,z,2):z + U(x,y,z,2);
  41215. float delta_I = 0, _energy_regul = 0;
  41216. if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXYZ(X,Y,Z,c) - I2(x,y,z,c));
  41217. else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,z,c) - I2._linear_atXYZ(X,Y,Z,c));
  41218. cimg_forC(U,c) {
  41219. const float
  41220. Ux = 0.5f*(U(_n1x,y,z,c) - U(_p1x,y,z,c)),
  41221. Uy = 0.5f*(U(x,_n1y,z,c) - U(x,_p1y,z,c)),
  41222. Uz = 0.5f*(U(x,y,_n1z,c) - U(x,y,_p1z,c)),
  41223. N2 = Ux*Ux + Uy*Uy + Uz*Uz,
  41224. N = std::sqrt(N2),
  41225. N3 = 1e-5f + N2*N,
  41226. coef_a = (1 - Ux*Ux/N2)/N,
  41227. coef_b = -2*Ux*Uy/N3,
  41228. coef_c = -2*Ux*Uz/N3,
  41229. coef_d = (1 - Uy*Uy/N2)/N,
  41230. coef_e = -2*Uy*Uz/N3,
  41231. coef_f = (1 - Uz*Uz/N2)/N,
  41232. Uxx = U(_n1x,y,z,c) + U(_p1x,y,z,c),
  41233. Uyy = U(x,_n1y,z,c) + U(x,_p1y,z,c),
  41234. Uzz = U(x,y,_n1z,c) + U(x,y,_p1z,c),
  41235. Uxy = 0.25f*(U(_n1x,_n1y,z,c) + U(_p1x,_p1y,z,c) - U(_n1x,_p1y,z,c) - U(_n1x,_p1y,z,c)),
  41236. Uxz = 0.25f*(U(_n1x,y,_n1z,c) + U(_p1x,y,_p1z,c) - U(_n1x,y,_p1z,c) - U(_n1x,y,_p1z,c)),
  41237. Uyz = 0.25f*(U(x,_n1y,_n1z,c) + U(x,_p1y,_p1z,c) - U(x,_n1y,_p1z,c) - U(x,_n1y,_p1z,c));
  41238. U(x,y,z,c) = (float)(U(x,y,z,c) + dt*(delta_I*dI[c]._linear_atXYZ(X,Y,Z) +
  41239. nsmoothness* ( coef_a*Uxx + coef_b*Uxy +
  41240. coef_c*Uxz + coef_d*Uyy +
  41241. coef_e*Uyz + coef_f*Uzz ))
  41242. )/(1 + 2*(coef_a + coef_d + coef_f)*nsmoothness*dt);
  41243. _energy_regul+=N;
  41244. }
  41245. if (is_backward) { // Constraint displacement vectors to stay in image
  41246. if (U(x,y,z,0)>x) U(x,y,z,0) = (float)x;
  41247. if (U(x,y,z,1)>y) U(x,y,z,1) = (float)y;
  41248. if (U(x,y,z,2)>z) U(x,y,z,2) = (float)z;
  41249. bound = (float)x - _width; if (U(x,y,z,0)<=bound) U(x,y,z,0) = bound;
  41250. bound = (float)y - _height; if (U(x,y,z,1)<=bound) U(x,y,z,1) = bound;
  41251. bound = (float)z - _depth; if (U(x,y,z,2)<=bound) U(x,y,z,2) = bound;
  41252. } else {
  41253. if (U(x,y,z,0)<-x) U(x,y,z,0) = -(float)x;
  41254. if (U(x,y,z,1)<-y) U(x,y,z,1) = -(float)y;
  41255. if (U(x,y,z,2)<-z) U(x,y,z,2) = -(float)z;
  41256. bound = (float)_width - x; if (U(x,y,z,0)>=bound) U(x,y,z,0) = bound;
  41257. bound = (float)_height - y; if (U(x,y,z,1)>=bound) U(x,y,z,1) = bound;
  41258. bound = (float)_depth - z; if (U(x,y,z,2)>=bound) U(x,y,z,2) = bound;
  41259. }
  41260. _energy+=delta_I*delta_I + nsmoothness*_energy_regul;
  41261. }
  41262. if (V) cimg_forXYZ(V,_x,_y,_z) if (V(_x,_y,_z,3)) { // Apply constraints
  41263. U(_x,_y,_z,0) = V(_x,_y,_z,0)/factor;
  41264. U(_x,_y,_z,1) = V(_x,_y,_z,1)/factor;
  41265. U(_x,_y,_z,2) = V(_x,_y,_z,2)/factor;
  41266. }
  41267. }
  41268. }
  41269. } else { // 2D version
  41270. if (smoothness>=0) // Isotropic regularization
  41271. cimg_pragma_openmp(parallel for cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*8 &&
  41272. _width>=(cimg_openmp_sizefactor)*16) reduction(+:_energy))
  41273. cimg_forY(U,y) {
  41274. const int _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y;
  41275. cimg_for3X(U,x) {
  41276. const float
  41277. X = is_backward?x - U(x,y,0):x + U(x,y,0),
  41278. Y = is_backward?y - U(x,y,1):y + U(x,y,1);
  41279. float delta_I = 0, _energy_regul = 0;
  41280. if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXY(X,Y,c) - I2(x,y,c));
  41281. else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,c) - I2._linear_atXY(X,Y,c));
  41282. cimg_forC(U,c) {
  41283. const float
  41284. Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)),
  41285. Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)),
  41286. Uxx = U(_n1x,y,c) + U(_p1x,y,c),
  41287. Uyy = U(x,_n1y,c) + U(x,_p1y,c);
  41288. U(x,y,c) = (float)(U(x,y,c) + dt*(delta_I*dI[c]._linear_atXY(X,Y) +
  41289. smoothness*( Uxx + Uyy )))/(1 + 4*smoothness*dt);
  41290. _energy_regul+=Ux*Ux + Uy*Uy;
  41291. }
  41292. if (is_backward) { // Constraint displacement vectors to stay in image
  41293. if (U(x,y,0)>x) U(x,y,0) = (float)x;
  41294. if (U(x,y,1)>y) U(x,y,1) = (float)y;
  41295. bound = (float)x - _width; if (U(x,y,0)<=bound) U(x,y,0) = bound;
  41296. bound = (float)y - _height; if (U(x,y,1)<=bound) U(x,y,1) = bound;
  41297. } else {
  41298. if (U(x,y,0)<-x) U(x,y,0) = -(float)x;
  41299. if (U(x,y,1)<-y) U(x,y,1) = -(float)y;
  41300. bound = (float)_width - x; if (U(x,y,0)>=bound) U(x,y,0) = bound;
  41301. bound = (float)_height - y; if (U(x,y,1)>=bound) U(x,y,1) = bound;
  41302. }
  41303. _energy+=delta_I*delta_I + smoothness*_energy_regul;
  41304. }
  41305. if (V) cimg_forXY(V,_x,_y) if (V(_x,_y,2)) { // Apply constraints
  41306. U(_x,_y,0) = V(_x,_y,0)/factor;
  41307. U(_x,_y,1) = V(_x,_y,1)/factor;
  41308. }
  41309. } else { // Anisotropic regularization
  41310. const float nsmoothness = -smoothness;
  41311. cimg_pragma_openmp(parallel for cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*8 &&
  41312. _width>=(cimg_openmp_sizefactor)*16) reduction(+:_energy))
  41313. cimg_forY(U,y) {
  41314. const int _p1y = y?y - 1:0, _n1y = y<U.height() - 1?y + 1:y;
  41315. cimg_for3X(U,x) {
  41316. const float
  41317. X = is_backward?x - U(x,y,0):x + U(x,y,0),
  41318. Y = is_backward?y - U(x,y,1):y + U(x,y,1);
  41319. float delta_I = 0, _energy_regul = 0;
  41320. if (is_backward) cimg_forC(I2,c) delta_I+=(float)(I1._linear_atXY(X,Y,c) - I2(x,y,c));
  41321. else cimg_forC(I2,c) delta_I+=(float)(I1(x,y,c) - I2._linear_atXY(X,Y,c));
  41322. cimg_forC(U,c) {
  41323. const float
  41324. Ux = 0.5f*(U(_n1x,y,c) - U(_p1x,y,c)),
  41325. Uy = 0.5f*(U(x,_n1y,c) - U(x,_p1y,c)),
  41326. N2 = Ux*Ux + Uy*Uy,
  41327. N = std::sqrt(N2),
  41328. N3 = 1e-5f + N2*N,
  41329. coef_a = Uy*Uy/N3,
  41330. coef_b = -2*Ux*Uy/N3,
  41331. coef_c = Ux*Ux/N3,
  41332. Uxx = U(_n1x,y,c) + U(_p1x,y,c),
  41333. Uyy = U(x,_n1y,c) + U(x,_p1y,c),
  41334. Uxy = 0.25f*(U(_n1x,_n1y,c) + U(_p1x,_p1y,c) - U(_n1x,_p1y,c) - U(_n1x,_p1y,c));
  41335. U(x,y,c) = (float)(U(x,y,c) + dt*(delta_I*dI[c]._linear_atXY(X,Y) +
  41336. nsmoothness*( coef_a*Uxx + coef_b*Uxy + coef_c*Uyy )))/
  41337. (1 + 2*(coef_a + coef_c)*nsmoothness*dt);
  41338. _energy_regul+=N;
  41339. }
  41340. if (is_backward) { // Constraint displacement vectors to stay in image
  41341. if (U(x,y,0)>x) U(x,y,0) = (float)x;
  41342. if (U(x,y,1)>y) U(x,y,1) = (float)y;
  41343. bound = (float)x - _width; if (U(x,y,0)<=bound) U(x,y,0) = bound;
  41344. bound = (float)y - _height; if (U(x,y,1)<=bound) U(x,y,1) = bound;
  41345. } else {
  41346. if (U(x,y,0)<-x) U(x,y,0) = -(float)x;
  41347. if (U(x,y,1)<-y) U(x,y,1) = -(float)y;
  41348. bound = (float)_width - x; if (U(x,y,0)>=bound) U(x,y,0) = bound;
  41349. bound = (float)_height - y; if (U(x,y,1)>=bound) U(x,y,1) = bound;
  41350. }
  41351. _energy+=delta_I*delta_I + nsmoothness*_energy_regul;
  41352. }
  41353. if (V) cimg_forXY(V,_x,_y) if (V(_x,_y,2)) { // Apply constraints
  41354. U(_x,_y,0) = V(_x,_y,0)/factor;
  41355. U(_x,_y,1) = V(_x,_y,1)/factor;
  41356. }
  41357. }
  41358. }
  41359. }
  41360. const float d_energy = (_energy - energy)/(sw*sh*sd);
  41361. if (d_energy<=0 && -d_energy<_precision) break;
  41362. if (d_energy>0) dt*=0.5f;
  41363. energy = _energy;
  41364. }
  41365. }
  41366. return U;
  41367. }
  41368. //! Compute correspondence map between two images, using a patch-matching algorithm.
  41369. /**
  41370. \param patch_image The image containing the reference patches to match with the instance image.
  41371. \param patch_width Width of the patch used for matching.
  41372. \param patch_height Height of the patch used for matching.
  41373. \param patch_depth Depth of the patch used for matching.
  41374. \param nb_iterations Number of patch-match iterations.
  41375. \param nb_randoms Number of randomization attempts (per pixel).
  41376. \param patch_penalization Penalization factor in score related patch occurrences.
  41377. if negative, also tells that identity result is not avoided.
  41378. \param guide Image used as the initial correspondence estimate for the algorithm.
  41379. 'guide' may have a last channel with boolean values (0=false | other=true) that
  41380. tells for each pixel if its correspondence vector is constrained to its initial value (constraint mask).
  41381. \param[out] matching_score Returned as the image of matching scores.
  41382. **/
  41383. template<typename t1, typename t2>
  41384. CImg<T>& matchpatch(const CImg<T>& patch_image,
  41385. const unsigned int patch_width,
  41386. const unsigned int patch_height,
  41387. const unsigned int patch_depth,
  41388. const unsigned int nb_iterations,
  41389. const unsigned int nb_randoms,
  41390. const float patch_penalization,
  41391. const CImg<t1> &guide,
  41392. CImg<t2> &matching_score) {
  41393. return get_matchpatch(patch_image,patch_width,patch_height,patch_depth,
  41394. nb_iterations,nb_randoms,patch_penalization,guide,matching_score).move_to(*this);
  41395. }
  41396. //! Compute correspondence map between two images, using the patch-match algorithm \newinstance.
  41397. template<typename t1, typename t2>
  41398. CImg<intT> get_matchpatch(const CImg<T>& patch_image,
  41399. const unsigned int patch_width,
  41400. const unsigned int patch_height,
  41401. const unsigned int patch_depth,
  41402. const unsigned int nb_iterations,
  41403. const unsigned int nb_randoms,
  41404. const float patch_penalization,
  41405. const CImg<t1> &guide,
  41406. CImg<t2> &matching_score) const {
  41407. return _matchpatch(patch_image,patch_width,patch_height,patch_depth,
  41408. nb_iterations,nb_randoms,patch_penalization,
  41409. guide,true,matching_score);
  41410. }
  41411. //! Compute correspondence map between two images, using the patch-match algorithm \overloading.
  41412. template<typename t>
  41413. CImg<T>& matchpatch(const CImg<T>& patch_image,
  41414. const unsigned int patch_width,
  41415. const unsigned int patch_height,
  41416. const unsigned int patch_depth,
  41417. const unsigned int nb_iterations=5,
  41418. const unsigned int nb_randoms=5,
  41419. const float patch_penalization=0,
  41420. const CImg<t> &guide=CImg<t>::const_empty()) {
  41421. return get_matchpatch(patch_image,patch_width,patch_height,patch_depth,
  41422. nb_iterations,nb_randoms,patch_penalization,guide).move_to(*this);
  41423. }
  41424. //! Compute correspondence map between two images, using the patch-match algorithm \overloading.
  41425. template<typename t>
  41426. CImg<intT> get_matchpatch(const CImg<T>& patch_image,
  41427. const unsigned int patch_width,
  41428. const unsigned int patch_height,
  41429. const unsigned int patch_depth,
  41430. const unsigned int nb_iterations=5,
  41431. const unsigned int nb_randoms=5,
  41432. const float patch_penalization=0,
  41433. const CImg<t> &guide=CImg<t>::const_empty()) const {
  41434. CImg<T> matching_score;
  41435. return _matchpatch(patch_image,patch_width,patch_height,patch_depth,
  41436. nb_iterations,nb_randoms,patch_penalization,guide,false,matching_score);
  41437. }
  41438. template<typename t1, typename t2>
  41439. CImg<intT> _matchpatch(const CImg<T>& patch_image,
  41440. const unsigned int patch_width,
  41441. const unsigned int patch_height,
  41442. const unsigned int patch_depth,
  41443. const unsigned int nb_iterations,
  41444. const unsigned int nb_randoms,
  41445. const float patch_penalization,
  41446. const CImg<t1> &guide,
  41447. const bool is_matching_score,
  41448. CImg<t2> &matching_score) const {
  41449. if (is_empty()) return CImg<intT>::const_empty();
  41450. if (patch_image._spectrum!=_spectrum)
  41451. throw CImgArgumentException(_cimg_instance
  41452. "matchpatch(): Instance image and specified patch image (%u,%u,%u,%u,%p) "
  41453. "have different spectrums.",
  41454. cimg_instance,
  41455. patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum,
  41456. patch_image._data);
  41457. if (patch_width>_width || patch_height>_height || patch_depth>_depth)
  41458. throw CImgArgumentException(_cimg_instance
  41459. "matchpatch(): Specified patch size %ux%ux%u is bigger than the dimensions "
  41460. "of the instance image.",
  41461. cimg_instance,patch_width,patch_height,patch_depth);
  41462. if (patch_width>patch_image._width || patch_height>patch_image._height || patch_depth>patch_image._depth)
  41463. throw CImgArgumentException(_cimg_instance
  41464. "matchpatch(): Specified patch size %ux%ux%u is bigger than the dimensions "
  41465. "of the patch image image (%u,%u,%u,%u,%p).",
  41466. cimg_instance,patch_width,patch_height,patch_depth,
  41467. patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum,
  41468. patch_image._data);
  41469. const unsigned int
  41470. _constraint = patch_image._depth>1?3:2,
  41471. constraint = guide._spectrum>_constraint?_constraint:0;
  41472. if (guide &&
  41473. (guide._width!=_width || guide._height!=_height || guide._depth!=_depth || guide._spectrum<_constraint))
  41474. throw CImgArgumentException(_cimg_instance
  41475. "matchpatch(): Specified guide (%u,%u,%u,%u,%p) has invalid dimensions "
  41476. "considering instance and patch image (%u,%u,%u,%u,%p).",
  41477. cimg_instance,
  41478. guide._width,guide._height,guide._depth,guide._spectrum,guide._data,
  41479. patch_image._width,patch_image._height,patch_image._depth,patch_image._spectrum,
  41480. patch_image._data);
  41481. CImg<intT> a_map(_width,_height,_depth,patch_image._depth>1?3:2);
  41482. CImg<ucharT> is_updated(_width,_height,_depth,1,3);
  41483. CImg<floatT> score(_width,_height,_depth), penalty;
  41484. const float _patch_penalization = cimg::abs(patch_penalization);
  41485. const bool allow_identity = patch_penalization>=0;
  41486. if (_patch_penalization!=0)
  41487. penalty.assign(patch_image._width,patch_image._height,patch_image._depth,1,0);
  41488. const int
  41489. psizew = (int)patch_width, psizew1 = psizew/2, psizew2 = psizew - psizew1 - 1,
  41490. psizeh = (int)patch_height, psizeh1 = psizeh/2, psizeh2 = psizeh - psizeh1 - 1,
  41491. psized = (int)patch_depth, psized1 = psized/2, psized2 = psized - psized1 - 1;
  41492. // Interleave image buffers to speed up patch comparison (more cache-friendly).
  41493. CImg<T> in_this = get_permute_axes("cxyz");
  41494. in_this._width = _width*_spectrum;
  41495. in_this._height = _height;
  41496. in_this._depth = _depth;
  41497. in_this._spectrum = 1;
  41498. CImg<T> in_patch = patch_image.get_permute_axes("cxyz");
  41499. in_patch._width = patch_image._width*patch_image._spectrum;
  41500. in_patch._height = patch_image._height;
  41501. in_patch._depth = patch_image._depth;
  41502. in_patch._spectrum = 1;
  41503. if (_depth>1 || patch_image._depth>1) { // 3D version
  41504. // Initialize correspondence map.
  41505. if (guide)
  41506. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if_size(_width,64))
  41507. cimg_forXYZ(*this,x,y,z) { // User-defined initialization
  41508. const int
  41509. cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1,
  41510. cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
  41511. cz1 = z<=psized1?z:(z<depth() - psized2?psized1:psized + z - depth()), cz2 = psized - cz1 - 1,
  41512. u = cimg::cut((int)guide(x,y,z,0),cx1,patch_image.width() - 1 - cx2),
  41513. v = cimg::cut((int)guide(x,y,z,1),cy1,patch_image.height() - 1 - cy2),
  41514. w = cimg::cut((int)guide(x,y,z,2),cz1,patch_image.depth() - 1 - cz2);
  41515. a_map(x,y,z,0) = u;
  41516. a_map(x,y,z,1) = v;
  41517. a_map(x,y,z,2) = w;
  41518. score(x,y,z) = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
  41519. x - cx1,y - cy1,z - cz1,
  41520. u - cx1,v - cy1,w - cz1,
  41521. u,v,w,0,allow_identity,cimg::type<float>::inf());
  41522. } else cimg_pragma_openmp(parallel cimg_openmp_if_size(_width,64)) {
  41523. cimg_uint64 rng = (cimg::_rand(),cimg::rng());
  41524. #if cimg_use_openmp!=0
  41525. rng+=omp_get_thread_num();
  41526. #endif
  41527. cimg_pragma_openmp(for cimg_openmp_collapse(2))
  41528. cimg_forXYZ(*this,x,y,z) { // Random initialization
  41529. const int
  41530. cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1,
  41531. cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
  41532. cz1 = z<=psized1?z:(z<depth() - psized2?psized1:psized + z - depth()), cz2 = psized - cz1 - 1,
  41533. u = (int)cimg::round(cimg::rand(cx1,patch_image.width() - 1 - cx2,&rng)),
  41534. v = (int)cimg::round(cimg::rand(cy1,patch_image.height() - 1 - cy2,&rng)),
  41535. w = (int)cimg::round(cimg::rand(cz1,patch_image.depth() - 1 - cz2,&rng));
  41536. a_map(x,y,z,0) = u;
  41537. a_map(x,y,z,1) = v;
  41538. a_map(x,y,z,2) = w;
  41539. score(x,y,z) = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
  41540. x - cx1,y - cy1,z - cz1,
  41541. u - cx1,v - cy1,w - cz1,
  41542. u,v,w,0,allow_identity,cimg::type<float>::inf());
  41543. }
  41544. cimg::srand(rng);
  41545. }
  41546. // Start iteration loop.
  41547. cimg_abort_init;
  41548. for (unsigned int iter = 0; iter<nb_iterations; ++iter) {
  41549. cimg_abort_test;
  41550. const bool is_backward = iter&1, is_forward = !is_backward;
  41551. const unsigned int cmask = is_backward?1:2, nmask = 3 - cmask;
  41552. cimg_pragma_openmp(parallel cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64)) {
  41553. cimg_uint64 rng = (cimg::_rand(),cimg::rng());
  41554. #if cimg_use_openmp!=0
  41555. rng+=omp_get_thread_num();
  41556. #endif
  41557. cimg_pragma_openmp(for cimg_openmp_collapse(2))
  41558. cimg_forXYZ(*this,X,Y,Z) {
  41559. const int
  41560. x = is_backward?width() - 1 - X:X,
  41561. y = is_backward?height() - 1 - Y:Y,
  41562. z = is_backward?depth() - 1 - Z:Z;
  41563. if (score(x,y,z)<=1e-5 || (constraint && guide(x,y,z,constraint)!=0)) continue;
  41564. const int
  41565. cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1,
  41566. cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
  41567. cz1 = z<=psized1?z:(z<depth() - psized2?psized1:psized + z - depth()), cz2 = psized - cz1 - 1,
  41568. xp = x - cx1,
  41569. yp = y - cy1,
  41570. zp = z - cz1;
  41571. 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;
  41572. const float best_score0 = score(x,y,z);
  41573. float best_score = best_score0, s;
  41574. if (is_forward && x>0 && (is_updated(x - 1,y,z)&cmask)) { // Compare with left neighbor
  41575. u = a_map(x - 1,y,z,0);
  41576. v = a_map(x - 1,y,z,1);
  41577. w = a_map(x - 1,y,z,2);
  41578. if (u>=cx1 - 1 && u<patch_image.width() - 1 - cx2 &&
  41579. v>=cy1 && v<patch_image.height() - cy2 &&
  41580. w>=cz1 && w<patch_image.depth() - cz2) {
  41581. s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
  41582. xp,yp,zp,u + 1 - cx1,v - cy1,w - cz1,
  41583. u,v,w,_patch_penalization,allow_identity,best_score);
  41584. if (s<best_score) { best_u = u + 1; best_v = v; best_w = w; best_score = s; }
  41585. }
  41586. }
  41587. if (is_forward && y>0 && (is_updated(x,y - 1,z)&cmask)) { // Compare with up neighbor
  41588. u = a_map(x,y - 1,z,0);
  41589. v = a_map(x,y - 1,z,1);
  41590. w = a_map(x,y - 1,z,2);
  41591. if (u>=cx1 && u<patch_image.width() - cx2 &&
  41592. v>=cy1 - 1 && v<patch_image.height() - 1 - cy2 &&
  41593. w>=cz1 && w<patch_image.depth() - cz2) {
  41594. s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
  41595. xp,yp,zp,u - cx1,v + 1 - cy1,w - cz1,
  41596. u,v,w,_patch_penalization,allow_identity,best_score);
  41597. if (s<best_score) { best_u = u; best_v = v + 1; best_w = w; best_score = s; }
  41598. }
  41599. }
  41600. if (is_forward && z>0 && (is_updated(x,y,z - 1)&cmask)) { // Compare with backward neighbor
  41601. u = a_map(x,y,z - 1,0);
  41602. v = a_map(x,y,z - 1,1);
  41603. w = a_map(x,y,z - 1,2);
  41604. if (u>=cx1 && u<patch_image.width() - cx2 &&
  41605. v>=cy1 && v<patch_image.height() - cy2 &&
  41606. w>=cz1 - 1 && w<patch_image.depth() - 1 - cz2) {
  41607. s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
  41608. xp,yp,zp,u - cx1,v - cy1,w + 1 - cz1,
  41609. u,v,w,_patch_penalization,allow_identity,best_score);
  41610. if (s<best_score) { best_u = u; best_v = v; best_w = w + 1; best_score = s; }
  41611. }
  41612. }
  41613. if (is_backward && x<width() - 1 && (is_updated(x + 1,y,z)&cmask)) { // Compare with right neighbor
  41614. u = a_map(x + 1,y,z,0);
  41615. v = a_map(x + 1,y,z,1);
  41616. w = a_map(x + 1,y,z,2);
  41617. if (u>=cx1 + 1 && u<patch_image.width() + 1 - cx2 &&
  41618. v>=cy1 && v<patch_image.height() - cy2 &&
  41619. w>=cz1 && w<patch_image.depth() - cz2) {
  41620. s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
  41621. xp,yp,zp,u - 1 - cx1,v - cy1,w - cz1,
  41622. u,v,w,_patch_penalization,allow_identity,best_score);
  41623. if (s<best_score) { best_u = u - 1; best_v = v; best_w = w; best_score = s; }
  41624. }
  41625. }
  41626. if (is_backward && y<height() - 1 && (is_updated(x,y + 1,z)&cmask)) { // Compare with bottom neighbor
  41627. u = a_map(x,y + 1,z,0);
  41628. v = a_map(x,y + 1,z,1);
  41629. w = a_map(x,y + 1,z,2);
  41630. if (u>=cx1 && u<patch_image.width() - cx2 &&
  41631. v>=cy1 + 1 && v<patch_image.height() + 1 - cy2 &&
  41632. w>=cz1 && w<patch_image.depth() - cz2) {
  41633. s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
  41634. xp,yp,zp,u - cx1,v - 1 - cy1,w - cz1,
  41635. u,v,w,_patch_penalization,allow_identity,best_score);
  41636. if (s<best_score) { best_u = u; best_v = v - 1; best_w = w; best_score = s; }
  41637. }
  41638. }
  41639. if (is_backward && z<depth() - 1 && (is_updated(x,y,z + 1)&cmask)) { // Compare with forward neighbor
  41640. u = a_map(x,y,z + 1,0);
  41641. v = a_map(x,y,z + 1,1);
  41642. w = a_map(x,y,z + 1,2);
  41643. if (u>=cx1 && u<patch_image.width() - cx2 &&
  41644. v>=cy1 && v<patch_image.height() - cy2 &&
  41645. w>=cz1 + 1 && w<patch_image.depth() + 1 - cz2) {
  41646. s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
  41647. xp,yp,zp,u - cx1,v - cy1,w - 1 - cz1,
  41648. u,v,w,_patch_penalization,allow_identity,best_score);
  41649. if (s<best_score) { best_u = u; best_v = v; best_w = w - 1; best_score = s; }
  41650. }
  41651. }
  41652. float
  41653. dw = (float)patch_image.width(),
  41654. dh = (float)patch_image.height(),
  41655. dd = (float)patch_image.depth();
  41656. for (unsigned int i = 0; i<nb_randoms; ++i) {
  41657. u = (int)cimg::round(cimg::rand(std::max((float)cx1,best_u - dw),
  41658. std::min(patch_image.width() - 1.f - cx2,best_u + dw),&rng));
  41659. v = (int)cimg::round(cimg::rand(std::max((float)cy1,best_v - dh),
  41660. std::min(patch_image.height() - 1.f - cy2,best_v + dh),&rng));
  41661. w = (int)cimg::round(cimg::rand(std::max((float)cz1,best_w - dd),
  41662. std::min(patch_image.depth() - 1.f - cz2,best_w + dd),&rng));
  41663. if (u!=best_u || v!=best_v || w!=best_w) {
  41664. s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
  41665. xp,yp,zp,u - cx1,v - cy1,w - cz1,
  41666. u,v,w,_patch_penalization,allow_identity,best_score);
  41667. if (s<best_score) { best_u = u; best_v = v; best_w = w; best_score = s; }
  41668. dw = std::max(5.f,dw*0.5f); dh = std::max(5.f,dh*0.5f); dd = std::max(5.f,dd*0.5f);
  41669. }
  41670. }
  41671. if (best_score<best_score0) {
  41672. if (_patch_penalization!=0) {
  41673. float &p_penalty = penalty(a_map(x,y,z,0),a_map(x,y,z,1),a_map(x,y,z,2));
  41674. if (p_penalty) cimg_pragma_openmp(atomic) --p_penalty;
  41675. }
  41676. a_map(x,y,z,0) = best_u;
  41677. a_map(x,y,z,1) = best_v;
  41678. a_map(x,y,z,2) = best_w;
  41679. score(x,y,z) = best_score;
  41680. is_updated(x,y,z) = 3;
  41681. } else is_updated(x,y,z)&=~nmask;
  41682. if (_patch_penalization!=0) cimg_pragma_openmp(atomic) ++penalty(best_u,best_v,best_w);
  41683. }
  41684. cimg::srand(rng);
  41685. }
  41686. // Update score according to new penalties.
  41687. if (penalty)
  41688. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64))
  41689. cimg_forXYZ(score,x,y,z) {
  41690. const float p_score = score(x,y,z);
  41691. const int
  41692. cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()),
  41693. cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()),
  41694. cz1 = z<=psized1?z:(z<depth() - psized2?psized1:psized + z - depth()),
  41695. xp = x - cx1,
  41696. yp = y - cy1,
  41697. zp = z - cz1,
  41698. u = a_map(x,y,z,0),
  41699. v = a_map(x,y,z,1),
  41700. w = a_map(x,y,z,2);
  41701. const float n_score = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,patch_depth,_spectrum,
  41702. xp,yp,zp,u - cx1,v - cy1,w - cz1,
  41703. u,v,w,_patch_penalization,allow_identity,cimg::type<float>::inf());
  41704. if (n_score!=p_score) { score(x,y,z) = n_score; is_updated(x,y) = 3; }
  41705. }
  41706. }
  41707. } else { // 2D version
  41708. // Initialize correspondence map.
  41709. if (guide)
  41710. cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width,64))
  41711. cimg_forXY(*this,x,y) { // User-defined initialization
  41712. const int
  41713. cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1,
  41714. cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
  41715. u = cimg::cut((int)guide(x,y,0),cx1,patch_image.width() - 1 - cx2),
  41716. v = cimg::cut((int)guide(x,y,1),cy1,patch_image.height() - 1 - cy2);
  41717. a_map(x,y,0) = u;
  41718. a_map(x,y,1) = v;
  41719. score(x,y) = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum,
  41720. x - cx1,y - cy1,u - cx1,v - cy1,
  41721. u,v,0,allow_identity,cimg::type<float>::inf());
  41722. } else cimg_pragma_openmp(parallel cimg_openmp_if_size(_width,64)) {
  41723. cimg_uint64 rng = (cimg::_rand(),cimg::rng());
  41724. #if cimg_use_openmp!=0
  41725. rng+=omp_get_thread_num();
  41726. #endif
  41727. cimg_pragma_openmp(for)
  41728. cimg_forXY(*this,x,y) { // Random initialization
  41729. const int
  41730. cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1,
  41731. cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
  41732. u = (int)cimg::round(cimg::rand(cx1,patch_image.width() - 1 - cx2,&rng)),
  41733. v = (int)cimg::round(cimg::rand(cy1,patch_image.height() - 1 - cy2,&rng));
  41734. a_map(x,y,0) = u;
  41735. a_map(x,y,1) = v;
  41736. score(x,y) = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum,
  41737. x - cx1,y - cy1,u - cx1,v - cy1,
  41738. u,v,0,allow_identity,cimg::type<float>::inf());
  41739. }
  41740. cimg::srand(rng);
  41741. }
  41742. // Start iteration loop.
  41743. cimg_abort_init;
  41744. for (unsigned int iter = 0; iter<nb_iterations; ++iter) {
  41745. cimg_abort_test;
  41746. const bool is_backward = iter&1, is_forward = !is_backward;
  41747. const unsigned int cmask = is_backward?1:2, nmask = 3 - cmask;
  41748. cimg_pragma_openmp(parallel cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64)) {
  41749. cimg_uint64 rng = (cimg::_rand(),cimg::rng());
  41750. #if cimg_use_openmp!=0
  41751. rng+=omp_get_thread_num();
  41752. #endif
  41753. cimg_pragma_openmp(for)
  41754. cimg_forXY(*this,X,Y) {
  41755. const int
  41756. x = is_backward?width() - 1 - X:X,
  41757. y = is_backward?height() - 1 - Y:Y;
  41758. if (score(x,y)<=1e-5 || (constraint && guide(x,y,constraint)!=0)) continue;
  41759. const int
  41760. cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()), cx2 = psizew - cx1 - 1,
  41761. cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()), cy2 = psizeh - cy1 - 1,
  41762. xp = x - cx1,
  41763. yp = y - cy1;
  41764. int best_u = a_map(x,y,0), best_v = a_map(x,y,1), u, v;
  41765. const float best_score0 = score(x,y);
  41766. float best_score = best_score0, s;
  41767. if (is_forward && x>0 && (is_updated(x - 1,y)&cmask)) { // Compare with left neighbor
  41768. u = a_map(x - 1,y,0);
  41769. v = a_map(x - 1,y,1);
  41770. if (u>=cx1 - 1 && u<patch_image.width() - 1 - cx2 &&
  41771. v>=cy1 && v<patch_image.height() - cy2) {
  41772. s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum,
  41773. xp,yp,u + 1 - cx1,v - cy1,
  41774. u,v,_patch_penalization,allow_identity,best_score);
  41775. if (s<best_score) { best_u = u + 1; best_v = v; best_score = s; }
  41776. }
  41777. }
  41778. if (is_forward && y>0 && (is_updated(x,y - 1)&cmask)) { // Compare with up neighbor
  41779. u = a_map(x,y - 1,0);
  41780. v = a_map(x,y - 1,1);
  41781. if (u>=cx1 && u<patch_image.width() - cx2 &&
  41782. v>=cy1 - 1 && v<patch_image.height() - 1 - cy2) {
  41783. s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum,
  41784. xp,yp,u - cx1,v + 1 - cy1,
  41785. u,v,_patch_penalization,allow_identity,best_score);
  41786. if (s<best_score) { best_u = u; best_v = v + 1; best_score = s; }
  41787. }
  41788. }
  41789. if (is_backward && x<width() - 1 && (is_updated(x + 1,y)&cmask)) { // Compare with right neighbor
  41790. u = a_map(x + 1,y,0);
  41791. v = a_map(x + 1,y,1);
  41792. if (u>=cx1 + 1 && u<patch_image.width() + 1 - cx2 &&
  41793. v>=cy1 && v<patch_image.height() - cy2) {
  41794. s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum,
  41795. xp,yp,u - 1 - cx1,v - cy1,
  41796. u,v,_patch_penalization,allow_identity,best_score);
  41797. if (s<best_score) { best_u = u - 1; best_v = v; best_score = s; }
  41798. }
  41799. }
  41800. if (is_backward && y<height() - 1 && (is_updated(x,y + 1)&cmask)) { // Compare with bottom neighbor
  41801. u = a_map(x,y + 1,0);
  41802. v = a_map(x,y + 1,1);
  41803. if (u>=cx1 && u<patch_image.width() - cx2 &&
  41804. v>=cy1 + 1 && v<patch_image.height() + 1 - cy2) {
  41805. s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum,
  41806. xp,yp,u - cx1,v - 1 - cy1,
  41807. u,v,_patch_penalization,allow_identity,best_score);
  41808. if (s<best_score) { best_u = u; best_v = v - 1; best_score = s; }
  41809. }
  41810. }
  41811. float
  41812. dw = (float)patch_image.width(),
  41813. dh = (float)patch_image.height();
  41814. for (unsigned int i = 0; i<nb_randoms; ++i) {
  41815. u = (int)cimg::round(cimg::rand(std::max((float)cx1,best_u - dw),
  41816. std::min(patch_image.width() - 1.f - cx2,best_u + dw),&rng));
  41817. v = (int)cimg::round(cimg::rand(std::max((float)cy1,best_v - dh),
  41818. std::min(patch_image.height() - 1.f - cy2,best_v + dh),&rng));
  41819. if (u!=best_u || v!=best_v) {
  41820. s = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum,
  41821. xp,yp,u - cx1,v - cy1,
  41822. u,v,_patch_penalization,allow_identity,best_score);
  41823. if (s<best_score) { best_u = u; best_v = v; best_score = s; }
  41824. dw = std::max(5.f,dw*0.5f); dh = std::max(5.f,dh*0.5f);
  41825. }
  41826. }
  41827. if (best_score<best_score0) {
  41828. if (_patch_penalization!=0) {
  41829. float &p_penalty = penalty(a_map(x,y,0),a_map(x,y,1));
  41830. if (p_penalty) cimg_pragma_openmp(atomic) --p_penalty;
  41831. }
  41832. a_map(x,y,0) = best_u;
  41833. a_map(x,y,1) = best_v;
  41834. score(x,y) = best_score;
  41835. is_updated(x,y) = 3;
  41836. } else is_updated(x,y)&=~nmask;
  41837. if (_patch_penalization!=0) cimg_pragma_openmp(atomic) ++penalty(best_u,best_v);
  41838. }
  41839. cimg::srand(rng);
  41840. }
  41841. // Update score according to new penalties.
  41842. if (penalty)
  41843. cimg_pragma_openmp(parallel for cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*64))
  41844. cimg_forXY(score,x,y) {
  41845. const float p_score = score(x,y);
  41846. const int
  41847. cx1 = x<=psizew1?x:(x<width() - psizew2?psizew1:psizew + x - width()),
  41848. cy1 = y<=psizeh1?y:(y<height() - psizeh2?psizeh1:psizeh + y - height()),
  41849. xp = x - cx1,
  41850. yp = y - cy1,
  41851. u = a_map(x,y,0),
  41852. v = a_map(x,y,1);
  41853. const float n_score = _matchpatch(in_this,in_patch,penalty,patch_width,patch_height,_spectrum,
  41854. xp,yp,u - cx1,v - cy1,
  41855. u,v,_patch_penalization,allow_identity,cimg::type<float>::inf());
  41856. if (n_score!=p_score) { score(x,y) = n_score; is_updated(x,y) = 3; }
  41857. }
  41858. }
  41859. }
  41860. if (is_matching_score) score.move_to(matching_score);
  41861. return a_map;
  41862. }
  41863. // Compute SSD between two patches in different images.
  41864. static float _matchpatch(const CImg<T>& img1, const CImg<T>& img2, const CImg<floatT>& penalty,
  41865. const unsigned int psizew, const unsigned int psizeh,
  41866. const unsigned int psized, const unsigned int psizec,
  41867. const int x1, const int y1, const int z1,
  41868. const int x2, const int y2, const int z2,
  41869. const int xc, const int yc, const int zc,
  41870. const float patch_penalization,
  41871. const bool allow_identity,
  41872. const float max_score) { // 3D version
  41873. if (!allow_identity && cimg::hypot((float)x1 - x2,(float)y1 - y2,(float)z1 - z2)<patch_penalization)
  41874. return cimg::type<float>::inf();
  41875. const T *p1 = img1.data(x1*psizec,y1,z1), *p2 = img2.data(x2*psizec,y2,z2);
  41876. const unsigned int psizewc = psizew*psizec;
  41877. const ulongT
  41878. offx1 = (ulongT)img1._width - psizewc,
  41879. offx2 = (ulongT)img2._width - psizewc,
  41880. offy1 = (ulongT)img1._width*img1._height - (ulongT)psizeh*img1._width,
  41881. offy2 = (ulongT)img2._width*img2._height - (ulongT)psizeh*img2._width;
  41882. float ssd = 0;
  41883. for (unsigned int k = 0; k<psized; ++k) {
  41884. for (unsigned int j = 0; j<psizeh; ++j) {
  41885. for (unsigned int i = 0; i<psizewc; ++i)
  41886. ssd += cimg::sqr((Tfloat)*(p1++) - *(p2++));
  41887. if (ssd>max_score) return max_score;
  41888. p1+=offx1; p2+=offx2;
  41889. }
  41890. p1+=offy1; p2+=offy2;
  41891. }
  41892. return patch_penalization==0?ssd:cimg::sqr(std::sqrt(ssd) +
  41893. patch_penalization*psizewc*psizeh*psized*penalty(xc,yc,zc)/100);
  41894. }
  41895. static float _matchpatch(const CImg<T>& img1, const CImg<T>& img2, const CImg<floatT>& penalty,
  41896. const unsigned int psizew, const unsigned int psizeh, const unsigned int psizec,
  41897. const int x1, const int y1,
  41898. const int x2, const int y2,
  41899. const int xc, const int yc,
  41900. const float patch_penalization,
  41901. const bool allow_identity,
  41902. const float max_score) { // 2D version
  41903. if (!allow_identity && cimg::hypot((float)x1 - x2,(float)y1 - y2)<patch_penalization)
  41904. return cimg::type<float>::inf();
  41905. const T *p1 = img1.data(x1*psizec,y1), *p2 = img2.data(x2*psizec,y2);
  41906. const unsigned int psizewc = psizew*psizec;
  41907. const ulongT
  41908. offx1 = (ulongT)img1._width - psizewc,
  41909. offx2 = (ulongT)img2._width - psizewc;
  41910. float ssd = 0;
  41911. for (unsigned int j = 0; j<psizeh; ++j) {
  41912. for (unsigned int i = 0; i<psizewc; ++i)
  41913. ssd += cimg::sqr((Tfloat)*(p1++) - *(p2++));
  41914. if (ssd>max_score) return max_score;
  41915. p1+=offx1; p2+=offx2;
  41916. }
  41917. return patch_penalization==0?ssd:cimg::sqr(std::sqrt(ssd) +
  41918. patch_penalization*psizewc*psizeh*penalty(xc,yc)/100);
  41919. }
  41920. //! Compute Euclidean distance function to a specified value.
  41921. /**
  41922. \param value Reference value.
  41923. \param metric Type of metric. Can be <tt>{ 0=Chebyshev | 1=Manhattan | 2=Euclidean | 3=Squared-euclidean }</tt>.
  41924. \note
  41925. The distance transform implementation has been submitted by A. Meijster, and implements
  41926. the article 'W.H. Hesselink, A. Meijster, J.B.T.M. Roerdink,
  41927. "A general algorithm for computing distance transforms in linear time.",
  41928. In: Mathematical Morphology and its Applications to Image and Signal Processing,
  41929. J. Goutsias, L. Vincent, and D.S. Bloomberg (eds.), Kluwer, 2000, pp. 331-340.'
  41930. The submitted code has then been modified to fit CImg coding style and constraints.
  41931. **/
  41932. CImg<T>& distance(const T& value, const unsigned int metric=2) {
  41933. if (is_empty()) return *this;
  41934. if (cimg::type<Tint>::string()!=pixel_type()) // For datatype < int
  41935. return CImg<Tint>(*this,false).distance((Tint)value,metric).
  41936. cut((Tint)cimg::type<T>::min(),(Tint)cimg::type<T>::max()).move_to(*this);
  41937. bool is_value = false;
  41938. cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,(T)0:(T)std::max(0,99999999); // (avoid VC++ warning)
  41939. if (!is_value) return fill(cimg::type<T>::max());
  41940. switch (metric) {
  41941. case 0 : return _distance_core(_distance_sep_cdt,_distance_dist_cdt); // Chebyshev
  41942. case 1 : return _distance_core(_distance_sep_mdt,_distance_dist_mdt); // Manhattan
  41943. case 3 : return _distance_core(_distance_sep_edt,_distance_dist_edt); // Squared Euclidean
  41944. default : return _distance_core(_distance_sep_edt,_distance_dist_edt).sqrt(); // Euclidean
  41945. }
  41946. return *this;
  41947. }
  41948. //! Compute distance to a specified value \newinstance.
  41949. CImg<Tfloat> get_distance(const T& value, const unsigned int metric=2) const {
  41950. return CImg<Tfloat>(*this,false).distance((Tfloat)value,metric);
  41951. }
  41952. static longT _distance_sep_edt(const longT i, const longT u, const longT *const g) {
  41953. return (u*u - i*i + g[u] - g[i])/(2*(u - i));
  41954. }
  41955. static longT _distance_dist_edt(const longT x, const longT i, const longT *const g) {
  41956. return (x - i)*(x - i) + g[i];
  41957. }
  41958. static longT _distance_sep_mdt(const longT i, const longT u, const longT *const g) {
  41959. return (u - i<=g[u] - g[i]?999999999:(g[u] - g[i] + u + i)/2);
  41960. }
  41961. static longT _distance_dist_mdt(const longT x, const longT i, const longT *const g) {
  41962. return (x<i?i - x:x - i) + g[i];
  41963. }
  41964. static longT _distance_sep_cdt(const longT i, const longT u, const longT *const g) {
  41965. const longT h = (i + u)/2;
  41966. if (g[i]<=g[u]) { return h<i + g[u]?i + g[u]:h; }
  41967. return h<u - g[i]?h:u - g[i];
  41968. }
  41969. static longT _distance_dist_cdt(const longT x, const longT i, const longT *const g) {
  41970. const longT d = x<i?i - x:x - i;
  41971. return d<g[i]?g[i]:d;
  41972. }
  41973. static void _distance_scan(const unsigned int len,
  41974. const longT *const g,
  41975. longT (*const sep)(const longT, const longT, const longT *const),
  41976. longT (*const f)(const longT, const longT, const longT *const),
  41977. longT *const s,
  41978. longT *const t,
  41979. longT *const dt) {
  41980. longT q = s[0] = t[0] = 0;
  41981. for (int u = 1; u<(int)len; ++u) { // Forward scan
  41982. while ((q>=0) && f(t[q],s[q],g)>f(t[q],u,g)) { --q; }
  41983. if (q<0) { q = 0; s[0] = u; }
  41984. else { const longT w = 1 + sep(s[q], u, g); if (w<(longT)len) { ++q; s[q] = u; t[q] = w; }}
  41985. }
  41986. for (int u = (int)len - 1; u>=0; --u) { dt[u] = f(u,s[q],g); if (u==t[q]) --q; } // Backward scan
  41987. }
  41988. CImg<T>& _distance_core(longT (*const sep)(const longT, const longT, const longT *const),
  41989. longT (*const f)(const longT, const longT, const longT *const)) {
  41990. // Check for g++ 4.9.X, as OpenMP seems to crash for this particular function. I have no clues why.
  41991. #define cimg_is_gcc49x (__GNUC__==4 && __GNUC_MINOR__==9)
  41992. const ulongT wh = (ulongT)_width*_height;
  41993. #if cimg_use_openmp!=0 && !cimg_is_gcc49x
  41994. cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
  41995. #endif
  41996. cimg_forC(*this,c) {
  41997. CImg<longT> g(_width), dt(_width), s(_width), t(_width);
  41998. CImg<T> img = get_shared_channel(c);
  41999. #if cimg_use_openmp!=0 && !cimg_is_gcc49x
  42000. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2) cimg_openmp_if(_width>=(cimg_openmp_sizefactor)*512 &&
  42001. _height*_depth>=16)
  42002. firstprivate(g,dt,s,t))
  42003. #endif
  42004. cimg_forYZ(*this,y,z) { // Over X-direction
  42005. cimg_forX(*this,x) g[x] = (longT)img(x,y,z,0,wh);
  42006. _distance_scan(_width,g,sep,f,s,t,dt);
  42007. cimg_forX(*this,x) img(x,y,z,0,wh) = (T)dt[x];
  42008. }
  42009. if (_height>1) {
  42010. g.assign(_height); dt.assign(_height); s.assign(_height); t.assign(_height);
  42011. #if cimg_use_openmp!=0 && !cimg_is_gcc49x
  42012. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
  42013. cimg_openmp_if(_height>=(cimg_openmp_sizefactor)*512 && _width*_depth>=16)
  42014. firstprivate(g,dt,s,t))
  42015. #endif
  42016. cimg_forXZ(*this,x,z) { // Over Y-direction
  42017. cimg_forY(*this,y) g[y] = (longT)img(x,y,z,0,wh);
  42018. _distance_scan(_height,g,sep,f,s,t,dt);
  42019. cimg_forY(*this,y) img(x,y,z,0,wh) = (T)dt[y];
  42020. }
  42021. }
  42022. if (_depth>1) {
  42023. g.assign(_depth); dt.assign(_depth); s.assign(_depth); t.assign(_depth);
  42024. #if cimg_use_openmp!=0 && !cimg_is_gcc49x
  42025. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
  42026. cimg_openmp_if(_depth>=(cimg_openmp_sizefactor)*512 && _width*_height>=16)
  42027. firstprivate(g,dt,s,t))
  42028. #endif
  42029. cimg_forXY(*this,x,y) { // Over Z-direction
  42030. cimg_forZ(*this,z) g[z] = (longT)img(x,y,z,0,wh);
  42031. _distance_scan(_depth,g,sep,f,s,t,dt);
  42032. cimg_forZ(*this,z) img(x,y,z,0,wh) = (T)dt[z];
  42033. }
  42034. }
  42035. }
  42036. return *this;
  42037. }
  42038. //! Compute chamfer distance to a specified value, with a custom metric.
  42039. /**
  42040. \param value Reference value.
  42041. \param metric_mask Metric mask.
  42042. \note The algorithm code has been initially proposed by A. Meijster, and modified by D. Tschumperlé.
  42043. **/
  42044. template<typename t>
  42045. CImg<T>& distance(const T& value, const CImg<t>& metric_mask) {
  42046. if (is_empty()) return *this;
  42047. bool is_value = false;
  42048. cimg_for(*this,ptr,T) *ptr = *ptr==value?is_value=true,0:(T)999999999;
  42049. if (!is_value) return fill(cimg::type<T>::max());
  42050. const ulongT wh = (ulongT)_width*_height;
  42051. cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2))
  42052. cimg_forC(*this,c) {
  42053. CImg<T> img = get_shared_channel(c);
  42054. cimg_pragma_openmp(parallel for cimg_openmp_collapse(3)
  42055. cimg_openmp_if(_width*_height*_depth>=(cimg_openmp_sizefactor)*1024))
  42056. cimg_forXYZ(metric_mask,dx,dy,dz) {
  42057. const t weight = metric_mask(dx,dy,dz);
  42058. if (weight) {
  42059. for (int z = dz, nz = 0; z<depth(); ++z,++nz) { // Forward scan
  42060. for (int y = dy , ny = 0; y<height(); ++y,++ny) {
  42061. for (int x = dx, nx = 0; x<width(); ++x,++nx) {
  42062. const T dd = img(nx,ny,nz,0,wh) + weight;
  42063. if (dd<img(x,y,z,0,wh)) img(x,y,z,0,wh) = dd;
  42064. }
  42065. }
  42066. }
  42067. for (int z = depth() - 1 - dz, nz = depth() - 1; z>=0; --z,--nz) { // Backward scan
  42068. for (int y = height() - 1 - dy, ny = height() - 1; y>=0; --y,--ny) {
  42069. for (int x = width() - 1 - dx, nx = width() - 1; x>=0; --x,--nx) {
  42070. const T dd = img(nx,ny,nz,0,wh) + weight;
  42071. if (dd<img(x,y,z,0,wh)) img(x,y,z,0,wh) = dd;
  42072. }
  42073. }
  42074. }
  42075. }
  42076. }
  42077. }
  42078. return *this;
  42079. }
  42080. //! Compute chamfer distance to a specified value, with a custom metric \newinstance.
  42081. template<typename t>
  42082. CImg<Tfloat> get_distance(const T& value, const CImg<t>& metric_mask) const {
  42083. return CImg<Tfloat>(*this,false).distance(value,metric_mask);
  42084. }
  42085. //! Compute distance to a specified value, according to a custom metric (use dijkstra algorithm).
  42086. /**
  42087. \param value Reference value.
  42088. \param metric Field of distance potentials.
  42089. \param is_high_connectivity Tells if the algorithm uses low or high connectivity.
  42090. \param[out] return_path An image containing the nodes of the minimal path.
  42091. **/
  42092. template<typename t, typename to>
  42093. CImg<T>& distance_dijkstra(const T& value, const CImg<t>& metric, const bool is_high_connectivity,
  42094. CImg<to>& return_path) {
  42095. return get_distance_dijkstra(value,metric,is_high_connectivity,return_path).move_to(*this);
  42096. }
  42097. //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm) \newinstance.
  42098. template<typename t, typename to>
  42099. CImg<typename cimg::superset<t,long>::type>
  42100. get_distance_dijkstra(const T& value, const CImg<t>& metric, const bool is_high_connectivity,
  42101. CImg<to>& return_path) const {
  42102. if (is_empty()) return return_path.assign();
  42103. if (!is_sameXYZ(metric))
  42104. throw CImgArgumentException(_cimg_instance
  42105. "distance_dijkstra(): image instance and metric map (%u,%u,%u,%u) "
  42106. "have incompatible dimensions.",
  42107. cimg_instance,
  42108. metric._width,metric._height,metric._depth,metric._spectrum);
  42109. typedef typename cimg::superset<t,long>::type td; // Type used for computing cumulative distances
  42110. CImg<td> result(_width,_height,_depth,_spectrum), Q;
  42111. CImg<boolT> is_queued(_width,_height,_depth,1);
  42112. if (return_path) return_path.assign(_width,_height,_depth,_spectrum);
  42113. cimg_forC(*this,c) {
  42114. const CImg<T> img = get_shared_channel(c);
  42115. const CImg<t> met = metric.get_shared_channel(c%metric._spectrum);
  42116. CImg<td> res = result.get_shared_channel(c);
  42117. CImg<to> path = return_path?return_path.get_shared_channel(c):CImg<to>();
  42118. unsigned int sizeQ = 0;
  42119. // Detect initial seeds.
  42120. is_queued.fill(0);
  42121. cimg_forXYZ(img,x,y,z) if (img(x,y,z)==value) {
  42122. Q._priority_queue_insert(is_queued,sizeQ,0,x,y,z);
  42123. res(x,y,z) = 0;
  42124. if (path) path(x,y,z) = (to)0;
  42125. }
  42126. // Start distance propagation.
  42127. while (sizeQ) {
  42128. // Get and remove point with minimal potential from the queue.
  42129. const int x = (int)Q(0,1), y = (int)Q(0,2), z = (int)Q(0,3);
  42130. const td P = (td)-Q(0,0);
  42131. Q._priority_queue_remove(sizeQ);
  42132. // Update neighbors.
  42133. td npot = 0;
  42134. if (x - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x - 1,y,z) + P),x - 1,y,z)) {
  42135. res(x - 1,y,z) = npot; if (path) path(x - 1,y,z) = (to)2;
  42136. }
  42137. if (x + 1<width() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x + 1,y,z) + P),x + 1,y,z)) {
  42138. res(x + 1,y,z) = npot; if (path) path(x + 1,y,z) = (to)1;
  42139. }
  42140. if (y - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y - 1,z) + P),x,y - 1,z)) {
  42141. res(x,y - 1,z) = npot; if (path) path(x,y - 1,z) = (to)8;
  42142. }
  42143. if (y + 1<height() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y + 1,z) + P),x,y + 1,z)) {
  42144. res(x,y + 1,z) = npot; if (path) path(x,y + 1,z) = (to)4;
  42145. }
  42146. if (z - 1>=0 && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y,z - 1) + P),x,y,z - 1)) {
  42147. res(x,y,z - 1) = npot; if (path) path(x,y,z - 1) = (to)32;
  42148. }
  42149. if (z + 1<depth() && Q._priority_queue_insert(is_queued,sizeQ,-(npot=met(x,y,z + 1) + P),x,y,z + 1)) {
  42150. res(x,y,z + 1) = npot; if (path) path(x,y,z + 1) = (to)16;
  42151. }
  42152. if (is_high_connectivity) {
  42153. const float sqrt2 = std::sqrt(2.f), sqrt3 = std::sqrt(3.f);
  42154. // Diagonal neighbors on slice z.
  42155. if (x - 1>=0 && y - 1>=0 &&
  42156. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y - 1,z) + P)),x - 1,y - 1,z)) {
  42157. res(x - 1,y - 1,z) = npot; if (path) path(x - 1,y - 1,z) = (to)10;
  42158. }
  42159. if (x + 1<width() && y - 1>=0 &&
  42160. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y - 1,z) + P)),x + 1,y - 1,z)) {
  42161. res(x + 1,y - 1,z) = npot; if (path) path(x + 1,y - 1,z) = (to)9;
  42162. }
  42163. if (x - 1>=0 && y + 1<height() &&
  42164. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y + 1,z) + P)),x - 1,y + 1,z)) {
  42165. res(x - 1,y + 1,z) = npot; if (path) path(x - 1,y + 1,z) = (to)6;
  42166. }
  42167. if (x + 1<width() && y + 1<height() &&
  42168. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y + 1,z) + P)),x + 1,y + 1,z)) {
  42169. res(x + 1,y + 1,z) = npot; if (path) path(x + 1,y + 1,z) = (to)5;
  42170. }
  42171. if (z - 1>=0) { // Diagonal neighbors on slice z - 1
  42172. if (x - 1>=0 &&
  42173. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y,z - 1) + P)),x - 1,y,z - 1)) {
  42174. res(x - 1,y,z - 1) = npot; if (path) path(x - 1,y,z - 1) = (to)34;
  42175. }
  42176. if (x + 1<width() &&
  42177. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y,z - 1) + P)),x + 1,y,z - 1)) {
  42178. res(x + 1,y,z - 1) = npot; if (path) path(x + 1,y,z - 1) = (to)33;
  42179. }
  42180. if (y - 1>=0 &&
  42181. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y - 1,z - 1) + P)),x,y - 1,z - 1)) {
  42182. res(x,y - 1,z - 1) = npot; if (path) path(x,y - 1,z - 1) = (to)40;
  42183. }
  42184. if (y + 1<height() &&
  42185. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y + 1,z - 1) + P)),x,y + 1,z - 1)) {
  42186. res(x,y + 1,z - 1) = npot; if (path) path(x,y + 1,z - 1) = (to)36;
  42187. }
  42188. if (x - 1>=0 && y - 1>=0 &&
  42189. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y - 1,z - 1) + P)),
  42190. x - 1,y - 1,z - 1)) {
  42191. res(x - 1,y - 1,z - 1) = npot; if (path) path(x - 1,y - 1,z - 1) = (to)42;
  42192. }
  42193. if (x + 1<width() && y - 1>=0 &&
  42194. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y - 1,z - 1) + P)),
  42195. x + 1,y - 1,z - 1)) {
  42196. res(x + 1,y - 1,z - 1) = npot; if (path) path(x + 1,y - 1,z - 1) = (to)41;
  42197. }
  42198. if (x - 1>=0 && y + 1<height() &&
  42199. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y + 1,z - 1) + P)),
  42200. x - 1,y + 1,z - 1)) {
  42201. res(x - 1,y + 1,z - 1) = npot; if (path) path(x - 1,y + 1,z - 1) = (to)38;
  42202. }
  42203. if (x + 1<width() && y + 1<height() &&
  42204. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y + 1,z - 1) + P)),
  42205. x + 1,y + 1,z - 1)) {
  42206. res(x + 1,y + 1,z - 1) = npot; if (path) path(x + 1,y + 1,z - 1) = (to)37;
  42207. }
  42208. }
  42209. if (z + 1<depth()) { // Diagonal neighbors on slice z + 1
  42210. if (x - 1>=0 &&
  42211. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x - 1,y,z + 1) + P)),x - 1,y,z + 1)) {
  42212. res(x - 1,y,z + 1) = npot; if (path) path(x - 1,y,z + 1) = (to)18;
  42213. }
  42214. if (x + 1<width() &&
  42215. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x + 1,y,z + 1) + P)),x + 1,y,z + 1)) {
  42216. res(x + 1,y,z + 1) = npot; if (path) path(x + 1,y,z + 1) = (to)17;
  42217. }
  42218. if (y - 1>=0 &&
  42219. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y - 1,z + 1) + P)),x,y - 1,z + 1)) {
  42220. res(x,y - 1,z + 1) = npot; if (path) path(x,y - 1,z + 1) = (to)24;
  42221. }
  42222. if (y + 1<height() &&
  42223. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt2*met(x,y + 1,z + 1) + P)),x,y + 1,z + 1)) {
  42224. res(x,y + 1,z + 1) = npot; if (path) path(x,y + 1,z + 1) = (to)20;
  42225. }
  42226. if (x - 1>=0 && y - 1>=0 &&
  42227. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y - 1,z + 1) + P)),
  42228. x - 1,y - 1,z + 1)) {
  42229. res(x - 1,y - 1,z + 1) = npot; if (path) path(x - 1,y - 1,z + 1) = (to)26;
  42230. }
  42231. if (x + 1<width() && y - 1>=0 &&
  42232. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y - 1,z + 1) + P)),
  42233. x + 1,y - 1,z + 1)) {
  42234. res(x + 1,y - 1,z + 1) = npot; if (path) path(x + 1,y - 1,z + 1) = (to)25;
  42235. }
  42236. if (x - 1>=0 && y + 1<height() &&
  42237. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x - 1,y + 1,z + 1) + P)),
  42238. x - 1,y + 1,z + 1)) {
  42239. res(x - 1,y + 1,z + 1) = npot; if (path) path(x - 1,y + 1,z + 1) = (to)22;
  42240. }
  42241. if (x + 1<width() && y + 1<height() &&
  42242. Q._priority_queue_insert(is_queued,sizeQ,-(npot=(td)(sqrt3*met(x + 1,y + 1,z + 1) + P)),
  42243. x + 1,y + 1,z + 1)) {
  42244. res(x + 1,y + 1,z + 1) = npot; if (path) path(x + 1,y + 1,z + 1) = (to)21;
  42245. }
  42246. }
  42247. }
  42248. }
  42249. }
  42250. return result;
  42251. }
  42252. //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm). \overloading.
  42253. template<typename t>
  42254. CImg<T>& distance_dijkstra(const T& value, const CImg<t>& metric,
  42255. const bool is_high_connectivity=false) {
  42256. return get_distance_dijkstra(value,metric,is_high_connectivity).move_to(*this);
  42257. }
  42258. //! Compute distance map to a specified value, according to a custom metric (use dijkstra algorithm). \newinstance.
  42259. template<typename t>
  42260. CImg<Tfloat> get_distance_dijkstra(const T& value, const CImg<t>& metric,
  42261. const bool is_high_connectivity=false) const {
  42262. CImg<T> return_path;
  42263. return get_distance_dijkstra(value,metric,is_high_connectivity,return_path);
  42264. }
  42265. //! Compute distance map to one source point, according to a custom metric (use fast marching algorithm).
  42266. /**
  42267. \param value Reference value.
  42268. \param metric Field of distance potentials.
  42269. **/
  42270. template<typename t>
  42271. CImg<T>& distance_eikonal(const T& value, const CImg<t>& metric) {
  42272. return get_distance_eikonal(value,metric).move_to(*this);
  42273. }
  42274. //! Compute distance map to one source point, according to a custom metric (use fast marching algorithm).
  42275. template<typename t>
  42276. CImg<Tfloat> get_distance_eikonal(const T& value, const CImg<t>& metric) const {
  42277. if (is_empty()) return *this;
  42278. if (!is_sameXYZ(metric))
  42279. throw CImgArgumentException(_cimg_instance
  42280. "distance_eikonal(): image instance and metric map (%u,%u,%u,%u) have "
  42281. "incompatible dimensions.",
  42282. cimg_instance,
  42283. metric._width,metric._height,metric._depth,metric._spectrum);
  42284. CImg<Tfloat> result(_width,_height,_depth,_spectrum,cimg::type<Tfloat>::max()), Q;
  42285. CImg<charT> state(_width,_height,_depth); // -1=far away, 0=narrow, 1=frozen
  42286. cimg_pragma_openmp(parallel for cimg_openmp_if(_spectrum>=2) firstprivate(Q,state))
  42287. cimg_forC(*this,c) {
  42288. const CImg<T> img = get_shared_channel(c);
  42289. const CImg<t> met = metric.get_shared_channel(c%metric._spectrum);
  42290. CImg<Tfloat> res = result.get_shared_channel(c);
  42291. unsigned int sizeQ = 0;
  42292. state.fill(-1);
  42293. // Detect initial seeds.
  42294. Tfloat *ptr1 = res._data; char *ptr2 = state._data;
  42295. cimg_for(img,ptr0,T) { if (*ptr0==value) { *ptr1 = 0; *ptr2 = 1; } ++ptr1; ++ptr2; }
  42296. // Initialize seeds neighbors.
  42297. ptr2 = state._data;
  42298. cimg_forXYZ(img,x,y,z) if (*(ptr2++)==1) {
  42299. if (x - 1>=0 && state(x - 1,y,z)==-1) {
  42300. const Tfloat dist = res(x - 1,y,z) = __distance_eikonal(res,met(x - 1,y,z),x - 1,y,z);
  42301. Q._eik_priority_queue_insert(state,sizeQ,-dist,x - 1,y,z);
  42302. }
  42303. if (x + 1<width() && state(x + 1,y,z)==-1) {
  42304. const Tfloat dist = res(x + 1,y,z) = __distance_eikonal(res,met(x + 1,y,z),x + 1,y,z);
  42305. Q._eik_priority_queue_insert(state,sizeQ,-dist,x + 1,y,z);
  42306. }
  42307. if (y - 1>=0 && state(x,y - 1,z)==-1) {
  42308. const Tfloat dist = res(x,y - 1,z) = __distance_eikonal(res,met(x,y - 1,z),x,y - 1,z);
  42309. Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y - 1,z);
  42310. }
  42311. if (y + 1<height() && state(x,y + 1,z)==-1) {
  42312. const Tfloat dist = res(x,y + 1,z) = __distance_eikonal(res,met(x,y + 1,z),x,y + 1,z);
  42313. Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y + 1,z);
  42314. }
  42315. if (z - 1>=0 && state(x,y,z - 1)==-1) {
  42316. const Tfloat dist = res(x,y,z - 1) = __distance_eikonal(res,met(x,y,z - 1),x,y,z - 1);
  42317. Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z - 1);
  42318. }
  42319. if (z + 1<depth() && state(x,y,z + 1)==-1) {
  42320. const Tfloat dist = res(x,y,z + 1) = __distance_eikonal(res,met(x,y,z + 1),x,y,z + 1);
  42321. Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z + 1);
  42322. }
  42323. }
  42324. // Propagate front.
  42325. while (sizeQ) {
  42326. int x = -1, y = -1, z = -1;
  42327. while (sizeQ && x<0) {
  42328. x = (int)Q(0,1); y = (int)Q(0,2); z = (int)Q(0,3);
  42329. Q._priority_queue_remove(sizeQ);
  42330. if (state(x,y,z)==1) x = -1; else state(x,y,z) = 1;
  42331. }
  42332. if (x>=0) {
  42333. if (x - 1>=0 && state(x - 1,y,z)!=1) {
  42334. const Tfloat dist = __distance_eikonal(res,met(x - 1,y,z),x - 1,y,z);
  42335. if (dist<res(x - 1,y,z)) {
  42336. res(x - 1,y,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x - 1,y,z);
  42337. }
  42338. }
  42339. if (x + 1<width() && state(x + 1,y,z)!=1) {
  42340. const Tfloat dist = __distance_eikonal(res,met(x + 1,y,z),x + 1,y,z);
  42341. if (dist<res(x + 1,y,z)) {
  42342. res(x + 1,y,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x + 1,y,z);
  42343. }
  42344. }
  42345. if (y - 1>=0 && state(x,y - 1,z)!=1) {
  42346. const Tfloat dist = __distance_eikonal(res,met(x,y - 1,z),x,y - 1,z);
  42347. if (dist<res(x,y - 1,z)) {
  42348. res(x,y - 1,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y - 1,z);
  42349. }
  42350. }
  42351. if (y + 1<height() && state(x,y + 1,z)!=1) {
  42352. const Tfloat dist = __distance_eikonal(res,met(x,y + 1,z),x,y + 1,z);
  42353. if (dist<res(x,y + 1,z)) {
  42354. res(x,y + 1,z) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y + 1,z);
  42355. }
  42356. }
  42357. if (z - 1>=0 && state(x,y,z - 1)!=1) {
  42358. const Tfloat dist = __distance_eikonal(res,met(x,y,z - 1),x,y,z - 1);
  42359. if (dist<res(x,y,z - 1)) {
  42360. res(x,y,z - 1) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z - 1);
  42361. }
  42362. }
  42363. if (z + 1<depth() && state(x,y,z + 1)!=1) {
  42364. const Tfloat dist = __distance_eikonal(res,met(x,y,z + 1),x,y,z + 1);
  42365. if (dist<res(x,y,z + 1)) {
  42366. res(x,y,z + 1) = dist; Q._eik_priority_queue_insert(state,sizeQ,-dist,x,y,z + 1);
  42367. }
  42368. }
  42369. }
  42370. }
  42371. }
  42372. return result;
  42373. }
  42374. // Locally solve eikonal equation.
  42375. Tfloat __distance_eikonal(const CImg<Tfloat>& res, const Tfloat P,
  42376. const int x=0, const int y=0, const int z=0) const {
  42377. const Tfloat M = (Tfloat)cimg::type<T>::max();
  42378. T T1 = (T)std::min(x - 1>=0?res(x - 1,y,z):M,x + 1<width()?res(x + 1,y,z):M);
  42379. Tfloat root = 0;
  42380. if (_depth>1) { // 3D
  42381. T
  42382. T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1<height()?res(x,y + 1,z):M),
  42383. T3 = (T)std::min(z - 1>=0?res(x,y,z - 1):M,z + 1<depth()?res(x,y,z + 1):M);
  42384. if (T1>T2) cimg::swap(T1,T2);
  42385. if (T2>T3) cimg::swap(T2,T3);
  42386. if (T1>T2) cimg::swap(T1,T2);
  42387. if (P<=0) return (Tfloat)T1;
  42388. if (T3<M && ___distance_eikonal(3,-2*(T1 + T2 + T3),T1*T1 + T2*T2 + T3*T3 - P*P,root))
  42389. return std::max((Tfloat)T3,root);
  42390. if (T2<M && ___distance_eikonal(2,-2*(T1 + T2),T1*T1 + T2*T2 - P*P,root))
  42391. return std::max((Tfloat)T2,root);
  42392. return P + T1;
  42393. } else if (_height>1) { // 2D
  42394. T T2 = (T)std::min(y - 1>=0?res(x,y - 1,z):M,y + 1<height()?res(x,y + 1,z):M);
  42395. if (T1>T2) cimg::swap(T1,T2);
  42396. if (P<=0) return (Tfloat)T1;
  42397. if (T2<M && ___distance_eikonal(2,-2*(T1 + T2),T1*T1 + T2*T2 - P*P,root))
  42398. return std::max((Tfloat)T2,root);
  42399. return P + T1;
  42400. } else { // 1D
  42401. if (P<=0) return (Tfloat)T1;
  42402. return P + T1;
  42403. }
  42404. return 0;
  42405. }
  42406. // Find max root of a 2nd-order polynomial.
  42407. static bool ___distance_eikonal(const Tfloat a, const Tfloat b, const Tfloat c, Tfloat &root) {
  42408. const Tfloat delta = b*b - 4*a*c;
  42409. if (delta<0) return false;
  42410. root = 0.5f*(-b + std::sqrt(delta))/a;
  42411. return true;
  42412. }
  42413. // Insert new point in heap.
  42414. template<typename t>
  42415. void _eik_priority_queue_insert(CImg<charT>& state, unsigned int& siz, const t value,
  42416. const unsigned int x, const unsigned int y, const unsigned int z) {
  42417. if (state(x,y,z)>0) return;
  42418. state(x,y,z) = 0;
  42419. if (++siz>=_width) { if (!is_empty()) resize(_width*2,4,1,1,0); else assign(64,4); }
  42420. (*this)(siz - 1,0) = (T)value; (*this)(siz - 1,1) = (T)x; (*this)(siz - 1,2) = (T)y; (*this)(siz - 1,3) = (T)z;
  42421. for (unsigned int pos = siz - 1, par = 0; pos && value>(t)(*this)(par=(pos + 1)/2 - 1,0); pos = par) {
  42422. cimg::swap((*this)(pos,0),(*this)(par,0)); cimg::swap((*this)(pos,1),(*this)(par,1));
  42423. cimg::swap((*this)(pos,2),(*this)(par,2)); cimg::swap((*this)(pos,3),(*this)(par,3));
  42424. }
  42425. }
  42426. //! Compute distance function to 0-valued isophotes, using the Eikonal PDE.
  42427. /**
  42428. \param nb_iterations Number of PDE iterations.
  42429. \param band_size Size of the narrow band.
  42430. \param time_step Time step of the PDE iterations.
  42431. **/
  42432. CImg<T>& distance_eikonal(const unsigned int nb_iterations, const float band_size=0, const float time_step=0.5f) {
  42433. if (is_empty()) return *this;
  42434. CImg<Tfloat> velocity(*this,false);
  42435. for (unsigned int iteration = 0; iteration<nb_iterations; ++iteration) {
  42436. Tfloat *ptrd = velocity._data, veloc_max = 0;
  42437. if (_depth>1) { // 3D
  42438. CImg_3x3x3(I,Tfloat);
  42439. cimg_forC(*this,c) cimg_for3x3x3(*this,x,y,z,c,I,Tfloat) if (band_size<=0 || cimg::abs(Iccc)<band_size) {
  42440. const Tfloat
  42441. gx = (Incc - Ipcc)/2,
  42442. gy = (Icnc - Icpc)/2,
  42443. gz = (Iccn - Iccp)/2,
  42444. sgn = -cimg::sign(Iccc),
  42445. ix = gx*sgn>0?(Incc - Iccc):(Iccc - Ipcc),
  42446. iy = gy*sgn>0?(Icnc - Iccc):(Iccc - Icpc),
  42447. iz = gz*sgn>0?(Iccn - Iccc):(Iccc - Iccp),
  42448. ng = 1e-5f + cimg::hypot(gx,gy,gz),
  42449. ngx = gx/ng,
  42450. ngy = gy/ng,
  42451. ngz = gz/ng,
  42452. veloc = sgn*(ngx*ix + ngy*iy + ngz*iz - 1);
  42453. *(ptrd++) = veloc;
  42454. if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
  42455. } else *(ptrd++) = 0;
  42456. } else { // 2D version
  42457. CImg_3x3(I,Tfloat);
  42458. cimg_forC(*this,c) cimg_for3x3(*this,x,y,0,c,I,Tfloat) if (band_size<=0 || cimg::abs(Icc)<band_size) {
  42459. const Tfloat
  42460. gx = (Inc - Ipc)/2,
  42461. gy = (Icn - Icp)/2,
  42462. sgn = -cimg::sign(Icc),
  42463. ix = gx*sgn>0?(Inc - Icc):(Icc - Ipc),
  42464. iy = gy*sgn>0?(Icn - Icc):(Icc - Icp),
  42465. ng = std::max((Tfloat)1e-5,cimg::hypot(gx,gy)),
  42466. ngx = gx/ng,
  42467. ngy = gy/ng,
  42468. veloc = sgn*(ngx*ix + ngy*iy - 1);
  42469. *(ptrd++) = veloc;
  42470. if (veloc>veloc_max) veloc_max = veloc; else if (-veloc>veloc_max) veloc_max = -veloc;
  42471. } else *(ptrd++) = 0;
  42472. }
  42473. if (veloc_max>0) *this+=(velocity*=time_step/veloc_max);
  42474. }
  42475. return *this;
  42476. }
  42477. //! Compute distance function to 0-valued isophotes, using the Eikonal PDE \newinstance.
  42478. CImg<Tfloat> get_distance_eikonal(const unsigned int nb_iterations, const float band_size=0,
  42479. const float time_step=0.5f) const {
  42480. return CImg<Tfloat>(*this,false).distance_eikonal(nb_iterations,band_size,time_step);
  42481. }
  42482. //! Compute Haar multiscale wavelet transform.
  42483. /**
  42484. \param axis Axis considered for the transform.
  42485. \param invert Set inverse of direct transform.
  42486. \param nb_scales Number of scales used for the transform.
  42487. **/
  42488. CImg<T>& haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) {
  42489. return get_haar(axis,invert,nb_scales).move_to(*this);
  42490. }
  42491. //! Compute Haar multiscale wavelet transform \newinstance.
  42492. CImg<Tfloat> get_haar(const char axis, const bool invert=false, const unsigned int nb_scales=1) const {
  42493. if (is_empty() || !nb_scales) return +*this;
  42494. CImg<Tfloat> res;
  42495. const Tfloat sqrt2 = std::sqrt(2.f);
  42496. if (nb_scales==1) {
  42497. switch (cimg::lowercase(axis)) { // Single scale transform
  42498. case 'x' : {
  42499. const unsigned int w = _width/2;
  42500. if (w) {
  42501. if ((w%2) && w!=1)
  42502. throw CImgInstanceException(_cimg_instance
  42503. "haar(): Sub-image width %u is not even.",
  42504. cimg_instance,
  42505. w);
  42506. res.assign(_width,_height,_depth,_spectrum);
  42507. if (invert) cimg_forYZC(*this,y,z,c) { // Inverse transform along X
  42508. for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) {
  42509. const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(xw,y,z,c);
  42510. res(x2++,y,z,c) = (val0 - val1)/sqrt2;
  42511. res(x2++,y,z,c) = (val0 + val1)/sqrt2;
  42512. }
  42513. } else cimg_forYZC(*this,y,z,c) { // Direct transform along X
  42514. for (unsigned int x = 0, xw = w, x2 = 0; x<w; ++x, ++xw) {
  42515. const Tfloat val0 = (Tfloat)(*this)(x2++,y,z,c), val1 = (Tfloat)(*this)(x2++,y,z,c);
  42516. res(x,y,z,c) = (val0 + val1)/sqrt2;
  42517. res(xw,y,z,c) = (val1 - val0)/sqrt2;
  42518. }
  42519. }
  42520. } else return *this;
  42521. } break;
  42522. case 'y' : {
  42523. const unsigned int h = _height/2;
  42524. if (h) {
  42525. if ((h%2) && h!=1)
  42526. throw CImgInstanceException(_cimg_instance
  42527. "haar(): Sub-image height %u is not even.",
  42528. cimg_instance,
  42529. h);
  42530. res.assign(_width,_height,_depth,_spectrum);
  42531. if (invert) cimg_forXZC(*this,x,z,c) { // Inverse transform along Y
  42532. for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) {
  42533. const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,yh,z,c);
  42534. res(x,y2++,z,c) = (val0 - val1)/sqrt2;
  42535. res(x,y2++,z,c) = (val0 + val1)/sqrt2;
  42536. }
  42537. } else cimg_forXZC(*this,x,z,c) {
  42538. for (unsigned int y = 0, yh = h, y2 = 0; y<h; ++y, ++yh) { // Direct transform along Y
  42539. const Tfloat val0 = (Tfloat)(*this)(x,y2++,z,c), val1 = (Tfloat)(*this)(x,y2++,z,c);
  42540. res(x,y,z,c) = (val0 + val1)/sqrt2;
  42541. res(x,yh,z,c) = (val1 - val0)/sqrt2;
  42542. }
  42543. }
  42544. } else return *this;
  42545. } break;
  42546. case 'z' : {
  42547. const unsigned int d = _depth/2;
  42548. if (d) {
  42549. if ((d%2) && d!=1)
  42550. throw CImgInstanceException(_cimg_instance
  42551. "haar(): Sub-image depth %u is not even.",
  42552. cimg_instance,
  42553. d);
  42554. res.assign(_width,_height,_depth,_spectrum);
  42555. if (invert) cimg_forXYC(*this,x,y,c) { // Inverse transform along Z
  42556. for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) {
  42557. const Tfloat val0 = (Tfloat)(*this)(x,y,z,c), val1 = (Tfloat)(*this)(x,y,zd,c);
  42558. res(x,y,z2++,c) = (val0 - val1)/sqrt2;
  42559. res(x,y,z2++,c) = (val0 + val1)/sqrt2;
  42560. }
  42561. } else cimg_forXYC(*this,x,y,c) {
  42562. for (unsigned int z = 0, zd = d, z2 = 0; z<d; ++z, ++zd) { // Direct transform along Z
  42563. const Tfloat val0 = (Tfloat)(*this)(x,y,z2++,c), val1 = (Tfloat)(*this)(x,y,z2++,c);
  42564. res(x,y,z,c) = (val0 + val1)/sqrt2;
  42565. res(x,y,zd,c) = (val1 - val0)/sqrt2;
  42566. }
  42567. }
  42568. } else return *this;
  42569. } break;
  42570. default :
  42571. throw CImgArgumentException(_cimg_instance
  42572. "haar(): Invalid specified axis '%c' "
  42573. "(should be { x | y | z }).",
  42574. cimg_instance,
  42575. axis);
  42576. }
  42577. } else { // Multi-scale version
  42578. if (invert) {
  42579. res.assign(*this,false);
  42580. switch (cimg::lowercase(axis)) {
  42581. case 'x' : {
  42582. unsigned int w = _width;
  42583. for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2;
  42584. for (w = w?w:1; w<=_width; w*=2) res.draw_image(res.get_crop(0,w - 1).get_haar('x',true,1));
  42585. } break;
  42586. case 'y' : {
  42587. unsigned int h = _width;
  42588. for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2;
  42589. 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));
  42590. } break;
  42591. case 'z' : {
  42592. unsigned int d = _depth;
  42593. for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2;
  42594. for (d = d?d:1; d<=_depth; d*=2)
  42595. res.draw_image(res.get_crop(0,0,0,_width - 1,_height - 1,d - 1).get_haar('z',true,1));
  42596. } break;
  42597. default :
  42598. throw CImgArgumentException(_cimg_instance
  42599. "haar(): Invalid specified axis '%c' "
  42600. "(should be { x | y | z }).",
  42601. cimg_instance,
  42602. axis);
  42603. }
  42604. } else { // Direct transform
  42605. res = get_haar(axis,false,1);
  42606. switch (cimg::lowercase(axis)) {
  42607. case 'x' : {
  42608. for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2)
  42609. res.draw_image(res.get_crop(0,w - 1).get_haar('x',false,1));
  42610. } break;
  42611. case 'y' : {
  42612. for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2)
  42613. res.draw_image(res.get_crop(0,0,_width - 1,h - 1).get_haar('y',false,1));
  42614. } break;
  42615. case 'z' : {
  42616. for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2)
  42617. res.draw_image(res.get_crop(0,0,0,_width - 1,_height - 1,d - 1).get_haar('z',false,1));
  42618. } break;
  42619. default :
  42620. throw CImgArgumentException(_cimg_instance
  42621. "haar(): Invalid specified axis '%c' "
  42622. "(should be { x | y | z }).",
  42623. cimg_instance,
  42624. axis);
  42625. }
  42626. }
  42627. }
  42628. return res;
  42629. }
  42630. //! Compute Haar multiscale wavelet transform \overloading.
  42631. /**
  42632. \param invert Set inverse of direct transform.
  42633. \param nb_scales Number of scales used for the transform.
  42634. **/
  42635. CImg<T>& haar(const bool invert=false, const unsigned int nb_scales=1) {
  42636. return get_haar(invert,nb_scales).move_to(*this);
  42637. }
  42638. //! Compute Haar multiscale wavelet transform \newinstance.
  42639. CImg<Tfloat> get_haar(const bool invert=false, const unsigned int nb_scales=1) const {
  42640. CImg<Tfloat> res;
  42641. if (nb_scales==1) { // Single scale transform
  42642. if (_width>1) get_haar('x',invert,1).move_to(res);
  42643. if (_height>1) { if (res) res.haar('y',invert,1); else get_haar('y',invert,1).move_to(res); }
  42644. if (_depth>1) { if (res) res.haar('z',invert,1); else get_haar('z',invert,1).move_to(res); }
  42645. if (res) return res;
  42646. } else { // Multi-scale transform
  42647. if (invert) { // Inverse transform
  42648. res.assign(*this,false);
  42649. if (_width>1) {
  42650. if (_height>1) {
  42651. if (_depth>1) {
  42652. unsigned int w = _width, h = _height, d = _depth;
  42653. for (unsigned int s = 1; w && h && d && s<nb_scales; ++s) { w/=2; h/=2; d/=2; }
  42654. 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)
  42655. res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,d - 1).get_haar(true,1));
  42656. } else {
  42657. unsigned int w = _width, h = _height;
  42658. for (unsigned int s = 1; w && h && s<nb_scales; ++s) { w/=2; h/=2; }
  42659. for (w = w?w:1, h = h?h:1; w<=_width && h<=_height; w*=2, h*=2)
  42660. res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,0).get_haar(true,1));
  42661. }
  42662. } else {
  42663. if (_depth>1) {
  42664. unsigned int w = _width, d = _depth;
  42665. for (unsigned int s = 1; w && d && s<nb_scales; ++s) { w/=2; d/=2; }
  42666. for (w = w?w:1, d = d?d:1; w<=_width && d<=_depth; w*=2, d*=2)
  42667. res.draw_image(res.get_crop(0,0,0,w - 1,0,d - 1).get_haar(true,1));
  42668. } else {
  42669. unsigned int w = _width;
  42670. for (unsigned int s = 1; w && s<nb_scales; ++s) w/=2;
  42671. for (w = w?w:1; w<=_width; w*=2)
  42672. res.draw_image(res.get_crop(0,0,0,w - 1,0,0).get_haar(true,1));
  42673. }
  42674. }
  42675. } else {
  42676. if (_height>1) {
  42677. if (_depth>1) {
  42678. unsigned int h = _height, d = _depth;
  42679. for (unsigned int s = 1; h && d && s<nb_scales; ++s) { h/=2; d/=2; }
  42680. for (h = h?h:1, d = d?d:1; h<=_height && d<=_depth; h*=2, d*=2)
  42681. res.draw_image(res.get_crop(0,0,0,0,h - 1,d - 1).get_haar(true,1));
  42682. } else {
  42683. unsigned int h = _height;
  42684. for (unsigned int s = 1; h && s<nb_scales; ++s) h/=2;
  42685. for (h = h?h:1; h<=_height; h*=2)
  42686. res.draw_image(res.get_crop(0,0,0,0,h - 1,0).get_haar(true,1));
  42687. }
  42688. } else {
  42689. if (_depth>1) {
  42690. unsigned int d = _depth;
  42691. for (unsigned int s = 1; d && s<nb_scales; ++s) d/=2;
  42692. for (d = d?d:1; d<=_depth; d*=2)
  42693. res.draw_image(res.get_crop(0,0,0,0,0,d - 1).get_haar(true,1));
  42694. } else return *this;
  42695. }
  42696. }
  42697. } else { // Direct transform
  42698. res = get_haar(false,1);
  42699. if (_width>1) {
  42700. if (_height>1) {
  42701. if (_depth>1)
  42702. for (unsigned int s = 1, w = _width/2, h = _height/2, d = _depth/2; w && h && d && s<nb_scales;
  42703. ++s, w/=2, h/=2, d/=2)
  42704. res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,d - 1).haar(false,1));
  42705. else for (unsigned int s = 1, w = _width/2, h = _height/2; w && h && s<nb_scales; ++s, w/=2, h/=2)
  42706. res.draw_image(res.get_crop(0,0,0,w - 1,h - 1,0).haar(false,1));
  42707. } else {
  42708. if (_depth>1) for (unsigned int s = 1, w = _width/2, d = _depth/2; w && d && s<nb_scales; ++s, w/=2, d/=2)
  42709. res.draw_image(res.get_crop(0,0,0,w - 1,0,d - 1).haar(false,1));
  42710. else for (unsigned int s = 1, w = _width/2; w && s<nb_scales; ++s, w/=2)
  42711. res.draw_image(res.get_crop(0,0,0,w - 1,0,0).haar(false,1));
  42712. }
  42713. } else {
  42714. if (_height>1) {
  42715. if (_depth>1)
  42716. for (unsigned int s = 1, h = _height/2, d = _depth/2; h && d && s<nb_scales; ++s, h/=2, d/=2)
  42717. res.draw_image(res.get_crop(0,0,0,0,h - 1,d - 1).haar(false,1));
  42718. else for (unsigned int s = 1, h = _height/2; h && s<nb_scales; ++s, h/=2)
  42719. res.draw_image(res.get_crop(0,0,0,0,h - 1,0).haar(false,1));
  42720. } else {
  42721. if (_depth>1) for (unsigned int s = 1, d = _depth/2; d && s<nb_scales; ++s, d/=2)
  42722. res.draw_image(res.get_crop(0,0,0,0,0,d - 1).haar(false,1));
  42723. else return *this;
  42724. }
  42725. }
  42726. }
  42727. return res;
  42728. }
  42729. return *this;
  42730. }
  42731. //! Compute 1D Fast Fourier Transform, along a specified axis.
  42732. /**
  42733. \param axis Axis along which the FFT is computed.
  42734. \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed.
  42735. **/
  42736. CImgList<Tfloat> get_FFT(const char axis, const bool is_inverse=false) const {
  42737. CImgList<Tfloat> res(*this,CImg<Tfloat>());
  42738. CImg<Tfloat>::FFT(res[0],res[1],axis,is_inverse);
  42739. return res;
  42740. }
  42741. //! Compute n-D Fast Fourier Transform.
  42742. /*
  42743. \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed.
  42744. **/
  42745. CImgList<Tfloat> get_FFT(const bool is_inverse=false) const {
  42746. CImgList<Tfloat> res(*this,CImg<Tfloat>());
  42747. CImg<Tfloat>::FFT(res[0],res[1],is_inverse);
  42748. return res;
  42749. }
  42750. //! Compute 1D Fast Fourier Transform, along a specified axis.
  42751. /**
  42752. \param[in,out] real Real part of the pixel values.
  42753. \param[in,out] imag Imaginary part of the pixel values.
  42754. \param axis Axis along which the FFT is computed.
  42755. \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed.
  42756. **/
  42757. static void FFT(CImg<T>& real, CImg<T>& imag, const char axis, const bool is_inverse=false,
  42758. const unsigned int nb_threads=0) {
  42759. if (!real)
  42760. throw CImgInstanceException("CImg<%s>::FFT(): Specified real part is empty.",
  42761. pixel_type());
  42762. if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,(T)0);
  42763. if (!real.is_sameXYZC(imag))
  42764. throw CImgInstanceException("CImg<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and "
  42765. "imaginary part (%u,%u,%u,%u,%p) have different dimensions.",
  42766. pixel_type(),
  42767. real._width,real._height,real._depth,real._spectrum,real._data,
  42768. imag._width,imag._height,imag._depth,imag._spectrum,imag._data);
  42769. const char _axis = cimg::lowercase(axis);
  42770. if (_axis!='x' && _axis!='y' && _axis!='z')
  42771. throw CImgArgumentException("CImgList<%s>::FFT(): Invalid specified axis '%c' for real and imaginary parts "
  42772. "(%u,%u,%u,%u) "
  42773. "(should be { x | y | z }).",
  42774. pixel_type(),axis,
  42775. real._width,real._height,real._depth,real._spectrum);
  42776. cimg::unused(nb_threads);
  42777. #ifdef cimg_use_fftw3
  42778. cimg::mutex(12);
  42779. #ifndef cimg_use_fftw3_singlethread
  42780. fftw_plan_with_nthreads(nb_threads?nb_threads:cimg::nb_cpus());
  42781. #endif
  42782. fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth);
  42783. if (!data_in)
  42784. throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) "
  42785. "for computing FFT of image (%u,%u,%u,%u) along the X-axis.",
  42786. pixel_type(),
  42787. cimg::strbuffersize(sizeof(fftw_complex)*real._width*real._height*real._depth),
  42788. real._width,real._height,real._depth,real._spectrum);
  42789. double *const ptrf = (double*)data_in;
  42790. fftw_plan data_plan =
  42791. _axis=='x'?fftw_plan_many_dft(1,(int*)&real._width,real.height()*real.depth(),
  42792. data_in,0,1,real.width(),
  42793. data_in,0,1,real.width(),
  42794. is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE):
  42795. _axis=='y'?fftw_plan_many_dft(1,(int*)&real._height,real.width()*real.depth(),
  42796. data_in,0,1,real.height(),
  42797. data_in,0,1,real.height(),
  42798. is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE):
  42799. fftw_plan_many_dft(1,(int*)&real._depth,real.width()*real.height(),
  42800. data_in,0,1,real.depth(),
  42801. data_in,0,1,real.depth(),
  42802. is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
  42803. cimg_forC(real,c) {
  42804. CImg<T> realc = real.get_shared_channel(c), imagc = imag.get_shared_channel(c);
  42805. switch (_axis) {
  42806. case 'x' :
  42807. cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
  42808. cimg_forXYZ(realc,x,y,z) {
  42809. const ulongT
  42810. i = realc.offset(x,y,z),
  42811. j = 2*(x + (ulongT)y*realc._width + (ulongT)z*realc._width*realc._height);
  42812. ptrf[j] = (double)realc[i];
  42813. ptrf[j + 1] = (double)imagc[i];
  42814. }
  42815. break;
  42816. case 'y' :
  42817. cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
  42818. cimg_forXYZ(realc,x,y,z) {
  42819. const ulongT
  42820. i = realc.offset(x,y,z),
  42821. j = 2*(y + (ulongT)x*realc._height + (ulongT)z*realc._width*realc._height);
  42822. ptrf[j] = (double)realc[i];
  42823. ptrf[j + 1] = (double)imagc[i];
  42824. }
  42825. break;
  42826. default :
  42827. cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
  42828. cimg_forXYZ(realc,x,y,z) {
  42829. const ulongT
  42830. i = realc.offset(x,y,z),
  42831. j = 2*(z + (ulongT)x*realc._depth + (ulongT)y*realc._width*realc._depth);
  42832. ptrf[j] = (double)realc[i];
  42833. ptrf[j + 1] = (double)imagc[i];
  42834. }
  42835. }
  42836. fftw_execute(data_plan);
  42837. const double a = is_inverse?1.0/(_axis=='x'?real.width():_axis=='y'?real.height():real.depth()):1.0;
  42838. switch (_axis) {
  42839. case 'x' :
  42840. cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
  42841. cimg_forXYZ(realc,x,y,z) {
  42842. const ulongT
  42843. i = 2*(x + (ulongT)y*realc._width + (ulongT)z*realc._width*realc._height),
  42844. j = realc.offset(x,y,z);
  42845. realc[j] = (T)(a*ptrf[i]);
  42846. imagc[j] = (T)(a*ptrf[i + 1]);
  42847. }
  42848. break;
  42849. case 'y' :
  42850. cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
  42851. cimg_forXYZ(realc,x,y,z) {
  42852. const ulongT
  42853. i = 2*(y + (ulongT)x*realc._height + (ulongT)z*realc._width*realc._height),
  42854. j = realc.offset(x,y,z);
  42855. realc[j] = (T)(a*ptrf[i]);
  42856. imagc[j] = (T)(a*ptrf[i + 1]);
  42857. }
  42858. break;
  42859. default :
  42860. cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
  42861. cimg_forXYZ(realc,x,y,z) {
  42862. const ulongT
  42863. i = 2*(z + (ulongT)x*realc._depth + (ulongT)y*realc._width*realc._depth),
  42864. j = realc.offset(x,y,z);
  42865. realc[j] = (T)(a*ptrf[i]);
  42866. imagc[j] = (T)(a*ptrf[i + 1]);
  42867. }
  42868. }
  42869. }
  42870. fftw_destroy_plan(data_plan);
  42871. fftw_free(data_in);
  42872. #ifndef cimg_use_fftw3_singlethread
  42873. fftw_cleanup_threads();
  42874. #endif
  42875. cimg::mutex(12,0);
  42876. #else
  42877. switch (_axis) {
  42878. case 'x' : { // Fourier along X, using built-in functions
  42879. const unsigned int N = real._width, N2 = N>>1;
  42880. if (((N - 1)&N) && N!=1)
  42881. throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) "
  42882. "have non 2^N dimension along the X-axis.",
  42883. pixel_type(),
  42884. real._width,real._height,real._depth,real._spectrum);
  42885. for (unsigned int i = 0, j = 0; i<N2; ++i) {
  42886. if (j>i) cimg_forYZC(real,y,z,c) {
  42887. cimg::swap(real(i,y,z,c),real(j,y,z,c));
  42888. cimg::swap(imag(i,y,z,c),imag(j,y,z,c));
  42889. if (j<N2) {
  42890. const unsigned int ri = N - 1 - i, rj = N - 1 - j;
  42891. cimg::swap(real(ri,y,z,c),real(rj,y,z,c));
  42892. cimg::swap(imag(ri,y,z,c),imag(rj,y,z,c));
  42893. }
  42894. }
  42895. for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
  42896. }
  42897. for (unsigned int delta = 2; delta<=N; delta<<=1) {
  42898. const unsigned int delta2 = delta>>1;
  42899. for (unsigned int i = 0; i<N; i+=delta) {
  42900. float wr = 1, wi = 0;
  42901. const float
  42902. angle = (float)((is_inverse?+1:-1)*2*cimg::PI/delta),
  42903. ca = (float)std::cos(angle),
  42904. sa = (float)std::sin(angle);
  42905. for (unsigned int k = 0; k<delta2; ++k) {
  42906. const unsigned int j = i + k, nj = j + delta2;
  42907. cimg_forYZC(real,y,z,c) {
  42908. 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);
  42909. const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
  42910. nir = (T)(ir - tmpr);
  42911. nii = (T)(ii - tmpi);
  42912. ir+=(T)tmpr;
  42913. ii+=(T)tmpi;
  42914. }
  42915. const float nwr = wr*ca-wi*sa;
  42916. wi = wi*ca + wr*sa;
  42917. wr = nwr;
  42918. }
  42919. }
  42920. }
  42921. if (is_inverse) { real/=N; imag/=N; }
  42922. } break;
  42923. case 'y' : { // Fourier along Y, using built-in functions
  42924. const unsigned int N = real._height, N2 = N>>1;
  42925. if (((N - 1)&N) && N!=1)
  42926. throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) "
  42927. "have non 2^N dimension along the Y-axis.",
  42928. pixel_type(),
  42929. real._width,real._height,real._depth,real._spectrum);
  42930. for (unsigned int i = 0, j = 0; i<N2; ++i) {
  42931. if (j>i) cimg_forXZC(real,x,z,c) {
  42932. cimg::swap(real(x,i,z,c),real(x,j,z,c));
  42933. cimg::swap(imag(x,i,z,c),imag(x,j,z,c));
  42934. if (j<N2) {
  42935. const unsigned int ri = N - 1 - i, rj = N - 1 - j;
  42936. cimg::swap(real(x,ri,z,c),real(x,rj,z,c));
  42937. cimg::swap(imag(x,ri,z,c),imag(x,rj,z,c));
  42938. }
  42939. }
  42940. for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
  42941. }
  42942. for (unsigned int delta = 2; delta<=N; delta<<=1) {
  42943. const unsigned int delta2 = (delta>>1);
  42944. for (unsigned int i = 0; i<N; i+=delta) {
  42945. float wr = 1, wi = 0;
  42946. const float
  42947. angle = (float)((is_inverse?+1:-1)*2*cimg::PI/delta),
  42948. ca = (float)std::cos(angle),
  42949. sa = (float)std::sin(angle);
  42950. for (unsigned int k = 0; k<delta2; ++k) {
  42951. const unsigned int j = i + k, nj = j + delta2;
  42952. cimg_forXZC(real,x,z,c) {
  42953. 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);
  42954. const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
  42955. nir = (T)(ir - tmpr);
  42956. nii = (T)(ii - tmpi);
  42957. ir+=(T)tmpr;
  42958. ii+=(T)tmpi;
  42959. }
  42960. const float nwr = wr*ca-wi*sa;
  42961. wi = wi*ca + wr*sa;
  42962. wr = nwr;
  42963. }
  42964. }
  42965. }
  42966. if (is_inverse) { real/=N; imag/=N; }
  42967. } break;
  42968. default : { // Fourier along Z, using built-in functions
  42969. const unsigned int N = real._depth, N2 = N>>1;
  42970. if (((N - 1)&N) && N!=1)
  42971. throw CImgInstanceException("CImgList<%s>::FFT(): Specified real and imaginary parts (%u,%u,%u,%u) "
  42972. "have non 2^N dimension along the Z-axis.",
  42973. pixel_type(),
  42974. real._width,real._height,real._depth,real._spectrum);
  42975. for (unsigned int i = 0, j = 0; i<N2; ++i) {
  42976. if (j>i) cimg_forXYC(real,x,y,c) {
  42977. cimg::swap(real(x,y,i,c),real(x,y,j,c));
  42978. cimg::swap(imag(x,y,i,c),imag(x,y,j,c));
  42979. if (j<N2) {
  42980. const unsigned int ri = N - 1 - i, rj = N - 1 - j;
  42981. cimg::swap(real(x,y,ri,c),real(x,y,rj,c));
  42982. cimg::swap(imag(x,y,ri,c),imag(x,y,rj,c));
  42983. }
  42984. }
  42985. for (unsigned int m = N, n = N2; (j+=n)>=m; j-=m, m = n, n>>=1) {}
  42986. }
  42987. for (unsigned int delta = 2; delta<=N; delta<<=1) {
  42988. const unsigned int delta2 = (delta>>1);
  42989. for (unsigned int i = 0; i<N; i+=delta) {
  42990. float wr = 1, wi = 0;
  42991. const float
  42992. angle = (float)((is_inverse?+1:-1)*2*cimg::PI/delta),
  42993. ca = (float)std::cos(angle),
  42994. sa = (float)std::sin(angle);
  42995. for (unsigned int k = 0; k<delta2; ++k) {
  42996. const unsigned int j = i + k, nj = j + delta2;
  42997. cimg_forXYC(real,x,y,c) {
  42998. 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);
  42999. const float tmpr = (float)(wr*nir - wi*nii), tmpi = (float)(wr*nii + wi*nir);
  43000. nir = (T)(ir - tmpr);
  43001. nii = (T)(ii - tmpi);
  43002. ir+=(T)tmpr;
  43003. ii+=(T)tmpi;
  43004. }
  43005. const float nwr = wr*ca-wi*sa;
  43006. wi = wi*ca + wr*sa;
  43007. wr = nwr;
  43008. }
  43009. }
  43010. }
  43011. if (is_inverse) { real/=N; imag/=N; }
  43012. } break;
  43013. }
  43014. #endif
  43015. }
  43016. //! Compute n-D Fast Fourier Transform.
  43017. /**
  43018. \param[in,out] real Real part of the pixel values.
  43019. \param[in,out] imag Imaginary part of the pixel values.
  43020. \param is_inverse Tells if the forward (\c false) or inverse (\c true) FFT is computed.
  43021. \param nb_threads Number of parallel threads used for the computation.
  43022. Use \c 0 to set this to the number of available cpus.
  43023. **/
  43024. static void FFT(CImg<T>& real, CImg<T>& imag, const bool is_inverse=false,
  43025. const unsigned int nb_threads=0) {
  43026. if (!real)
  43027. throw CImgInstanceException("CImgList<%s>::FFT(): Empty specified real part.",
  43028. pixel_type());
  43029. if (!imag) imag.assign(real._width,real._height,real._depth,real._spectrum,(T)0);
  43030. if (!real.is_sameXYZC(imag))
  43031. throw CImgInstanceException("CImgList<%s>::FFT(): Specified real part (%u,%u,%u,%u,%p) and "
  43032. "imaginary part (%u,%u,%u,%u,%p) have different dimensions.",
  43033. pixel_type(),
  43034. real._width,real._height,real._depth,real._spectrum,real._data,
  43035. imag._width,imag._height,imag._depth,imag._spectrum,imag._data);
  43036. cimg::unused(nb_threads);
  43037. #ifdef cimg_use_fftw3
  43038. cimg::mutex(12);
  43039. #ifndef cimg_use_fftw3_singlethread
  43040. fftw_plan_with_nthreads(nb_threads?nb_threads:cimg::nb_cpus());
  43041. #endif
  43042. fftw_complex *data_in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*real._width*real._height*real._depth);
  43043. if (!data_in)
  43044. throw CImgInstanceException("CImgList<%s>::FFT(): Failed to allocate memory (%s) "
  43045. "for computing FFT of image (%u,%u,%u,%u).",
  43046. pixel_type(),
  43047. cimg::strbuffersize(sizeof(fftw_complex)*real._width*
  43048. real._height*real._depth*real._spectrum),
  43049. real._width,real._height,real._depth,real._spectrum);
  43050. double *const ptrf = (double*)data_in;
  43051. fftw_plan data_plan =
  43052. real._depth>1?fftw_plan_dft_3d(real._depth,real._height,real._width,data_in,data_in,
  43053. is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE):
  43054. real._height>1?fftw_plan_dft_2d(real._height,real._width,data_in,data_in,
  43055. is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE):
  43056. fftw_plan_dft_1d(real._width,data_in,data_in,
  43057. is_inverse?FFTW_BACKWARD:FFTW_FORWARD,FFTW_ESTIMATE);
  43058. cimg_forC(real,c) {
  43059. CImg<T> realc = real.get_shared_channel(c), imagc = imag.get_shared_channel(c);
  43060. cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
  43061. cimg_rofoff(realc,i) { const ulongT i2 = 2*i; ptrf[i2] = (double)realc[i]; ptrf[i2 + 1] = (double)imagc[i]; }
  43062. fftw_execute(data_plan);
  43063. if (is_inverse) {
  43064. const double a = 1.0/(real.width()*real.height()*real.depth());
  43065. cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
  43066. cimg_rofoff(realc,i) { const ulongT i2 = 2*i; realc[i] = (T)(a*ptrf[i2]); imagc[i] = (T)(a*ptrf[i2 + 1]); }
  43067. } else
  43068. cimg_pragma_openmp(parallel for cimg_openmp_if_size(real.width()*real.height()*real.depth(),125000))
  43069. cimg_rofoff(realc,i) { const ulongT i2 = 2*i; realc[i] = (T)ptrf[i2]; imagc[i] = (T)ptrf[i2 + 1]; }
  43070. }
  43071. fftw_destroy_plan(data_plan);
  43072. fftw_free(data_in);
  43073. #ifndef cimg_use_fftw3_singlethread
  43074. fftw_cleanup_threads();
  43075. #endif
  43076. cimg::mutex(12,0);
  43077. #else
  43078. if (real._depth>1) FFT(real,imag,'z',is_inverse);
  43079. if (real._height>1) FFT(real,imag,'y',is_inverse);
  43080. if (real._width>1) FFT(real,imag,'x',is_inverse);
  43081. #endif
  43082. }
  43083. //@}
  43084. //-------------------------------------
  43085. //
  43086. //! \name 3D Objects Management
  43087. //@{
  43088. //-------------------------------------
  43089. //! Rotate 3D object's vertices.
  43090. /**
  43091. \param x X-coordinate of the rotation axis, or first quaternion coordinate.
  43092. \param y Y-coordinate of the rotation axis, or second quaternion coordinate.
  43093. \param z Z-coordinate of the rotation axis, or second quaternion coordinate.
  43094. \param w Angle of the rotation axis (in degree), or fourth quaternion coordinate.
  43095. \param is_quaternion Tell is the four arguments denotes a set { axis + angle } or a quaternion (x,y,z,w).
  43096. **/
  43097. CImg<T>& rotate_object3d(const float x, const float y, const float z, const float w,
  43098. const bool is_quaternion=false) {
  43099. return get_rotate_object3d(x,y,z,w,is_quaternion).move_to(*this);
  43100. }
  43101. CImg<Tfloat> get_rotate_object3d(const float x, const float y, const float z, const float w,
  43102. const bool is_quaternion=false) const {
  43103. if (_height!=3 || _depth>1 || _spectrum>1)
  43104. throw CImgInstanceException(_cimg_instance
  43105. "rotate_object3d(): Instance is not a set of 3D vertices.",
  43106. cimg_instance);
  43107. return CImg<Tfloat>::rotation_matrix(x,y,z,w,is_quaternion)**this;
  43108. }
  43109. //! Shift 3D object's vertices.
  43110. /**
  43111. \param tx X-coordinate of the 3D displacement vector.
  43112. \param ty Y-coordinate of the 3D displacement vector.
  43113. \param tz Z-coordinate of the 3D displacement vector.
  43114. **/
  43115. CImg<T>& shift_object3d(const float tx, const float ty=0, const float tz=0) {
  43116. if (_height!=3 || _depth>1 || _spectrum>1)
  43117. throw CImgInstanceException(_cimg_instance
  43118. "shift_object3d(): Instance is not a set of 3D vertices.",
  43119. cimg_instance);
  43120. get_shared_row(0)+=tx; get_shared_row(1)+=ty; get_shared_row(2)+=tz;
  43121. return *this;
  43122. }
  43123. //! Shift 3D object's vertices \newinstance.
  43124. CImg<Tfloat> get_shift_object3d(const float tx, const float ty=0, const float tz=0) const {
  43125. return CImg<Tfloat>(*this,false).shift_object3d(tx,ty,tz);
  43126. }
  43127. //! Shift 3D object's vertices, so that it becomes centered.
  43128. /**
  43129. \note The object center is computed as its barycenter.
  43130. **/
  43131. CImg<T>& shift_object3d() {
  43132. if (_height!=3 || _depth>1 || _spectrum>1)
  43133. throw CImgInstanceException(_cimg_instance
  43134. "shift_object3d(): Instance is not a set of 3D vertices.",
  43135. cimg_instance);
  43136. CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
  43137. float
  43138. xm, xM = (float)xcoords.max_min(xm),
  43139. ym, yM = (float)ycoords.max_min(ym),
  43140. zm, zM = (float)zcoords.max_min(zm);
  43141. xcoords-=(xm + xM)/2; ycoords-=(ym + yM)/2; zcoords-=(zm + zM)/2;
  43142. return *this;
  43143. }
  43144. //! Shift 3D object's vertices, so that it becomes centered \newinstance.
  43145. CImg<Tfloat> get_shift_object3d() const {
  43146. return CImg<Tfloat>(*this,false).shift_object3d();
  43147. }
  43148. //! Resize 3D object.
  43149. /**
  43150. \param sx Width of the 3D object's bounding box.
  43151. \param sy Height of the 3D object's bounding box.
  43152. \param sz Depth of the 3D object's bounding box.
  43153. **/
  43154. CImg<T>& resize_object3d(const float sx, const float sy=-100, const float sz=-100) {
  43155. if (_height!=3 || _depth>1 || _spectrum>1)
  43156. throw CImgInstanceException(_cimg_instance
  43157. "resize_object3d(): Instance is not a set of 3D vertices.",
  43158. cimg_instance);
  43159. CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
  43160. float
  43161. xm, xM = (float)xcoords.max_min(xm),
  43162. ym, yM = (float)ycoords.max_min(ym),
  43163. zm, zM = (float)zcoords.max_min(zm);
  43164. if (xm<xM) { if (sx>0) xcoords*=sx/(xM-xm); else xcoords*=-sx/100; }
  43165. if (ym<yM) { if (sy>0) ycoords*=sy/(yM-ym); else ycoords*=-sy/100; }
  43166. if (zm<zM) { if (sz>0) zcoords*=sz/(zM-zm); else zcoords*=-sz/100; }
  43167. return *this;
  43168. }
  43169. //! Resize 3D object \newinstance.
  43170. CImg<Tfloat> get_resize_object3d(const float sx, const float sy=-100, const float sz=-100) const {
  43171. return CImg<Tfloat>(*this,false).resize_object3d(sx,sy,sz);
  43172. }
  43173. //! Resize 3D object to unit size.
  43174. CImg<T> resize_object3d() {
  43175. if (_height!=3 || _depth>1 || _spectrum>1)
  43176. throw CImgInstanceException(_cimg_instance
  43177. "resize_object3d(): Instance is not a set of 3D vertices.",
  43178. cimg_instance);
  43179. CImg<T> xcoords = get_shared_row(0), ycoords = get_shared_row(1), zcoords = get_shared_row(2);
  43180. float
  43181. xm, xM = (float)xcoords.max_min(xm),
  43182. ym, yM = (float)ycoords.max_min(ym),
  43183. zm, zM = (float)zcoords.max_min(zm);
  43184. const float dx = xM - xm, dy = yM - ym, dz = zM - zm, dmax = cimg::max(dx,dy,dz);
  43185. if (dmax>0) { xcoords/=dmax; ycoords/=dmax; zcoords/=dmax; }
  43186. return *this;
  43187. }
  43188. //! Resize 3D object to unit size \newinstance.
  43189. CImg<Tfloat> get_resize_object3d() const {
  43190. return CImg<Tfloat>(*this,false).resize_object3d();
  43191. }
  43192. //! Merge two 3D objects together.
  43193. /**
  43194. \param[in,out] primitives Primitives data of the current 3D object.
  43195. \param obj_vertices Vertices data of the additional 3D object.
  43196. \param obj_primitives Primitives data of the additional 3D object.
  43197. **/
  43198. template<typename tf, typename tp, typename tff>
  43199. CImg<T>& append_object3d(CImgList<tf>& primitives, const CImg<tp>& obj_vertices,
  43200. const CImgList<tff>& obj_primitives) {
  43201. if (!obj_vertices || !obj_primitives) return *this;
  43202. if (obj_vertices._height!=3 || obj_vertices._depth>1 || obj_vertices._spectrum>1)
  43203. throw CImgInstanceException(_cimg_instance
  43204. "append_object3d(): Specified vertice image (%u,%u,%u,%u,%p) is not a "
  43205. "set of 3D vertices.",
  43206. cimg_instance,
  43207. obj_vertices._width,obj_vertices._height,
  43208. obj_vertices._depth,obj_vertices._spectrum,obj_vertices._data);
  43209. if (is_empty()) { primitives.assign(obj_primitives); return assign(obj_vertices); }
  43210. if (_height!=3 || _depth>1 || _spectrum>1)
  43211. throw CImgInstanceException(_cimg_instance
  43212. "append_object3d(): Instance is not a set of 3D vertices.",
  43213. cimg_instance);
  43214. const unsigned int P = _width;
  43215. append(obj_vertices,'x');
  43216. const unsigned int N = primitives._width;
  43217. primitives.insert(obj_primitives);
  43218. for (unsigned int i = N; i<primitives._width; ++i) {
  43219. CImg<tf> &p = primitives[i];
  43220. switch (p.size()) {
  43221. case 1 : p[0]+=P; break; // Point
  43222. case 5 : p[0]+=P; p[1]+=P; break; // Sphere
  43223. case 2 : case 6 : p[0]+=P; p[1]+=P; break; // Segment
  43224. case 3 : case 9 : p[0]+=P; p[1]+=P; p[2]+=P; break; // Triangle
  43225. case 4 : case 12 : p[0]+=P; p[1]+=P; p[2]+=P; p[3]+=P; break; // Rectangle
  43226. }
  43227. }
  43228. return *this;
  43229. }
  43230. //! Texturize primitives of a 3D object.
  43231. /**
  43232. \param[in,out] primitives Primitives data of the 3D object.
  43233. \param[in,out] colors Colors data of the 3D object.
  43234. \param texture Texture image to map to 3D object.
  43235. \param coords Texture-mapping coordinates.
  43236. **/
  43237. template<typename tp, typename tc, typename tt, typename tx>
  43238. const CImg<T>& texturize_object3d(CImgList<tp>& primitives, CImgList<tc>& colors,
  43239. const CImg<tt>& texture, const CImg<tx>& coords=CImg<tx>::const_empty()) const {
  43240. if (is_empty()) return *this;
  43241. if (_height!=3)
  43242. throw CImgInstanceException(_cimg_instance
  43243. "texturize_object3d(): image instance is not a set of 3D points.",
  43244. cimg_instance);
  43245. if (coords && (coords._width!=_width || coords._height!=2))
  43246. throw CImgArgumentException(_cimg_instance
  43247. "texturize_object3d(): Invalid specified texture coordinates (%u,%u,%u,%u,%p).",
  43248. cimg_instance,
  43249. coords._width,coords._height,coords._depth,coords._spectrum,coords._data);
  43250. CImg<intT> _coords;
  43251. if (!coords) { // If no texture coordinates specified, do a default XY-projection
  43252. _coords.assign(_width,2);
  43253. float
  43254. xmin, xmax = (float)get_shared_row(0).max_min(xmin),
  43255. ymin, ymax = (float)get_shared_row(1).max_min(ymin),
  43256. dx = xmax>xmin?xmax-xmin:1,
  43257. dy = ymax>ymin?ymax-ymin:1;
  43258. cimg_forX(*this,p) {
  43259. _coords(p,0) = (int)(((*this)(p,0) - xmin)*texture._width/dx);
  43260. _coords(p,1) = (int)(((*this)(p,1) - ymin)*texture._height/dy);
  43261. }
  43262. } else _coords = coords;
  43263. int texture_ind = -1;
  43264. cimglist_for(primitives,l) {
  43265. CImg<tp> &p = primitives[l];
  43266. const unsigned int siz = p.size();
  43267. switch (siz) {
  43268. case 1 : { // Point
  43269. const unsigned int i0 = (unsigned int)p[0];
  43270. const int x0 = _coords(i0,0), y0 = _coords(i0,1);
  43271. texture.get_vector_at(x0<=0?0:x0>=texture.width()?texture.width() - 1:x0,
  43272. y0<=0?0:y0>=texture.height()?texture.height() - 1:y0).move_to(colors[l]);
  43273. } break;
  43274. case 2 : case 6 : { // Line
  43275. const unsigned int i0 = (unsigned int)p[0], i1 = (unsigned int)p[1];
  43276. const int
  43277. x0 = _coords(i0,0), y0 = _coords(i0,1),
  43278. x1 = _coords(i1,0), y1 = _coords(i1,1);
  43279. if (texture_ind<0) colors[texture_ind=l].assign(texture,false);
  43280. else colors[l].assign(colors[texture_ind],true);
  43281. CImg<tp>::vector(i0,i1,x0,y0,x1,y1).move_to(p);
  43282. } break;
  43283. case 3 : case 9 : { // Triangle
  43284. const unsigned int i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2];
  43285. const int
  43286. x0 = _coords(i0,0), y0 = _coords(i0,1),
  43287. x1 = _coords(i1,0), y1 = _coords(i1,1),
  43288. x2 = _coords(i2,0), y2 = _coords(i2,1);
  43289. if (texture_ind<0) colors[texture_ind=l].assign(texture,false);
  43290. else colors[l].assign(colors[texture_ind],true);
  43291. CImg<tp>::vector(i0,i1,i2,x0,y0,x1,y1,x2,y2).move_to(p);
  43292. } break;
  43293. case 4 : case 12 : { // Quadrangle
  43294. const unsigned int
  43295. i0 = (unsigned int)p[0], i1 = (unsigned int)p[1], i2 = (unsigned int)p[2], i3 = (unsigned int)p[3];
  43296. const int
  43297. x0 = _coords(i0,0), y0 = _coords(i0,1),
  43298. x1 = _coords(i1,0), y1 = _coords(i1,1),
  43299. x2 = _coords(i2,0), y2 = _coords(i2,1),
  43300. x3 = _coords(i3,0), y3 = _coords(i3,1);
  43301. if (texture_ind<0) colors[texture_ind=l].assign(texture,false);
  43302. else colors[l].assign(colors[texture_ind],true);
  43303. CImg<tp>::vector(i0,i1,i2,i3,x0,y0,x1,y1,x2,y2,x3,y3).move_to(p);
  43304. } break;
  43305. }
  43306. }
  43307. return *this;
  43308. }
  43309. //! Generate a 3D elevation of the image instance.
  43310. /**
  43311. \param[out] primitives The returned list of the 3D object primitives
  43312. (template type \e tf should be at least \e unsigned \e int).
  43313. \param[out] colors The returned list of the 3D object colors.
  43314. \param elevation The input elevation map.
  43315. \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
  43316. \par Example
  43317. \code
  43318. const CImg<float> img("reference.jpg");
  43319. CImgList<unsigned int> faces3d;
  43320. CImgList<unsigned char> colors3d;
  43321. const CImg<float> points3d = img.get_elevation3d(faces3d,colors3d,img.get_norm()*0.2);
  43322. CImg<unsigned char>().display_object3d("Elevation3d",points3d,faces3d,colors3d);
  43323. \endcode
  43324. \image html ref_elevation3d.jpg
  43325. **/
  43326. template<typename tf, typename tc, typename te>
  43327. CImg<floatT> get_elevation3d(CImgList<tf>& primitives, CImgList<tc>& colors, const CImg<te>& elevation) const {
  43328. if (!is_sameXY(elevation) || elevation._depth>1 || elevation._spectrum>1)
  43329. throw CImgArgumentException(_cimg_instance
  43330. "get_elevation3d(): Instance and specified elevation (%u,%u,%u,%u,%p) "
  43331. "have incompatible dimensions.",
  43332. cimg_instance,
  43333. elevation._width,elevation._height,elevation._depth,
  43334. elevation._spectrum,elevation._data);
  43335. if (is_empty()) return *this;
  43336. float m, M = (float)max_min(m);
  43337. if (M==m) ++M;
  43338. colors.assign();
  43339. const unsigned int size_x1 = _width - 1, size_y1 = _height - 1;
  43340. for (unsigned int y = 0; y<size_y1; ++y)
  43341. for (unsigned int x = 0; x<size_x1; ++x) {
  43342. const unsigned char
  43343. r = (unsigned char)(((*this)(x,y,0) - m)*255/(M-m)),
  43344. g = (unsigned char)(_spectrum>1?((*this)(x,y,1) - m)*255/(M-m):r),
  43345. b = (unsigned char)(_spectrum>2?((*this)(x,y,2) - m)*255/(M-m):_spectrum>1?0:r);
  43346. CImg<tc>::vector((tc)r,(tc)g,(tc)b).move_to(colors);
  43347. }
  43348. const typename CImg<te>::_functor2d_int func(elevation);
  43349. return elevation3d(primitives,func,0,0,_width - 1.f,_height - 1.f,_width,_height);
  43350. }
  43351. //! Generate the 3D projection planes of the image instance.
  43352. /**
  43353. \param[out] primitives Primitives data of the returned 3D object.
  43354. \param[out] colors Colors data of the returned 3D object.
  43355. \param x0 X-coordinate of the projection point.
  43356. \param y0 Y-coordinate of the projection point.
  43357. \param z0 Z-coordinate of the projection point.
  43358. \param normalize_colors Tells if the created textures have normalized colors.
  43359. **/
  43360. template<typename tf, typename tc>
  43361. CImg<floatT> get_projections3d(CImgList<tf>& primitives, CImgList<tc>& colors,
  43362. const unsigned int x0, const unsigned int y0, const unsigned int z0,
  43363. const bool normalize_colors=false) const {
  43364. float m = 0, M = 0, delta = 1;
  43365. if (normalize_colors) { m = (float)min_max(M); delta = 255/(m==M?1:M-m); }
  43366. const unsigned int
  43367. _x0 = (x0>=_width)?_width - 1:x0,
  43368. _y0 = (y0>=_height)?_height - 1:y0,
  43369. _z0 = (z0>=_depth)?_depth - 1:z0;
  43370. CImg<tc> img_xy, img_xz, img_yz;
  43371. if (normalize_colors) {
  43372. ((get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1)-=m)*=delta).move_to(img_xy);
  43373. ((get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1)-=m)*=delta).resize(_width,_depth,1,-100,-1).
  43374. move_to(img_xz);
  43375. ((get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1)-=m)*=delta).resize(_height,_depth,1,-100,-1).
  43376. move_to(img_yz);
  43377. } else {
  43378. get_crop(0,0,_z0,0,_width - 1,_height - 1,_z0,_spectrum - 1).move_to(img_xy);
  43379. get_crop(0,_y0,0,0,_width - 1,_y0,_depth - 1,_spectrum - 1).resize(_width,_depth,1,-100,-1).move_to(img_xz);
  43380. get_crop(_x0,0,0,0,_x0,_height - 1,_depth - 1,_spectrum - 1).resize(_height,_depth,1,-100,-1).move_to(img_yz);
  43381. }
  43382. CImg<floatT> points(12,3,1,1,
  43383. 0,_width - 1,_width - 1,0, 0,_width - 1,_width - 1,0, _x0,_x0,_x0,_x0,
  43384. 0,0,_height - 1,_height - 1, _y0,_y0,_y0,_y0, 0,_height - 1,_height - 1,0,
  43385. _z0,_z0,_z0,_z0, 0,0,_depth - 1,_depth - 1, 0,0,_depth - 1,_depth - 1);
  43386. primitives.assign();
  43387. 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).
  43388. move_to(primitives);
  43389. 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).
  43390. move_to(primitives);
  43391. 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).
  43392. move_to(primitives);
  43393. colors.assign();
  43394. img_xy.move_to(colors);
  43395. img_xz.move_to(colors);
  43396. img_yz.move_to(colors);
  43397. return points;
  43398. }
  43399. //! Generate a isoline of the image instance as a 3D object.
  43400. /**
  43401. \param[out] primitives The returned list of the 3D object primitives
  43402. (template type \e tf should be at least \e unsigned \e int).
  43403. \param isovalue The returned list of the 3D object colors.
  43404. \param size_x The number of subdivisions along the X-axis.
  43405. \param size_y The number of subdisivions along the Y-axis.
  43406. \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
  43407. \par Example
  43408. \code
  43409. const CImg<float> img("reference.jpg");
  43410. CImgList<unsigned int> faces3d;
  43411. const CImg<float> points3d = img.get_isoline3d(faces3d,100);
  43412. CImg<unsigned char>().display_object3d("Isoline3d",points3d,faces3d,colors3d);
  43413. \endcode
  43414. \image html ref_isoline3d.jpg
  43415. **/
  43416. template<typename tf>
  43417. CImg<floatT> get_isoline3d(CImgList<tf>& primitives, const float isovalue,
  43418. const int size_x=-100, const int size_y=-100) const {
  43419. if (_spectrum>1)
  43420. throw CImgInstanceException(_cimg_instance
  43421. "get_isoline3d(): Instance is not a scalar image.",
  43422. cimg_instance);
  43423. if (_depth>1)
  43424. throw CImgInstanceException(_cimg_instance
  43425. "get_isoline3d(): Instance is not a 2D image.",
  43426. cimg_instance);
  43427. primitives.assign();
  43428. if (is_empty()) return *this;
  43429. CImg<floatT> vertices;
  43430. if ((size_x==-100 && size_y==-100) || (size_x==width() && size_y==height())) {
  43431. const _functor2d_int func(*this);
  43432. vertices = isoline3d(primitives,func,isovalue,0,0,width() - 1.f,height() - 1.f,width(),height());
  43433. } else {
  43434. const _functor2d_float func(*this);
  43435. vertices = isoline3d(primitives,func,isovalue,0,0,width() - 1.f,height() - 1.f,size_x,size_y);
  43436. }
  43437. return vertices;
  43438. }
  43439. //! Compute isolines of a function, as a 3D object.
  43440. /**
  43441. \param[out] primitives Primitives data of the resulting 3D object.
  43442. \param func Elevation functor. Must have <tt>operator()(x,y)</tt> defined.
  43443. \param isovalue Isovalue to extract from function.
  43444. \param x0 X-coordinate of the starting point.
  43445. \param y0 Y-coordinate of the starting point.
  43446. \param x1 X-coordinate of the ending point.
  43447. \param y1 Y-coordinate of the ending point.
  43448. \param size_x Resolution of the function along the X-axis.
  43449. \param size_y Resolution of the function along the Y-axis.
  43450. \note Use the marching squares algorithm for extracting the isolines.
  43451. **/
  43452. template<typename tf, typename tfunc>
  43453. static CImg<floatT> isoline3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue,
  43454. const float x0, const float y0, const float x1, const float y1,
  43455. const int size_x=256, const int size_y=256) {
  43456. CImgList<floatT> vertices;
  43457. primitives.assign();
  43458. typename CImg<floatT>::_functor_isoline3d add_vertex(vertices);
  43459. typename CImg<tf>::_functor_isoline3d add_segment(primitives);
  43460. isoline3d(add_vertex,add_segment,func,isovalue,x0,y0,x1,y1,size_x,size_y);
  43461. return vertices>'x';
  43462. }
  43463. //! Compute isolines of a function, as a 3D object.
  43464. /**
  43465. \param[out] add_vertex : Functor with operator()(x,y,z) defined for adding new vertex.
  43466. \param[out] add_segment : Functor with operator()(i,j) defined for adding new segment.
  43467. \param func Elevation function. Is of type <tt>float (*func)(const float x,const float y)</tt>.
  43468. \param isovalue Isovalue to extract from function.
  43469. \param x0 X-coordinate of the starting point.
  43470. \param y0 Y-coordinate of the starting point.
  43471. \param x1 X-coordinate of the ending point.
  43472. \param y1 Y-coordinate of the ending point.
  43473. \param size_x Resolution of the function along the X-axis.
  43474. \param size_y Resolution of the function along the Y-axis.
  43475. \note Use the marching squares algorithm for extracting the isolines.
  43476. **/
  43477. template<typename tv, typename tf, typename tfunc>
  43478. static void isoline3d(tv& add_vertex, tf& add_segment, const tfunc& func, const float isovalue,
  43479. const float x0, const float y0, const float x1, const float y1,
  43480. const int size_x, const int size_y) {
  43481. static const unsigned int edges[16] = { 0x0, 0x9, 0x3, 0xa, 0x6, 0xf, 0x5, 0xc, 0xc,
  43482. 0x5, 0xf, 0x6, 0xa, 0x3, 0x9, 0x0 };
  43483. static const int segments[16][4] = { { -1,-1,-1,-1 }, { 0,3,-1,-1 }, { 0,1,-1,-1 }, { 1,3,-1,-1 },
  43484. { 1,2,-1,-1 }, { 0,1,2,3 }, { 0,2,-1,-1 }, { 2,3,-1,-1 },
  43485. { 2,3,-1,-1 }, { 0,2,-1,-1}, { 0,3,1,2 }, { 1,2,-1,-1 },
  43486. { 1,3,-1,-1 }, { 0,1,-1,-1}, { 0,3,-1,-1}, { -1,-1,-1,-1 } };
  43487. const unsigned int
  43488. _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)),
  43489. _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)),
  43490. nx = _nx?_nx:1,
  43491. ny = _ny?_ny:1,
  43492. nxm1 = nx - 1,
  43493. nym1 = ny - 1;
  43494. if (!nxm1 || !nym1) return;
  43495. const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1;
  43496. CImg<intT> indices1(nx,1,1,2,-1), indices2(nx,1,1,2);
  43497. CImg<floatT> values1(nx), values2(nx);
  43498. float X = x0, Y = y0, nX = X + dx, nY = Y + dy;
  43499. int nb_vertices = 0;
  43500. // Fill first line with values
  43501. cimg_forX(values1,x) { values1(x) = (float)func(X,Y); X+=dx; }
  43502. // Run the marching squares algorithm
  43503. for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y=nY, nY+=dy) {
  43504. X = x0; nX = X + dx;
  43505. indices2.fill(-1);
  43506. values2(0) = (float)func(X,nY);
  43507. for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X=nX, nX+=dx) {
  43508. // Determine square configuration
  43509. const float
  43510. val0 = values1(xi),
  43511. val1 = values1(nxi),
  43512. val2 = values2(nxi) = (float)func(nX,nY),
  43513. val3 = values2(xi);
  43514. const unsigned int
  43515. configuration = (val0<isovalue?1U:0U) | (val1<isovalue?2U:0U) |
  43516. (val2<isovalue?4U:0U) | (val3<isovalue?8U:0U),
  43517. edge = edges[configuration];
  43518. // Compute intersection vertices
  43519. if (edge) {
  43520. if ((edge&1) && indices1(xi,0)<0) {
  43521. const float Xi = X + (isovalue-val0)*dx/(val1-val0);
  43522. indices1(xi,0) = nb_vertices++;
  43523. add_vertex(Xi,Y,0.0f);
  43524. }
  43525. if ((edge&2) && indices1(nxi,1)<0) {
  43526. const float Yi = Y + (isovalue-val1)*dy/(val2-val1);
  43527. indices1(nxi,1) = nb_vertices++;
  43528. add_vertex(nX,Yi,0.0f);
  43529. }
  43530. if ((edge&4) && indices2(xi,0)<0) {
  43531. const float Xi = X + (isovalue-val3)*dx/(val2-val3);
  43532. indices2(xi,0) = nb_vertices++;
  43533. add_vertex(Xi,nY,0.0f);
  43534. }
  43535. if ((edge&8) && indices1(xi,1)<0) {
  43536. const float Yi = Y + (isovalue-val0)*dy/(val3-val0);
  43537. indices1(xi,1) = nb_vertices++;
  43538. add_vertex(X,Yi,0.0f);
  43539. }
  43540. // Create segments
  43541. for (const int *segment = segments[configuration]; *segment!=-1; ) {
  43542. const unsigned int p0 = (unsigned int)*(segment++), p1 = (unsigned int)*(segment++);
  43543. const int
  43544. i0 = _isoline3d_index(p0,indices1,indices2,xi,nxi),
  43545. i1 = _isoline3d_index(p1,indices1,indices2,xi,nxi);
  43546. add_segment(i0,i1);
  43547. }
  43548. }
  43549. }
  43550. values1.swap(values2);
  43551. indices1.swap(indices2);
  43552. }
  43553. }
  43554. //! Compute isolines of a function, as a 3D object \overloading.
  43555. template<typename tf>
  43556. static CImg<floatT> isoline3d(CImgList<tf>& primitives, const char *const expression, const float isovalue,
  43557. const float x0, const float y0, const float x1, const float y1,
  43558. const int size_x=256, const int size_y=256) {
  43559. const _functor2d_expr func(expression);
  43560. return isoline3d(primitives,func,isovalue,x0,y0,x1,y1,size_x,size_y);
  43561. }
  43562. template<typename t>
  43563. static int _isoline3d_index(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2,
  43564. const unsigned int x, const unsigned int nx) {
  43565. switch (edge) {
  43566. case 0 : return (int)indices1(x,0);
  43567. case 1 : return (int)indices1(nx,1);
  43568. case 2 : return (int)indices2(x,0);
  43569. case 3 : return (int)indices1(x,1);
  43570. }
  43571. return 0;
  43572. }
  43573. //! Generate an isosurface of the image instance as a 3D object.
  43574. /**
  43575. \param[out] primitives The returned list of the 3D object primitives
  43576. (template type \e tf should be at least \e unsigned \e int).
  43577. \param isovalue The returned list of the 3D object colors.
  43578. \param size_x Number of subdivisions along the X-axis.
  43579. \param size_y Number of subdisivions along the Y-axis.
  43580. \param size_z Number of subdisivions along the Z-axis.
  43581. \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
  43582. \par Example
  43583. \code
  43584. const CImg<float> img = CImg<unsigned char>("reference.jpg").resize(-100,-100,20);
  43585. CImgList<unsigned int> faces3d;
  43586. const CImg<float> points3d = img.get_isosurface3d(faces3d,100);
  43587. CImg<unsigned char>().display_object3d("Isosurface3d",points3d,faces3d,colors3d);
  43588. \endcode
  43589. \image html ref_isosurface3d.jpg
  43590. **/
  43591. template<typename tf>
  43592. CImg<floatT> get_isosurface3d(CImgList<tf>& primitives, const float isovalue,
  43593. const int size_x=-100, const int size_y=-100, const int size_z=-100) const {
  43594. if (_spectrum>1)
  43595. throw CImgInstanceException(_cimg_instance
  43596. "get_isosurface3d(): Instance is not a scalar image.",
  43597. cimg_instance);
  43598. primitives.assign();
  43599. if (is_empty()) return *this;
  43600. CImg<floatT> vertices;
  43601. if ((size_x==-100 && size_y==-100 && size_z==-100) || (size_x==width() && size_y==height() && size_z==depth())) {
  43602. const _functor3d_int func(*this);
  43603. vertices = isosurface3d(primitives,func,isovalue,0,0,0,width() - 1.f,height() - 1.f,depth() - 1.f,
  43604. width(),height(),depth());
  43605. } else {
  43606. const _functor3d_float func(*this);
  43607. vertices = isosurface3d(primitives,func,isovalue,0,0,0,width() - 1.f,height() - 1.f,depth() - 1.f,
  43608. size_x,size_y,size_z);
  43609. }
  43610. return vertices;
  43611. }
  43612. //! Compute isosurface of a function, as a 3D object.
  43613. /**
  43614. \param[out] primitives Primitives data of the resulting 3D object.
  43615. \param func Implicit function. Is of type <tt>float (*func)(const float x, const float y, const float z)</tt>.
  43616. \param isovalue Isovalue to extract.
  43617. \param x0 X-coordinate of the starting point.
  43618. \param y0 Y-coordinate of the starting point.
  43619. \param z0 Z-coordinate of the starting point.
  43620. \param x1 X-coordinate of the ending point.
  43621. \param y1 Y-coordinate of the ending point.
  43622. \param z1 Z-coordinate of the ending point.
  43623. \param size_x Resolution of the elevation function along the X-axis.
  43624. \param size_y Resolution of the elevation function along the Y-axis.
  43625. \param size_z Resolution of the elevation function along the Z-axis.
  43626. \note Use the marching cubes algorithm for extracting the isosurface.
  43627. **/
  43628. template<typename tf, typename tfunc>
  43629. static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const tfunc& func, const float isovalue,
  43630. const float x0, const float y0, const float z0,
  43631. const float x1, const float y1, const float z1,
  43632. const int size_x=32, const int size_y=32, const int size_z=32) {
  43633. CImgList<floatT> vertices;
  43634. primitives.assign();
  43635. typename CImg<floatT>::_functor_isosurface3d add_vertex(vertices);
  43636. typename CImg<tf>::_functor_isosurface3d add_triangle(primitives);
  43637. isosurface3d(add_vertex,add_triangle,func,isovalue,x0,y0,z0,x1,y1,z1,size_x,size_y,size_z);
  43638. return vertices>'x';
  43639. }
  43640. //! Compute isosurface of a function, as a 3D object.
  43641. /**
  43642. \param[out] add_vertex : Functor with operator()(x,y,z) defined for adding new vertex.
  43643. \param[out] add_triangle : Functor with operator()(i,j) defined for adding new segment.
  43644. \param func Implicit function. Is of type <tt>float (*func)(const float x, const float y, const float z)</tt>.
  43645. \param isovalue Isovalue to extract.
  43646. \param x0 X-coordinate of the starting point.
  43647. \param y0 Y-coordinate of the starting point.
  43648. \param z0 Z-coordinate of the starting point.
  43649. \param x1 X-coordinate of the ending point.
  43650. \param y1 Y-coordinate of the ending point.
  43651. \param z1 Z-coordinate of the ending point.
  43652. \param size_x Resolution of the elevation function along the X-axis.
  43653. \param size_y Resolution of the elevation function along the Y-axis.
  43654. \param size_z Resolution of the elevation function along the Z-axis.
  43655. \note Use the marching cubes algorithm for extracting the isosurface.
  43656. **/
  43657. template<typename tv, typename tf, typename tfunc>
  43658. static void isosurface3d(tv& add_vertex, tf& add_triangle, const tfunc& func, const float isovalue,
  43659. const float x0, const float y0, const float z0,
  43660. const float x1, const float y1, const float z1,
  43661. const int size_x, const int size_y, const int size_z) {
  43662. static const unsigned int edges[256] = {
  43663. 0x000, 0x109, 0x203, 0x30a, 0x406, 0x50f, 0x605, 0x70c, 0x80c, 0x905, 0xa0f, 0xb06, 0xc0a, 0xd03, 0xe09, 0xf00,
  43664. 0x190, 0x99 , 0x393, 0x29a, 0x596, 0x49f, 0x795, 0x69c, 0x99c, 0x895, 0xb9f, 0xa96, 0xd9a, 0xc93, 0xf99, 0xe90,
  43665. 0x230, 0x339, 0x33 , 0x13a, 0x636, 0x73f, 0x435, 0x53c, 0xa3c, 0xb35, 0x83f, 0x936, 0xe3a, 0xf33, 0xc39, 0xd30,
  43666. 0x3a0, 0x2a9, 0x1a3, 0xaa , 0x7a6, 0x6af, 0x5a5, 0x4ac, 0xbac, 0xaa5, 0x9af, 0x8a6, 0xfaa, 0xea3, 0xda9, 0xca0,
  43667. 0x460, 0x569, 0x663, 0x76a, 0x66 , 0x16f, 0x265, 0x36c, 0xc6c, 0xd65, 0xe6f, 0xf66, 0x86a, 0x963, 0xa69, 0xb60,
  43668. 0x5f0, 0x4f9, 0x7f3, 0x6fa, 0x1f6, 0xff , 0x3f5, 0x2fc, 0xdfc, 0xcf5, 0xfff, 0xef6, 0x9fa, 0x8f3, 0xbf9, 0xaf0,
  43669. 0x650, 0x759, 0x453, 0x55a, 0x256, 0x35f, 0x55 , 0x15c, 0xe5c, 0xf55, 0xc5f, 0xd56, 0xa5a, 0xb53, 0x859, 0x950,
  43670. 0x7c0, 0x6c9, 0x5c3, 0x4ca, 0x3c6, 0x2cf, 0x1c5, 0xcc , 0xfcc, 0xec5, 0xdcf, 0xcc6, 0xbca, 0xac3, 0x9c9, 0x8c0,
  43671. 0x8c0, 0x9c9, 0xac3, 0xbca, 0xcc6, 0xdcf, 0xec5, 0xfcc, 0xcc , 0x1c5, 0x2cf, 0x3c6, 0x4ca, 0x5c3, 0x6c9, 0x7c0,
  43672. 0x950, 0x859, 0xb53, 0xa5a, 0xd56, 0xc5f, 0xf55, 0xe5c, 0x15c, 0x55 , 0x35f, 0x256, 0x55a, 0x453, 0x759, 0x650,
  43673. 0xaf0, 0xbf9, 0x8f3, 0x9fa, 0xef6, 0xfff, 0xcf5, 0xdfc, 0x2fc, 0x3f5, 0xff , 0x1f6, 0x6fa, 0x7f3, 0x4f9, 0x5f0,
  43674. 0xb60, 0xa69, 0x963, 0x86a, 0xf66, 0xe6f, 0xd65, 0xc6c, 0x36c, 0x265, 0x16f, 0x66 , 0x76a, 0x663, 0x569, 0x460,
  43675. 0xca0, 0xda9, 0xea3, 0xfaa, 0x8a6, 0x9af, 0xaa5, 0xbac, 0x4ac, 0x5a5, 0x6af, 0x7a6, 0xaa , 0x1a3, 0x2a9, 0x3a0,
  43676. 0xd30, 0xc39, 0xf33, 0xe3a, 0x936, 0x83f, 0xb35, 0xa3c, 0x53c, 0x435, 0x73f, 0x636, 0x13a, 0x33 , 0x339, 0x230,
  43677. 0xe90, 0xf99, 0xc93, 0xd9a, 0xa96, 0xb9f, 0x895, 0x99c, 0x69c, 0x795, 0x49f, 0x596, 0x29a, 0x393, 0x99 , 0x190,
  43678. 0xf00, 0xe09, 0xd03, 0xc0a, 0xb06, 0xa0f, 0x905, 0x80c, 0x70c, 0x605, 0x50f, 0x406, 0x30a, 0x203, 0x109, 0x000
  43679. };
  43680. static const int triangles[256][16] = {
  43681. { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43682. { 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43683. { 0, 1, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43684. { 1, 8, 3, 9, 8, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43685. { 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43686. { 0, 8, 3, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43687. { 9, 2, 10, 0, 2, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43688. { 2, 8, 3, 2, 10, 8, 10, 9, 8, -1, -1, -1, -1, -1, -1, -1 },
  43689. { 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43690. { 0, 11, 2, 8, 11, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43691. { 1, 9, 0, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43692. { 1, 11, 2, 1, 9, 11, 9, 8, 11, -1, -1, -1, -1, -1, -1, -1 },
  43693. { 3, 10, 1, 11, 10, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43694. { 0, 10, 1, 0, 8, 10, 8, 11, 10, -1, -1, -1, -1, -1, -1, -1 },
  43695. { 3, 9, 0, 3, 11, 9, 11, 10, 9, -1, -1, -1, -1, -1, -1, -1 },
  43696. { 9, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43697. { 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43698. { 4, 3, 0, 7, 3, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43699. { 0, 1, 9, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43700. { 4, 1, 9, 4, 7, 1, 7, 3, 1, -1, -1, -1, -1, -1, -1, -1 },
  43701. { 1, 2, 10, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43702. { 3, 4, 7, 3, 0, 4, 1, 2, 10, -1, -1, -1, -1, -1, -1, -1 },
  43703. { 9, 2, 10, 9, 0, 2, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 },
  43704. { 2, 10, 9, 2, 9, 7, 2, 7, 3, 7, 9, 4, -1, -1, -1, -1 },
  43705. { 8, 4, 7, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43706. { 11, 4, 7, 11, 2, 4, 2, 0, 4, -1, -1, -1, -1, -1, -1, -1 },
  43707. { 9, 0, 1, 8, 4, 7, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 },
  43708. { 4, 7, 11, 9, 4, 11, 9, 11, 2, 9, 2, 1, -1, -1, -1, -1 },
  43709. { 3, 10, 1, 3, 11, 10, 7, 8, 4, -1, -1, -1, -1, -1, -1, -1 },
  43710. { 1, 11, 10, 1, 4, 11, 1, 0, 4, 7, 11, 4, -1, -1, -1, -1 },
  43711. { 4, 7, 8, 9, 0, 11, 9, 11, 10, 11, 0, 3, -1, -1, -1, -1 },
  43712. { 4, 7, 11, 4, 11, 9, 9, 11, 10, -1, -1, -1, -1, -1, -1, -1 },
  43713. { 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43714. { 9, 5, 4, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43715. { 0, 5, 4, 1, 5, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43716. { 8, 5, 4, 8, 3, 5, 3, 1, 5, -1, -1, -1, -1, -1, -1, -1 },
  43717. { 1, 2, 10, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43718. { 3, 0, 8, 1, 2, 10, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 },
  43719. { 5, 2, 10, 5, 4, 2, 4, 0, 2, -1, -1, -1, -1, -1, -1, -1 },
  43720. { 2, 10, 5, 3, 2, 5, 3, 5, 4, 3, 4, 8, -1, -1, -1, -1 },
  43721. { 9, 5, 4, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43722. { 0, 11, 2, 0, 8, 11, 4, 9, 5, -1, -1, -1, -1, -1, -1, -1 },
  43723. { 0, 5, 4, 0, 1, 5, 2, 3, 11, -1, -1, -1, -1, -1, -1, -1 },
  43724. { 2, 1, 5, 2, 5, 8, 2, 8, 11, 4, 8, 5, -1, -1, -1, -1 },
  43725. { 10, 3, 11, 10, 1, 3, 9, 5, 4, -1, -1, -1, -1, -1, -1, -1 },
  43726. { 4, 9, 5, 0, 8, 1, 8, 10, 1, 8, 11, 10, -1, -1, -1, -1 },
  43727. { 5, 4, 0, 5, 0, 11, 5, 11, 10, 11, 0, 3, -1, -1, -1, -1 },
  43728. { 5, 4, 8, 5, 8, 10, 10, 8, 11, -1, -1, -1, -1, -1, -1, -1 },
  43729. { 9, 7, 8, 5, 7, 9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43730. { 9, 3, 0, 9, 5, 3, 5, 7, 3, -1, -1, -1, -1, -1, -1, -1 },
  43731. { 0, 7, 8, 0, 1, 7, 1, 5, 7, -1, -1, -1, -1, -1, -1, -1 },
  43732. { 1, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43733. { 9, 7, 8, 9, 5, 7, 10, 1, 2, -1, -1, -1, -1, -1, -1, -1 },
  43734. { 10, 1, 2, 9, 5, 0, 5, 3, 0, 5, 7, 3, -1, -1, -1, -1 },
  43735. { 8, 0, 2, 8, 2, 5, 8, 5, 7, 10, 5, 2, -1, -1, -1, -1 },
  43736. { 2, 10, 5, 2, 5, 3, 3, 5, 7, -1, -1, -1, -1, -1, -1, -1 },
  43737. { 7, 9, 5, 7, 8, 9, 3, 11, 2, -1, -1, -1, -1, -1, -1, -1 },
  43738. { 9, 5, 7, 9, 7, 2, 9, 2, 0, 2, 7, 11, -1, -1, -1, -1 },
  43739. { 2, 3, 11, 0, 1, 8, 1, 7, 8, 1, 5, 7, -1, -1, -1, -1 },
  43740. { 11, 2, 1, 11, 1, 7, 7, 1, 5, -1, -1, -1, -1, -1, -1, -1 },
  43741. { 9, 5, 8, 8, 5, 7, 10, 1, 3, 10, 3, 11, -1, -1, -1, -1 },
  43742. { 5, 7, 0, 5, 0, 9, 7, 11, 0, 1, 0, 10, 11, 10, 0, -1 },
  43743. { 11, 10, 0, 11, 0, 3, 10, 5, 0, 8, 0, 7, 5, 7, 0, -1 },
  43744. { 11, 10, 5, 7, 11, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43745. { 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43746. { 0, 8, 3, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43747. { 9, 0, 1, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43748. { 1, 8, 3, 1, 9, 8, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 },
  43749. { 1, 6, 5, 2, 6, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43750. { 1, 6, 5, 1, 2, 6, 3, 0, 8, -1, -1, -1, -1, -1, -1, -1 },
  43751. { 9, 6, 5, 9, 0, 6, 0, 2, 6, -1, -1, -1, -1, -1, -1, -1 },
  43752. { 5, 9, 8, 5, 8, 2, 5, 2, 6, 3, 2, 8, -1, -1, -1, -1 },
  43753. { 2, 3, 11, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43754. { 11, 0, 8, 11, 2, 0, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 },
  43755. { 0, 1, 9, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1, -1, -1, -1 },
  43756. { 5, 10, 6, 1, 9, 2, 9, 11, 2, 9, 8, 11, -1, -1, -1, -1 },
  43757. { 6, 3, 11, 6, 5, 3, 5, 1, 3, -1, -1, -1, -1, -1, -1, -1 },
  43758. { 0, 8, 11, 0, 11, 5, 0, 5, 1, 5, 11, 6, -1, -1, -1, -1 },
  43759. { 3, 11, 6, 0, 3, 6, 0, 6, 5, 0, 5, 9, -1, -1, -1, -1 },
  43760. { 6, 5, 9, 6, 9, 11, 11, 9, 8, -1, -1, -1, -1, -1, -1, -1 },
  43761. { 5, 10, 6, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43762. { 4, 3, 0, 4, 7, 3, 6, 5, 10, -1, -1, -1, -1, -1, -1, -1 },
  43763. { 1, 9, 0, 5, 10, 6, 8, 4, 7, -1, -1, -1, -1, -1, -1, -1 },
  43764. { 10, 6, 5, 1, 9, 7, 1, 7, 3, 7, 9, 4, -1, -1, -1, -1 },
  43765. { 6, 1, 2, 6, 5, 1, 4, 7, 8, -1, -1, -1, -1, -1, -1, -1 },
  43766. { 1, 2, 5, 5, 2, 6, 3, 0, 4, 3, 4, 7, -1, -1, -1, -1 },
  43767. { 8, 4, 7, 9, 0, 5, 0, 6, 5, 0, 2, 6, -1, -1, -1, -1 },
  43768. { 7, 3, 9, 7, 9, 4, 3, 2, 9, 5, 9, 6, 2, 6, 9, -1 },
  43769. { 3, 11, 2, 7, 8, 4, 10, 6, 5, -1, -1, -1, -1, -1, -1, -1 },
  43770. { 5, 10, 6, 4, 7, 2, 4, 2, 0, 2, 7, 11, -1, -1, -1, -1 },
  43771. { 0, 1, 9, 4, 7, 8, 2, 3, 11, 5, 10, 6, -1, -1, -1, -1 },
  43772. { 9, 2, 1, 9, 11, 2, 9, 4, 11, 7, 11, 4, 5, 10, 6, -1 },
  43773. { 8, 4, 7, 3, 11, 5, 3, 5, 1, 5, 11, 6, -1, -1, -1, -1 },
  43774. { 5, 1, 11, 5, 11, 6, 1, 0, 11, 7, 11, 4, 0, 4, 11, -1 },
  43775. { 0, 5, 9, 0, 6, 5, 0, 3, 6, 11, 6, 3, 8, 4, 7, -1 },
  43776. { 6, 5, 9, 6, 9, 11, 4, 7, 9, 7, 11, 9, -1, -1, -1, -1 },
  43777. { 10, 4, 9, 6, 4, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43778. { 4, 10, 6, 4, 9, 10, 0, 8, 3, -1, -1, -1, -1, -1, -1, -1 },
  43779. { 10, 0, 1, 10, 6, 0, 6, 4, 0, -1, -1, -1, -1, -1, -1, -1 },
  43780. { 8, 3, 1, 8, 1, 6, 8, 6, 4, 6, 1, 10, -1, -1, -1, -1 },
  43781. { 1, 4, 9, 1, 2, 4, 2, 6, 4, -1, -1, -1, -1, -1, -1, -1 },
  43782. { 3, 0, 8, 1, 2, 9, 2, 4, 9, 2, 6, 4, -1, -1, -1, -1 },
  43783. { 0, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43784. { 8, 3, 2, 8, 2, 4, 4, 2, 6, -1, -1, -1, -1, -1, -1, -1 },
  43785. { 10, 4, 9, 10, 6, 4, 11, 2, 3, -1, -1, -1, -1, -1, -1, -1 },
  43786. { 0, 8, 2, 2, 8, 11, 4, 9, 10, 4, 10, 6, -1, -1, -1, -1 },
  43787. { 3, 11, 2, 0, 1, 6, 0, 6, 4, 6, 1, 10, -1, -1, -1, -1 },
  43788. { 6, 4, 1, 6, 1, 10, 4, 8, 1, 2, 1, 11, 8, 11, 1, -1 },
  43789. { 9, 6, 4, 9, 3, 6, 9, 1, 3, 11, 6, 3, -1, -1, -1, -1 },
  43790. { 8, 11, 1, 8, 1, 0, 11, 6, 1, 9, 1, 4, 6, 4, 1, -1 },
  43791. { 3, 11, 6, 3, 6, 0, 0, 6, 4, -1, -1, -1, -1, -1, -1, -1 },
  43792. { 6, 4, 8, 11, 6, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43793. { 7, 10, 6, 7, 8, 10, 8, 9, 10, -1, -1, -1, -1, -1, -1, -1 },
  43794. { 0, 7, 3, 0, 10, 7, 0, 9, 10, 6, 7, 10, -1, -1, -1, -1 },
  43795. { 10, 6, 7, 1, 10, 7, 1, 7, 8, 1, 8, 0, -1, -1, -1, -1 },
  43796. { 10, 6, 7, 10, 7, 1, 1, 7, 3, -1, -1, -1, -1, -1, -1, -1 },
  43797. { 1, 2, 6, 1, 6, 8, 1, 8, 9, 8, 6, 7, -1, -1, -1, -1 },
  43798. { 2, 6, 9, 2, 9, 1, 6, 7, 9, 0, 9, 3, 7, 3, 9, -1 },
  43799. { 7, 8, 0, 7, 0, 6, 6, 0, 2, -1, -1, -1, -1, -1, -1, -1 },
  43800. { 7, 3, 2, 6, 7, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43801. { 2, 3, 11, 10, 6, 8, 10, 8, 9, 8, 6, 7, -1, -1, -1, -1 },
  43802. { 2, 0, 7, 2, 7, 11, 0, 9, 7, 6, 7, 10, 9, 10, 7, -1 },
  43803. { 1, 8, 0, 1, 7, 8, 1, 10, 7, 6, 7, 10, 2, 3, 11, -1 },
  43804. { 11, 2, 1, 11, 1, 7, 10, 6, 1, 6, 7, 1, -1, -1, -1, -1 },
  43805. { 8, 9, 6, 8, 6, 7, 9, 1, 6, 11, 6, 3, 1, 3, 6, -1 },
  43806. { 0, 9, 1, 11, 6, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43807. { 7, 8, 0, 7, 0, 6, 3, 11, 0, 11, 6, 0, -1, -1, -1, -1 },
  43808. { 7, 11, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43809. { 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43810. { 3, 0, 8, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43811. { 0, 1, 9, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43812. { 8, 1, 9, 8, 3, 1, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 },
  43813. { 10, 1, 2, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43814. { 1, 2, 10, 3, 0, 8, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 },
  43815. { 2, 9, 0, 2, 10, 9, 6, 11, 7, -1, -1, -1, -1, -1, -1, -1 },
  43816. { 6, 11, 7, 2, 10, 3, 10, 8, 3, 10, 9, 8, -1, -1, -1, -1 },
  43817. { 7, 2, 3, 6, 2, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43818. { 7, 0, 8, 7, 6, 0, 6, 2, 0, -1, -1, -1, -1, -1, -1, -1 },
  43819. { 2, 7, 6, 2, 3, 7, 0, 1, 9, -1, -1, -1, -1, -1, -1, -1 },
  43820. { 1, 6, 2, 1, 8, 6, 1, 9, 8, 8, 7, 6, -1, -1, -1, -1 },
  43821. { 10, 7, 6, 10, 1, 7, 1, 3, 7, -1, -1, -1, -1, -1, -1, -1 },
  43822. { 10, 7, 6, 1, 7, 10, 1, 8, 7, 1, 0, 8, -1, -1, -1, -1 },
  43823. { 0, 3, 7, 0, 7, 10, 0, 10, 9, 6, 10, 7, -1, -1, -1, -1 },
  43824. { 7, 6, 10, 7, 10, 8, 8, 10, 9, -1, -1, -1, -1, -1, -1, -1 },
  43825. { 6, 8, 4, 11, 8, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43826. { 3, 6, 11, 3, 0, 6, 0, 4, 6, -1, -1, -1, -1, -1, -1, -1 },
  43827. { 8, 6, 11, 8, 4, 6, 9, 0, 1, -1, -1, -1, -1, -1, -1, -1 },
  43828. { 9, 4, 6, 9, 6, 3, 9, 3, 1, 11, 3, 6, -1, -1, -1, -1 },
  43829. { 6, 8, 4, 6, 11, 8, 2, 10, 1, -1, -1, -1, -1, -1, -1, -1 },
  43830. { 1, 2, 10, 3, 0, 11, 0, 6, 11, 0, 4, 6, -1, -1, -1, -1 },
  43831. { 4, 11, 8, 4, 6, 11, 0, 2, 9, 2, 10, 9, -1, -1, -1, -1 },
  43832. { 10, 9, 3, 10, 3, 2, 9, 4, 3, 11, 3, 6, 4, 6, 3, -1 },
  43833. { 8, 2, 3, 8, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1 },
  43834. { 0, 4, 2, 4, 6, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43835. { 1, 9, 0, 2, 3, 4, 2, 4, 6, 4, 3, 8, -1, -1, -1, -1 },
  43836. { 1, 9, 4, 1, 4, 2, 2, 4, 6, -1, -1, -1, -1, -1, -1, -1 },
  43837. { 8, 1, 3, 8, 6, 1, 8, 4, 6, 6, 10, 1, -1, -1, -1, -1 },
  43838. { 10, 1, 0, 10, 0, 6, 6, 0, 4, -1, -1, -1, -1, -1, -1, -1 },
  43839. { 4, 6, 3, 4, 3, 8, 6, 10, 3, 0, 3, 9, 10, 9, 3, -1 },
  43840. { 10, 9, 4, 6, 10, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43841. { 4, 9, 5, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43842. { 0, 8, 3, 4, 9, 5, 11, 7, 6, -1, -1, -1, -1, -1, -1, -1 },
  43843. { 5, 0, 1, 5, 4, 0, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 },
  43844. { 11, 7, 6, 8, 3, 4, 3, 5, 4, 3, 1, 5, -1, -1, -1, -1 },
  43845. { 9, 5, 4, 10, 1, 2, 7, 6, 11, -1, -1, -1, -1, -1, -1, -1 },
  43846. { 6, 11, 7, 1, 2, 10, 0, 8, 3, 4, 9, 5, -1, -1, -1, -1 },
  43847. { 7, 6, 11, 5, 4, 10, 4, 2, 10, 4, 0, 2, -1, -1, -1, -1 },
  43848. { 3, 4, 8, 3, 5, 4, 3, 2, 5, 10, 5, 2, 11, 7, 6, -1 },
  43849. { 7, 2, 3, 7, 6, 2, 5, 4, 9, -1, -1, -1, -1, -1, -1, -1 },
  43850. { 9, 5, 4, 0, 8, 6, 0, 6, 2, 6, 8, 7, -1, -1, -1, -1 },
  43851. { 3, 6, 2, 3, 7, 6, 1, 5, 0, 5, 4, 0, -1, -1, -1, -1 },
  43852. { 6, 2, 8, 6, 8, 7, 2, 1, 8, 4, 8, 5, 1, 5, 8, -1 },
  43853. { 9, 5, 4, 10, 1, 6, 1, 7, 6, 1, 3, 7, -1, -1, -1, -1 },
  43854. { 1, 6, 10, 1, 7, 6, 1, 0, 7, 8, 7, 0, 9, 5, 4, -1 },
  43855. { 4, 0, 10, 4, 10, 5, 0, 3, 10, 6, 10, 7, 3, 7, 10, -1 },
  43856. { 7, 6, 10, 7, 10, 8, 5, 4, 10, 4, 8, 10, -1, -1, -1, -1 },
  43857. { 6, 9, 5, 6, 11, 9, 11, 8, 9, -1, -1, -1, -1, -1, -1, -1 },
  43858. { 3, 6, 11, 0, 6, 3, 0, 5, 6, 0, 9, 5, -1, -1, -1, -1 },
  43859. { 0, 11, 8, 0, 5, 11, 0, 1, 5, 5, 6, 11, -1, -1, -1, -1 },
  43860. { 6, 11, 3, 6, 3, 5, 5, 3, 1, -1, -1, -1, -1, -1, -1, -1 },
  43861. { 1, 2, 10, 9, 5, 11, 9, 11, 8, 11, 5, 6, -1, -1, -1, -1 },
  43862. { 0, 11, 3, 0, 6, 11, 0, 9, 6, 5, 6, 9, 1, 2, 10, -1 },
  43863. { 11, 8, 5, 11, 5, 6, 8, 0, 5, 10, 5, 2, 0, 2, 5, -1 },
  43864. { 6, 11, 3, 6, 3, 5, 2, 10, 3, 10, 5, 3, -1, -1, -1, -1 },
  43865. { 5, 8, 9, 5, 2, 8, 5, 6, 2, 3, 8, 2, -1, -1, -1, -1 },
  43866. { 9, 5, 6, 9, 6, 0, 0, 6, 2, -1, -1, -1, -1, -1, -1, -1 },
  43867. { 1, 5, 8, 1, 8, 0, 5, 6, 8, 3, 8, 2, 6, 2, 8, -1 },
  43868. { 1, 5, 6, 2, 1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43869. { 1, 3, 6, 1, 6, 10, 3, 8, 6, 5, 6, 9, 8, 9, 6, -1 },
  43870. { 10, 1, 0, 10, 0, 6, 9, 5, 0, 5, 6, 0, -1, -1, -1, -1 },
  43871. { 0, 3, 8, 5, 6, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43872. { 10, 5, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43873. { 11, 5, 10, 7, 5, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43874. { 11, 5, 10, 11, 7, 5, 8, 3, 0, -1, -1, -1, -1, -1, -1, -1 },
  43875. { 5, 11, 7, 5, 10, 11, 1, 9, 0, -1, -1, -1, -1, -1, -1, -1 },
  43876. { 10, 7, 5, 10, 11, 7, 9, 8, 1, 8, 3, 1, -1, -1, -1, -1 },
  43877. { 11, 1, 2, 11, 7, 1, 7, 5, 1, -1, -1, -1, -1, -1, -1, -1 },
  43878. { 0, 8, 3, 1, 2, 7, 1, 7, 5, 7, 2, 11, -1, -1, -1, -1 },
  43879. { 9, 7, 5, 9, 2, 7, 9, 0, 2, 2, 11, 7, -1, -1, -1, -1 },
  43880. { 7, 5, 2, 7, 2, 11, 5, 9, 2, 3, 2, 8, 9, 8, 2, -1 },
  43881. { 2, 5, 10, 2, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1 },
  43882. { 8, 2, 0, 8, 5, 2, 8, 7, 5, 10, 2, 5, -1, -1, -1, -1 },
  43883. { 9, 0, 1, 5, 10, 3, 5, 3, 7, 3, 10, 2, -1, -1, -1, -1 },
  43884. { 9, 8, 2, 9, 2, 1, 8, 7, 2, 10, 2, 5, 7, 5, 2, -1 },
  43885. { 1, 3, 5, 3, 7, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43886. { 0, 8, 7, 0, 7, 1, 1, 7, 5, -1, -1, -1, -1, -1, -1, -1 },
  43887. { 9, 0, 3, 9, 3, 5, 5, 3, 7, -1, -1, -1, -1, -1, -1, -1 },
  43888. { 9, 8, 7, 5, 9, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43889. { 5, 8, 4, 5, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1 },
  43890. { 5, 0, 4, 5, 11, 0, 5, 10, 11, 11, 3, 0, -1, -1, -1, -1 },
  43891. { 0, 1, 9, 8, 4, 10, 8, 10, 11, 10, 4, 5, -1, -1, -1, -1 },
  43892. { 10, 11, 4, 10, 4, 5, 11, 3, 4, 9, 4, 1, 3, 1, 4, -1 },
  43893. { 2, 5, 1, 2, 8, 5, 2, 11, 8, 4, 5, 8, -1, -1, -1, -1 },
  43894. { 0, 4, 11, 0, 11, 3, 4, 5, 11, 2, 11, 1, 5, 1, 11, -1 },
  43895. { 0, 2, 5, 0, 5, 9, 2, 11, 5, 4, 5, 8, 11, 8, 5, -1 },
  43896. { 9, 4, 5, 2, 11, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43897. { 2, 5, 10, 3, 5, 2, 3, 4, 5, 3, 8, 4, -1, -1, -1, -1 },
  43898. { 5, 10, 2, 5, 2, 4, 4, 2, 0, -1, -1, -1, -1, -1, -1, -1 },
  43899. { 3, 10, 2, 3, 5, 10, 3, 8, 5, 4, 5, 8, 0, 1, 9, -1 },
  43900. { 5, 10, 2, 5, 2, 4, 1, 9, 2, 9, 4, 2, -1, -1, -1, -1 },
  43901. { 8, 4, 5, 8, 5, 3, 3, 5, 1, -1, -1, -1, -1, -1, -1, -1 },
  43902. { 0, 4, 5, 1, 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43903. { 8, 4, 5, 8, 5, 3, 9, 0, 5, 0, 3, 5, -1, -1, -1, -1 },
  43904. { 9, 4, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43905. { 4, 11, 7, 4, 9, 11, 9, 10, 11, -1, -1, -1, -1, -1, -1, -1 },
  43906. { 0, 8, 3, 4, 9, 7, 9, 11, 7, 9, 10, 11, -1, -1, -1, -1 },
  43907. { 1, 10, 11, 1, 11, 4, 1, 4, 0, 7, 4, 11, -1, -1, -1, -1 },
  43908. { 3, 1, 4, 3, 4, 8, 1, 10, 4, 7, 4, 11, 10, 11, 4, -1 },
  43909. { 4, 11, 7, 9, 11, 4, 9, 2, 11, 9, 1, 2, -1, -1, -1, -1 },
  43910. { 9, 7, 4, 9, 11, 7, 9, 1, 11, 2, 11, 1, 0, 8, 3, -1 },
  43911. { 11, 7, 4, 11, 4, 2, 2, 4, 0, -1, -1, -1, -1, -1, -1, -1 },
  43912. { 11, 7, 4, 11, 4, 2, 8, 3, 4, 3, 2, 4, -1, -1, -1, -1 },
  43913. { 2, 9, 10, 2, 7, 9, 2, 3, 7, 7, 4, 9, -1, -1, -1, -1 },
  43914. { 9, 10, 7, 9, 7, 4, 10, 2, 7, 8, 7, 0, 2, 0, 7, -1 },
  43915. { 3, 7, 10, 3, 10, 2, 7, 4, 10, 1, 10, 0, 4, 0, 10, -1 },
  43916. { 1, 10, 2, 8, 7, 4, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43917. { 4, 9, 1, 4, 1, 7, 7, 1, 3, -1, -1, -1, -1, -1, -1, -1 },
  43918. { 4, 9, 1, 4, 1, 7, 0, 8, 1, 8, 7, 1, -1, -1, -1, -1 },
  43919. { 4, 0, 3, 7, 4, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43920. { 4, 8, 7, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43921. { 9, 10, 8, 10, 11, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43922. { 3, 0, 9, 3, 9, 11, 11, 9, 10, -1, -1, -1, -1, -1, -1, -1 },
  43923. { 0, 1, 10, 0, 10, 8, 8, 10, 11, -1, -1, -1, -1, -1, -1, -1 },
  43924. { 3, 1, 10, 11, 3, 10, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43925. { 1, 2, 11, 1, 11, 9, 9, 11, 8, -1, -1, -1, -1, -1, -1, -1 },
  43926. { 3, 0, 9, 3, 9, 11, 1, 2, 9, 2, 11, 9, -1, -1, -1, -1 },
  43927. { 0, 2, 11, 8, 0, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43928. { 3, 2, 11, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43929. { 2, 3, 8, 2, 8, 10, 10, 8, 9, -1, -1, -1, -1, -1, -1, -1 },
  43930. { 9, 10, 2, 0, 9, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43931. { 2, 3, 8, 2, 8, 10, 0, 1, 8, 1, 10, 8, -1, -1, -1, -1 },
  43932. { 1, 10, 2, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43933. { 1, 3, 8, 9, 1, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43934. { 0, 9, 1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43935. { 0, 3, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 },
  43936. { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1 }
  43937. };
  43938. const unsigned int
  43939. _nx = (unsigned int)(size_x>=0?size_x:cimg::round((x1-x0)*-size_x/100 + 1)),
  43940. _ny = (unsigned int)(size_y>=0?size_y:cimg::round((y1-y0)*-size_y/100 + 1)),
  43941. _nz = (unsigned int)(size_z>=0?size_z:cimg::round((z1-z0)*-size_z/100 + 1)),
  43942. nx = _nx?_nx:1,
  43943. ny = _ny?_ny:1,
  43944. nz = _nz?_nz:1,
  43945. nxm1 = nx - 1,
  43946. nym1 = ny - 1,
  43947. nzm1 = nz - 1;
  43948. if (!nxm1 || !nym1 || !nzm1) return;
  43949. const float dx = (x1 - x0)/nxm1, dy = (y1 - y0)/nym1, dz = (z1 - z0)/nzm1;
  43950. CImg<intT> indices1(nx,ny,1,3,-1), indices2(indices1);
  43951. CImg<floatT> values1(nx,ny), values2(nx,ny);
  43952. float X = 0, Y = 0, Z = 0, nX = 0, nY = 0, nZ = 0;
  43953. int nb_vertices = 0;
  43954. // Fill the first plane with function values
  43955. Y = y0;
  43956. cimg_forY(values1,y) {
  43957. X = x0;
  43958. cimg_forX(values1,x) { values1(x,y) = (float)func(X,Y,z0); X+=dx; }
  43959. Y+=dy;
  43960. }
  43961. // Run Marching Cubes algorithm
  43962. Z = z0; nZ = Z + dz;
  43963. for (unsigned int zi = 0; zi<nzm1; ++zi, Z = nZ, nZ+=dz) {
  43964. Y = y0; nY = Y + dy;
  43965. indices2.fill(-1);
  43966. X = x0; for (unsigned int xi = 0; xi<nx; ++xi) { values2(xi,0) = (float)func(X,Y,nZ); X += dx; }
  43967. for (unsigned int yi = 0, nyi = 1; yi<nym1; ++yi, ++nyi, Y = nY, nY+=dy) {
  43968. X = x0; nX = X + dx;
  43969. values2(0,nyi) = (float)func(X,nY,nZ);
  43970. for (unsigned int xi = 0, nxi = 1; xi<nxm1; ++xi, ++nxi, X = nX, nX+=dx) {
  43971. // Determine cube configuration
  43972. const float
  43973. val0 = values1(xi,yi),
  43974. val1 = values1(nxi,yi),
  43975. val2 = values1(nxi,nyi),
  43976. val3 = values1(xi,nyi),
  43977. val4 = values2(xi,yi),
  43978. val5 = values2(nxi,yi),
  43979. val6 = values2(nxi,nyi) = (float)func(nX,nY,nZ),
  43980. val7 = values2(xi,nyi);
  43981. const unsigned int configuration =
  43982. (val0<isovalue?1U:0U) | (val1<isovalue?2U:0U) | (val2<isovalue?4U:0U) | (val3<isovalue?8U:0U) |
  43983. (val4<isovalue?16U:0U) | (val5<isovalue?32U:0U) | (val6<isovalue?64U:0U) | (val7<isovalue?128U:0U),
  43984. edge = edges[configuration];
  43985. // Compute intersection vertices
  43986. if (edge) {
  43987. if ((edge&1) && indices1(xi,yi,0)<0) {
  43988. const float Xi = X + (isovalue-val0)*dx/(val1-val0);
  43989. indices1(xi,yi,0) = nb_vertices++;
  43990. add_vertex(Xi,Y,Z);
  43991. }
  43992. if ((edge&2) && indices1(nxi,yi,1)<0) {
  43993. const float Yi = Y + (isovalue-val1)*dy/(val2-val1);
  43994. indices1(nxi,yi,1) = nb_vertices++;
  43995. add_vertex(nX,Yi,Z);
  43996. }
  43997. if ((edge&4) && indices1(xi,nyi,0)<0) {
  43998. const float Xi = X + (isovalue-val3)*dx/(val2-val3);
  43999. indices1(xi,nyi,0) = nb_vertices++;
  44000. add_vertex(Xi,nY,Z);
  44001. }
  44002. if ((edge&8) && indices1(xi,yi,1)<0) {
  44003. const float Yi = Y + (isovalue-val0)*dy/(val3-val0);
  44004. indices1(xi,yi,1) = nb_vertices++;
  44005. add_vertex(X,Yi,Z);
  44006. }
  44007. if ((edge&16) && indices2(xi,yi,0)<0) {
  44008. const float Xi = X + (isovalue-val4)*dx/(val5-val4);
  44009. indices2(xi,yi,0) = nb_vertices++;
  44010. add_vertex(Xi,Y,nZ);
  44011. }
  44012. if ((edge&32) && indices2(nxi,yi,1)<0) {
  44013. const float Yi = Y + (isovalue-val5)*dy/(val6-val5);
  44014. indices2(nxi,yi,1) = nb_vertices++;
  44015. add_vertex(nX,Yi,nZ);
  44016. }
  44017. if ((edge&64) && indices2(xi,nyi,0)<0) {
  44018. const float Xi = X + (isovalue-val7)*dx/(val6-val7);
  44019. indices2(xi,nyi,0) = nb_vertices++;
  44020. add_vertex(Xi,nY,nZ);
  44021. }
  44022. if ((edge&128) && indices2(xi,yi,1)<0) {
  44023. const float Yi = Y + (isovalue-val4)*dy/(val7-val4);
  44024. indices2(xi,yi,1) = nb_vertices++;
  44025. add_vertex(X,Yi,nZ);
  44026. }
  44027. if ((edge&256) && indices1(xi,yi,2)<0) {
  44028. const float Zi = Z+ (isovalue-val0)*dz/(val4-val0);
  44029. indices1(xi,yi,2) = nb_vertices++;
  44030. add_vertex(X,Y,Zi);
  44031. }
  44032. if ((edge&512) && indices1(nxi,yi,2)<0) {
  44033. const float Zi = Z + (isovalue-val1)*dz/(val5-val1);
  44034. indices1(nxi,yi,2) = nb_vertices++;
  44035. add_vertex(nX,Y,Zi);
  44036. }
  44037. if ((edge&1024) && indices1(nxi,nyi,2)<0) {
  44038. const float Zi = Z + (isovalue-val2)*dz/(val6-val2);
  44039. indices1(nxi,nyi,2) = nb_vertices++;
  44040. add_vertex(nX,nY,Zi);
  44041. }
  44042. if ((edge&2048) && indices1(xi,nyi,2)<0) {
  44043. const float Zi = Z + (isovalue-val3)*dz/(val7-val3);
  44044. indices1(xi,nyi,2) = nb_vertices++;
  44045. add_vertex(X,nY,Zi);
  44046. }
  44047. // Create triangles
  44048. for (const int *triangle = triangles[configuration]; *triangle!=-1; ) {
  44049. const unsigned int
  44050. p0 = (unsigned int)*(triangle++),
  44051. p1 = (unsigned int)*(triangle++),
  44052. p2 = (unsigned int)*(triangle++);
  44053. const int
  44054. i0 = _isosurface3d_index(p0,indices1,indices2,xi,yi,nxi,nyi),
  44055. i1 = _isosurface3d_index(p1,indices1,indices2,xi,yi,nxi,nyi),
  44056. i2 = _isosurface3d_index(p2,indices1,indices2,xi,yi,nxi,nyi);
  44057. add_triangle(i0,i2,i1);
  44058. }
  44059. }
  44060. }
  44061. }
  44062. cimg::swap(values1,values2);
  44063. cimg::swap(indices1,indices2);
  44064. }
  44065. }
  44066. //! Compute isosurface of a function, as a 3D object \overloading.
  44067. template<typename tf>
  44068. static CImg<floatT> isosurface3d(CImgList<tf>& primitives, const char *const expression, const float isovalue,
  44069. const float x0, const float y0, const float z0,
  44070. const float x1, const float y1, const float z1,
  44071. const int dx=32, const int dy=32, const int dz=32) {
  44072. const _functor3d_expr func(expression);
  44073. return isosurface3d(primitives,func,isovalue,x0,y0,z0,x1,y1,z1,dx,dy,dz);
  44074. }
  44075. template<typename t>
  44076. static int _isosurface3d_index(const unsigned int edge, const CImg<t>& indices1, const CImg<t>& indices2,
  44077. const unsigned int x, const unsigned int y,
  44078. const unsigned int nx, const unsigned int ny) {
  44079. switch (edge) {
  44080. case 0 : return indices1(x,y,0);
  44081. case 1 : return indices1(nx,y,1);
  44082. case 2 : return indices1(x,ny,0);
  44083. case 3 : return indices1(x,y,1);
  44084. case 4 : return indices2(x,y,0);
  44085. case 5 : return indices2(nx,y,1);
  44086. case 6 : return indices2(x,ny,0);
  44087. case 7 : return indices2(x,y,1);
  44088. case 8 : return indices1(x,y,2);
  44089. case 9 : return indices1(nx,y,2);
  44090. case 10 : return indices1(nx,ny,2);
  44091. case 11 : return indices1(x,ny,2);
  44092. }
  44093. return 0;
  44094. }
  44095. // Define functors for accessing image values (used in previous functions).
  44096. struct _functor2d_int {
  44097. const CImg<T>& ref;
  44098. _functor2d_int(const CImg<T>& pref):ref(pref) {}
  44099. float operator()(const float x, const float y) const {
  44100. return (float)ref((int)x,(int)y);
  44101. }
  44102. };
  44103. struct _functor2d_float {
  44104. const CImg<T>& ref;
  44105. _functor2d_float(const CImg<T>& pref):ref(pref) {}
  44106. float operator()(const float x, const float y) const {
  44107. return (float)ref._linear_atXY(x,y);
  44108. }
  44109. };
  44110. struct _functor2d_expr {
  44111. _cimg_math_parser *mp;
  44112. ~_functor2d_expr() { mp->end(); delete mp; }
  44113. _functor2d_expr(const char *const expr):mp(0) {
  44114. mp = new _cimg_math_parser(expr,0,CImg<T>::const_empty(),0);
  44115. }
  44116. float operator()(const float x, const float y) const {
  44117. return (float)(*mp)(x,y,0,0);
  44118. }
  44119. };
  44120. struct _functor3d_int {
  44121. const CImg<T>& ref;
  44122. _functor3d_int(const CImg<T>& pref):ref(pref) {}
  44123. float operator()(const float x, const float y, const float z) const {
  44124. return (float)ref((int)x,(int)y,(int)z);
  44125. }
  44126. };
  44127. struct _functor3d_float {
  44128. const CImg<T>& ref;
  44129. _functor3d_float(const CImg<T>& pref):ref(pref) {}
  44130. float operator()(const float x, const float y, const float z) const {
  44131. return (float)ref._linear_atXYZ(x,y,z);
  44132. }
  44133. };
  44134. struct _functor3d_expr {
  44135. _cimg_math_parser *mp;
  44136. ~_functor3d_expr() { mp->end(); delete mp; }
  44137. _functor3d_expr(const char *const expr):mp(0) {
  44138. mp = new _cimg_math_parser(expr,0,CImg<T>::const_empty(),0);
  44139. }
  44140. float operator()(const float x, const float y, const float z) const {
  44141. return (float)(*mp)(x,y,z,0);
  44142. }
  44143. };
  44144. struct _functor4d_int {
  44145. const CImg<T>& ref;
  44146. _functor4d_int(const CImg<T>& pref):ref(pref) {}
  44147. float operator()(const float x, const float y, const float z, const unsigned int c) const {
  44148. return (float)ref((int)x,(int)y,(int)z,c);
  44149. }
  44150. };
  44151. struct _functor_isoline3d {
  44152. CImgList<T>& list;
  44153. _functor_isoline3d(CImgList<T>& _list):list(_list) {}
  44154. template<typename t>
  44155. void operator()(const t x, const t y, const t z) { CImg<T>::vector((T)x,(T)y,(T)z).move_to(list); }
  44156. template<typename t>
  44157. void operator()(const t i, const t j) { CImg<T>::vector((T)i,(T)j).move_to(list); }
  44158. };
  44159. struct _functor_isosurface3d {
  44160. CImgList<T>& list;
  44161. _functor_isosurface3d(CImgList<T>& _list):list(_list) {}
  44162. template<typename t>
  44163. void operator()(const t x, const t y, const t z) { CImg<T>::vector((T)x,(T)y,(T)z).move_to(list); }
  44164. };
  44165. //! Compute 3D elevation of a function as a 3D object.
  44166. /**
  44167. \param[out] primitives Primitives data of the resulting 3D object.
  44168. \param func Elevation function. Is of type <tt>float (*func)(const float x,const float y)</tt>.
  44169. \param x0 X-coordinate of the starting point.
  44170. \param y0 Y-coordinate of the starting point.
  44171. \param x1 X-coordinate of the ending point.
  44172. \param y1 Y-coordinate of the ending point.
  44173. \param size_x Resolution of the function along the X-axis.
  44174. \param size_y Resolution of the function along the Y-axis.
  44175. **/
  44176. template<typename tf, typename tfunc>
  44177. static CImg<floatT> elevation3d(CImgList<tf>& primitives, const tfunc& func,
  44178. const float x0, const float y0, const float x1, const float y1,
  44179. const int size_x=256, const int size_y=256) {
  44180. const float
  44181. nx0 = x0<x1?x0:x1, ny0 = y0<y1?y0:y1,
  44182. nx1 = x0<x1?x1:x0, ny1 = y0<y1?y1:y0;
  44183. const unsigned int
  44184. _nsize_x = (unsigned int)(size_x>=0?size_x:(nx1-nx0)*-size_x/100),
  44185. nsize_x = _nsize_x?_nsize_x:1, nsize_x1 = nsize_x - 1,
  44186. _nsize_y = (unsigned int)(size_y>=0?size_y:(ny1-ny0)*-size_y/100),
  44187. nsize_y = _nsize_y?_nsize_y:1, nsize_y1 = nsize_y - 1;
  44188. if (nsize_x<2 || nsize_y<2)
  44189. throw CImgArgumentException("CImg<%s>::elevation3d(): Invalid specified size (%d,%d).",
  44190. pixel_type(),
  44191. nsize_x,nsize_y);
  44192. CImg<floatT> vertices(nsize_x*nsize_y,3);
  44193. floatT *ptr_x = vertices.data(0,0), *ptr_y = vertices.data(0,1), *ptr_z = vertices.data(0,2);
  44194. for (unsigned int y = 0; y<nsize_y; ++y) {
  44195. const float Y = ny0 + y*(ny1-ny0)/nsize_y1;
  44196. for (unsigned int x = 0; x<nsize_x; ++x) {
  44197. const float X = nx0 + x*(nx1-nx0)/nsize_x1;
  44198. *(ptr_x++) = (float)x;
  44199. *(ptr_y++) = (float)y;
  44200. *(ptr_z++) = (float)func(X,Y);
  44201. }
  44202. }
  44203. primitives.assign(nsize_x1*nsize_y1,1,4);
  44204. for (unsigned int p = 0, y = 0; y<nsize_y1; ++y) {
  44205. const unsigned int yw = y*nsize_x;
  44206. for (unsigned int x = 0; x<nsize_x1; ++x) {
  44207. const unsigned int xpyw = x + yw, xpyww = xpyw + nsize_x;
  44208. primitives[p++].fill(xpyw,xpyww,xpyww + 1,xpyw + 1);
  44209. }
  44210. }
  44211. return vertices;
  44212. }
  44213. //! Compute 3D elevation of a function, as a 3D object \overloading.
  44214. template<typename tf>
  44215. static CImg<floatT> elevation3d(CImgList<tf>& primitives, const char *const expression,
  44216. const float x0, const float y0, const float x1, const float y1,
  44217. const int size_x=256, const int size_y=256) {
  44218. const _functor2d_expr func(expression);
  44219. return elevation3d(primitives,func,x0,y0,x1,y1,size_x,size_y);
  44220. }
  44221. //! Generate a 3D box object.
  44222. /**
  44223. \param[out] primitives The returned list of the 3D object primitives
  44224. (template type \e tf should be at least \e unsigned \e int).
  44225. \param size_x The width of the box (dimension along the X-axis).
  44226. \param size_y The height of the box (dimension along the Y-axis).
  44227. \param size_z The depth of the box (dimension along the Z-axis).
  44228. \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
  44229. \par Example
  44230. \code
  44231. CImgList<unsigned int> faces3d;
  44232. const CImg<float> points3d = CImg<float>::box3d(faces3d,10,20,30);
  44233. CImg<unsigned char>().display_object3d("Box3d",points3d,faces3d);
  44234. \endcode
  44235. \image html ref_box3d.jpg
  44236. **/
  44237. template<typename tf>
  44238. static CImg<floatT> box3d(CImgList<tf>& primitives,
  44239. const float size_x=200, const float size_y=100, const float size_z=100) {
  44240. 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);
  44241. return CImg<floatT>(8,3,1,1,
  44242. 0.,size_x,size_x, 0., 0.,size_x,size_x, 0.,
  44243. 0., 0.,size_y,size_y, 0., 0.,size_y,size_y,
  44244. 0., 0., 0., 0.,size_z,size_z,size_z,size_z);
  44245. }
  44246. //! Generate a 3D cone.
  44247. /**
  44248. \param[out] primitives The returned list of the 3D object primitives
  44249. (template type \e tf should be at least \e unsigned \e int).
  44250. \param radius The radius of the cone basis.
  44251. \param size_z The cone's height.
  44252. \param subdivisions The number of basis angular subdivisions.
  44253. \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
  44254. \par Example
  44255. \code
  44256. CImgList<unsigned int> faces3d;
  44257. const CImg<float> points3d = CImg<float>::cone3d(faces3d,50);
  44258. CImg<unsigned char>().display_object3d("Cone3d",points3d,faces3d);
  44259. \endcode
  44260. \image html ref_cone3d.jpg
  44261. **/
  44262. template<typename tf>
  44263. static CImg<floatT> cone3d(CImgList<tf>& primitives,
  44264. const float radius=50, const float size_z=100, const unsigned int subdivisions=24) {
  44265. primitives.assign();
  44266. if (!subdivisions) return CImg<floatT>();
  44267. CImgList<floatT> vertices(2,1,3,1,1,
  44268. 0.,0.,size_z,
  44269. 0.,0.,0.);
  44270. for (float delta = 360.f/subdivisions, angle = 0; angle<360; angle+=delta) {
  44271. const float a = (float)(angle*cimg::PI/180);
  44272. CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0).move_to(vertices);
  44273. }
  44274. const unsigned int nbr = vertices._width - 2;
  44275. for (unsigned int p = 0; p<nbr; ++p) {
  44276. const unsigned int curr = 2 + p, next = 2 + ((p + 1)%nbr);
  44277. CImg<tf>::vector(1,next,curr).move_to(primitives);
  44278. CImg<tf>::vector(0,curr,next).move_to(primitives);
  44279. }
  44280. return vertices>'x';
  44281. }
  44282. //! Generate a 3D cylinder.
  44283. /**
  44284. \param[out] primitives The returned list of the 3D object primitives
  44285. (template type \e tf should be at least \e unsigned \e int).
  44286. \param radius The radius of the cylinder basis.
  44287. \param size_z The cylinder's height.
  44288. \param subdivisions The number of basis angular subdivisions.
  44289. \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
  44290. \par Example
  44291. \code
  44292. CImgList<unsigned int> faces3d;
  44293. const CImg<float> points3d = CImg<float>::cylinder3d(faces3d,50);
  44294. CImg<unsigned char>().display_object3d("Cylinder3d",points3d,faces3d);
  44295. \endcode
  44296. \image html ref_cylinder3d.jpg
  44297. **/
  44298. template<typename tf>
  44299. static CImg<floatT> cylinder3d(CImgList<tf>& primitives,
  44300. const float radius=50, const float size_z=100, const unsigned int subdivisions=24) {
  44301. primitives.assign();
  44302. if (!subdivisions) return CImg<floatT>();
  44303. CImgList<floatT> vertices(2,1,3,1,1,
  44304. 0.,0.,0.,
  44305. 0.,0.,size_z);
  44306. for (float delta = 360.f/subdivisions, angle = 0; angle<360; angle+=delta) {
  44307. const float a = (float)(angle*cimg::PI/180);
  44308. CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),0.f).move_to(vertices);
  44309. CImg<floatT>::vector((float)(radius*std::cos(a)),(float)(radius*std::sin(a)),size_z).move_to(vertices);
  44310. }
  44311. const unsigned int nbr = (vertices._width - 2)/2;
  44312. for (unsigned int p = 0; p<nbr; ++p) {
  44313. const unsigned int curr = 2 + 2*p, next = 2 + (2*((p + 1)%nbr));
  44314. CImg<tf>::vector(0,next,curr).move_to(primitives);
  44315. CImg<tf>::vector(1,curr + 1,next + 1).move_to(primitives);
  44316. CImg<tf>::vector(curr,next,next + 1,curr + 1).move_to(primitives);
  44317. }
  44318. return vertices>'x';
  44319. }
  44320. //! Generate a 3D torus.
  44321. /**
  44322. \param[out] primitives The returned list of the 3D object primitives
  44323. (template type \e tf should be at least \e unsigned \e int).
  44324. \param radius1 The large radius.
  44325. \param radius2 The small radius.
  44326. \param subdivisions1 The number of angular subdivisions for the large radius.
  44327. \param subdivisions2 The number of angular subdivisions for the small radius.
  44328. \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
  44329. \par Example
  44330. \code
  44331. CImgList<unsigned int> faces3d;
  44332. const CImg<float> points3d = CImg<float>::torus3d(faces3d,20,4);
  44333. CImg<unsigned char>().display_object3d("Torus3d",points3d,faces3d);
  44334. \endcode
  44335. \image html ref_torus3d.jpg
  44336. **/
  44337. template<typename tf>
  44338. static CImg<floatT> torus3d(CImgList<tf>& primitives,
  44339. const float radius1=100, const float radius2=30,
  44340. const unsigned int subdivisions1=24, const unsigned int subdivisions2=12) {
  44341. primitives.assign();
  44342. if (!subdivisions1 || !subdivisions2) return CImg<floatT>();
  44343. CImgList<floatT> vertices;
  44344. for (unsigned int v = 0; v<subdivisions1; ++v) {
  44345. const float
  44346. beta = (float)(v*2*cimg::PI/subdivisions1),
  44347. xc = radius1*(float)std::cos(beta),
  44348. yc = radius1*(float)std::sin(beta);
  44349. for (unsigned int u = 0; u<subdivisions2; ++u) {
  44350. const float
  44351. alpha = (float)(u*2*cimg::PI/subdivisions2),
  44352. x = xc + radius2*(float)(std::cos(alpha)*std::cos(beta)),
  44353. y = yc + radius2*(float)(std::cos(alpha)*std::sin(beta)),
  44354. z = radius2*(float)std::sin(alpha);
  44355. CImg<floatT>::vector(x,y,z).move_to(vertices);
  44356. }
  44357. }
  44358. for (unsigned int vv = 0; vv<subdivisions1; ++vv) {
  44359. const unsigned int nv = (vv + 1)%subdivisions1;
  44360. for (unsigned int uu = 0; uu<subdivisions2; ++uu) {
  44361. const unsigned int nu = (uu + 1)%subdivisions2, svv = subdivisions2*vv, snv = subdivisions2*nv;
  44362. CImg<tf>::vector(svv + nu,svv + uu,snv + uu,snv + nu).move_to(primitives);
  44363. }
  44364. }
  44365. return vertices>'x';
  44366. }
  44367. //! Generate a 3D XY-plane.
  44368. /**
  44369. \param[out] primitives The returned list of the 3D object primitives
  44370. (template type \e tf should be at least \e unsigned \e int).
  44371. \param size_x The width of the plane (dimension along the X-axis).
  44372. \param size_y The height of the plane (dimensions along the Y-axis).
  44373. \param subdivisions_x The number of planar subdivisions along the X-axis.
  44374. \param subdivisions_y The number of planar subdivisions along the Y-axis.
  44375. \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
  44376. \par Example
  44377. \code
  44378. CImgList<unsigned int> faces3d;
  44379. const CImg<float> points3d = CImg<float>::plane3d(faces3d,100,50);
  44380. CImg<unsigned char>().display_object3d("Plane3d",points3d,faces3d);
  44381. \endcode
  44382. \image html ref_plane3d.jpg
  44383. **/
  44384. template<typename tf>
  44385. static CImg<floatT> plane3d(CImgList<tf>& primitives,
  44386. const float size_x=100, const float size_y=100,
  44387. const unsigned int subdivisions_x=10, const unsigned int subdivisions_y=10) {
  44388. primitives.assign();
  44389. if (!subdivisions_x || !subdivisions_y) return CImg<floatT>();
  44390. CImgList<floatT> vertices;
  44391. const unsigned int w = subdivisions_x + 1, h = subdivisions_y + 1;
  44392. const float fx = (float)size_x/w, fy = (float)size_y/h;
  44393. for (unsigned int y = 0; y<h; ++y) for (unsigned int x = 0; x<w; ++x)
  44394. CImg<floatT>::vector(fx*x,fy*y,0).move_to(vertices);
  44395. for (unsigned int y = 0; y<subdivisions_y; ++y) for (unsigned int x = 0; x<subdivisions_x; ++x) {
  44396. const int off1 = x + y*w, off2 = x + 1 + y*w, off3 = x + 1 + (y + 1)*w, off4 = x + (y + 1)*w;
  44397. CImg<tf>::vector(off1,off4,off3,off2).move_to(primitives);
  44398. }
  44399. return vertices>'x';
  44400. }
  44401. //! Generate a 3D sphere.
  44402. /**
  44403. \param[out] primitives The returned list of the 3D object primitives
  44404. (template type \e tf should be at least \e unsigned \e int).
  44405. \param radius The radius of the sphere (dimension along the X-axis).
  44406. \param subdivisions The number of recursive subdivisions from an initial icosahedron.
  44407. \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
  44408. \par Example
  44409. \code
  44410. CImgList<unsigned int> faces3d;
  44411. const CImg<float> points3d = CImg<float>::sphere3d(faces3d,100,4);
  44412. CImg<unsigned char>().display_object3d("Sphere3d",points3d,faces3d);
  44413. \endcode
  44414. \image html ref_sphere3d.jpg
  44415. **/
  44416. template<typename tf>
  44417. static CImg<floatT> sphere3d(CImgList<tf>& primitives,
  44418. const float radius=50, const unsigned int subdivisions=3) {
  44419. // Create initial icosahedron
  44420. primitives.assign();
  44421. const double tmp = (1 + std::sqrt(5.f))/2, a = 1./std::sqrt(1 + tmp*tmp), b = tmp*a;
  44422. 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,
  44423. -a,0.,-b, -a,0.,b, 0.,b,a, 0.,-b,a, 0.,-b,-a, 0.,b,-a);
  44424. 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,
  44425. 8,0,11, 8,11,1, 9,10,3, 9,2,10, 8,4,0, 11,0,5, 4,9,3,
  44426. 5,3,10, 7,8,1, 6,1,11, 7,2,9, 6,10,2);
  44427. // edge - length/2
  44428. float he = (float)a;
  44429. // Recurse subdivisions
  44430. for (unsigned int i = 0; i<subdivisions; ++i) {
  44431. const unsigned int L = primitives._width;
  44432. he/=2;
  44433. const float he2 = he*he;
  44434. for (unsigned int l = 0; l<L; ++l) {
  44435. const unsigned int
  44436. p0 = (unsigned int)primitives(0,0), p1 = (unsigned int)primitives(0,1), p2 = (unsigned int)primitives(0,2);
  44437. const float
  44438. x0 = vertices(p0,0), y0 = vertices(p0,1), z0 = vertices(p0,2),
  44439. x1 = vertices(p1,0), y1 = vertices(p1,1), z1 = vertices(p1,2),
  44440. x2 = vertices(p2,0), y2 = vertices(p2,1), z2 = vertices(p2,2),
  44441. tnx0 = (x0 + x1)/2, tny0 = (y0 + y1)/2, tnz0 = (z0 + z1)/2,
  44442. nn0 = cimg::hypot(tnx0,tny0,tnz0),
  44443. tnx1 = (x0 + x2)/2, tny1 = (y0 + y2)/2, tnz1 = (z0 + z2)/2,
  44444. nn1 = cimg::hypot(tnx1,tny1,tnz1),
  44445. tnx2 = (x1 + x2)/2, tny2 = (y1 + y2)/2, tnz2 = (z1 + z2)/2,
  44446. nn2 = cimg::hypot(tnx2,tny2,tnz2),
  44447. nx0 = tnx0/nn0, ny0 = tny0/nn0, nz0 = tnz0/nn0,
  44448. nx1 = tnx1/nn1, ny1 = tny1/nn1, nz1 = tnz1/nn1,
  44449. nx2 = tnx2/nn2, ny2 = tny2/nn2, nz2 = tnz2/nn2;
  44450. int i0 = -1, i1 = -1, i2 = -1;
  44451. cimglist_for(vertices,p) {
  44452. const float x = (float)vertices(p,0), y = (float)vertices(p,1), z = (float)vertices(p,2);
  44453. if (cimg::sqr(x-nx0) + cimg::sqr(y-ny0) + cimg::sqr(z-nz0)<he2) i0 = p;
  44454. if (cimg::sqr(x-nx1) + cimg::sqr(y-ny1) + cimg::sqr(z-nz1)<he2) i1 = p;
  44455. if (cimg::sqr(x-nx2) + cimg::sqr(y-ny2) + cimg::sqr(z-nz2)<he2) i2 = p;
  44456. }
  44457. if (i0<0) { CImg<floatT>::vector(nx0,ny0,nz0).move_to(vertices); i0 = vertices.width() - 1; }
  44458. if (i1<0) { CImg<floatT>::vector(nx1,ny1,nz1).move_to(vertices); i1 = vertices.width() - 1; }
  44459. if (i2<0) { CImg<floatT>::vector(nx2,ny2,nz2).move_to(vertices); i2 = vertices.width() - 1; }
  44460. primitives.remove(0);
  44461. CImg<tf>::vector(p0,i0,i1).move_to(primitives);
  44462. CImg<tf>::vector((tf)i0,(tf)p1,(tf)i2).move_to(primitives);
  44463. CImg<tf>::vector((tf)i1,(tf)i2,(tf)p2).move_to(primitives);
  44464. CImg<tf>::vector((tf)i1,(tf)i0,(tf)i2).move_to(primitives);
  44465. }
  44466. }
  44467. return (vertices>'x')*=radius;
  44468. }
  44469. //! Generate a 3D ellipsoid.
  44470. /**
  44471. \param[out] primitives The returned list of the 3D object primitives
  44472. (template type \e tf should be at least \e unsigned \e int).
  44473. \param tensor The tensor which gives the shape and size of the ellipsoid.
  44474. \param subdivisions The number of recursive subdivisions from an initial stretched icosahedron.
  44475. \return The N vertices (xi,yi,zi) of the 3D object as a Nx3 CImg<float> image (0<=i<=N - 1).
  44476. \par Example
  44477. \code
  44478. CImgList<unsigned int> faces3d;
  44479. const CImg<float> tensor = CImg<float>::diagonal(10,7,3),
  44480. points3d = CImg<float>::ellipsoid3d(faces3d,tensor,4);
  44481. CImg<unsigned char>().display_object3d("Ellipsoid3d",points3d,faces3d);
  44482. \endcode
  44483. \image html ref_ellipsoid3d.jpg
  44484. **/
  44485. template<typename tf, typename t>
  44486. static CImg<floatT> ellipsoid3d(CImgList<tf>& primitives,
  44487. const CImg<t>& tensor, const unsigned int subdivisions=3) {
  44488. primitives.assign();
  44489. if (!subdivisions) return CImg<floatT>();
  44490. CImg<floatT> S, V;
  44491. tensor.symmetric_eigen(S,V);
  44492. const float orient =
  44493. (V(0,1)*V(1,2) - V(0,2)*V(1,1))*V(2,0) +
  44494. (V(0,2)*V(1,0) - V(0,0)*V(1,2))*V(2,1) +
  44495. (V(0,0)*V(1,1) - V(0,1)*V(1,0))*V(2,2);
  44496. if (orient<0) { V(2,0) = -V(2,0); V(2,1) = -V(2,1); V(2,2) = -V(2,2); }
  44497. const float l0 = S[0], l1 = S[1], l2 = S[2];
  44498. CImg<floatT> vertices = sphere3d(primitives,1.,subdivisions);
  44499. vertices.get_shared_row(0)*=l0;
  44500. vertices.get_shared_row(1)*=l1;
  44501. vertices.get_shared_row(2)*=l2;
  44502. return V*vertices;
  44503. }
  44504. //! Convert 3D object into a CImg3d representation.
  44505. /**
  44506. \param primitives Primitives data of the 3D object.
  44507. \param colors Colors data of the 3D object.
  44508. \param opacities Opacities data of the 3D object.
  44509. \param full_check Tells if full checking of the 3D object must be performed.
  44510. **/
  44511. template<typename tp, typename tc, typename to>
  44512. CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
  44513. const CImgList<tc>& colors,
  44514. const to& opacities,
  44515. const bool full_check=true) {
  44516. return get_object3dtoCImg3d(primitives,colors,opacities,full_check).move_to(*this);
  44517. }
  44518. //! Convert 3D object into a CImg3d representation \overloading.
  44519. template<typename tp, typename tc>
  44520. CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
  44521. const CImgList<tc>& colors,
  44522. const bool full_check=true) {
  44523. return get_object3dtoCImg3d(primitives,colors,full_check).move_to(*this);
  44524. }
  44525. //! Convert 3D object into a CImg3d representation \overloading.
  44526. template<typename tp>
  44527. CImg<T>& object3dtoCImg3d(const CImgList<tp>& primitives,
  44528. const bool full_check=true) {
  44529. return get_object3dtoCImg3d(primitives,full_check).move_to(*this);
  44530. }
  44531. //! Convert 3D object into a CImg3d representation \overloading.
  44532. CImg<T>& object3dtoCImg3d(const bool full_check=true) {
  44533. return get_object3dtoCImg3d(full_check).move_to(*this);
  44534. }
  44535. //! Convert 3D object into a CImg3d representation \newinstance.
  44536. template<typename tp, typename tc, typename to>
  44537. CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
  44538. const CImgList<tc>& colors,
  44539. const to& opacities,
  44540. const bool full_check=true) const {
  44541. CImg<charT> error_message(1024);
  44542. if (!is_object3d(primitives,colors,opacities,full_check,error_message))
  44543. throw CImgInstanceException(_cimg_instance
  44544. "object3dtoCImg3d(): Invalid specified 3D object (%u,%u) (%s).",
  44545. cimg_instance,_width,primitives._width,error_message.data());
  44546. CImg<floatT> res(1,_size_object3dtoCImg3d(primitives,colors,opacities));
  44547. float *ptrd = res._data;
  44548. // Put magick number.
  44549. *(ptrd++) = 'C' + 0.5f; *(ptrd++) = 'I' + 0.5f; *(ptrd++) = 'm' + 0.5f;
  44550. *(ptrd++) = 'g' + 0.5f; *(ptrd++) = '3' + 0.5f; *(ptrd++) = 'd' + 0.5f;
  44551. // Put number of vertices and primitives.
  44552. *(ptrd++) = cimg::uint2float(_width);
  44553. *(ptrd++) = cimg::uint2float(primitives._width);
  44554. // Put vertex data.
  44555. if (is_empty() || !primitives) return res;
  44556. const T *ptrx = data(0,0), *ptry = data(0,1), *ptrz = data(0,2);
  44557. cimg_forX(*this,p) {
  44558. *(ptrd++) = (float)*(ptrx++);
  44559. *(ptrd++) = (float)*(ptry++);
  44560. *(ptrd++) = (float)*(ptrz++);
  44561. }
  44562. // Put primitive data.
  44563. cimglist_for(primitives,p) {
  44564. *(ptrd++) = (float)primitives[p].size();
  44565. const tp *ptrp = primitives[p]._data;
  44566. cimg_foroff(primitives[p],i) *(ptrd++) = cimg::uint2float((unsigned int)*(ptrp++));
  44567. }
  44568. // Put color/texture data.
  44569. const unsigned int csiz = std::min(colors._width,primitives._width);
  44570. for (int c = 0; c<(int)csiz; ++c) {
  44571. const CImg<tc>& color = colors[c];
  44572. const tc *ptrc = color._data;
  44573. if (color.size()==3) { *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*(ptrc++); *(ptrd++) = (float)*ptrc; }
  44574. else {
  44575. *(ptrd++) = -128.f;
  44576. int shared_ind = -1;
  44577. if (color.is_shared()) for (int i = 0; i<c; ++i) if (ptrc==colors[i]._data) { shared_ind = i; break; }
  44578. if (shared_ind<0) {
  44579. *(ptrd++) = (float)color._width;
  44580. *(ptrd++) = (float)color._height;
  44581. *(ptrd++) = (float)color._spectrum;
  44582. cimg_foroff(color,l) *(ptrd++) = (float)*(ptrc++);
  44583. } else {
  44584. *(ptrd++) = (float)cimg::uint2float((unsigned int)shared_ind);
  44585. *(ptrd++) = 0;
  44586. *(ptrd++) = 0;
  44587. }
  44588. }
  44589. }
  44590. const int csiz2 = primitives.width() - colors.width();
  44591. for (int c = 0; c<csiz2; ++c) { *(ptrd++) = 200.f; *(ptrd++) = 200.f; *(ptrd++) = 200.f; }
  44592. // Put opacity data.
  44593. ptrd = _object3dtoCImg3d(opacities,ptrd);
  44594. const float *ptre = res.end();
  44595. while (ptrd<ptre) *(ptrd++) = 1.f;
  44596. return res;
  44597. }
  44598. template<typename to>
  44599. float* _object3dtoCImg3d(const CImgList<to>& opacities, float *ptrd) const {
  44600. cimglist_for(opacities,o) {
  44601. const CImg<to>& opacity = opacities[o];
  44602. const to *ptro = opacity._data;
  44603. if (opacity.size()==1) *(ptrd++) = (float)*ptro;
  44604. else {
  44605. *(ptrd++) = -128.f;
  44606. int shared_ind = -1;
  44607. if (opacity.is_shared()) for (int i = 0; i<o; ++i) if (ptro==opacities[i]._data) { shared_ind = i; break; }
  44608. if (shared_ind<0) {
  44609. *(ptrd++) = (float)opacity._width;
  44610. *(ptrd++) = (float)opacity._height;
  44611. *(ptrd++) = (float)opacity._spectrum;
  44612. cimg_foroff(opacity,l) *(ptrd++) = (float)*(ptro++);
  44613. } else {
  44614. *(ptrd++) = (float)cimg::uint2float((unsigned int)shared_ind);
  44615. *(ptrd++) = 0;
  44616. *(ptrd++) = 0;
  44617. }
  44618. }
  44619. }
  44620. return ptrd;
  44621. }
  44622. template<typename to>
  44623. float* _object3dtoCImg3d(const CImg<to>& opacities, float *ptrd) const {
  44624. const to *ptro = opacities._data;
  44625. cimg_foroff(opacities,o) *(ptrd++) = (float)*(ptro++);
  44626. return ptrd;
  44627. }
  44628. template<typename tp, typename tc, typename to>
  44629. unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives,
  44630. const CImgList<tc>& colors,
  44631. const CImgList<to>& opacities) const {
  44632. unsigned int siz = 8U + 3*_width;
  44633. cimglist_for(primitives,p) siz+=primitives[p].size() + 1;
  44634. for (int c = std::min(primitives.width(),colors.width()) - 1; c>=0; --c) {
  44635. if (colors[c].is_shared()) siz+=4;
  44636. else { const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4 + csiz:3; }
  44637. }
  44638. if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width);
  44639. cimglist_for(opacities,o) {
  44640. if (opacities[o].is_shared()) siz+=4;
  44641. else { const unsigned int osiz = opacities[o].size(); siz+=(osiz!=1)?4 + osiz:1; }
  44642. }
  44643. siz+=primitives._width - opacities._width;
  44644. return siz;
  44645. }
  44646. template<typename tp, typename tc, typename to>
  44647. unsigned int _size_object3dtoCImg3d(const CImgList<tp>& primitives,
  44648. const CImgList<tc>& colors,
  44649. const CImg<to>& opacities) const {
  44650. unsigned int siz = 8U + 3*_width;
  44651. cimglist_for(primitives,p) siz+=primitives[p].size() + 1;
  44652. for (int c = std::min(primitives.width(),colors.width()) - 1; c>=0; --c) {
  44653. const unsigned int csiz = colors[c].size(); siz+=(csiz!=3)?4 + csiz:3;
  44654. }
  44655. if (colors._width<primitives._width) siz+=3*(primitives._width - colors._width);
  44656. siz+=primitives.size();
  44657. cimg::unused(opacities);
  44658. return siz;
  44659. }
  44660. //! Convert 3D object into a CImg3d representation \overloading.
  44661. template<typename tp, typename tc>
  44662. CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
  44663. const CImgList<tc>& colors,
  44664. const bool full_check=true) const {
  44665. CImgList<T> opacities;
  44666. return get_object3dtoCImg3d(primitives,colors,opacities,full_check);
  44667. }
  44668. //! Convert 3D object into a CImg3d representation \overloading.
  44669. template<typename tp>
  44670. CImg<floatT> get_object3dtoCImg3d(const CImgList<tp>& primitives,
  44671. const bool full_check=true) const {
  44672. CImgList<T> colors, opacities;
  44673. return get_object3dtoCImg3d(primitives,colors,opacities,full_check);
  44674. }
  44675. //! Convert 3D object into a CImg3d representation \overloading.
  44676. CImg<floatT> get_object3dtoCImg3d(const bool full_check=true) const {
  44677. CImgList<T> opacities, colors;
  44678. CImgList<uintT> primitives(width(),1,1,1,1);
  44679. cimglist_for(primitives,p) primitives(p,0) = p;
  44680. return get_object3dtoCImg3d(primitives,colors,opacities,full_check);
  44681. }
  44682. //! Convert CImg3d representation into a 3D object.
  44683. /**
  44684. \param[out] primitives Primitives data of the 3D object.
  44685. \param[out] colors Colors data of the 3D object.
  44686. \param[out] opacities Opacities data of the 3D object.
  44687. \param full_check Tells if full checking of the 3D object must be performed.
  44688. **/
  44689. template<typename tp, typename tc, typename to>
  44690. CImg<T>& CImg3dtoobject3d(CImgList<tp>& primitives,
  44691. CImgList<tc>& colors,
  44692. CImgList<to>& opacities,
  44693. const bool full_check=true) {
  44694. return get_CImg3dtoobject3d(primitives,colors,opacities,full_check).move_to(*this);
  44695. }
  44696. //! Convert CImg3d representation into a 3D object \newinstance.
  44697. template<typename tp, typename tc, typename to>
  44698. CImg<T> get_CImg3dtoobject3d(CImgList<tp>& primitives,
  44699. CImgList<tc>& colors,
  44700. CImgList<to>& opacities,
  44701. const bool full_check=true) const {
  44702. CImg<charT> error_message(1024);
  44703. if (!is_CImg3d(full_check,error_message))
  44704. throw CImgInstanceException(_cimg_instance
  44705. "CImg3dtoobject3d(): image instance is not a CImg3d (%s).",
  44706. cimg_instance,error_message.data());
  44707. const T *ptrs = _data + 6;
  44708. const unsigned int
  44709. nb_points = cimg::float2uint((float)*(ptrs++)),
  44710. nb_primitives = cimg::float2uint((float)*(ptrs++));
  44711. const CImg<T> points = CImg<T>(ptrs,3,nb_points,1,1,true).get_transpose();
  44712. ptrs+=3*nb_points;
  44713. primitives.assign(nb_primitives);
  44714. cimglist_for(primitives,p) {
  44715. const unsigned int nb_inds = (unsigned int)*(ptrs++);
  44716. primitives[p].assign(1,nb_inds);
  44717. tp *ptrp = primitives[p]._data;
  44718. for (unsigned int i = 0; i<nb_inds; ++i)
  44719. *(ptrp++) = (tp)cimg::float2uint((float)*(ptrs++));
  44720. }
  44721. colors.assign(nb_primitives);
  44722. cimglist_for(colors,c) {
  44723. if (*ptrs==(T)-128) {
  44724. ++ptrs;
  44725. const unsigned int
  44726. w = (unsigned int)cimg::float2uint((float)*(ptrs++)),
  44727. h = (unsigned int)*(ptrs++),
  44728. s = (unsigned int)*(ptrs++);
  44729. if (!h && !s) colors[c].assign(colors[w],true);
  44730. else { colors[c].assign(ptrs,w,h,1,s,false); ptrs+=(ulongT)w*h*s; }
  44731. } else { colors[c].assign(ptrs,1,1,1,3,false); ptrs+=3; }
  44732. }
  44733. opacities.assign(nb_primitives);
  44734. cimglist_for(opacities,o) {
  44735. if (*ptrs==(T)-128) {
  44736. ++ptrs;
  44737. const unsigned int
  44738. w = (unsigned int)cimg::float2uint((float)*(ptrs++)),
  44739. h = (unsigned int)*(ptrs++),
  44740. s = (unsigned int)*(ptrs++);
  44741. if (!h && !s) opacities[o].assign(opacities[w],true);
  44742. else { opacities[o].assign(ptrs,w,h,1,s,false); ptrs+=(ulongT)w*h*s; }
  44743. } else opacities[o].assign(1,1,1,1,*(ptrs++));
  44744. }
  44745. return points;
  44746. }
  44747. //@}
  44748. //---------------------------
  44749. //
  44750. //! \name Drawing Functions
  44751. //@{
  44752. //---------------------------
  44753. #define cimg_init_scanline(opacity) \
  44754. static const T _sc_maxval = (T)std::min(cimg::type<T>::max(),(T)cimg::type<tc>::max()); \
  44755. const float _sc_nopacity = cimg::abs((float)opacity), _sc_copacity = 1 - std::max((float)opacity,0.f); \
  44756. const ulongT _sc_whd = (ulongT)_width*_height*_depth; \
  44757. cimg::unused(_sc_maxval);
  44758. #define cimg_draw_scanline(x0,x1,y,color,opacity,brightness) \
  44759. _draw_scanline(x0,x1,y,color,opacity,brightness,_sc_nopacity,_sc_copacity,_sc_whd,_sc_maxval)
  44760. // [internal] The following _draw_scanline() routines are *non user-friendly functions*,
  44761. // used only for internal purpose.
  44762. // Pre-requisites: x0<=x1, y-coordinate is valid, col is valid.
  44763. template<typename tc>
  44764. CImg<T>& _draw_scanline(const int x0, const int x1, const int y,
  44765. const tc *const color, const float opacity,
  44766. const float brightness,
  44767. const float nopacity, const float copacity, const ulongT whd, const T _sc_maxval) {
  44768. const int nx0 = x0>0?x0:0, nx1 = x1<width()?x1:width() - 1, dx = nx1 - nx0;
  44769. if (dx>=0) {
  44770. const tc *col = color;
  44771. const ulongT off = whd - dx - 1;
  44772. T *ptrd = data(nx0,y);
  44773. if (opacity>=1) { // ** Opaque drawing **
  44774. if (brightness==1) { // Brightness==1
  44775. if (sizeof(T)!=1) cimg_forC(*this,c) {
  44776. const T val = (T)*(col++);
  44777. for (int x = dx; x>=0; --x) *(ptrd++) = val;
  44778. ptrd+=off;
  44779. } else cimg_forC(*this,c) {
  44780. const T val = (T)*(col++);
  44781. std::memset(ptrd,(int)val,dx + 1);
  44782. ptrd+=whd;
  44783. }
  44784. } else if (brightness<1) { // Brightness<1
  44785. if (sizeof(T)!=1) cimg_forC(*this,c) {
  44786. const T val = (T)(*(col++)*brightness);
  44787. for (int x = dx; x>=0; --x) *(ptrd++) = val;
  44788. ptrd+=off;
  44789. } else cimg_forC(*this,c) {
  44790. const T val = (T)(*(col++)*brightness);
  44791. std::memset(ptrd,(int)val,dx + 1);
  44792. ptrd+=whd;
  44793. }
  44794. } else { // Brightness>1
  44795. if (sizeof(T)!=1) cimg_forC(*this,c) {
  44796. const T val = (T)((2-brightness)**(col++) + (brightness - 1)*_sc_maxval);
  44797. for (int x = dx; x>=0; --x) *(ptrd++) = val;
  44798. ptrd+=off;
  44799. } else cimg_forC(*this,c) {
  44800. const T val = (T)((2-brightness)**(col++) + (brightness - 1)*_sc_maxval);
  44801. std::memset(ptrd,(int)val,dx + 1);
  44802. ptrd+=whd;
  44803. }
  44804. }
  44805. } else { // ** Transparent drawing **
  44806. if (brightness==1) { // Brightness==1
  44807. cimg_forC(*this,c) {
  44808. const Tfloat val = *(col++)*nopacity;
  44809. for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; }
  44810. ptrd+=off;
  44811. }
  44812. } else if (brightness<=1) { // Brightness<1
  44813. cimg_forC(*this,c) {
  44814. const Tfloat val = *(col++)*brightness*nopacity;
  44815. for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; }
  44816. ptrd+=off;
  44817. }
  44818. } else { // Brightness>1
  44819. cimg_forC(*this,c) {
  44820. const Tfloat val = ((2-brightness)**(col++) + (brightness - 1)*_sc_maxval)*nopacity;
  44821. for (int x = dx; x>=0; --x) { *ptrd = (T)(val + *ptrd*copacity); ++ptrd; }
  44822. ptrd+=off;
  44823. }
  44824. }
  44825. }
  44826. }
  44827. return *this;
  44828. }
  44829. //! Draw a 3D point.
  44830. /**
  44831. \param x0 X-coordinate of the point.
  44832. \param y0 Y-coordinate of the point.
  44833. \param z0 Z-coordinate of the point.
  44834. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  44835. \param opacity Drawing opacity.
  44836. \note
  44837. - To set pixel values without clipping needs, you should use the faster CImg::operator()() function.
  44838. \par Example:
  44839. \code
  44840. CImg<unsigned char> img(100,100,1,3,0);
  44841. const unsigned char color[] = { 255,128,64 };
  44842. img.draw_point(50,50,color);
  44843. \endcode
  44844. **/
  44845. template<typename tc>
  44846. CImg<T>& draw_point(const int x0, const int y0, const int z0,
  44847. const tc *const color, const float opacity=1) {
  44848. if (is_empty()) return *this;
  44849. if (!color)
  44850. throw CImgArgumentException(_cimg_instance
  44851. "draw_point(): Specified color is (null).",
  44852. cimg_instance);
  44853. if (x0>=0 && y0>=0 && z0>=0 && x0<width() && y0<height() && z0<depth()) {
  44854. const ulongT whd = (ulongT)_width*_height*_depth;
  44855. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
  44856. T *ptrd = data(x0,y0,z0,0);
  44857. const tc *col = color;
  44858. if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)*(col++); ptrd+=whd; }
  44859. else cimg_forC(*this,c) { *ptrd = (T)(*(col++)*nopacity + *ptrd*copacity); ptrd+=whd; }
  44860. }
  44861. return *this;
  44862. }
  44863. //! Draw a 2D point \simplification.
  44864. template<typename tc>
  44865. CImg<T>& draw_point(const int x0, const int y0,
  44866. const tc *const color, const float opacity=1) {
  44867. return draw_point(x0,y0,0,color,opacity);
  44868. }
  44869. // Draw a points cloud.
  44870. /**
  44871. \param points Image of vertices coordinates.
  44872. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  44873. \param opacity Drawing opacity.
  44874. **/
  44875. template<typename t, typename tc>
  44876. CImg<T>& draw_point(const CImg<t>& points,
  44877. const tc *const color, const float opacity=1) {
  44878. if (is_empty() || !points) return *this;
  44879. switch (points._height) {
  44880. case 0 : case 1 :
  44881. throw CImgArgumentException(_cimg_instance
  44882. "draw_point(): Invalid specified point set (%u,%u,%u,%u,%p).",
  44883. cimg_instance,
  44884. points._width,points._height,points._depth,points._spectrum,points._data);
  44885. case 2 : {
  44886. cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),color,opacity);
  44887. } break;
  44888. default : {
  44889. cimg_forX(points,i) draw_point((int)points(i,0),(int)points(i,1),(int)points(i,2),color,opacity);
  44890. }
  44891. }
  44892. return *this;
  44893. }
  44894. //! Draw a 2D line.
  44895. /**
  44896. \param x0 X-coordinate of the starting line point.
  44897. \param y0 Y-coordinate of the starting line point.
  44898. \param x1 X-coordinate of the ending line point.
  44899. \param y1 Y-coordinate of the ending line point.
  44900. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  44901. \param opacity Drawing opacity.
  44902. \param pattern An integer whose bits describe the line pattern.
  44903. \param init_hatch Tells if a reinitialization of the hash state must be done.
  44904. \note
  44905. - Line routine uses Bresenham's algorithm.
  44906. - Set \p init_hatch = false to draw consecutive hatched segments without breaking the line pattern.
  44907. \par Example:
  44908. \code
  44909. CImg<unsigned char> img(100,100,1,3,0);
  44910. const unsigned char color[] = { 255,128,64 };
  44911. img.draw_line(40,40,80,70,color);
  44912. \endcode
  44913. **/
  44914. template<typename tc>
  44915. CImg<T>& draw_line(int x0, int y0,
  44916. int x1, int y1,
  44917. const tc *const color, const float opacity=1,
  44918. const unsigned int pattern=~0U, const bool init_hatch=true) {
  44919. if (is_empty() || !opacity || !pattern ||
  44920. std::min(y0,y1)>=height() || std::max(y0,y1)<0 ||
  44921. std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
  44922. int
  44923. w1 = width() - 1, h1 = height() - 1,
  44924. dx01 = x1 - x0, dy01 = y1 - y0;
  44925. const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
  44926. if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
  44927. if (pattern==~0U && y0>y1) { cimg::swap(x0,x1,y0,y1); dx01*=-1; dy01*=-1; }
  44928. static unsigned int hatch = ~0U - (~0U>>1);
  44929. if (init_hatch) hatch = ~0U - (~0U>>1);
  44930. cimg_init_scanline(opacity);
  44931. const int
  44932. step = y0<=y1?1:-1,
  44933. hdy01 = dy01*cimg::sign(dx01)/2,
  44934. cy0 = cimg::cut(y0,0,h1),
  44935. cy1 = cimg::cut(y1,0,h1) + step;
  44936. dy01+=dy01?0:1;
  44937. for (int y = cy0; y!=cy1; y+=step) {
  44938. const int
  44939. yy0 = y - y0,
  44940. x = x0 + (dx01*yy0 + hdy01)/dy01;
  44941. if (x>=0 && x<=w1 && pattern&hatch) {
  44942. T *const ptrd = is_horizontal?data(y,x):data(x,y);
  44943. cimg_forC(*this,c) {
  44944. const T val = color[c];
  44945. ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
  44946. }
  44947. }
  44948. if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
  44949. }
  44950. return *this;
  44951. }
  44952. //! Draw a 2D line, with z-buffering.
  44953. /**
  44954. \param zbuffer Zbuffer image.
  44955. \param x0 X-coordinate of the starting point.
  44956. \param y0 Y-coordinate of the starting point.
  44957. \param z0 Z-coordinate of the starting point
  44958. \param x1 X-coordinate of the ending point.
  44959. \param y1 Y-coordinate of the ending point.
  44960. \param z1 Z-coordinate of the ending point.
  44961. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  44962. \param opacity Drawing opacity.
  44963. \param pattern An integer whose bits describe the line pattern.
  44964. \param init_hatch Tells if a reinitialization of the hash state must be done.
  44965. **/
  44966. template<typename tz, typename tc>
  44967. CImg<T>& draw_line(CImg<tz>& zbuffer,
  44968. int x0, int y0, const float z0,
  44969. int x1, int y1, const float z1,
  44970. const tc *const color, const float opacity=1,
  44971. const unsigned int pattern=~0U, const bool init_hatch=true) {
  44972. if (is_empty() || z0<=0 || z1<=0 || !opacity || !pattern) return *this;
  44973. if (!color)
  44974. throw CImgArgumentException(_cimg_instance
  44975. "draw_line(): Specified color is (null).",
  44976. cimg_instance);
  44977. if (!is_sameXY(zbuffer))
  44978. throw CImgArgumentException(_cimg_instance
  44979. "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
  44980. "different dimensions.",
  44981. cimg_instance,
  44982. zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
  44983. if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
  44984. float iz0 = 1/z0, iz1 = 1/z1;
  44985. int
  44986. w1 = width() - 1, h1 = height() - 1,
  44987. dx01 = x1 - x0, dy01 = y1 - y0;
  44988. float diz01 = iz1 - iz0;
  44989. const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
  44990. if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
  44991. if (pattern==~0U && y0>y1) {
  44992. cimg::swap(x0,x1,y0,y1,iz0,iz1);
  44993. dx01*=-1; dy01*=-1; diz01*=-1;
  44994. }
  44995. static unsigned int hatch = ~0U - (~0U>>1);
  44996. if (init_hatch) hatch = ~0U - (~0U>>1);
  44997. cimg_init_scanline(opacity);
  44998. const int
  44999. step = y0<=y1?1:-1, hdy01 = dy01*cimg::sign(dx01)/2,
  45000. cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
  45001. dy01+=dy01?0:1;
  45002. for (int y = cy0; y!=cy1; y+=step) {
  45003. const int
  45004. yy0 = y - y0,
  45005. x = x0 + (dx01*yy0 + hdy01)/dy01;
  45006. const float iz = iz0 + diz01*yy0/dy01;
  45007. tz *const ptrz = is_horizontal?zbuffer.data(y,x):zbuffer.data(x,y);
  45008. if (x>=0 && x<=w1 && pattern&hatch && iz>=*ptrz) {
  45009. *ptrz = (tz)iz;
  45010. T *const ptrd = is_horizontal?data(y,x):data(x,y);
  45011. cimg_forC(*this,c) {
  45012. const T val = color[c];
  45013. ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
  45014. }
  45015. }
  45016. if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
  45017. }
  45018. return *this;
  45019. }
  45020. //! Draw a textured 2D line.
  45021. /**
  45022. \param x0 X-coordinate of the starting line point.
  45023. \param y0 Y-coordinate of the starting line point.
  45024. \param x1 X-coordinate of the ending line point.
  45025. \param y1 Y-coordinate of the ending line point.
  45026. \param texture Texture image defining the pixel colors.
  45027. \param tx0 X-coordinate of the starting texture point.
  45028. \param ty0 Y-coordinate of the starting texture point.
  45029. \param tx1 X-coordinate of the ending texture point.
  45030. \param ty1 Y-coordinate of the ending texture point.
  45031. \param opacity Drawing opacity.
  45032. \param pattern An integer whose bits describe the line pattern.
  45033. \param init_hatch Tells if the hash variable must be reinitialized.
  45034. \note
  45035. - Line routine uses the well known Bresenham's algorithm.
  45036. \par Example:
  45037. \code
  45038. CImg<unsigned char> img(100,100,1,3,0), texture("texture256x256.ppm");
  45039. const unsigned char color[] = { 255,128,64 };
  45040. img.draw_line(40,40,80,70,texture,0,0,255,255);
  45041. \endcode
  45042. **/
  45043. template<typename tc>
  45044. CImg<T>& draw_line(int x0, int y0,
  45045. int x1, int y1,
  45046. const CImg<tc>& texture,
  45047. int tx0, int ty0,
  45048. int tx1, int ty1,
  45049. const float opacity=1,
  45050. const unsigned int pattern=~0U, const bool init_hatch=true) {
  45051. if (is_empty() || !opacity || !pattern) return *this;
  45052. if (texture._depth>1 || texture._spectrum<_spectrum)
  45053. throw CImgArgumentException(_cimg_instance
  45054. "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
  45055. cimg_instance,
  45056. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  45057. if (is_overlapped(texture)) return draw_line(x0,y0,x1,y1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
  45058. if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
  45059. int w1 = width() - 1, h1 = height() - 1;
  45060. longT
  45061. dx01 = (longT)x1 - x0, dy01 = (longT)y1 - y0,
  45062. dtx01 = (longT)tx1 - tx0, dty01 = (longT)ty1 - ty0;
  45063. const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
  45064. if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
  45065. if (pattern==~0U && y0>y1) {
  45066. cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1);
  45067. dx01*=-1; dy01*=-1; dtx01*=-1; dty01*=-1;
  45068. }
  45069. const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
  45070. static unsigned int hatch = ~0U - (~0U>>1);
  45071. if (init_hatch) hatch = ~0U - (~0U>>1);
  45072. cimg_init_scanline(opacity);
  45073. const int step = y0<=y1?1:-1, cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
  45074. const longT
  45075. hdy01 = dy01*cimg::sign(dx01)/2,
  45076. hdy01tx = dy01*cimg::sign(dtx01)/2,
  45077. hdy01ty = dy01*cimg::sign(dty01)/2;
  45078. dy01+=dy01?0:1;
  45079. for (int y = cy0; y!=cy1; y+=step) {
  45080. const longT
  45081. yy0 = (longT)y - y0,
  45082. x = x0 + (dx01*yy0 + hdy01)/dy01,
  45083. tx = tx0 + (dtx01*yy0 + hdy01tx)/dy01,
  45084. ty = ty0 + (dty01*yy0 + hdy01ty)/dy01;
  45085. if (x>=0 && x<=w1 && pattern&hatch) {
  45086. T *const ptrd = is_horizontal?data(y,x):data(x,y);
  45087. const tc *const color = &texture._atXY(tx,ty);
  45088. cimg_forC(*this,c) {
  45089. const T val = color[c*twhd];
  45090. ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
  45091. }
  45092. }
  45093. if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
  45094. }
  45095. return *this;
  45096. }
  45097. //! Draw a textured 2D line, with perspective correction.
  45098. /**
  45099. \param x0 X-coordinate of the starting point.
  45100. \param y0 Y-coordinate of the starting point.
  45101. \param z0 Z-coordinate of the starting point
  45102. \param x1 X-coordinate of the ending point.
  45103. \param y1 Y-coordinate of the ending point.
  45104. \param z1 Z-coordinate of the ending point.
  45105. \param texture Texture image defining the pixel colors.
  45106. \param tx0 X-coordinate of the starting texture point.
  45107. \param ty0 Y-coordinate of the starting texture point.
  45108. \param tx1 X-coordinate of the ending texture point.
  45109. \param ty1 Y-coordinate of the ending texture point.
  45110. \param opacity Drawing opacity.
  45111. \param pattern An integer whose bits describe the line pattern.
  45112. \param init_hatch Tells if the hash variable must be reinitialized.
  45113. **/
  45114. template<typename tc>
  45115. CImg<T>& draw_line(int x0, int y0, const float z0,
  45116. int x1, int y1, const float z1,
  45117. const CImg<tc>& texture,
  45118. const int tx0, const int ty0,
  45119. const int tx1, const int ty1,
  45120. const float opacity=1,
  45121. const unsigned int pattern=~0U, const bool init_hatch=true) {
  45122. if (is_empty() || z0<=0 || z1<=0 || !opacity || !pattern) return *this;
  45123. if (texture._depth>1 || texture._spectrum<_spectrum)
  45124. throw CImgArgumentException(_cimg_instance
  45125. "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
  45126. cimg_instance,
  45127. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  45128. if (is_overlapped(texture))
  45129. return draw_line(x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
  45130. if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
  45131. float iz0 = 1/z0, iz1 = 1/z1;
  45132. int w1 = width() - 1, h1 = height() - 1;
  45133. longT dx01 = (longT)x1 - x0, dy01 = (longT)y1 - y0;
  45134. float
  45135. diz01 = iz1 - iz0,
  45136. txz0 = tx0*iz0, txz1 = tx1*iz1,
  45137. tyz0 = ty0*iz0, tyz1 = ty1*iz1,
  45138. dtxz01 = txz1 - txz0, dtyz01 = tyz1 - tyz0;
  45139. const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
  45140. if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
  45141. if (pattern==~0U && y0>y1) {
  45142. cimg::swap(x0,x1,y0,y1,iz0,iz1,txz0,txz1,tyz0,tyz1);
  45143. dx01*=-1; dy01*=-1; diz01*=-1; dtxz01*=-1; dtyz01*=-1;
  45144. }
  45145. const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
  45146. static unsigned int hatch = ~0U - (~0U>>1);
  45147. if (init_hatch) hatch = ~0U - (~0U>>1);
  45148. cimg_init_scanline(opacity);
  45149. const int step = y0<=y1?1:-1, cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
  45150. const longT hdy01 = dy01*cimg::sign(dx01)/2;
  45151. dy01+=dy01?0:1;
  45152. for (int y = cy0; y!=cy1; y+=step) {
  45153. const longT
  45154. yy0 = (longT)y - y0,
  45155. x = x0 + (dx01*yy0 + hdy01)/dy01;
  45156. const float
  45157. iz = iz0 + diz01*yy0/dy01,
  45158. txz = txz0 + dtxz01*yy0/dy01,
  45159. tyz = tyz0 + dtyz01*yy0/dy01;
  45160. if (x>=0 && x<=w1 && pattern&hatch) {
  45161. const int
  45162. tx = (int)cimg::round(txz/iz),
  45163. ty = (int)cimg::round(tyz/iz);
  45164. T *const ptrd = is_horizontal?data(y,x):data(x,y);
  45165. const tc *const color = &texture._atXY(tx,ty);
  45166. cimg_forC(*this,c) {
  45167. const T val = color[c*twhd];
  45168. ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
  45169. }
  45170. }
  45171. if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
  45172. }
  45173. return *this;
  45174. }
  45175. //! Draw a textured 2D line, with perspective correction and z-buffering.
  45176. /**
  45177. \param zbuffer Z-buffer image.
  45178. \param x0 X-coordinate of the starting point.
  45179. \param y0 Y-coordinate of the starting point.
  45180. \param z0 Z-coordinate of the starting point
  45181. \param x1 X-coordinate of the ending point.
  45182. \param y1 Y-coordinate of the ending point.
  45183. \param z1 Z-coordinate of the ending point.
  45184. \param texture Texture image defining the pixel colors.
  45185. \param tx0 X-coordinate of the starting texture point.
  45186. \param ty0 Y-coordinate of the starting texture point.
  45187. \param tx1 X-coordinate of the ending texture point.
  45188. \param ty1 Y-coordinate of the ending texture point.
  45189. \param opacity Drawing opacity.
  45190. \param pattern An integer whose bits describe the line pattern.
  45191. \param init_hatch Tells if the hash variable must be reinitialized.
  45192. **/
  45193. template<typename tz, typename tc>
  45194. CImg<T>& draw_line(CImg<tz>& zbuffer,
  45195. int x0, int y0, const float z0,
  45196. int x1, int y1, const float z1,
  45197. const CImg<tc>& texture,
  45198. const int tx0, const int ty0,
  45199. const int tx1, const int ty1,
  45200. const float opacity=1,
  45201. const unsigned int pattern=~0U, const bool init_hatch=true) {
  45202. if (is_empty() || z0<=0 || z1<=0 || !opacity || !pattern) return *this;
  45203. if (!is_sameXY(zbuffer))
  45204. throw CImgArgumentException(_cimg_instance
  45205. "draw_line(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
  45206. "different dimensions.",
  45207. cimg_instance,
  45208. zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
  45209. if (texture._depth>1 || texture._spectrum<_spectrum)
  45210. throw CImgArgumentException(_cimg_instance
  45211. "draw_line(): Invalid specified texture (%u,%u,%u,%u,%p).",
  45212. cimg_instance,
  45213. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  45214. if (is_overlapped(texture))
  45215. return draw_line(zbuffer,x0,y0,z0,x1,y1,z1,+texture,tx0,ty0,tx1,ty1,opacity,pattern,init_hatch);
  45216. if (std::min(y0,y1)>=height() || std::max(y0,y1)<0 || std::min(x0,x1)>=width() || std::max(x0,x1)<0) return *this;
  45217. float iz0 = 1/z0, iz1 = 1/z1;
  45218. int w1 = width() - 1, h1 = height() - 1;
  45219. longT dx01 = (longT)x1 - x0, dy01 = (longT)y1 - y0;
  45220. float
  45221. diz01 = iz1 - iz0,
  45222. txz0 = tx0*iz0, txz1 = tx1*iz1,
  45223. tyz0 = ty0*iz0, tyz1 = ty1*iz1,
  45224. dtxz01 = txz1 - txz0, dtyz01 = tyz1 - tyz0;
  45225. const bool is_horizontal = cimg::abs(dx01)>cimg::abs(dy01);
  45226. if (is_horizontal) cimg::swap(x0,y0,x1,y1,w1,h1,dx01,dy01);
  45227. if (pattern==~0U && y0>y1) {
  45228. cimg::swap(x0,x1,y0,y1,iz0,iz1,txz0,txz1,tyz0,tyz1);
  45229. dx01*=-1; dy01*=-1; diz01*=-1; dtxz01*=-1; dtyz01*=-1;
  45230. }
  45231. const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
  45232. static unsigned int hatch = ~0U - (~0U>>1);
  45233. if (init_hatch) hatch = ~0U - (~0U>>1);
  45234. cimg_init_scanline(opacity);
  45235. const int step = y0<=y1?1:-1, cy0 = cimg::cut(y0,0,h1), cy1 = cimg::cut(y1,0,h1) + step;
  45236. const longT hdy01 = dy01*cimg::sign(dx01)/2;
  45237. dy01+=dy01?0:1;
  45238. for (int y = cy0; y!=cy1; y+=step) {
  45239. const longT
  45240. yy0 = (longT)y - y0,
  45241. x = x0 + (dx01*yy0 + hdy01)/dy01;
  45242. const float
  45243. iz = iz0 + diz01*yy0/dy01,
  45244. txz = txz0 + dtxz01*yy0/dy01,
  45245. tyz = tyz0 + dtyz01*yy0/dy01;
  45246. tz *const ptrz = is_horizontal?zbuffer.data(y,x):zbuffer.data(x,y);
  45247. if (x>=0 && x<=w1 && pattern&hatch && iz>=*ptrz) {
  45248. *ptrz = (tz)iz;
  45249. const int
  45250. tx = (int)cimg::round(txz/iz),
  45251. ty = (int)cimg::round(tyz/iz);
  45252. T *const ptrd = is_horizontal?data(y,x):data(x,y);
  45253. const tc *const color = &texture._atXY(tx,ty);
  45254. cimg_forC(*this,c) {
  45255. const T val = color[c*twhd];
  45256. ptrd[c*_sc_whd] = opacity>=1?val:(T)(val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
  45257. }
  45258. }
  45259. if (!(hatch>>=1)) hatch = ~0U - (~0U>>1);
  45260. }
  45261. return *this;
  45262. }
  45263. //! Draw a set of consecutive lines.
  45264. /**
  45265. \param points Coordinates of vertices, stored as a list of vectors.
  45266. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  45267. \param opacity Drawing opacity.
  45268. \param pattern An integer whose bits describe the line pattern.
  45269. \param init_hatch If set to true, init hatch motif.
  45270. \note
  45271. - This function uses several call to the single CImg::draw_line() procedure,
  45272. depending on the vectors size in \p points.
  45273. **/
  45274. template<typename tp, typename tc>
  45275. CImg<T>& draw_line(const CImg<tp>& points,
  45276. const tc *const color, const float opacity=1,
  45277. const unsigned int pattern=~0U, const bool init_hatch=true) {
  45278. if (is_empty() || !points) return *this;
  45279. if (!color)
  45280. throw CImgArgumentException(_cimg_instance
  45281. "draw_line(): Specified color is (null).",
  45282. cimg_instance);
  45283. if (points.height()!=2)
  45284. throw CImgArgumentException(_cimg_instance
  45285. "draw_line(): Invalid specified point set (%u,%u,%u,%u).",
  45286. cimg_instance,
  45287. points._width,points._height,points._depth,points._spectrum);
  45288. CImg<intT> ipoints;
  45289. if (cimg::type<tp>::is_float()) ipoints = points.get_round();
  45290. else ipoints.assign(points,cimg::type<tp>::string()==cimg::type<int>::string());
  45291. bool ninit_hatch = init_hatch;
  45292. const int x0 = ipoints(0,0), y0 = ipoints(0,1);
  45293. int ox = x0, oy = y0;
  45294. for (unsigned int i = 1; i<ipoints._width; ++i) {
  45295. const int x = ipoints(i,0), y = ipoints(i,1);
  45296. draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
  45297. ninit_hatch = false;
  45298. ox = x; oy = y;
  45299. }
  45300. return *this;
  45301. }
  45302. //! Draw a 2D arrow.
  45303. /**
  45304. \param x0 X-coordinate of the starting arrow point (tail).
  45305. \param y0 Y-coordinate of the starting arrow point (tail).
  45306. \param x1 X-coordinate of the ending arrow point (head).
  45307. \param y1 Y-coordinate of the ending arrow point (head).
  45308. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  45309. \param angle Aperture angle of the arrow head.
  45310. \param length Length of the arrow head. If negative, describes a percentage of the arrow length.
  45311. \param opacity Drawing opacity.
  45312. \param pattern An integer whose bits describe the line pattern.
  45313. **/
  45314. template<typename tc>
  45315. CImg<T>& draw_arrow(const int x0, const int y0,
  45316. const int x1, const int y1,
  45317. const tc *const color, const float opacity=1,
  45318. const float angle=30, const float length=-10,
  45319. const unsigned int pattern=~0U) {
  45320. if (is_empty()) return *this;
  45321. const float u = (float)(x0 - x1), v = (float)(y0 - y1), sq = u*u + v*v,
  45322. deg = (float)(angle*cimg::PI/180), ang = (sq>0)?(float)std::atan2(v,u):0.f,
  45323. l = (length>=0)?length:-length*(float)std::sqrt(sq)/100;
  45324. if (sq>0) {
  45325. const float
  45326. cl = (float)std::cos(ang - deg), sl = (float)std::sin(ang - deg),
  45327. cr = (float)std::cos(ang + deg), sr = (float)std::sin(ang + deg);
  45328. const int
  45329. xl = x1 + (int)(l*cl), yl = y1 + (int)(l*sl),
  45330. xr = x1 + (int)(l*cr), yr = y1 + (int)(l*sr),
  45331. xc = x1 + (int)((l + 1)*(cl + cr))/2, yc = y1 + (int)((l + 1)*(sl + sr))/2;
  45332. draw_line(x0,y0,xc,yc,color,opacity,pattern).draw_triangle(x1,y1,xl,yl,xr,yr,color,opacity);
  45333. } else draw_point(x0,y0,color,opacity);
  45334. return *this;
  45335. }
  45336. //! Draw a 2D spline.
  45337. /**
  45338. \param x0 X-coordinate of the starting curve point
  45339. \param y0 Y-coordinate of the starting curve point
  45340. \param u0 X-coordinate of the starting velocity
  45341. \param v0 Y-coordinate of the starting velocity
  45342. \param x1 X-coordinate of the ending curve point
  45343. \param y1 Y-coordinate of the ending curve point
  45344. \param u1 X-coordinate of the ending velocity
  45345. \param v1 Y-coordinate of the ending velocity
  45346. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  45347. \param precision Curve drawing precision.
  45348. \param opacity Drawing opacity.
  45349. \param pattern An integer whose bits describe the line pattern.
  45350. \param init_hatch If \c true, init hatch motif.
  45351. \note
  45352. - The curve is a 2D cubic Bezier spline, from the set of specified starting/ending points
  45353. and corresponding velocity vectors.
  45354. - The spline is drawn as a sequence of connected segments. The \p precision parameter sets the
  45355. average number of pixels in each drawn segment.
  45356. - A cubic Bezier curve is sometimes defined by a set of 4 points { (\p x0,\p y0), (\p xa,\p ya),
  45357. (\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
  45358. and (\p xa,\p ya), (\p xb,\p yb) are two
  45359. \e control points.
  45360. The starting and ending velocities (\p u0,\p v0) and (\p u1,\p v1) can be deduced easily from
  45361. the control points as
  45362. \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).
  45363. \par Example:
  45364. \code
  45365. CImg<unsigned char> img(100,100,1,3,0);
  45366. const unsigned char color[] = { 255,255,255 };
  45367. img.draw_spline(30,30,0,100,90,40,0,-100,color);
  45368. \endcode
  45369. **/
  45370. template<typename tc>
  45371. CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
  45372. const int x1, const int y1, const float u1, const float v1,
  45373. const tc *const color, const float opacity=1,
  45374. const float precision=0.25, const unsigned int pattern=~0U,
  45375. const bool init_hatch=true) {
  45376. if (is_empty()) return *this;
  45377. if (!color)
  45378. throw CImgArgumentException(_cimg_instance
  45379. "draw_spline(): Specified color is (null).",
  45380. cimg_instance);
  45381. if (x0==x1 && y0==y1) return draw_point(x0,y0,color,opacity);
  45382. bool ninit_hatch = init_hatch;
  45383. const float
  45384. ax = u0 + u1 + 2*(x0 - x1),
  45385. bx = 3*(x1 - x0) - 2*u0 - u1,
  45386. ay = v0 + v1 + 2*(y0 - y1),
  45387. by = 3*(y1 - y0) - 2*v0 - v1,
  45388. _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1));
  45389. int ox = x0, oy = y0;
  45390. for (float t = 0; t<1; t+=_precision) {
  45391. const float t2 = t*t, t3 = t2*t;
  45392. const int
  45393. nx = (int)(ax*t3 + bx*t2 + u0*t + x0),
  45394. ny = (int)(ay*t3 + by*t2 + v0*t + y0);
  45395. draw_line(ox,oy,nx,ny,color,opacity,pattern,ninit_hatch);
  45396. ninit_hatch = false;
  45397. ox = nx; oy = ny;
  45398. }
  45399. return draw_line(ox,oy,x1,y1,color,opacity,pattern,false);
  45400. }
  45401. //! Draw a textured 2D spline.
  45402. /**
  45403. \param x0 X-coordinate of the starting curve point
  45404. \param y0 Y-coordinate of the starting curve point
  45405. \param u0 X-coordinate of the starting velocity
  45406. \param v0 Y-coordinate of the starting velocity
  45407. \param x1 X-coordinate of the ending curve point
  45408. \param y1 Y-coordinate of the ending curve point
  45409. \param u1 X-coordinate of the ending velocity
  45410. \param v1 Y-coordinate of the ending velocity
  45411. \param texture Texture image defining line pixel colors.
  45412. \param tx0 X-coordinate of the starting texture point.
  45413. \param ty0 Y-coordinate of the starting texture point.
  45414. \param tx1 X-coordinate of the ending texture point.
  45415. \param ty1 Y-coordinate of the ending texture point.
  45416. \param precision Curve drawing precision.
  45417. \param opacity Drawing opacity.
  45418. \param pattern An integer whose bits describe the line pattern.
  45419. \param init_hatch if \c true, reinit hatch motif.
  45420. **/
  45421. template<typename t>
  45422. CImg<T>& draw_spline(const int x0, const int y0, const float u0, const float v0,
  45423. const int x1, const int y1, const float u1, const float v1,
  45424. const CImg<t>& texture,
  45425. const int tx0, const int ty0, const int tx1, const int ty1,
  45426. const float opacity=1,
  45427. const float precision=4, const unsigned int pattern=~0U,
  45428. const bool init_hatch=true) {
  45429. if (texture._depth>1 || texture._spectrum<_spectrum)
  45430. throw CImgArgumentException(_cimg_instance
  45431. "draw_spline(): Invalid specified texture (%u,%u,%u,%u,%p).",
  45432. cimg_instance,
  45433. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  45434. if (is_empty()) return *this;
  45435. if (is_overlapped(texture))
  45436. return draw_spline(x0,y0,u0,v0,x1,y1,u1,v1,+texture,tx0,ty0,tx1,ty1,precision,opacity,pattern,init_hatch);
  45437. if (x0==x1 && y0==y1)
  45438. return draw_point(x0,y0,texture.get_vector_at(x0<=0?0:x0>=texture.width()?texture.width() - 1:x0,
  45439. y0<=0?0:y0>=texture.height()?texture.height() - 1:y0).data(),
  45440. opacity);
  45441. bool ninit_hatch = init_hatch;
  45442. const float
  45443. ax = u0 + u1 + 2*(x0 - x1),
  45444. bx = 3*(x1 - x0) - 2*u0 - u1,
  45445. ay = v0 + v1 + 2*(y0 - y1),
  45446. by = 3*(y1 - y0) - 2*v0 - v1,
  45447. _precision = 1/(cimg::hypot((float)x0 - x1,(float)y0 - y1)*(precision>0?precision:1));
  45448. int ox = x0, oy = y0, otx = tx0, oty = ty0;
  45449. for (float t1 = 0; t1<1; t1+=_precision) {
  45450. const float t2 = t1*t1, t3 = t2*t1;
  45451. const int
  45452. nx = (int)(ax*t3 + bx*t2 + u0*t1 + x0),
  45453. ny = (int)(ay*t3 + by*t2 + v0*t1 + y0),
  45454. ntx = tx0 + (int)((tx1 - tx0)*t1),
  45455. nty = ty0 + (int)((ty1 - ty0)*t1);
  45456. draw_line(ox,oy,nx,ny,texture,otx,oty,ntx,nty,opacity,pattern,ninit_hatch);
  45457. ninit_hatch = false;
  45458. ox = nx; oy = ny; otx = ntx; oty = nty;
  45459. }
  45460. return draw_line(ox,oy,x1,y1,texture,otx,oty,tx1,ty1,opacity,pattern,false);
  45461. }
  45462. //! Draw a set of consecutive splines.
  45463. /**
  45464. \param points Vertices data.
  45465. \param tangents Tangents data.
  45466. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  45467. \param opacity Drawing opacity.
  45468. \param is_closed_set Tells if the drawn spline set is closed.
  45469. \param precision Precision of the drawing.
  45470. \param pattern An integer whose bits describe the line pattern.
  45471. \param init_hatch If \c true, init hatch motif.
  45472. **/
  45473. template<typename tp, typename tt, typename tc>
  45474. CImg<T>& draw_spline(const CImg<tp>& points, const CImg<tt>& tangents,
  45475. const tc *const color, const float opacity=1,
  45476. const bool is_closed_set=false, const float precision=4,
  45477. const unsigned int pattern=~0U, const bool init_hatch=true) {
  45478. if (is_empty() || !points || !tangents || points._width<2 || tangents._width<2) return *this;
  45479. bool ninit_hatch = init_hatch;
  45480. switch (points._height) {
  45481. case 0 : case 1 :
  45482. throw CImgArgumentException(_cimg_instance
  45483. "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).",
  45484. cimg_instance,
  45485. points._width,points._height,points._depth,points._spectrum,points._data);
  45486. default : {
  45487. const int x0 = (int)points(0,0), y0 = (int)points(0,1);
  45488. const float u0 = (float)tangents(0,0), v0 = (float)tangents(0,1);
  45489. int ox = x0, oy = y0;
  45490. float ou = u0, ov = v0;
  45491. for (unsigned int i = 1; i<points._width; ++i) {
  45492. const int x = (int)points(i,0), y = (int)points(i,1);
  45493. const float u = (float)tangents(i,0), v = (float)tangents(i,1);
  45494. draw_spline(ox,oy,ou,ov,x,y,u,v,color,precision,opacity,pattern,ninit_hatch);
  45495. ninit_hatch = false;
  45496. ox = x; oy = y; ou = u; ov = v;
  45497. }
  45498. if (is_closed_set) draw_spline(ox,oy,ou,ov,x0,y0,u0,v0,color,precision,opacity,pattern,false);
  45499. }
  45500. }
  45501. return *this;
  45502. }
  45503. //! Draw a set of consecutive splines \overloading.
  45504. /**
  45505. Similar to previous function, with the point tangents automatically estimated from the given points set.
  45506. **/
  45507. template<typename tp, typename tc>
  45508. CImg<T>& draw_spline(const CImg<tp>& points,
  45509. const tc *const color, const float opacity=1,
  45510. const bool is_closed_set=false, const float precision=4,
  45511. const unsigned int pattern=~0U, const bool init_hatch=true) {
  45512. if (is_empty() || !points || points._width<2) return *this;
  45513. CImg<Tfloat> tangents;
  45514. switch (points._height) {
  45515. case 0 : case 1 :
  45516. throw CImgArgumentException(_cimg_instance
  45517. "draw_spline(): Invalid specified point set (%u,%u,%u,%u,%p).",
  45518. cimg_instance,
  45519. points._width,points._height,points._depth,points._spectrum,points._data);
  45520. case 2 : {
  45521. tangents.assign(points._width,points._height);
  45522. cimg_forX(points,p) {
  45523. const unsigned int
  45524. p0 = is_closed_set?(p + points.width() - 1)%points.width():(p?p - 1:0),
  45525. p1 = is_closed_set?(p + 1)%points.width():(p + 1<points.width()?p + 1:p);
  45526. const float
  45527. x = (float)points(p,0),
  45528. y = (float)points(p,1),
  45529. x0 = (float)points(p0,0),
  45530. y0 = (float)points(p0,1),
  45531. x1 = (float)points(p1,0),
  45532. y1 = (float)points(p1,1),
  45533. u0 = x - x0,
  45534. v0 = y - y0,
  45535. n0 = 1e-8f + cimg::hypot(u0,v0),
  45536. u1 = x1 - x,
  45537. v1 = y1 - y,
  45538. n1 = 1e-8f + cimg::hypot(u1,v1),
  45539. u = u0/n0 + u1/n1,
  45540. v = v0/n0 + v1/n1,
  45541. n = 1e-8f + cimg::hypot(u,v),
  45542. fact = 0.5f*(n0 + n1);
  45543. tangents(p,0) = (Tfloat)(fact*u/n);
  45544. tangents(p,1) = (Tfloat)(fact*v/n);
  45545. }
  45546. } break;
  45547. default : {
  45548. tangents.assign(points._width,points._height);
  45549. cimg_forX(points,p) {
  45550. const unsigned int
  45551. p0 = is_closed_set?(p + points.width() - 1)%points.width():(p?p - 1:0),
  45552. p1 = is_closed_set?(p + 1)%points.width():(p + 1<points.width()?p + 1:p);
  45553. const float
  45554. x = (float)points(p,0),
  45555. y = (float)points(p,1),
  45556. z = (float)points(p,2),
  45557. x0 = (float)points(p0,0),
  45558. y0 = (float)points(p0,1),
  45559. z0 = (float)points(p0,2),
  45560. x1 = (float)points(p1,0),
  45561. y1 = (float)points(p1,1),
  45562. z1 = (float)points(p1,2),
  45563. u0 = x - x0,
  45564. v0 = y - y0,
  45565. w0 = z - z0,
  45566. n0 = 1e-8f + cimg::hypot(u0,v0,w0),
  45567. u1 = x1 - x,
  45568. v1 = y1 - y,
  45569. w1 = z1 - z,
  45570. n1 = 1e-8f + cimg::hypot(u1,v1,w1),
  45571. u = u0/n0 + u1/n1,
  45572. v = v0/n0 + v1/n1,
  45573. w = w0/n0 + w1/n1,
  45574. n = 1e-8f + cimg::hypot(u,v,w),
  45575. fact = 0.5f*(n0 + n1);
  45576. tangents(p,0) = (Tfloat)(fact*u/n);
  45577. tangents(p,1) = (Tfloat)(fact*v/n);
  45578. tangents(p,2) = (Tfloat)(fact*w/n);
  45579. }
  45580. }
  45581. }
  45582. return draw_spline(points,tangents,color,opacity,is_closed_set,precision,pattern,init_hatch);
  45583. }
  45584. // [internal] Draw a filled triangle.
  45585. template<typename tc>
  45586. CImg<T>& _draw_triangle(int x0, int y0,
  45587. int x1, int y1,
  45588. int x2, int y2,
  45589. const tc *const color, const float opacity,
  45590. const float brightness) {
  45591. if (y0>y1) cimg::swap(x0,x1,y0,y1);
  45592. if (y0>y2) cimg::swap(x0,x2,y0,y2);
  45593. if (y1>y2) cimg::swap(x1,x2,y1,y2);
  45594. if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
  45595. const int
  45596. h1 = height() - 1,
  45597. cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1);
  45598. const longT
  45599. dx01 = (longT)x1 - x0, dx02 = (longT)x2 - x0, dx12 = (longT)x2 - x1,
  45600. dy01 = std::max((longT)1,(longT)y1 - y0),
  45601. dy02 = std::max((longT)1,(longT)y2 - y0),
  45602. dy12 = std::max((longT)1,(longT)y2 - y1),
  45603. hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
  45604. const float cbs = cimg::cut(brightness,0,2);
  45605. cimg_init_scanline(opacity);
  45606. for (int y = cy0; y<=cy2; ++y) {
  45607. const longT yy0 = (longT)y - y0, yy1 = (longT)y - y1;
  45608. longT
  45609. xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
  45610. xM = x0 + (dx02*yy0 + hdy02)/dy02;
  45611. if (xm>xM) cimg::swap(xm,xM);
  45612. cimg_draw_scanline(xm,xM,y,color,opacity,cbs);
  45613. }
  45614. return *this;
  45615. }
  45616. //! Draw a filled 2D triangle.
  45617. /**
  45618. \param x0 X-coordinate of the first vertex.
  45619. \param y0 Y-coordinate of the first vertex.
  45620. \param x1 X-coordinate of the second vertex.
  45621. \param y1 Y-coordinate of the second vertex.
  45622. \param x2 X-coordinate of the third vertex.
  45623. \param y2 Y-coordinate of the third vertex.
  45624. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  45625. \param opacity Drawing opacity.
  45626. **/
  45627. template<typename tc>
  45628. CImg<T>& draw_triangle(const int x0, const int y0,
  45629. const int x1, const int y1,
  45630. const int x2, const int y2,
  45631. const tc *const color, const float opacity=1) {
  45632. if (is_empty()) return *this;
  45633. if (!color)
  45634. throw CImgArgumentException(_cimg_instance
  45635. "draw_triangle(): Specified color is (null).",
  45636. cimg_instance);
  45637. _draw_triangle(x0,y0,x1,y1,x2,y2,color,opacity,1);
  45638. return *this;
  45639. }
  45640. //! Draw a outlined 2D triangle.
  45641. /**
  45642. \param x0 X-coordinate of the first vertex.
  45643. \param y0 Y-coordinate of the first vertex.
  45644. \param x1 X-coordinate of the second vertex.
  45645. \param y1 Y-coordinate of the second vertex.
  45646. \param x2 X-coordinate of the third vertex.
  45647. \param y2 Y-coordinate of the third vertex.
  45648. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  45649. \param opacity Drawing opacity.
  45650. \param pattern An integer whose bits describe the outline pattern.
  45651. **/
  45652. template<typename tc>
  45653. CImg<T>& draw_triangle(const int x0, const int y0,
  45654. const int x1, const int y1,
  45655. const int x2, const int y2,
  45656. const tc *const color, const float opacity,
  45657. const unsigned int pattern) {
  45658. if (is_empty()) return *this;
  45659. if (!color)
  45660. throw CImgArgumentException(_cimg_instance
  45661. "draw_triangle(): Specified color is (null).",
  45662. cimg_instance);
  45663. draw_line(x0,y0,x1,y1,color,opacity,pattern,true).
  45664. draw_line(x1,y1,x2,y2,color,opacity,pattern,false).
  45665. draw_line(x2,y2,x0,y0,color,opacity,pattern,false);
  45666. return *this;
  45667. }
  45668. //! Draw a filled 2D triangle, with z-buffering.
  45669. /**
  45670. \param zbuffer Z-buffer image.
  45671. \param x0 X-coordinate of the first vertex.
  45672. \param y0 Y-coordinate of the first vertex.
  45673. \param z0 Z-coordinate of the first vertex.
  45674. \param x1 X-coordinate of the second vertex.
  45675. \param y1 Y-coordinate of the second vertex.
  45676. \param z1 Z-coordinate of the second vertex.
  45677. \param x2 X-coordinate of the third vertex.
  45678. \param y2 Y-coordinate of the third vertex.
  45679. \param z2 Z-coordinate of the third vertex.
  45680. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  45681. \param opacity Drawing opacity.
  45682. \param brightness Brightness factor.
  45683. **/
  45684. template<typename tz, typename tc>
  45685. CImg<T>& draw_triangle(CImg<tz>& zbuffer,
  45686. int x0, int y0, const float z0,
  45687. int x1, int y1, const float z1,
  45688. int x2, int y2, const float z2,
  45689. const tc *const color, const float opacity=1,
  45690. const float brightness=1) {
  45691. if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
  45692. if (!color)
  45693. throw CImgArgumentException(_cimg_instance
  45694. "draw_triangle(): Specified color is (null).",
  45695. cimg_instance);
  45696. if (!is_sameXY(zbuffer))
  45697. throw CImgArgumentException(_cimg_instance
  45698. "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
  45699. "different dimensions.",
  45700. cimg_instance,
  45701. zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
  45702. float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
  45703. if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1);
  45704. if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2);
  45705. if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2);
  45706. if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
  45707. const int w1 = width() - 1, h1 = height() - 1, cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1);
  45708. const longT
  45709. dx01 = (longT)x1 - x0, dx02 = (longT)x2 - x0, dx12 = (longT)x2 - x1,
  45710. dy01 = std::max((longT)1,(longT)y1 - y0),
  45711. dy02 = std::max((longT)1,(longT)y2 - y0),
  45712. dy12 = std::max((longT)1,(longT)y2 - y1),
  45713. hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
  45714. const float diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1;
  45715. const float cbs = cimg::cut(brightness,0,2);
  45716. cimg_init_scanline(opacity);
  45717. for (int y = cy0; y<=cy2; ++y) {
  45718. const longT yy0 = (longT)y - y0, yy1 = (longT)y - y1;
  45719. longT
  45720. xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
  45721. xM = x0 + (dx02*yy0 + hdy02)/dy02;
  45722. float
  45723. izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
  45724. izM = iz0 + diz02*yy0/dy02;
  45725. if (xm>xM) cimg::swap(xm,xM,izm,izM);
  45726. if (xM>=0 && xm<=w1) {
  45727. const int
  45728. cxm = (int)cimg::cut(xm,(longT)0,(longT)w1),
  45729. cxM = (int)cimg::cut(xM,(longT)0,(longT)w1);
  45730. T *ptrd = data(cxm,y);
  45731. tz *ptrz = zbuffer.data(cxm,y);
  45732. const longT dxmM = std::max((longT)1,xM - xm);
  45733. const float dizmM = izM - izm;
  45734. for (int x = cxm; x<=cxM; ++x) {
  45735. const longT xxm = x - xm;
  45736. const float iz = izm + dizmM*xxm/dxmM;
  45737. if (iz>=*ptrz) {
  45738. *ptrz = (tz)iz;
  45739. cimg_forC(*this,c) {
  45740. const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval;
  45741. ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
  45742. }
  45743. }
  45744. ++ptrd; ++ptrz;
  45745. }
  45746. }
  45747. }
  45748. return *this;
  45749. }
  45750. //! Draw a Gouraud-shaded 2D triangle.
  45751. /**
  45752. \param x0 X-coordinate of the first vertex in the image instance.
  45753. \param y0 Y-coordinate of the first vertex in the image instance.
  45754. \param x1 X-coordinate of the second vertex in the image instance.
  45755. \param y1 Y-coordinate of the second vertex in the image instance.
  45756. \param x2 X-coordinate of the third vertex in the image instance.
  45757. \param y2 Y-coordinate of the third vertex in the image instance.
  45758. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  45759. \param bs0 Brightness factor of the first vertex (in [0,2]).
  45760. \param bs1 brightness factor of the second vertex (in [0,2]).
  45761. \param bs2 brightness factor of the third vertex (in [0,2]).
  45762. \param opacity Drawing opacity.
  45763. **/
  45764. template<typename tc>
  45765. CImg<T>& draw_triangle(int x0, int y0,
  45766. int x1, int y1,
  45767. int x2, int y2,
  45768. const tc *const color,
  45769. float bs0,
  45770. float bs1,
  45771. float bs2,
  45772. const float opacity=1) {
  45773. if (is_empty()) return *this;
  45774. if (!color)
  45775. throw CImgArgumentException(_cimg_instance
  45776. "draw_triangle(): Specified color is (null).",
  45777. cimg_instance);
  45778. if (y0>y1) cimg::swap(x0,x1,y0,y1,bs0,bs1);
  45779. if (y0>y2) cimg::swap(x0,x2,y0,y2,bs0,bs2);
  45780. if (y1>y2) cimg::swap(x1,x2,y1,y2,bs1,bs2);
  45781. if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
  45782. const int w1 = width() - 1, h1 = height() - 1, cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1);
  45783. const longT
  45784. dx01 = (longT)x1 - x0, dx02 = (longT)x2 - x0, dx12 = (longT)x2 - x1,
  45785. dy01 = std::max((longT)1,(longT)y1 - y0),
  45786. dy02 = std::max((longT)1,(longT)y2 - y0),
  45787. dy12 = std::max((longT)1,(longT)y2 - y1),
  45788. hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
  45789. const float dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
  45790. cimg_init_scanline(opacity);
  45791. for (int y = cy0; y<=cy2; ++y) {
  45792. const longT yy0 = (longT)y - y0, yy1 = (longT)y - y1;
  45793. longT
  45794. xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
  45795. xM = x0 + (dx02*yy0 + hdy02)/dy02;
  45796. float
  45797. bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
  45798. bsM = bs0 + dbs02*yy0/dy02;
  45799. if (xm>xM) cimg::swap(xm,xM,bsm,bsM);
  45800. if (xM>=0 && xm<=w1) {
  45801. const int
  45802. cxm = (int)cimg::cut(xm,(longT)0,(longT)w1),
  45803. cxM = (int)cimg::cut(xM,(longT)0,(longT)w1);
  45804. T *ptrd = data(cxm,y);
  45805. const longT dxmM = std::max((longT)1,xM - xm);
  45806. const float dbsmM = bsM - bsm;
  45807. for (int x = cxm; x<=cxM; ++x) {
  45808. const longT xxm = (longT)x - xm;
  45809. const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
  45810. cimg_forC(*this,c) {
  45811. const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval;
  45812. ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
  45813. }
  45814. ++ptrd;
  45815. }
  45816. }
  45817. }
  45818. return *this;
  45819. }
  45820. //! Draw a Gouraud-shaded 2D triangle, with z-buffering \overloading.
  45821. template<typename tz, typename tc>
  45822. CImg<T>& draw_triangle(CImg<tz>& zbuffer,
  45823. int x0, int y0, const float z0,
  45824. int x1, int y1, const float z1,
  45825. int x2, int y2, const float z2,
  45826. const tc *const color,
  45827. float bs0,
  45828. float bs1,
  45829. float bs2,
  45830. float opacity=1) {
  45831. if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
  45832. if (!color)
  45833. throw CImgArgumentException(_cimg_instance
  45834. "draw_triangle(): Specified color is (null).",
  45835. cimg_instance);
  45836. if (!is_sameXY(zbuffer))
  45837. throw CImgArgumentException(_cimg_instance
  45838. "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
  45839. "different dimensions.",
  45840. cimg_instance,
  45841. zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
  45842. float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
  45843. if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,bs0,bs1);
  45844. if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,bs0,bs2);
  45845. if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,bs1,bs2);
  45846. if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
  45847. const int
  45848. w1 = width() - 1, h1 = height() - 1,
  45849. dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
  45850. dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
  45851. cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
  45852. hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
  45853. const float
  45854. diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
  45855. dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
  45856. cimg_init_scanline(opacity);
  45857. for (int y = cy0; y<=cy2; ++y) {
  45858. const int yy0 = y - y0, yy1 = y - y1;
  45859. int
  45860. xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
  45861. xM = x0 + (dx02*yy0 + hdy02)/dy02;
  45862. float
  45863. izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
  45864. izM = iz0 + diz02*yy0/dy02,
  45865. bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
  45866. bsM = bs0 + dbs02*yy0/dy02;
  45867. if (xm>xM) cimg::swap(xm,xM,izm,izM,bsm,bsM);
  45868. if (xM>=0 && xm<=w1) {
  45869. const int
  45870. cxm = cimg::cut(xm,0,w1),
  45871. cxM = cimg::cut(xM,0,w1);
  45872. T *ptrd = data(cxm,y);
  45873. tz *ptrz = zbuffer.data(cxm,y);
  45874. const int dxmM = std::max(1,xM - xm);
  45875. const float dizmM = izM - izm, dbsmM = bsM - bsm;
  45876. for (int x = cxm; x<=cxM; ++x) {
  45877. const int xxm = x - xm;
  45878. const float iz = izm + dizmM*xxm/dxmM;
  45879. if (iz>=*ptrz) {
  45880. *ptrz = (tz)iz;
  45881. const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
  45882. cimg_forC(*this,c) {
  45883. const Tfloat val = cbs<=1?color[c]*cbs:(2 - cbs)*color[c] + (cbs - 1)*_sc_maxval;
  45884. ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
  45885. }
  45886. }
  45887. ++ptrd; ++ptrz;
  45888. }
  45889. }
  45890. }
  45891. return *this;
  45892. }
  45893. //! Draw a color-interpolated 2D triangle.
  45894. /**
  45895. \param x0 X-coordinate of the first vertex in the image instance.
  45896. \param y0 Y-coordinate of the first vertex in the image instance.
  45897. \param x1 X-coordinate of the second vertex in the image instance.
  45898. \param y1 Y-coordinate of the second vertex in the image instance.
  45899. \param x2 X-coordinate of the third vertex in the image instance.
  45900. \param y2 Y-coordinate of the third vertex in the image instance.
  45901. \param color1 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the first vertex.
  45902. \param color2 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the second vertex.
  45903. \param color3 Pointer to \c spectrum() consecutive values of type \c T, defining the color of the third vertex.
  45904. \param opacity Drawing opacity.
  45905. **/
  45906. template<typename tc>
  45907. CImg<T>& draw_triangle(int x0, int y0,
  45908. int x1, int y1,
  45909. int x2, int y2,
  45910. const tc *color0,
  45911. const tc *color1,
  45912. const tc *color2,
  45913. const float opacity=1) {
  45914. typedef typename cimg::superset<tc,int>::type stc;
  45915. if (is_empty()) return *this;
  45916. if (!color0 || !color1 || !color2)
  45917. throw CImgArgumentException(_cimg_instance
  45918. "draw_triangle(): One of the specified color is (null).",
  45919. cimg_instance);
  45920. if (y0>y1) cimg::swap(x0,x1,y0,y1,color0,color1);
  45921. if (y0>y2) cimg::swap(x0,x2,y0,y2,color0,color2);
  45922. if (y1>y2) cimg::swap(x1,x2,y1,y2,color1,color2);
  45923. if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
  45924. const int w1 = width() - 1, h1 = height() - 1, cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1);
  45925. const longT
  45926. dx01 = (longT)x1 - x0, dx02 = (longT)x2 - x0, dx12 = (longT)x2 - x1,
  45927. dy01 = std::max((longT)1,(longT)y1 - y0),
  45928. dy02 = std::max((longT)1,(longT)y2 - y0),
  45929. dy12 = std::max((longT)1,(longT)y2 - y1),
  45930. hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
  45931. cimg_init_scanline(opacity);
  45932. cimg_forC(*this,c) {
  45933. const stc dcolor01 = color1[c] - color0[c], dcolor02 = color2[c] - color0[c], dcolor12 = color2[c] - color1[c];
  45934. for (int y = cy0; y<=cy2; ++y) {
  45935. const longT yy0 = (longT)y - y0, yy1 = (longT)y - y1;
  45936. longT
  45937. xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
  45938. xM = x0 + (dx02*yy0 + hdy02)/dy02;
  45939. stc
  45940. colorm = y<y1?(color0[c] + dcolor01*yy0/dy01):(color1[c] + dcolor12*yy1/dy12),
  45941. colorM = color0[c] + dcolor02*yy0/dy02;
  45942. if (xm>xM) cimg::swap(xm,xM,colorm,colorM);
  45943. if (xM>=0 && xm<=w1) {
  45944. const int
  45945. cxm = (int)cimg::cut(xm,(longT)0,(longT)w1),
  45946. cxM = (int)cimg::cut(xM,(longT)0,(longT)w1);
  45947. T *ptrd = data(cxm,y);
  45948. const longT dxmM = std::max((longT)1,xM - xm);
  45949. const stc dcolormM = colorM - colorm;
  45950. for (int x = cxm; x<=cxM; ++x) {
  45951. const longT xxm = (longT)x - xm;
  45952. const stc col = colorm + dcolormM*xxm/dxmM;
  45953. ptrd[c*_sc_whd] = (T)(opacity>=1?col:col*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
  45954. ++ptrd;
  45955. }
  45956. }
  45957. }
  45958. }
  45959. return *this;
  45960. }
  45961. //! Draw a textured 2D triangle.
  45962. /**
  45963. \param x0 X-coordinate of the first vertex in the image instance.
  45964. \param y0 Y-coordinate of the first vertex in the image instance.
  45965. \param x1 X-coordinate of the second vertex in the image instance.
  45966. \param y1 Y-coordinate of the second vertex in the image instance.
  45967. \param x2 X-coordinate of the third vertex in the image instance.
  45968. \param y2 Y-coordinate of the third vertex in the image instance.
  45969. \param texture Texture image used to fill the triangle.
  45970. \param tx0 X-coordinate of the first vertex in the texture image.
  45971. \param ty0 Y-coordinate of the first vertex in the texture image.
  45972. \param tx1 X-coordinate of the second vertex in the texture image.
  45973. \param ty1 Y-coordinate of the second vertex in the texture image.
  45974. \param tx2 X-coordinate of the third vertex in the texture image.
  45975. \param ty2 Y-coordinate of the third vertex in the texture image.
  45976. \param opacity Drawing opacity.
  45977. \param brightness Brightness factor of the drawing (in [0,2]).
  45978. **/
  45979. template<typename tc>
  45980. CImg<T>& draw_triangle(int x0, int y0,
  45981. int x1, int y1,
  45982. int x2, int y2,
  45983. const CImg<tc>& texture,
  45984. int tx0, int ty0,
  45985. int tx1, int ty1,
  45986. int tx2, int ty2,
  45987. const float opacity=1,
  45988. const float brightness=1) {
  45989. if (is_empty()) return *this;
  45990. if (texture._depth>1 || texture._spectrum<_spectrum)
  45991. throw CImgArgumentException(_cimg_instance
  45992. "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
  45993. cimg_instance,
  45994. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  45995. if (is_overlapped(texture))
  45996. return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
  45997. if (y0>y1) cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1);
  45998. if (y0>y2) cimg::swap(x0,x2,y0,y2,tx0,tx2,ty0,ty2);
  45999. if (y1>y2) cimg::swap(x1,x2,y1,y2,tx1,ty1,tx2,ty2);
  46000. if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
  46001. const int
  46002. w1 = width() - 1, h1 = height() - 1,
  46003. dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
  46004. dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
  46005. cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
  46006. hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
  46007. dtx01 = tx1 - tx0, dtx02 = tx2 - tx0, dtx12 = tx2 - tx1,
  46008. dty01 = ty1 - ty0, dty02 = ty2 - ty0, dty12 = ty2 - ty1,
  46009. hdy01tx = dy01*cimg::sign(dtx01)/2, hdy02tx = dy02*cimg::sign(dtx02)/2, hdy12tx = dy12*cimg::sign(dtx12)/2,
  46010. hdy01ty = dy01*cimg::sign(dty01)/2, hdy02ty = dy02*cimg::sign(dty02)/2, hdy12ty = dy12*cimg::sign(dty12)/2;
  46011. const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
  46012. const float cbs = cimg::cut(brightness,0,2);
  46013. cimg_init_scanline(opacity);
  46014. for (int y = cy0; y<=cy2; ++y) {
  46015. const int yy0 = y - y0, yy1 = y - y1;
  46016. int
  46017. xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
  46018. xM = x0 + (dx02*yy0 + hdy02)/dy02,
  46019. txm = y<y1?tx0 + (dtx01*yy0 + hdy01tx)/dy01:tx1 + (dtx12*yy1 + hdy12tx)/dy12,
  46020. txM = tx0 + (dtx02*yy0 + hdy02tx)/dy02,
  46021. tym = y<y1?ty0 + (dty01*yy0 + hdy01ty)/dy01:ty1 + (dty12*yy1 + hdy12ty)/dy12,
  46022. tyM = ty0 + (dty02*yy0 + hdy02ty)/dy02;
  46023. if (xm>xM) cimg::swap(xm,xM,txm,txM,tym,tyM);
  46024. if (xM>=0 && xm<=w1) {
  46025. const int
  46026. cxm = cimg::cut(xm,0,w1),
  46027. cxM = cimg::cut(xM,0,w1);
  46028. T *ptrd = data(cxm,y);
  46029. const int
  46030. dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
  46031. dtxmM = txM - txm, dtymM = tyM - tym;
  46032. for (int x = cxm; x<=cxM; ++x) {
  46033. const int
  46034. xxm = x - xm,
  46035. tx = (txm*dxmM + dtxmM*xxm + hdxmM)/dxmM,
  46036. ty = (tym*dxmM + dtymM*xxm + hdxmM)/dxmM;
  46037. const tc *const color = &texture._atXY(tx,ty);
  46038. cimg_forC(*this,c) {
  46039. const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval;
  46040. ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
  46041. }
  46042. ++ptrd;
  46043. }
  46044. }
  46045. }
  46046. return *this;
  46047. }
  46048. //! Draw a 2D textured triangle, with perspective correction.
  46049. template<typename tc>
  46050. CImg<T>& draw_triangle(int x0, int y0, const float z0,
  46051. int x1, int y1, const float z1,
  46052. int x2, int y2, const float z2,
  46053. const CImg<tc>& texture,
  46054. int tx0, int ty0,
  46055. int tx1, int ty1,
  46056. int tx2, int ty2,
  46057. const float opacity=1,
  46058. const float brightness=1) {
  46059. if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
  46060. if (texture._depth>1 || texture._spectrum<_spectrum)
  46061. throw CImgArgumentException(_cimg_instance
  46062. "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
  46063. cimg_instance,
  46064. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  46065. if (is_overlapped(texture))
  46066. return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
  46067. float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
  46068. if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1);
  46069. if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2);
  46070. if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2);
  46071. if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
  46072. const int
  46073. w1 = width() - 1, h1 = height() - 1,
  46074. dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
  46075. dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
  46076. cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
  46077. hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
  46078. const float
  46079. diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
  46080. txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
  46081. tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
  46082. dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
  46083. dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1;
  46084. const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
  46085. const float cbs = cimg::cut(brightness,0,2);
  46086. cimg_init_scanline(opacity);
  46087. for (int y = cy0; y<=cy2; ++y) {
  46088. const int yy0 = y - y0, yy1 = y - y1;
  46089. int
  46090. xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
  46091. xM = x0 + (dx02*yy0 + hdy02)/dy02;
  46092. float
  46093. izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
  46094. izM = iz0 + diz02*yy0/dy02,
  46095. txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
  46096. txzM = txz0 + dtxz02*yy0/dy02,
  46097. tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
  46098. tyzM = tyz0 + dtyz02*yy0/dy02;
  46099. if (xm>xM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM);
  46100. if (xM>=0 && xm<=w1) {
  46101. const int
  46102. cxm = cimg::cut(xm,0,w1),
  46103. cxM = cimg::cut(xM,0,w1);
  46104. T *ptrd = data(cxm,y);
  46105. const int dxmM = std::max(1,xM - xm);
  46106. const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm;
  46107. for (int x = cxm; x<=cxM; ++x) {
  46108. const int xxm = x - xm;
  46109. const float
  46110. iz = izm + dizmM*xxm/dxmM,
  46111. txz = txzm + dtxzmM*xxm/dxmM,
  46112. tyz = tyzm + dtyzmM*xxm/dxmM;
  46113. const int
  46114. tx = (int)cimg::round(txz/iz),
  46115. ty = (int)cimg::round(tyz/iz);
  46116. const tc *const color = &texture._atXY(tx,ty);
  46117. cimg_forC(*this,c) {
  46118. const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval;
  46119. ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
  46120. }
  46121. ++ptrd;
  46122. }
  46123. }
  46124. }
  46125. return *this;
  46126. }
  46127. //! Draw a textured 2D triangle, with perspective correction and z-buffering.
  46128. template<typename tz, typename tc>
  46129. CImg<T>& draw_triangle(CImg<tz>& zbuffer,
  46130. int x0, int y0, const float z0,
  46131. int x1, int y1, const float z1,
  46132. int x2, int y2, const float z2,
  46133. const CImg<tc>& texture,
  46134. int tx0, int ty0,
  46135. int tx1, int ty1,
  46136. int tx2, int ty2,
  46137. const float opacity=1,
  46138. const float brightness=1) {
  46139. if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
  46140. if (!is_sameXY(zbuffer))
  46141. throw CImgArgumentException(_cimg_instance
  46142. "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
  46143. "different dimensions.",
  46144. cimg_instance,
  46145. zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
  46146. if (texture._depth>1 || texture._spectrum<_spectrum)
  46147. throw CImgArgumentException(_cimg_instance
  46148. "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
  46149. cimg_instance,
  46150. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  46151. if (is_overlapped(texture))
  46152. return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,opacity,brightness);
  46153. float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
  46154. if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1);
  46155. if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2);
  46156. if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2);
  46157. if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
  46158. const int
  46159. w1 = width() - 1, h1 = height() - 1,
  46160. dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
  46161. dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
  46162. cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
  46163. hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
  46164. const float
  46165. diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
  46166. txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
  46167. tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
  46168. dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
  46169. dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1;
  46170. const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
  46171. const float cbs = cimg::cut(brightness,0,2);
  46172. cimg_init_scanline(opacity);
  46173. for (int y = cy0; y<=cy2; ++y) {
  46174. const int yy0 = y - y0, yy1 = y - y1;
  46175. int
  46176. xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
  46177. xM = x0 + (dx02*yy0 + hdy02)/dy02;
  46178. float
  46179. izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
  46180. izM = iz0 + diz02*yy0/dy02,
  46181. txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
  46182. txzM = txz0 + dtxz02*yy0/dy02,
  46183. tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
  46184. tyzM = tyz0 + dtyz02*yy0/dy02;
  46185. if (xm>xM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM);
  46186. if (xM>=0 && xm<=w1) {
  46187. const int
  46188. cxm = cimg::cut(xm,0,w1),
  46189. cxM = cimg::cut(xM,0,w1);
  46190. T *ptrd = data(cxm,y);
  46191. tz *ptrz = zbuffer.data(cxm,y);
  46192. const int dxmM = std::max(1,xM - xm);
  46193. const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm;
  46194. for (int x = cxm; x<=cxM; ++x) {
  46195. const int xxm = x - xm;
  46196. const float iz = izm + dizmM*xxm/dxmM;
  46197. if (iz>=*ptrz) {
  46198. *ptrz = (tz)iz;
  46199. const float
  46200. txz = txzm + dtxzmM*xxm/dxmM,
  46201. tyz = tyzm + dtyzmM*xxm/dxmM;
  46202. const int
  46203. tx = (int)cimg::round(txz/iz),
  46204. ty = (int)cimg::round(tyz/iz);
  46205. const tc *const color = &texture._atXY(tx,ty);
  46206. cimg_forC(*this,c) {
  46207. const Tfloat val = cbs<=1?color[c*twhd]*cbs:(2 - cbs)*color[c*twhd] + (cbs - 1)*_sc_maxval;
  46208. ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
  46209. }
  46210. }
  46211. ++ptrd; ++ptrz;
  46212. }
  46213. }
  46214. }
  46215. return *this;
  46216. }
  46217. //! Draw a Phong-shaded 2D triangle.
  46218. /**
  46219. \param x0 X-coordinate of the first vertex in the image instance.
  46220. \param y0 Y-coordinate of the first vertex in the image instance.
  46221. \param x1 X-coordinate of the second vertex in the image instance.
  46222. \param y1 Y-coordinate of the second vertex in the image instance.
  46223. \param x2 X-coordinate of the third vertex in the image instance.
  46224. \param y2 Y-coordinate of the third vertex in the image instance.
  46225. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  46226. \param light Light image.
  46227. \param lx0 X-coordinate of the first vertex in the light image.
  46228. \param ly0 Y-coordinate of the first vertex in the light image.
  46229. \param lx1 X-coordinate of the second vertex in the light image.
  46230. \param ly1 Y-coordinate of the second vertex in the light image.
  46231. \param lx2 X-coordinate of the third vertex in the light image.
  46232. \param ly2 Y-coordinate of the third vertex in the light image.
  46233. \param opacity Drawing opacity.
  46234. **/
  46235. template<typename tc, typename tl>
  46236. CImg<T>& draw_triangle(int x0, int y0,
  46237. int x1, int y1,
  46238. int x2, int y2,
  46239. const tc *const color,
  46240. const CImg<tl>& light,
  46241. int lx0, int ly0,
  46242. int lx1, int ly1,
  46243. int lx2, int ly2,
  46244. const float opacity=1) {
  46245. if (is_empty()) return *this;
  46246. if (!color)
  46247. throw CImgArgumentException(_cimg_instance
  46248. "draw_triangle(): Specified color is (null).",
  46249. cimg_instance);
  46250. if (light._depth>1 || light._spectrum<_spectrum)
  46251. throw CImgArgumentException(_cimg_instance
  46252. "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
  46253. cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
  46254. if (y0>y1) cimg::swap(x0,x1,y0,y1,lx0,lx1,ly0,ly1);
  46255. if (y0>y2) cimg::swap(x0,x2,y0,y2,lx0,lx2,ly0,ly2);
  46256. if (y1>y2) cimg::swap(x1,x2,y1,y2,lx1,lx2,ly1,ly2);
  46257. if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
  46258. const int
  46259. w1 = width() - 1, h1 = height() - 1,
  46260. dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
  46261. dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
  46262. cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
  46263. hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
  46264. dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1,
  46265. dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1,
  46266. hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2,
  46267. hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2;
  46268. const ulongT lwhd = (ulongT)light._width*light._height*light._depth;
  46269. cimg_init_scanline(opacity);
  46270. for (int y = cy0; y<=cy2; ++y) {
  46271. const int yy0 = y - y0, yy1 = y - y1;
  46272. int
  46273. xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
  46274. xM = x0 + (dx02*yy0 + hdy02)/dy02,
  46275. lxm = y<y1?lx0 + (dlx01*yy0 + hdy01lx)/dy01:lx1 + (dlx12*yy1 + hdy12lx)/dy12,
  46276. lxM = lx0 + (dlx02*yy0 + hdy02lx)/dy02,
  46277. lym = y<y1?ly0 + (dly01*yy0 + hdy01ly)/dy01:ly1 + (dly12*yy1 + hdy12ly)/dy12,
  46278. lyM = ly0 + (dly02*yy0 + hdy02ly)/dy02;
  46279. if (xm>xM) cimg::swap(xm,xM,lxm,lxM,lym,lyM);
  46280. if (xM>=0 && xm<=w1) {
  46281. const int
  46282. cxm = cimg::cut(xm,0,w1),
  46283. cxM = cimg::cut(xM,0,w1);
  46284. T *ptrd = data(cxm,y);
  46285. const int
  46286. dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
  46287. dlxmM = lxM - lxm, dlymM = lyM - lym;
  46288. for (int x = cxm; x<=cxM; ++x) {
  46289. const int
  46290. xxm = x - xm,
  46291. lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM,
  46292. ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM;
  46293. const tl *const lig = &light._atXY(lx,ly);
  46294. cimg_forC(*this,c) {
  46295. const tc col = color[c];
  46296. const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
  46297. const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
  46298. ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
  46299. }
  46300. ++ptrd;
  46301. }
  46302. }
  46303. }
  46304. return *this;
  46305. }
  46306. //! Draw a Phong-shaded 2D triangle, with z-buffering.
  46307. template<typename tz, typename tc, typename tl>
  46308. CImg<T>& draw_triangle(CImg<tz>& zbuffer,
  46309. int x0, int y0, const float z0,
  46310. int x1, int y1, const float z1,
  46311. int x2, int y2, const float z2,
  46312. const tc *const color,
  46313. const CImg<tl>& light,
  46314. int lx0, int ly0,
  46315. int lx1, int ly1,
  46316. int lx2, int ly2,
  46317. const float opacity=1) {
  46318. if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
  46319. if (!color)
  46320. throw CImgArgumentException(_cimg_instance
  46321. "draw_triangle(): Specified color is (null).",
  46322. cimg_instance);
  46323. if (light._depth>1 || light._spectrum<_spectrum)
  46324. throw CImgArgumentException(_cimg_instance
  46325. "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
  46326. cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
  46327. if (!is_sameXY(zbuffer))
  46328. throw CImgArgumentException(_cimg_instance
  46329. "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
  46330. "different dimensions.",
  46331. cimg_instance,
  46332. zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
  46333. if (is_overlapped(light)) return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,
  46334. +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
  46335. float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
  46336. if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,lx0,lx1,ly0,ly1);
  46337. if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,lx0,lx2,ly0,ly2);
  46338. if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,lx1,lx2,ly1,ly2);
  46339. if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
  46340. const int
  46341. w1 = width() - 1, h1 = height() - 1,
  46342. dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
  46343. dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
  46344. cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
  46345. hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
  46346. dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1,
  46347. dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1,
  46348. hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2,
  46349. hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2;
  46350. const float diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1;
  46351. const ulongT lwhd = (ulongT)light._width*light._height*light._depth;
  46352. cimg_init_scanline(opacity);
  46353. for (int y = cy0; y<=cy2; ++y) {
  46354. const int yy0 = y - y0, yy1 = y - y1;
  46355. int
  46356. xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
  46357. xM = x0 + (dx02*yy0 + hdy02)/dy02,
  46358. lxm = y<y1?lx0 + (dlx01*yy0 + hdy01lx)/dy01:lx1 + (dlx12*yy1 + hdy12lx)/dy12,
  46359. lxM = lx0 + (dlx02*yy0 + hdy02lx)/dy02,
  46360. lym = y<y1?ly0 + (dly01*yy0 + hdy01ly)/dy01:ly1 + (dly12*yy1 + hdy12ly)/dy12,
  46361. lyM = ly0 + (dly02*yy0 + hdy02ly)/dy02;
  46362. float
  46363. izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
  46364. izM = iz0 + diz02*yy0/dy02;
  46365. if (xm>xM) cimg::swap(xm,xM,lxm,lxM,lym,lyM,izm,izM);
  46366. if (xM>=0 && xm<=w1) {
  46367. const int
  46368. cxm = cimg::cut(xm,0,w1),
  46369. cxM = cimg::cut(xM,0,w1);
  46370. T *ptrd = data(cxm,y);
  46371. tz *ptrz = zbuffer.data(cxm,y);
  46372. const int
  46373. dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
  46374. dlxmM = lxM - lxm, dlymM = lyM - lym;
  46375. const float dizmM = izM - izm;
  46376. for (int x = cxm; x<=cxM; ++x) {
  46377. const int xxm = x - xm;
  46378. const float iz = izm + dizmM*xxm/dxmM;
  46379. if (iz>=*ptrz) {
  46380. *ptrz = (tz)iz;
  46381. const int
  46382. lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM,
  46383. ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM;
  46384. const tl *const lig = &light._atXY(lx,ly);
  46385. cimg_forC(*this,c) {
  46386. const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
  46387. const tc col = color[c];
  46388. const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
  46389. ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
  46390. }
  46391. }
  46392. ++ptrd; ++ptrz;
  46393. }
  46394. }
  46395. }
  46396. return *this;
  46397. }
  46398. //! Draw a textured Gouraud-shaded 2D triangle.
  46399. /**
  46400. \param x0 X-coordinate of the first vertex in the image instance.
  46401. \param y0 Y-coordinate of the first vertex in the image instance.
  46402. \param x1 X-coordinate of the second vertex in the image instance.
  46403. \param y1 Y-coordinate of the second vertex in the image instance.
  46404. \param x2 X-coordinate of the third vertex in the image instance.
  46405. \param y2 Y-coordinate of the third vertex in the image instance.
  46406. \param texture Texture image used to fill the triangle.
  46407. \param tx0 X-coordinate of the first vertex in the texture image.
  46408. \param ty0 Y-coordinate of the first vertex in the texture image.
  46409. \param tx1 X-coordinate of the second vertex in the texture image.
  46410. \param ty1 Y-coordinate of the second vertex in the texture image.
  46411. \param tx2 X-coordinate of the third vertex in the texture image.
  46412. \param ty2 Y-coordinate of the third vertex in the texture image.
  46413. \param bs0 Brightness factor of the first vertex.
  46414. \param bs1 Brightness factor of the second vertex.
  46415. \param bs2 Brightness factor of the third vertex.
  46416. \param opacity Drawing opacity.
  46417. **/
  46418. template<typename tc>
  46419. CImg<T>& draw_triangle(int x0, int y0,
  46420. int x1, int y1,
  46421. int x2, int y2,
  46422. const CImg<tc>& texture,
  46423. int tx0, int ty0,
  46424. int tx1, int ty1,
  46425. int tx2, int ty2,
  46426. float bs0,
  46427. float bs1,
  46428. float bs2,
  46429. const float opacity=1) {
  46430. if (is_empty()) return *this;
  46431. if (texture._depth>1 || texture._spectrum<_spectrum)
  46432. throw CImgArgumentException(_cimg_instance
  46433. "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
  46434. cimg_instance,
  46435. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  46436. if (is_overlapped(texture))
  46437. return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
  46438. bs0,bs1,bs2,opacity);
  46439. if (y0>y1) cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1,bs0,bs1);
  46440. if (y0>y2) cimg::swap(x0,x2,y0,y2,tx0,tx2,ty0,ty2,bs0,bs2);
  46441. if (y1>y2) cimg::swap(x1,x2,y1,y2,tx1,tx2,ty1,ty2,bs1,bs2);
  46442. if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
  46443. const int
  46444. w1 = width() - 1, h1 = height() - 1,
  46445. dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
  46446. dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
  46447. cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
  46448. hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
  46449. dtx01 = tx1 - tx0, dtx02 = tx2 - tx0, dtx12 = tx2 - tx1,
  46450. dty01 = ty1 - ty0, dty02 = ty2 - ty0, dty12 = ty2 - ty1,
  46451. hdy01tx = dy01*cimg::sign(dtx01)/2, hdy02tx = dy02*cimg::sign(dtx02)/2, hdy12tx = dy12*cimg::sign(dtx12)/2,
  46452. hdy01ty = dy01*cimg::sign(dty01)/2, hdy02ty = dy02*cimg::sign(dty02)/2, hdy12ty = dy12*cimg::sign(dty12)/2;
  46453. const float dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
  46454. const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
  46455. cimg_init_scanline(opacity);
  46456. for (int y = cy0; y<=cy2; ++y) {
  46457. const int yy0 = y - y0, yy1 = y - y1;
  46458. int
  46459. xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
  46460. xM = x0 + (dx02*yy0 + hdy02)/dy02,
  46461. txm = y<y1?tx0 + (dtx01*yy0 + hdy01tx)/dy01:tx1 + (dtx12*yy1 + hdy12tx)/dy12,
  46462. txM = tx0 + (dtx02*yy0 + hdy02tx)/dy02,
  46463. tym = y<y1?ty0 + (dty01*yy0 + hdy01ty)/dy01:ty1 + (dty12*yy1 + hdy12ty)/dy12,
  46464. tyM = ty0 + (dty02*yy0 + hdy02ty)/dy02;
  46465. float
  46466. bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
  46467. bsM = bs0 + dbs02*yy0/dy02;
  46468. if (xm>xM) cimg::swap(xm,xM,txm,txM,tym,tyM,bsm,bsM);
  46469. if (xM>=0 && xm<=w1) {
  46470. const int
  46471. cxm = cimg::cut(xm,0,w1),
  46472. cxM = cimg::cut(xM,0,w1);
  46473. T *ptrd = data(cxm,y);
  46474. const int
  46475. dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
  46476. dtxmM = txM - txm, dtymM = tyM - tym;
  46477. const float dbsmM = bsM - bsm;
  46478. for (int x = cxm; x<=cxM; ++x) {
  46479. const int
  46480. xxm = x - xm,
  46481. tx = (txm*dxmM + dtxmM*xxm + hdxmM)/dxmM,
  46482. ty = (tym*dxmM + dtymM*xxm + hdxmM)/dxmM;
  46483. const float cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
  46484. const tc *const color = &texture._atXY(tx,ty);
  46485. cimg_forC(*this,c) {
  46486. const tc col = color[c*twhd];
  46487. const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
  46488. ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
  46489. }
  46490. ++ptrd;
  46491. }
  46492. }
  46493. }
  46494. return *this;
  46495. }
  46496. //! Draw a textured Gouraud-shaded 2D triangle, with perspective correction \overloading.
  46497. template<typename tc>
  46498. CImg<T>& draw_triangle(int x0, int y0, const float z0,
  46499. int x1, int y1, const float z1,
  46500. int x2, int y2, const float z2,
  46501. const CImg<tc>& texture,
  46502. int tx0, int ty0,
  46503. int tx1, int ty1,
  46504. int tx2, int ty2,
  46505. float bs0,
  46506. float bs1,
  46507. float bs2,
  46508. const float opacity=1) {
  46509. if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
  46510. if (texture._depth>1 || texture._spectrum<_spectrum)
  46511. throw CImgArgumentException(_cimg_instance
  46512. "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
  46513. cimg_instance,
  46514. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  46515. if (is_overlapped(texture)) return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
  46516. bs0,bs1,bs2,opacity);
  46517. float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
  46518. if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,bs0,bs1);
  46519. if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,bs0,bs2);
  46520. if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,bs1,bs2);
  46521. if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
  46522. const int
  46523. w1 = width() - 1, h1 = height() - 1,
  46524. dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
  46525. dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
  46526. cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
  46527. hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
  46528. const float
  46529. diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
  46530. txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
  46531. tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
  46532. dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
  46533. dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1,
  46534. dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
  46535. const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
  46536. cimg_init_scanline(opacity);
  46537. for (int y = cy0; y<=cy2; ++y) {
  46538. const int yy0 = y - y0, yy1 = y - y1;
  46539. int
  46540. xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
  46541. xM = x0 + (dx02*yy0 + hdy02)/dy02;
  46542. float
  46543. izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
  46544. izM = iz0 + diz02*yy0/dy02,
  46545. txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
  46546. txzM = txz0 + dtxz02*yy0/dy02,
  46547. tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
  46548. tyzM = tyz0 + dtyz02*yy0/dy02,
  46549. bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
  46550. bsM = bs0 + dbs02*yy0/dy02;
  46551. if (xm>xM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM,bsm,bsM);
  46552. if (xM>=0 && xm<=w1) {
  46553. const int
  46554. cxm = cimg::cut(xm,0,w1),
  46555. cxM = cimg::cut(xM,0,w1);
  46556. T *ptrd = data(cxm,y);
  46557. const int dxmM = std::max(1,xM - xm);
  46558. const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm, dbsmM = bsM - bsm;
  46559. for (int x = cxm; x<=cxM; ++x) {
  46560. const int xxm = x - xm;
  46561. const float
  46562. iz = izm + dizmM*xxm/dxmM,
  46563. txz = txzm + dtxzmM*xxm/dxmM,
  46564. tyz = tyzm + dtyzmM*xxm/dxmM,
  46565. cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
  46566. const int
  46567. tx = (int)cimg::round(txz/iz),
  46568. ty = (int)cimg::round(tyz/iz);
  46569. const tc *const color = &texture._atXY(tx,ty);
  46570. cimg_forC(*this,c) {
  46571. const tc col = color[c*twhd];
  46572. const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
  46573. ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
  46574. }
  46575. ++ptrd;
  46576. }
  46577. }
  46578. }
  46579. return *this;
  46580. }
  46581. //! Draw a textured Gouraud-shaded 2D triangle, with perspective correction and z-buffering \overloading.
  46582. template<typename tz, typename tc>
  46583. CImg<T>& draw_triangle(CImg<tz>& zbuffer,
  46584. int x0, int y0, const float z0,
  46585. int x1, int y1, const float z1,
  46586. int x2, int y2, const float z2,
  46587. const CImg<tc>& texture,
  46588. int tx0, int ty0,
  46589. int tx1, int ty1,
  46590. int tx2, int ty2,
  46591. float bs0,
  46592. float bs1,
  46593. float bs2,
  46594. const float opacity=1) {
  46595. if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
  46596. if (!is_sameXY(zbuffer))
  46597. throw CImgArgumentException(_cimg_instance
  46598. "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
  46599. "different dimensions.",
  46600. cimg_instance,
  46601. zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
  46602. if (texture._depth>1 || texture._spectrum<_spectrum)
  46603. throw CImgArgumentException(_cimg_instance
  46604. "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
  46605. cimg_instance,
  46606. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  46607. if (is_overlapped(texture))
  46608. return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,bs0,bs1,bs2,opacity);
  46609. float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
  46610. if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,bs0,bs1);
  46611. if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,bs0,bs2);
  46612. if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,bs1,bs2);
  46613. if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
  46614. const int
  46615. w1 = width() - 1, h1 = height() - 1,
  46616. dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
  46617. dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
  46618. cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
  46619. hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
  46620. const float
  46621. diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
  46622. txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
  46623. tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
  46624. dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
  46625. dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1,
  46626. dbs01 = bs1 - bs0, dbs02 = bs2 - bs0, dbs12 = bs2 - bs1;
  46627. const ulongT twhd = (ulongT)texture._width*texture._height*texture._depth;
  46628. cimg_init_scanline(opacity);
  46629. for (int y = cy0; y<=cy2; ++y) {
  46630. const int yy0 = y - y0, yy1 = y - y1;
  46631. int
  46632. xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
  46633. xM = x0 + (dx02*yy0 + hdy02)/dy02;
  46634. float
  46635. izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
  46636. izM = iz0 + diz02*yy0/dy02,
  46637. txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
  46638. txzM = txz0 + dtxz02*yy0/dy02,
  46639. tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
  46640. tyzM = tyz0 + dtyz02*yy0/dy02,
  46641. bsm = y<y1?(bs0 + dbs01*yy0/dy01):(bs1 + dbs12*yy1/dy12),
  46642. bsM = bs0 + dbs02*yy0/dy02;
  46643. if (xm>xM) cimg::swap(xm,xM,txzm,txzM,tyzm,tyzM,izm,izM,bsm,bsM);
  46644. if (xM>=0 && xm<=w1) {
  46645. const int
  46646. cxm = cimg::cut(xm,0,w1),
  46647. cxM = cimg::cut(xM,0,w1);
  46648. T *ptrd = data(cxm,y);
  46649. tz *ptrz = zbuffer.data(cxm,y);
  46650. const int dxmM = std::max(1,xM - xm);
  46651. const float dizmM = izM - izm, dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm, dbsmM = bsM - bsm;
  46652. for (int x = cxm; x<=cxM; ++x) {
  46653. const int xxm = x - xm;
  46654. const float iz = izm + dizmM*xxm/dxmM;
  46655. if (iz>=*ptrz) {
  46656. *ptrz = (tz)iz;
  46657. const float
  46658. txz = txzm + dtxzmM*xxm/dxmM,
  46659. tyz = tyzm + dtyzmM*xxm/dxmM,
  46660. cbs = cimg::cut(bsm + dbsmM*xxm/dxmM,0,2);
  46661. const int
  46662. tx = (int)cimg::round(txz/iz),
  46663. ty = (int)cimg::round(tyz/iz);
  46664. const tc *const color = &texture._atXY(tx,ty);
  46665. cimg_forC(*this,c) {
  46666. const tc col = color[c*twhd];
  46667. const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
  46668. ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
  46669. }
  46670. }
  46671. ++ptrd; ++ptrz;
  46672. }
  46673. }
  46674. }
  46675. return *this;
  46676. }
  46677. //! Draw a textured Phong-shaded 2D triangle.
  46678. /**
  46679. \param x0 X-coordinate of the first vertex in the image instance.
  46680. \param y0 Y-coordinate of the first vertex in the image instance.
  46681. \param x1 X-coordinate of the second vertex in the image instance.
  46682. \param y1 Y-coordinate of the second vertex in the image instance.
  46683. \param x2 X-coordinate of the third vertex in the image instance.
  46684. \param y2 Y-coordinate of the third vertex in the image instance.
  46685. \param texture Texture image used to fill the triangle.
  46686. \param tx0 X-coordinate of the first vertex in the texture image.
  46687. \param ty0 Y-coordinate of the first vertex in the texture image.
  46688. \param tx1 X-coordinate of the second vertex in the texture image.
  46689. \param ty1 Y-coordinate of the second vertex in the texture image.
  46690. \param tx2 X-coordinate of the third vertex in the texture image.
  46691. \param ty2 Y-coordinate of the third vertex in the texture image.
  46692. \param light Light image.
  46693. \param lx0 X-coordinate of the first vertex in the light image.
  46694. \param ly0 Y-coordinate of the first vertex in the light image.
  46695. \param lx1 X-coordinate of the second vertex in the light image.
  46696. \param ly1 Y-coordinate of the second vertex in the light image.
  46697. \param lx2 X-coordinate of the third vertex in the light image.
  46698. \param ly2 Y-coordinate of the third vertex in the light image.
  46699. \param opacity Drawing opacity.
  46700. **/
  46701. template<typename tc, typename tl>
  46702. CImg<T>& draw_triangle(int x0, int y0,
  46703. int x1, int y1,
  46704. int x2, int y2,
  46705. const CImg<tc>& texture,
  46706. int tx0, int ty0,
  46707. int tx1, int ty1,
  46708. int tx2, int ty2,
  46709. const CImg<tl>& light,
  46710. int lx0, int ly0,
  46711. int lx1, int ly1,
  46712. int lx2, int ly2,
  46713. const float opacity=1) {
  46714. if (is_empty()) return *this;
  46715. if (texture._depth>1 || texture._spectrum<_spectrum)
  46716. throw CImgArgumentException(_cimg_instance
  46717. "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
  46718. cimg_instance,
  46719. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  46720. if (light._depth>1 || light._spectrum<_spectrum)
  46721. throw CImgArgumentException(_cimg_instance
  46722. "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
  46723. cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
  46724. if (is_overlapped(texture))
  46725. return draw_triangle(x0,y0,x1,y1,x2,y2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
  46726. if (is_overlapped(light))
  46727. return draw_triangle(x0,y0,x1,y1,x2,y2,texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
  46728. if (y0>y1) cimg::swap(x0,x1,y0,y1,tx0,tx1,ty0,ty1,lx0,lx1,ly0,ly1);
  46729. if (y0>y2) cimg::swap(x0,x2,y0,y2,tx0,tx2,ty0,ty2,lx0,lx2,ly0,ly2);
  46730. if (y1>y2) cimg::swap(x1,x2,y1,y2,tx1,tx2,ty1,ty2,lx1,lx2,ly1,ly2);
  46731. if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
  46732. const int
  46733. w1 = width() - 1, h1 = height() - 1,
  46734. dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
  46735. dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
  46736. cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
  46737. hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2,
  46738. dtx01 = tx1 - tx0, dtx02 = tx2 - tx0, dtx12 = tx2 - tx1,
  46739. dty01 = ty1 - ty0, dty02 = ty2 - ty0, dty12 = ty2 - ty1,
  46740. hdy01tx = dy01*cimg::sign(dtx01)/2, hdy02tx = dy02*cimg::sign(dtx02)/2, hdy12tx = dy12*cimg::sign(dtx12)/2,
  46741. hdy01ty = dy01*cimg::sign(dty01)/2, hdy02ty = dy02*cimg::sign(dty02)/2, hdy12ty = dy12*cimg::sign(dty12)/2,
  46742. dlx01 = lx1 - lx0, dlx02 = lx2 - lx0, dlx12 = lx2 - lx1,
  46743. dly01 = ly1 - ly0, dly02 = ly2 - ly0, dly12 = ly2 - ly1,
  46744. hdy01lx = dy01*cimg::sign(dlx01)/2, hdy02lx = dy02*cimg::sign(dlx02)/2, hdy12lx = dy12*cimg::sign(dlx12)/2,
  46745. hdy01ly = dy01*cimg::sign(dly01)/2, hdy02ly = dy02*cimg::sign(dly02)/2, hdy12ly = dy12*cimg::sign(dly12)/2;
  46746. const ulongT
  46747. twhd = (ulongT)texture._width*texture._height*texture._depth,
  46748. lwhd = (ulongT)light._width*light._height*light._depth;
  46749. cimg_init_scanline(opacity);
  46750. for (int y = cy0; y<=cy2; ++y) {
  46751. const int yy0 = y - y0, yy1 = y - y1;
  46752. int
  46753. xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
  46754. xM = x0 + (dx02*yy0 + hdy02)/dy02,
  46755. txm = y<y1?tx0 + (dtx01*yy0 + hdy01tx)/dy01:tx1 + (dtx12*yy1 + hdy12tx)/dy12,
  46756. txM = tx0 + (dtx02*yy0 + hdy02tx)/dy02,
  46757. tym = y<y1?ty0 + (dty01*yy0 + hdy01ty)/dy01:ty1 + (dty12*yy1 + hdy12ty)/dy12,
  46758. tyM = ty0 + (dty02*yy0 + hdy02ty)/dy02,
  46759. lxm = y<y1?lx0 + (dlx01*yy0 + hdy01lx)/dy01:lx1 + (dlx12*yy1 + hdy12lx)/dy12,
  46760. lxM = lx0 + (dlx02*yy0 + hdy02lx)/dy02,
  46761. lym = y<y1?ly0 + (dly01*yy0 + hdy01ly)/dy01:ly1 + (dly12*yy1 + hdy12ly)/dy12,
  46762. lyM = ly0 + (dly02*yy0 + hdy02ly)/dy02;
  46763. if (xm>xM) cimg::swap(xm,xM,txm,txM,tym,tyM,lxm,lxM,lym,lyM);
  46764. if (xM>=0 && xm<=w1) {
  46765. const int
  46766. cxm = cimg::cut(xm,0,w1),
  46767. cxM = cimg::cut(xM,0,w1);
  46768. T *ptrd = data(cxm,y);
  46769. const int
  46770. dxmM = std::max(1,xM - xm), hdxmM = dxmM/2,
  46771. dtxmM = txM - txm, dtymM = tyM - tym,
  46772. dlxmM = lxM - lxm, dlymM = lyM - lym;
  46773. for (int x = cxm; x<=cxM; ++x) {
  46774. const int
  46775. xxm = x - xm,
  46776. tx = (txm*dxmM + dtxmM*xxm + hdxmM)/dxmM,
  46777. ty = (tym*dxmM + dtymM*xxm + hdxmM)/dxmM,
  46778. lx = (lxm*dxmM + dlxmM*xxm + hdxmM)/dxmM,
  46779. ly = (lym*dxmM + dlymM*xxm + hdxmM)/dxmM;
  46780. const tc *const color = &texture._atXY(tx,ty);
  46781. const tl *const lig = &light._atXY(lx,ly);
  46782. cimg_forC(*this,c) {
  46783. const tc col = color[c*twhd];
  46784. const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
  46785. const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
  46786. ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
  46787. }
  46788. ++ptrd;
  46789. }
  46790. }
  46791. }
  46792. return *this;
  46793. }
  46794. //! Draw a textured Phong-shaded 2D triangle, with perspective correction.
  46795. template<typename tc, typename tl>
  46796. CImg<T>& draw_triangle(int x0, int y0, const float z0,
  46797. int x1, int y1, const float z1,
  46798. int x2, int y2, const float z2,
  46799. const CImg<tc>& texture,
  46800. int tx0, int ty0,
  46801. int tx1, int ty1,
  46802. int tx2, int ty2,
  46803. const CImg<tl>& light,
  46804. int lx0, int ly0,
  46805. int lx1, int ly1,
  46806. int lx2, int ly2,
  46807. const float opacity=1) {
  46808. if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
  46809. if (texture._depth>1 || texture._spectrum<_spectrum)
  46810. throw CImgArgumentException(_cimg_instance
  46811. "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
  46812. cimg_instance,
  46813. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  46814. if (light._depth>1 || light._spectrum<_spectrum)
  46815. throw CImgArgumentException(_cimg_instance
  46816. "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
  46817. cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
  46818. if (is_overlapped(texture))
  46819. return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,+texture,tx0,ty0,tx1,ty1,tx2,ty2,
  46820. light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
  46821. if (is_overlapped(light))
  46822. return draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,texture,tx0,ty0,tx1,ty1,tx2,ty2,
  46823. +light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
  46824. float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
  46825. if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,lx0,lx1,ly0,ly1);
  46826. if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,lx0,lx2,ly0,ly2);
  46827. if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,lx1,lx2,ly1,ly2);
  46828. if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
  46829. const int
  46830. w1 = width() - 1, h1 = height() - 1,
  46831. dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
  46832. dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
  46833. cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
  46834. hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
  46835. const float
  46836. diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
  46837. txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
  46838. tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
  46839. dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
  46840. dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1,
  46841. lxz0 = lx0*iz0, lxz1 = lx1*iz1, lxz2 = lx2*iz2,
  46842. lyz0 = ly0*iz0, lyz1 = ly1*iz1, lyz2 = ly2*iz2,
  46843. dlxz01 = lxz1 - lxz0, dlxz02 = lxz2 - lxz0, dlxz12 = lxz2 - lxz1,
  46844. dlyz01 = lyz1 - lyz0, dlyz02 = lyz2 - lyz0, dlyz12 = lyz2 - lyz1;
  46845. const ulongT
  46846. twhd = (ulongT)texture._width*texture._height*texture._depth,
  46847. lwhd = (ulongT)light._width*light._height*light._depth;
  46848. cimg_init_scanline(opacity);
  46849. for (int y = cy0; y<=cy2; ++y) {
  46850. const int yy0 = y - y0, yy1 = y - y1;
  46851. int
  46852. xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
  46853. xM = x0 + (dx02*yy0 + hdy02)/dy02;
  46854. float
  46855. izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
  46856. izM = iz0 + diz02*yy0/dy02,
  46857. txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
  46858. txzM = txz0 + dtxz02*yy0/dy02,
  46859. tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
  46860. tyzM = tyz0 + dtyz02*yy0/dy02,
  46861. lxzm = y<y1?(lxz0 + dlxz01*yy0/dy01):(lxz1 + dlxz12*yy1/dy12),
  46862. lxzM = lxz0 + dlxz02*yy0/dy02,
  46863. lyzm = y<y1?(lyz0 + dlyz01*yy0/dy01):(lyz1 + dlyz12*yy1/dy12),
  46864. lyzM = lyz0 + dlyz02*yy0/dy02;
  46865. if (xm>xM) cimg::swap(xm,xM,izm,izM,txzm,txzM,tyzm,tyzM,lxzm,lxzM,lyzm,lyzM);
  46866. if (xM>=0 && xm<=w1) {
  46867. const int
  46868. cxm = cimg::cut(xm,0,w1),
  46869. cxM = cimg::cut(xM,0,w1);
  46870. T *ptrd = data(cxm,y);
  46871. const int dxmM = std::max(1,xM - xm);
  46872. const float
  46873. dizmM = izM - izm,
  46874. dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm,
  46875. dlxzmM = lxzM - lxzm, dlyzmM = lyzM - lyzm;
  46876. for (int x = cxm; x<=cxM; ++x) {
  46877. const int xxm = x - xm;
  46878. const float
  46879. iz = izm + dizmM*xxm/dxmM,
  46880. txz = txzm + dtxzmM*xxm/dxmM,
  46881. tyz = tyzm + dtyzmM*xxm/dxmM,
  46882. lxz = lxzm + dlxzmM*xxm/dxmM,
  46883. lyz = lyzm + dlyzmM*xxm/dxmM;
  46884. const int
  46885. tx = (int)cimg::round(txz/iz),
  46886. ty = (int)cimg::round(tyz/iz),
  46887. lx = (int)cimg::round(lxz/iz),
  46888. ly = (int)cimg::round(lyz/iz);
  46889. const tc *const color = &texture._atXY(tx,ty);
  46890. const tl *const lig = &light._atXY(lx,ly);
  46891. cimg_forC(*this,c) {
  46892. const tc col = color[c*twhd];
  46893. const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
  46894. const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
  46895. ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
  46896. }
  46897. ++ptrd;
  46898. }
  46899. }
  46900. }
  46901. return *this;
  46902. }
  46903. //! Draw a textured Phong-shaded 2D triangle, with perspective correction and z-buffering.
  46904. template<typename tz, typename tc, typename tl>
  46905. CImg<T>& draw_triangle(CImg<tz>& zbuffer,
  46906. int x0, int y0, const float z0,
  46907. int x1, int y1, const float z1,
  46908. int x2, int y2, const float z2,
  46909. const CImg<tc>& texture,
  46910. int tx0, int ty0,
  46911. int tx1, int ty1,
  46912. int tx2, int ty2,
  46913. const CImg<tl>& light,
  46914. int lx0, int ly0,
  46915. int lx1, int ly1,
  46916. int lx2, int ly2,
  46917. const float opacity=1) {
  46918. if (is_empty() || z0<=0 || z1<=0 || z2<=0) return *this;
  46919. if (!is_sameXY(zbuffer))
  46920. throw CImgArgumentException(_cimg_instance
  46921. "draw_triangle(): Instance and specified Z-buffer (%u,%u,%u,%u,%p) have "
  46922. "different dimensions.",
  46923. cimg_instance,
  46924. zbuffer._width,zbuffer._height,zbuffer._depth,zbuffer._spectrum,zbuffer._data);
  46925. if (texture._depth>1 || texture._spectrum<_spectrum)
  46926. throw CImgArgumentException(_cimg_instance
  46927. "draw_triangle(): Invalid specified texture (%u,%u,%u,%u,%p).",
  46928. cimg_instance,
  46929. texture._width,texture._height,texture._depth,texture._spectrum,texture._data);
  46930. if (light._depth>1 || light._spectrum<_spectrum)
  46931. throw CImgArgumentException(_cimg_instance
  46932. "draw_triangle(): Invalid specified light texture (%u,%u,%u,%u,%p).",
  46933. cimg_instance,light._width,light._height,light._depth,light._spectrum,light._data);
  46934. if (is_overlapped(texture))
  46935. return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,
  46936. +texture,tx0,ty0,tx1,ty1,tx2,ty2,light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
  46937. if (is_overlapped(light))
  46938. return draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,
  46939. texture,tx0,ty0,tx1,ty1,tx2,ty2,+light,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
  46940. float iz0 = 1/z0, iz1 = 1/z1, iz2 = 1/z2;
  46941. if (y0>y1) cimg::swap(x0,x1,y0,y1,iz0,iz1,tx0,tx1,ty0,ty1,lx0,lx1,ly0,ly1);
  46942. if (y0>y2) cimg::swap(x0,x2,y0,y2,iz0,iz2,tx0,tx2,ty0,ty2,lx0,lx2,ly0,ly2);
  46943. if (y1>y2) cimg::swap(x1,x2,y1,y2,iz1,iz2,tx1,tx2,ty1,ty2,lx1,lx2,ly1,ly2);
  46944. if (y2<0 || y0>=height() || cimg::min(x0,x1,x2)>=width() || cimg::max(x0,x1,x2)<0 || !opacity) return *this;
  46945. const int
  46946. w1 = width() - 1, h1 = height() - 1,
  46947. dx01 = x1 - x0, dx02 = x2 - x0, dx12 = x2 - x1,
  46948. dy01 = std::max(1,y1 - y0), dy02 = std::max(1,y2 - y0), dy12 = std::max(1,y2 - y1),
  46949. cy0 = cimg::cut(y0,0,h1), cy2 = cimg::cut(y2,0,h1),
  46950. hdy01 = dy01*cimg::sign(dx01)/2, hdy02 = dy02*cimg::sign(dx02)/2, hdy12 = dy12*cimg::sign(dx12)/2;
  46951. const float
  46952. diz01 = iz1 - iz0, diz02 = iz2 - iz0, diz12 = iz2 - iz1,
  46953. txz0 = tx0*iz0, txz1 = tx1*iz1, txz2 = tx2*iz2,
  46954. tyz0 = ty0*iz0, tyz1 = ty1*iz1, tyz2 = ty2*iz2,
  46955. dtxz01 = txz1 - txz0, dtxz02 = txz2 - txz0, dtxz12 = txz2 - txz1,
  46956. dtyz01 = tyz1 - tyz0, dtyz02 = tyz2 - tyz0, dtyz12 = tyz2 - tyz1,
  46957. lxz0 = lx0*iz0, lxz1 = lx1*iz1, lxz2 = lx2*iz2,
  46958. lyz0 = ly0*iz0, lyz1 = ly1*iz1, lyz2 = ly2*iz2,
  46959. dlxz01 = lxz1 - lxz0, dlxz02 = lxz2 - lxz0, dlxz12 = lxz2 - lxz1,
  46960. dlyz01 = lyz1 - lyz0, dlyz02 = lyz2 - lyz0, dlyz12 = lyz2 - lyz1;
  46961. const ulongT
  46962. twhd = (ulongT)texture._width*texture._height*texture._depth,
  46963. lwhd = (ulongT)light._width*light._height*light._depth;
  46964. cimg_init_scanline(opacity);
  46965. for (int y = cy0; y<=cy2; ++y) {
  46966. const int yy0 = y - y0, yy1 = y - y1;
  46967. int
  46968. xm = y<y1?x0 + (dx01*yy0 + hdy01)/dy01:x1 + (dx12*yy1 + hdy12)/dy12,
  46969. xM = x0 + (dx02*yy0 + hdy02)/dy02;
  46970. float
  46971. izm = y<y1?(iz0 + diz01*yy0/dy01):(iz1 + diz12*yy1/dy12),
  46972. izM = iz0 + diz02*yy0/dy02,
  46973. txzm = y<y1?(txz0 + dtxz01*yy0/dy01):(txz1 + dtxz12*yy1/dy12),
  46974. txzM = txz0 + dtxz02*yy0/dy02,
  46975. tyzm = y<y1?(tyz0 + dtyz01*yy0/dy01):(tyz1 + dtyz12*yy1/dy12),
  46976. tyzM = tyz0 + dtyz02*yy0/dy02,
  46977. lxzm = y<y1?(lxz0 + dlxz01*yy0/dy01):(lxz1 + dlxz12*yy1/dy12),
  46978. lxzM = lxz0 + dlxz02*yy0/dy02,
  46979. lyzm = y<y1?(lyz0 + dlyz01*yy0/dy01):(lyz1 + dlyz12*yy1/dy12),
  46980. lyzM = lyz0 + dlyz02*yy0/dy02;
  46981. if (xm>xM) cimg::swap(xm,xM,izm,izM,txzm,txzM,tyzm,tyzM,lxzm,lxzM,lyzm,lyzM);
  46982. if (xM>=0 && xm<=w1) {
  46983. const int
  46984. cxm = cimg::cut(xm,0,w1),
  46985. cxM = cimg::cut(xM,0,w1);
  46986. T *ptrd = data(cxm,y);
  46987. tz *ptrz = zbuffer.data(cxm,y);
  46988. const int dxmM = std::max(1,xM - xm);
  46989. const float
  46990. dizmM = izM - izm,
  46991. dtxzmM = txzM - txzm, dtyzmM = tyzM - tyzm,
  46992. dlxzmM = lxzM - lxzm, dlyzmM = lyzM - lyzm;
  46993. for (int x = cxm; x<=cxM; ++x) {
  46994. const int xxm = x - xm;
  46995. const float iz = izm + dizmM*xxm/dxmM;
  46996. if (iz>=*ptrz) {
  46997. *ptrz = (tz)iz;
  46998. const float
  46999. txz = txzm + dtxzmM*xxm/dxmM,
  47000. tyz = tyzm + dtyzmM*xxm/dxmM,
  47001. lxz = lxzm + dlxzmM*xxm/dxmM,
  47002. lyz = lyzm + dlyzmM*xxm/dxmM;
  47003. const int
  47004. tx = (int)cimg::round(txz/iz),
  47005. ty = (int)cimg::round(tyz/iz),
  47006. lx = (int)cimg::round(lxz/iz),
  47007. ly = (int)cimg::round(lyz/iz);
  47008. const tc *const color = &texture._atXY(tx,ty);
  47009. const tl *const lig = &light._atXY(lx,ly);
  47010. cimg_forC(*this,c) {
  47011. const tc col = color[c*twhd];
  47012. const float cbs = cimg::cut((float)lig[c*lwhd],0,2);
  47013. const Tfloat val = cbs<=1?cbs*col:(2 - cbs)*col + (cbs - 1)*_sc_maxval;
  47014. ptrd[c*_sc_whd] = (T)(opacity>=1?val:val*_sc_nopacity + ptrd[c*_sc_whd]*_sc_copacity);
  47015. }
  47016. }
  47017. ++ptrd; ++ptrz;
  47018. }
  47019. }
  47020. }
  47021. return *this;
  47022. }
  47023. //! Draw a filled 4D rectangle.
  47024. /**
  47025. \param x0 X-coordinate of the upper-left rectangle corner.
  47026. \param y0 Y-coordinate of the upper-left rectangle corner.
  47027. \param z0 Z-coordinate of the upper-left rectangle corner.
  47028. \param c0 C-coordinate of the upper-left rectangle corner.
  47029. \param x1 X-coordinate of the lower-right rectangle corner.
  47030. \param y1 Y-coordinate of the lower-right rectangle corner.
  47031. \param z1 Z-coordinate of the lower-right rectangle corner.
  47032. \param c1 C-coordinate of the lower-right rectangle corner.
  47033. \param val Scalar value used to fill the rectangle area.
  47034. \param opacity Drawing opacity.
  47035. **/
  47036. CImg<T>& draw_rectangle(const int x0, const int y0, const int z0, const int c0,
  47037. const int x1, const int y1, const int z1, const int c1,
  47038. const T val, const float opacity=1) {
  47039. if (is_empty()) return *this;
  47040. const int
  47041. nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
  47042. ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0,
  47043. nz0 = z0<z1?z0:z1, nz1 = z0^z1^nz0,
  47044. nc0 = c0<c1?c0:c1, nc1 = c0^c1^nc0;
  47045. const int
  47046. lx = (1 + nx1 - nx0) + (nx1>=width()?width() - 1 - nx1:0) + (nx0<0?nx0:0),
  47047. ly = (1 + ny1 - ny0) + (ny1>=height()?height() - 1 - ny1:0) + (ny0<0?ny0:0),
  47048. lz = (1 + nz1 - nz0) + (nz1>=depth()?depth() - 1 - nz1:0) + (nz0<0?nz0:0),
  47049. lc = (1 + nc1 - nc0) + (nc1>=spectrum()?spectrum() - 1 - nc1:0) + (nc0<0?nc0:0);
  47050. const ulongT
  47051. offX = (ulongT)_width - lx,
  47052. offY = (ulongT)_width*(_height - ly),
  47053. offZ = (ulongT)_width*_height*(_depth - lz);
  47054. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
  47055. T *ptrd = data(nx0<0?0:nx0,ny0<0?0:ny0,nz0<0?0:nz0,nc0<0?0:nc0);
  47056. if (lx>0 && ly>0 && lz>0 && lc>0)
  47057. for (int v = 0; v<lc; ++v) {
  47058. for (int z = 0; z<lz; ++z) {
  47059. for (int y = 0; y<ly; ++y) {
  47060. if (opacity>=1) {
  47061. if (sizeof(T)!=1) { for (int x = 0; x<lx; ++x) *(ptrd++) = val; ptrd+=offX; }
  47062. else { std::memset(ptrd,(int)val,lx); ptrd+=_width; }
  47063. } else { for (int x = 0; x<lx; ++x) { *ptrd = (T)(nopacity*val + *ptrd*copacity); ++ptrd; } ptrd+=offX; }
  47064. }
  47065. ptrd+=offY;
  47066. }
  47067. ptrd+=offZ;
  47068. }
  47069. return *this;
  47070. }
  47071. //! Draw a filled 3D rectangle.
  47072. /**
  47073. \param x0 X-coordinate of the upper-left rectangle corner.
  47074. \param y0 Y-coordinate of the upper-left rectangle corner.
  47075. \param z0 Z-coordinate of the upper-left rectangle corner.
  47076. \param x1 X-coordinate of the lower-right rectangle corner.
  47077. \param y1 Y-coordinate of the lower-right rectangle corner.
  47078. \param z1 Z-coordinate of the lower-right rectangle corner.
  47079. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  47080. \param opacity Drawing opacity.
  47081. **/
  47082. template<typename tc>
  47083. CImg<T>& draw_rectangle(const int x0, const int y0, const int z0,
  47084. const int x1, const int y1, const int z1,
  47085. const tc *const color, const float opacity=1) {
  47086. if (is_empty()) return *this;
  47087. if (!color)
  47088. throw CImgArgumentException(_cimg_instance
  47089. "draw_rectangle(): Specified color is (null).",
  47090. cimg_instance);
  47091. cimg_forC(*this,c) draw_rectangle(x0,y0,z0,c,x1,y1,z1,c,(T)color[c],opacity);
  47092. return *this;
  47093. }
  47094. //! Draw a filled 2D rectangle.
  47095. /**
  47096. \param x0 X-coordinate of the upper-left rectangle corner.
  47097. \param y0 Y-coordinate of the upper-left rectangle corner.
  47098. \param x1 X-coordinate of the lower-right rectangle corner.
  47099. \param y1 Y-coordinate of the lower-right rectangle corner.
  47100. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  47101. \param opacity Drawing opacity.
  47102. **/
  47103. template<typename tc>
  47104. CImg<T>& draw_rectangle(const int x0, const int y0,
  47105. const int x1, const int y1,
  47106. const tc *const color, const float opacity=1) {
  47107. return draw_rectangle(x0,y0,0,x1,y1,_depth - 1,color,opacity);
  47108. }
  47109. //! Draw a outlined 2D rectangle \overloading.
  47110. template<typename tc>
  47111. CImg<T>& draw_rectangle(const int x0, const int y0,
  47112. const int x1, const int y1,
  47113. const tc *const color, const float opacity,
  47114. const unsigned int pattern) {
  47115. if (is_empty()) return *this;
  47116. if (y0==y1) return draw_line(x0,y0,x1,y0,color,opacity,pattern,true);
  47117. if (x0==x1) return draw_line(x0,y0,x0,y1,color,opacity,pattern,true);
  47118. const int
  47119. nx0 = x0<x1?x0:x1, nx1 = x0^x1^nx0,
  47120. ny0 = y0<y1?y0:y1, ny1 = y0^y1^ny0;
  47121. if (ny1==ny0 + 1) return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
  47122. draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false);
  47123. return draw_line(nx0,ny0,nx1,ny0,color,opacity,pattern,true).
  47124. draw_line(nx1,ny0 + 1,nx1,ny1 - 1,color,opacity,pattern,false).
  47125. draw_line(nx1,ny1,nx0,ny1,color,opacity,pattern,false).
  47126. draw_line(nx0,ny1 - 1,nx0,ny0 + 1,color,opacity,pattern,false);
  47127. }
  47128. //! Draw a filled 2D polygon.
  47129. /**
  47130. \param points Set of polygon vertices.
  47131. \param color Pointer to \c spectrum() consecutive values of type \c T, defining the drawing color.
  47132. \param opacity Drawing opacity.
  47133. **/
  47134. template<typename tp, typename tc>
  47135. CImg<T>& draw_polygon(const CImg<tp>& points,
  47136. const tc *const color, const float opacity=1) {
  47137. if (is_empty() || !points) return *this;
  47138. if (!color)
  47139. throw CImgArgumentException(_cimg_instance
  47140. "draw_polygon(): Specified color is (null).",
  47141. cimg_instance);
  47142. if (points.height()!=2)
  47143. throw CImgArgumentException(_cimg_instance
  47144. "draw_polygon(): Invalid specified point set (%u,%u,%u,%u).",
  47145. cimg_instance,
  47146. points._width,points._height,points._depth,points._spectrum);
  47147. CImg<intT> ipoints;
  47148. if (cimg::type<tp>::is_float()) ipoints = points.get_round();
  47149. else ipoints.assign(points,cimg::type<tp>::string()==cimg::type<int>::string());
  47150. if (ipoints._width==1) return draw_point(ipoints(0,0),ipoints(0,1),color,opacity);
  47151. if (ipoints._width==2) return draw_line(ipoints(0,0),ipoints(0,1),ipoints(1,0),ipoints(1,1),color,opacity);
  47152. if (ipoints._width==3) return draw_triangle(ipoints(0,0),ipoints(0,1),ipoints(1,0),ipoints(1,1),
  47153. ipoints(2,0),ipoints(2,1),color,opacity);
  47154. cimg_init_scanline(opacity);
  47155. int
  47156. xmin = 0, ymin = 0,
  47157. xmax = ipoints.get_shared_row(0).max_min(xmin),
  47158. ymax = ipoints.get_shared_row(1).max_min(ymin);
  47159. if (xmax<0 || xmin>=width() || ymax<0 || ymin>=height()) return *this;
  47160. if (ymin==ymax) return draw_line(xmin,ymin,xmax,ymax,color,opacity);
  47161. ymin = std::max(0,ymin);
  47162. ymax = std::min(height() - 1,ymax);
  47163. CImg<intT> Xs(ipoints._width,ymax - ymin + 1);
  47164. CImg<uintT> count(Xs._height,1,1,1,0);
  47165. unsigned int n = 0, nn = 1;
  47166. bool go_on = true;
  47167. while (go_on) {
  47168. unsigned int an = (nn + 1)%ipoints._width;
  47169. const int x0 = ipoints(n,0), y0 = ipoints(n,1);
  47170. if (ipoints(nn,1)==y0) while (ipoints(an,1)==y0) { nn = an; (an+=1)%=ipoints._width; }
  47171. const int x1 = ipoints(nn,0), y1 = ipoints(nn,1);
  47172. unsigned int tn = an;
  47173. while (ipoints(tn,1)==y1) (tn+=1)%=ipoints._width;
  47174. if (y0!=y1) {
  47175. const int
  47176. y2 = ipoints(tn,1),
  47177. x01 = x1 - x0, y01 = y1 - y0, y12 = y2 - y1,
  47178. step = cimg::sign(y01),
  47179. tmax = std::max(1,cimg::abs(y01)),
  47180. htmax = tmax*cimg::sign(x01)/2,
  47181. tend = tmax - (step==cimg::sign(y12));
  47182. unsigned int y = (unsigned int)y0 - ymin;
  47183. for (int t = 0; t<=tend; ++t, y+=step)
  47184. if (y<Xs._height) Xs(count[y]++,y) = x0 + (t*x01 + htmax)/tmax;
  47185. }
  47186. go_on = nn>n;
  47187. n = nn;
  47188. nn = an;
  47189. }
  47190. cimg_pragma_openmp(parallel for cimg_openmp_if(Xs._height>=(cimg_openmp_sizefactor)*512))
  47191. cimg_forY(Xs,y) {
  47192. const CImg<intT> Xsy = Xs.get_shared_points(0,count[y] - 1,y).sort();
  47193. int px = width();
  47194. for (unsigned int k = 0; k<Xsy._width; k+=2) {
  47195. int x0 = Xsy[k];
  47196. const int x1 = Xsy[k + 1];
  47197. x0+=x0==px;
  47198. cimg_draw_scanline(x0,x1,y + ymin,color,opacity,1);
  47199. px = x1;
  47200. }
  47201. }
  47202. return *this;
  47203. }
  47204. //! Draw a outlined 2D or 3D polygon \overloading.
  47205. template<typename tp, typename tc>
  47206. CImg<T>& draw_polygon(const CImg<tp>& points,
  47207. const tc *const color, const float opacity, const unsigned int pattern) {
  47208. if (is_empty() || !points) return *this;
  47209. if (!color)
  47210. throw CImgArgumentException(_cimg_instance
  47211. "draw_polygon(): Specified color is (null).",
  47212. cimg_instance);
  47213. if (points.height()!=2)
  47214. throw CImgArgumentException(_cimg_instance
  47215. "draw_polygon(): Invalid specified point set (%u,%u,%u,%u).",
  47216. cimg_instance,
  47217. points._width,points._height,points._depth,points._spectrum);
  47218. CImg<intT> ipoints;
  47219. if (cimg::type<tp>::is_float()) ipoints = points.get_round();
  47220. else ipoints.assign(points,cimg::type<tp>::string()==cimg::type<int>::string());
  47221. if (ipoints._width==1) return draw_point(ipoints(0,0),ipoints(0,1),color,opacity);
  47222. if (ipoints._width==2) return draw_line(ipoints(0,0),ipoints(0,1),ipoints(1,0),ipoints(1,1),
  47223. color,opacity,pattern);
  47224. if (ipoints._width==3) return draw_triangle(ipoints(0,0),ipoints(0,1),ipoints(1,0),ipoints(1,1),
  47225. ipoints(2,0),ipoints(2,1),color,opacity,pattern);
  47226. bool ninit_hatch = true;
  47227. const int x0 = ipoints(0,0), y0 = ipoints(0,1);
  47228. int ox = x0, oy = y0;
  47229. for (unsigned int i = 1; i<ipoints._width; ++i) {
  47230. const int x = ipoints(i,0), y = ipoints(i,1);
  47231. draw_line(ox,oy,x,y,color,opacity,pattern,ninit_hatch);
  47232. ninit_hatch = false;
  47233. ox = x; oy = y;
  47234. }
  47235. draw_line(ox,oy,x0,y0,color,opacity,pattern,false);
  47236. return *this;
  47237. }
  47238. //! Draw a filled 2D ellipse.
  47239. /**
  47240. \param x0 X-coordinate of the ellipse center.
  47241. \param y0 Y-coordinate of the ellipse center.
  47242. \param r1 First radius of the ellipse.
  47243. \param r2 Second radius of the ellipse.
  47244. \param angle Angle of the first radius.
  47245. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  47246. \param opacity Drawing opacity.
  47247. **/
  47248. template<typename tc>
  47249. CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
  47250. const tc *const color, const float opacity=1) {
  47251. return _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,0U,true);
  47252. }
  47253. //! Draw a filled 2D ellipse \overloading.
  47254. /**
  47255. \param x0 X-coordinate of the ellipse center.
  47256. \param y0 Y-coordinate of the ellipse center.
  47257. \param tensor Diffusion tensor describing the ellipse.
  47258. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  47259. \param opacity Drawing opacity.
  47260. **/
  47261. template<typename t, typename tc>
  47262. CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
  47263. const tc *const color, const float opacity=1) {
  47264. CImgList<t> eig = tensor.get_symmetric_eigen();
  47265. const CImg<t> &val = eig[0], &vec = eig[1];
  47266. return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)),
  47267. std::atan2(vec(0,1),vec(0,0))*180/cimg::PI,
  47268. color,opacity);
  47269. }
  47270. //! Draw an outlined 2D ellipse.
  47271. /**
  47272. \param x0 X-coordinate of the ellipse center.
  47273. \param y0 Y-coordinate of the ellipse center.
  47274. \param r1 First radius of the ellipse.
  47275. \param r2 Second radius of the ellipse.
  47276. \param angle Angle of the first radius.
  47277. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  47278. \param opacity Drawing opacity.
  47279. \param pattern An integer whose bits describe the outline pattern.
  47280. **/
  47281. template<typename tc>
  47282. CImg<T>& draw_ellipse(const int x0, const int y0, const float r1, const float r2, const float angle,
  47283. const tc *const color, const float opacity, const unsigned int pattern) {
  47284. if (pattern) _draw_ellipse(x0,y0,r1,r2,angle,color,opacity,pattern,false);
  47285. return *this;
  47286. }
  47287. //! Draw an outlined 2D ellipse \overloading.
  47288. /**
  47289. \param x0 X-coordinate of the ellipse center.
  47290. \param y0 Y-coordinate of the ellipse center.
  47291. \param tensor Diffusion tensor describing the ellipse.
  47292. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  47293. \param opacity Drawing opacity.
  47294. \param pattern An integer whose bits describe the outline pattern.
  47295. **/
  47296. template<typename t, typename tc>
  47297. CImg<T>& draw_ellipse(const int x0, const int y0, const CImg<t> &tensor,
  47298. const tc *const color, const float opacity,
  47299. const unsigned int pattern) {
  47300. CImgList<t> eig = tensor.get_symmetric_eigen();
  47301. const CImg<t> &val = eig[0], &vec = eig[1];
  47302. return draw_ellipse(x0,y0,std::sqrt(val(0)),std::sqrt(val(1)),
  47303. std::atan2(vec(0,1),vec(0,0))*180/cimg::PI,
  47304. color,opacity,pattern);
  47305. }
  47306. template<typename tc>
  47307. CImg<T>& _draw_ellipse(const int x0, const int y0, const float radius1, const float radius2, const float angle,
  47308. const tc *const color, const float opacity,
  47309. const unsigned int pattern, const bool is_filled) {
  47310. if (is_empty() || (!is_filled && !pattern)) return *this;
  47311. const float radiusM = std::max(radius1,radius2);
  47312. if (radius1<0 || radius2<0 || x0 - radiusM>=width() || y0 + radiusM<0 || y0 - radiusM>=height()) return *this;
  47313. if (!color)
  47314. throw CImgArgumentException(_cimg_instance
  47315. "draw_ellipse(): Specified color is (null).",
  47316. cimg_instance);
  47317. const int iradius1 = (int)cimg::round(radius1), iradius2 = (int)cimg::round(radius2);
  47318. if (!iradius1 && !iradius2) return draw_point(x0,y0,color,opacity);
  47319. if (iradius1==iradius2) {
  47320. if (is_filled) return draw_circle(x0,y0,iradius1,color,opacity);
  47321. else if (pattern==~0U) return draw_circle(x0,y0,iradius1,color,opacity,pattern);
  47322. }
  47323. const float ang = (float)(angle*cimg::PI/180);
  47324. if (!is_filled) { // Outlined
  47325. const float ca = std::cos(ang), sa = std::sin(ang);
  47326. CImg<int> points((unsigned int)cimg::round(6*radiusM),2);
  47327. cimg_forX(points,k) {
  47328. const float
  47329. _ang = (float)(2*cimg::PI*k/points._width),
  47330. X = (float)(radius1*std::cos(_ang)),
  47331. Y = (float)(radius2*std::sin(_ang));
  47332. points(k,0) = (int)cimg::round(x0 + (X*ca - Y*sa));
  47333. points(k,1) = (int)cimg::round(y0 + (X*sa + Y*ca));
  47334. }
  47335. draw_polygon(points,color,opacity,pattern);
  47336. } else { // Filled
  47337. cimg_init_scanline(opacity);
  47338. const float
  47339. ca = std::cos(ang),
  47340. sa = -std::sin(ang),
  47341. ca2 = ca*ca,
  47342. sa2 = sa*sa,
  47343. casa = ca*sa,
  47344. i1 = 1/cimg::sqr(radius1),
  47345. i2 = 1/cimg::sqr(radius2),
  47346. t1 = i1*ca2 + i2*sa2,
  47347. t2 = (i2 - i1)*casa,
  47348. t3 = i2*ca2 + i1*sa2,
  47349. t12 = t1*2;
  47350. const int
  47351. _ymin = (int)std::floor(y0 - radiusM),
  47352. _ymax = (int)std::ceil(y0 + radiusM),
  47353. ymin = _ymin<0?0:_ymin,
  47354. ymax = _ymax>=height()?height() - 1:_ymax;
  47355. for (int y = ymin; y<=ymax; ++y) {
  47356. const float
  47357. Y = y - y0 + 0.5f,
  47358. B = 2*t2*Y,
  47359. C = t3*Y*Y - 1,
  47360. D = B*B - 4*t1*C;
  47361. if (D>=0) {
  47362. const float sD = std::sqrt(D);
  47363. const int
  47364. xmin = (int)(x0 + cimg::round((-B - sD)/t12)),
  47365. xmax = (int)(x0 + cimg::round((-B + sD)/t12));
  47366. cimg_draw_scanline(xmin,xmax,y,color,opacity,1);
  47367. }
  47368. }
  47369. }
  47370. return *this;
  47371. }
  47372. //! Draw a filled 2D circle.
  47373. /**
  47374. \param x0 X-coordinate of the circle center.
  47375. \param y0 Y-coordinate of the circle center.
  47376. \param radius Circle radius.
  47377. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  47378. \param opacity Drawing opacity.
  47379. \note
  47380. - Circle version of the Bresenham's algorithm is used.
  47381. **/
  47382. template<typename tc>
  47383. CImg<T>& draw_circle(const int x0, const int y0, int radius,
  47384. const tc *const color, const float opacity=1) {
  47385. if (is_empty()) return *this;
  47386. if (radius<0 || x0 - radius>=width() || y0 + radius<0 || y0 - radius>=height()) return *this;
  47387. if (!color)
  47388. throw CImgArgumentException(_cimg_instance
  47389. "draw_circle(): Specified color is (null).",
  47390. cimg_instance);
  47391. if (!radius) return draw_point(x0,y0,color,opacity);
  47392. cimg_init_scanline(opacity);
  47393. if (y0>=0 && y0<height()) cimg_draw_scanline(x0 - radius,x0 + radius,y0,color,opacity,1);
  47394. for (int f = 1 - radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
  47395. if (f>=0) {
  47396. const int x1 = x0 - x, x2 = x0 + x, y1 = y0 - y, y2 = y0 + y;
  47397. if (y1>=0 && y1<height()) cimg_draw_scanline(x1,x2,y1,color,opacity,1);
  47398. if (y2>=0 && y2<height()) cimg_draw_scanline(x1,x2,y2,color,opacity,1);
  47399. f+=(ddFy+=2); --y;
  47400. }
  47401. const bool no_diag = y!=(x++);
  47402. ++(f+=(ddFx+=2));
  47403. const int x1 = x0 - y, x2 = x0 + y, y1 = y0 - x, y2 = y0 + x;
  47404. if (no_diag) {
  47405. if (y1>=0 && y1<height()) cimg_draw_scanline(x1,x2,y1,color,opacity,1);
  47406. if (y2>=0 && y2<height()) cimg_draw_scanline(x1,x2,y2,color,opacity,1);
  47407. }
  47408. }
  47409. return *this;
  47410. }
  47411. //! Draw an outlined 2D circle.
  47412. /**
  47413. \param x0 X-coordinate of the circle center.
  47414. \param y0 Y-coordinate of the circle center.
  47415. \param radius Circle radius.
  47416. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  47417. \param opacity Drawing opacity.
  47418. \param pattern An integer whose bits describe the outline pattern.
  47419. **/
  47420. template<typename tc>
  47421. CImg<T>& draw_circle(const int x0, const int y0, int radius,
  47422. const tc *const color, const float opacity,
  47423. const unsigned int pattern) {
  47424. if (pattern!=~0U) return draw_ellipse(x0,y0,radius,radius,0,color,opacity,pattern);
  47425. if (is_empty()) return *this;
  47426. if (!color)
  47427. throw CImgArgumentException(_cimg_instance
  47428. "draw_circle(): Specified color is (null).",
  47429. cimg_instance);
  47430. if (radius<0 || x0 - radius>=width() || y0 + radius<0 || y0 - radius>=height()) return *this;
  47431. if (!radius) return draw_point(x0,y0,color,opacity);
  47432. draw_point(x0 - radius,y0,color,opacity).draw_point(x0 + radius,y0,color,opacity).
  47433. draw_point(x0,y0 - radius,color,opacity).draw_point(x0,y0 + radius,color,opacity);
  47434. if (radius==1) return *this;
  47435. for (int f = 1 - radius, ddFx = 0, ddFy = -(radius<<1), x = 0, y = radius; x<y; ) {
  47436. if (f>=0) { f+=(ddFy+=2); --y; }
  47437. ++x; ++(f+=(ddFx+=2));
  47438. if (x!=y + 1) {
  47439. const int x1 = x0 - y, x2 = x0 + y, y1 = y0 - x, y2 = y0 + x,
  47440. x3 = x0 - x, x4 = x0 + x, y3 = y0 - y, y4 = y0 + y;
  47441. draw_point(x1,y1,color,opacity).draw_point(x1,y2,color,opacity).
  47442. draw_point(x2,y1,color,opacity).draw_point(x2,y2,color,opacity);
  47443. if (x!=y)
  47444. draw_point(x3,y3,color,opacity).draw_point(x4,y4,color,opacity).
  47445. draw_point(x4,y3,color,opacity).draw_point(x3,y4,color,opacity);
  47446. }
  47447. }
  47448. return *this;
  47449. }
  47450. //! Draw an image.
  47451. /**
  47452. \param sprite Sprite image.
  47453. \param x0 X-coordinate of the sprite position.
  47454. \param y0 Y-coordinate of the sprite position.
  47455. \param z0 Z-coordinate of the sprite position.
  47456. \param c0 C-coordinate of the sprite position.
  47457. \param opacity Drawing opacity.
  47458. **/
  47459. template<typename t>
  47460. CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
  47461. const CImg<t>& sprite, const float opacity=1) {
  47462. if (is_empty() || !sprite) return *this;
  47463. if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
  47464. if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared())
  47465. return assign(sprite,false);
  47466. const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0;
  47467. const int
  47468. dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0,
  47469. sx0 = dx0 - x0, sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0,
  47470. lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0),
  47471. ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0),
  47472. lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0),
  47473. lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0);
  47474. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
  47475. if (lx>0 && ly>0 && lz>0 && lc>0) {
  47476. for (int c = 0; c<lc; ++c)
  47477. for (int z = 0; z<lz; ++z)
  47478. for (int y = 0; y<ly; ++y) {
  47479. T *ptrd = data(dx0,dy0 + y,dz0 + z,dc0 + c);
  47480. const t *ptrs = sprite.data(sx0,sy0 + y,sz0 + z,sc0 + c);
  47481. if (opacity>=1) for (int x = 0; x<lx; ++x) *(ptrd++) = (T)*(ptrs++);
  47482. else for (int x = 0; x<lx; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
  47483. }
  47484. }
  47485. return *this;
  47486. }
  47487. //! Draw an image \specialization.
  47488. CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
  47489. const CImg<T>& sprite, const float opacity=1) {
  47490. if (is_empty() || !sprite) return *this;
  47491. if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,opacity);
  47492. if (x0==0 && y0==0 && z0==0 && c0==0 && is_sameXYZC(sprite) && opacity>=1 && !is_shared())
  47493. return assign(sprite,false);
  47494. const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0;
  47495. const int
  47496. dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0,
  47497. sx0 = dx0 - x0, sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0,
  47498. lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0),
  47499. ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0),
  47500. lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0),
  47501. lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0);
  47502. const ulongT slx = lx*sizeof(T);
  47503. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
  47504. if (lx>0 && ly>0 && lz>0 && lc>0) {
  47505. for (int c = 0; c<lc; ++c)
  47506. for (int z = 0; z<lz; ++z)
  47507. for (int y = 0; y<ly; ++y) {
  47508. T *ptrd = data(dx0,dy0 + y,dz0 + z,dc0 + c);
  47509. const T *ptrs = sprite.data(sx0,sy0 + y,sz0 + z,sc0 + c);
  47510. if (opacity>=1) std::memcpy(ptrd,ptrs,slx);
  47511. else for (int x = 0; x<lx; ++x) { *ptrd = (T)(nopacity*(*(ptrs++)) + *ptrd*copacity); ++ptrd; }
  47512. }
  47513. }
  47514. return *this;
  47515. }
  47516. //! Draw an image \overloading.
  47517. template<typename t>
  47518. CImg<T>& draw_image(const int x0, const int y0, const int z0,
  47519. const CImg<t>& sprite, const float opacity=1) {
  47520. return draw_image(x0,y0,z0,0,sprite,opacity);
  47521. }
  47522. //! Draw an image \overloading.
  47523. template<typename t>
  47524. CImg<T>& draw_image(const int x0, const int y0,
  47525. const CImg<t>& sprite, const float opacity=1) {
  47526. return draw_image(x0,y0,0,sprite,opacity);
  47527. }
  47528. //! Draw an image \overloading.
  47529. template<typename t>
  47530. CImg<T>& draw_image(const int x0,
  47531. const CImg<t>& sprite, const float opacity=1) {
  47532. return draw_image(x0,0,sprite,opacity);
  47533. }
  47534. //! Draw an image \overloading.
  47535. template<typename t>
  47536. CImg<T>& draw_image(const CImg<t>& sprite, const float opacity=1) {
  47537. return draw_image(0,sprite,opacity);
  47538. }
  47539. //! Draw a masked image.
  47540. /**
  47541. \param sprite Sprite image.
  47542. \param mask Mask image.
  47543. \param x0 X-coordinate of the sprite position in the image instance.
  47544. \param y0 Y-coordinate of the sprite position in the image instance.
  47545. \param z0 Z-coordinate of the sprite position in the image instance.
  47546. \param c0 C-coordinate of the sprite position in the image instance.
  47547. \param mask_max_value Maximum pixel value of the mask image \c mask.
  47548. \param opacity Drawing opacity.
  47549. \note
  47550. - Pixel values of \c mask set the opacity of the corresponding pixels in \c sprite.
  47551. - Dimensions along x,y and z of \p sprite and \p mask must be the same.
  47552. **/
  47553. template<typename ti, typename tm>
  47554. CImg<T>& draw_image(const int x0, const int y0, const int z0, const int c0,
  47555. const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
  47556. const float mask_max_value=1) {
  47557. if (is_empty() || !sprite || !mask) return *this;
  47558. if (is_overlapped(sprite)) return draw_image(x0,y0,z0,c0,+sprite,mask,opacity,mask_max_value);
  47559. if (is_overlapped(mask)) return draw_image(x0,y0,z0,c0,sprite,+mask,opacity,mask_max_value);
  47560. if (mask._width!=sprite._width || mask._height!=sprite._height || mask._depth!=sprite._depth)
  47561. throw CImgArgumentException(_cimg_instance
  47562. "draw_image(): Sprite (%u,%u,%u,%u,%p) and mask (%u,%u,%u,%u,%p) have "
  47563. "incompatible dimensions.",
  47564. cimg_instance,
  47565. sprite._width,sprite._height,sprite._depth,sprite._spectrum,sprite._data,
  47566. mask._width,mask._height,mask._depth,mask._spectrum,mask._data);
  47567. const bool bx = x0<0, by = y0<0, bz = z0<0, bc = c0<0;
  47568. const int
  47569. dx0 = bx?0:x0, dy0 = by?0:y0, dz0 = bz?0:z0, dc0 = bc?0:c0,
  47570. sx0 = dx0 - x0, sy0 = dy0 - y0, sz0 = dz0 - z0, sc0 = dc0 - c0,
  47571. lx = sprite.width() - sx0 - (x0 + sprite.width()>width()?x0 + sprite.width() - width():0),
  47572. ly = sprite.height() - sy0 - (y0 + sprite.height()>height()?y0 + sprite.height() - height():0),
  47573. lz = sprite.depth() - sz0 - (z0 + sprite.depth()>depth()?z0 + sprite.depth() - depth():0),
  47574. lc = sprite.spectrum() - sc0 - (c0 + sprite.spectrum()>spectrum()?c0 + sprite.spectrum() - spectrum():0);
  47575. const ulongT msize = mask.size();
  47576. if (lx>0 && ly>0 && lz>0 && lc>0) {
  47577. for (int c = 0; c<lc; ++c)
  47578. for (int z = 0; z<lz; ++z)
  47579. for (int y = 0; y<ly; ++y) {
  47580. T *ptrd = data(dx0,dy0 + y,dz0 + z,dc0 + c);
  47581. const ti *ptrs = sprite.data(sx0,sy0 + y,sz0 + z,sc0 + c);
  47582. const tm *ptrm = mask._data + (mask.offset(sx0,sy0 + y,sz0 + z,sc0 + c)%msize);
  47583. for (int x = 0; x<lx; ++x) {
  47584. const float mopacity = (float)(*(ptrm++)*opacity),
  47585. nopacity = cimg::abs(mopacity), copacity = mask_max_value - std::max(mopacity,0.f);
  47586. *ptrd = (T)((nopacity*(*(ptrs++)) + *ptrd*copacity)/mask_max_value);
  47587. ++ptrd;
  47588. }
  47589. }
  47590. }
  47591. return *this;
  47592. }
  47593. //! Draw a masked image \overloading.
  47594. template<typename ti, typename tm>
  47595. CImg<T>& draw_image(const int x0, const int y0, const int z0,
  47596. const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
  47597. const float mask_max_value=1) {
  47598. return draw_image(x0,y0,z0,0,sprite,mask,opacity,mask_max_value);
  47599. }
  47600. //! Draw a image \overloading.
  47601. template<typename ti, typename tm>
  47602. CImg<T>& draw_image(const int x0, const int y0,
  47603. const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
  47604. const float mask_max_value=1) {
  47605. return draw_image(x0,y0,0,sprite,mask,opacity,mask_max_value);
  47606. }
  47607. //! Draw a image \overloading.
  47608. template<typename ti, typename tm>
  47609. CImg<T>& draw_image(const int x0,
  47610. const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
  47611. const float mask_max_value=1) {
  47612. return draw_image(x0,0,sprite,mask,opacity,mask_max_value);
  47613. }
  47614. //! Draw an image.
  47615. template<typename ti, typename tm>
  47616. CImg<T>& draw_image(const CImg<ti>& sprite, const CImg<tm>& mask, const float opacity=1,
  47617. const float mask_max_value=1) {
  47618. return draw_image(0,sprite,mask,opacity,mask_max_value);
  47619. }
  47620. //! Draw a text string.
  47621. /**
  47622. \param x0 X-coordinate of the text in the image instance.
  47623. \param y0 Y-coordinate of the text in the image instance.
  47624. \param text Format of the text ('printf'-style format string).
  47625. \param foreground_color Pointer to \c spectrum() consecutive values, defining the foreground drawing color.
  47626. \param background_color Pointer to \c spectrum() consecutive values, defining the background drawing color.
  47627. \param opacity Drawing opacity.
  47628. \param font Font used for drawing text.
  47629. **/
  47630. template<typename tc1, typename tc2, typename t>
  47631. CImg<T>& draw_text(const int x0, const int y0,
  47632. const char *const text,
  47633. const tc1 *const foreground_color, const tc2 *const background_color,
  47634. const float opacity, const CImgList<t>* const font, ...) {
  47635. if (!font || !*font) return *this;
  47636. CImg<charT> tmp(2048);
  47637. std::va_list ap; va_start(ap,font); cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
  47638. return _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,*font,false);
  47639. }
  47640. //! Draw a text string \overloading.
  47641. /**
  47642. \note A transparent background is used for the text.
  47643. **/
  47644. template<typename tc, typename t>
  47645. CImg<T>& draw_text(const int x0, const int y0,
  47646. const char *const text,
  47647. const tc *const foreground_color, const int,
  47648. const float opacity, const CImgList<t>* const font, ...) {
  47649. if (!font || !*font) return *this;
  47650. CImg<charT> tmp(2048);
  47651. std::va_list ap; va_start(ap,font); cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
  47652. return _draw_text(x0,y0,tmp,foreground_color,(tc*)0,opacity,*font,false);
  47653. }
  47654. //! Draw a text string \overloading.
  47655. /**
  47656. \note A transparent foreground is used for the text.
  47657. **/
  47658. template<typename tc, typename t>
  47659. CImg<T>& draw_text(const int x0, const int y0,
  47660. const char *const text,
  47661. const int, const tc *const background_color,
  47662. const float opacity, const CImgList<t>* const font, ...) {
  47663. if (!font || !*font) return *this;
  47664. CImg<charT> tmp(2048);
  47665. std::va_list ap; va_start(ap,font); cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
  47666. return _draw_text(x0,y0,tmp,(tc*)0,background_color,opacity,*font,false);
  47667. }
  47668. //! Draw a text string \overloading.
  47669. /**
  47670. \param x0 X-coordinate of the text in the image instance.
  47671. \param y0 Y-coordinate of the text in the image instance.
  47672. \param text Format of the text ('printf'-style format string).
  47673. \param foreground_color Array of spectrum() values of type \c T,
  47674. defining the foreground color (0 means 'transparent').
  47675. \param background_color Array of spectrum() values of type \c T,
  47676. defining the background color (0 means 'transparent').
  47677. \param opacity Drawing opacity.
  47678. \param font_height Height of the text font (exact match for 13,32,64,128, interpolated otherwise).
  47679. **/
  47680. template<typename tc1, typename tc2>
  47681. CImg<T>& draw_text(const int x0, const int y0,
  47682. const char *const text,
  47683. const tc1 *const foreground_color, const tc2 *const background_color,
  47684. const float opacity=1, const unsigned int font_height=13, ...) {
  47685. if (!font_height) return *this;
  47686. CImg<charT> tmp(2048);
  47687. std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
  47688. const CImgList<ucharT>& font = CImgList<ucharT>::font(font_height,true);
  47689. _draw_text(x0,y0,tmp,foreground_color,background_color,opacity,font,true);
  47690. return *this;
  47691. }
  47692. //! Draw a text string \overloading.
  47693. template<typename tc>
  47694. CImg<T>& draw_text(const int x0, const int y0,
  47695. const char *const text,
  47696. const tc *const foreground_color, const int background_color=0,
  47697. const float opacity=1, const unsigned int font_height=13, ...) {
  47698. if (!font_height) return *this;
  47699. cimg::unused(background_color);
  47700. CImg<charT> tmp(2048);
  47701. std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
  47702. return draw_text(x0,y0,"%s",foreground_color,(const tc*)0,opacity,font_height,tmp._data);
  47703. }
  47704. //! Draw a text string \overloading.
  47705. template<typename tc>
  47706. CImg<T>& draw_text(const int x0, const int y0,
  47707. const char *const text,
  47708. const int, const tc *const background_color,
  47709. const float opacity=1, const unsigned int font_height=13, ...) {
  47710. if (!font_height) return *this;
  47711. CImg<charT> tmp(2048);
  47712. std::va_list ap; va_start(ap,font_height); cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
  47713. return draw_text(x0,y0,"%s",(tc*)0,background_color,opacity,font_height,tmp._data);
  47714. }
  47715. template<typename tc1, typename tc2, typename t>
  47716. CImg<T>& _draw_text(const int x0, const int y0,
  47717. const char *const text,
  47718. const tc1 *const foreground_color, const tc2 *const background_color,
  47719. const float opacity, const CImgList<t>& font,
  47720. const bool is_native_font) {
  47721. if (!text || !font) return *this;
  47722. const unsigned int text_length = (unsigned int)std::strlen(text);
  47723. const int padding_x = font[0]._height<48?1:font[0]._height<128?(int)std::ceil(font[0]._height/51.0f + 0.745f):4;
  47724. unsigned char o_ch, ch = 0;
  47725. int x, y, w;
  47726. CImg<intT> left_paddings(text_length,1,1,1,0);
  47727. const CImg<t> empty = CImg<t>::empty();
  47728. if (is_empty() || is_native_font) {
  47729. // Pre-compute necessary size of the image as well as left paddings of each character.
  47730. x = y = w = 0;
  47731. o_ch = 0;
  47732. for (unsigned int i = 0; i<text_length; ++i) {
  47733. ch = (unsigned char)text[i];
  47734. switch (ch) {
  47735. case '\n' :
  47736. if (font._width>10) y+=font[10]._height; else y+=font[0]._height;
  47737. if (x>w) w = x;
  47738. x = 0;
  47739. break;
  47740. case '\t' :
  47741. if (font._width>32) x+=4*font[32]._width; else x+=4*font[0]._width;
  47742. break;
  47743. case ' ' :
  47744. if (font._width>32) x+=font[32]._width; else x+=font[0]._width;
  47745. break;
  47746. default : if (ch<font._width) {
  47747. int left_padding = 0;
  47748. if (is_native_font && font[0]._height<128) { /// Determine left padding for native fonts.
  47749. if (ch==':' || ch=='!' || ch=='.' || ch==';')
  47750. left_padding = 2*padding_x;
  47751. else if (o_ch==',' || (o_ch=='.' && (ch<'0' || ch>'9')) || o_ch==';' || o_ch==':' || o_ch=='!')
  47752. left_padding = 4*padding_x;
  47753. else if (((o_ch=='i' || o_ch=='l' || o_ch=='I' || o_ch=='J' || o_ch=='M' || o_ch=='N') &&
  47754. ((ch>='0' && ch<='9') ||
  47755. (ch>='a' && ch<='z' && ch!='v' && ch!='x' && ch!='y') ||
  47756. (ch>='B' && ch<='Z' && ch!='J' && ch!='T' && ch!='V' && ch!='X' && ch!='Y'))) ||
  47757. o_ch=='.' || o_ch=='\'' || ch=='\'')
  47758. left_padding = padding_x;
  47759. else if ((o_ch<'0' || o_ch>'9') && ch!='-') {
  47760. const CImg<t> &mask = ch + 256U<font._width?font[ch + 256]:empty;
  47761. if (o_ch && ch>' ' && o_ch>' ' && mask._height>13) {
  47762. const CImg<t> &o_mask = o_ch + 256U<font._width?font[o_ch + 256]:empty;
  47763. if (o_mask._height>13) {
  47764. const int w1 = mask.width()>0?o_mask.width() - 1:0, w2 = w1>1?w1 - 1:0, w3 = w2>1?w2 - 1:0;
  47765. left_padding = -10;
  47766. cimg_forY(mask,k) {
  47767. const int
  47768. lpad = o_mask(w1,k)>=8?0:
  47769. o_mask._width<=2 || o_mask(w2,k)>=8?-1:
  47770. o_mask._width<=3 || o_mask(w3,k)>=8?-2:-3,
  47771. rpad = mask(0,k)>=8?0:
  47772. mask._width<=2 || mask(1,k)>=8?-1:
  47773. mask._width<=3 || mask(2,k)>=8?-2:-3;
  47774. left_padding = std::max(left_padding,lpad + rpad);
  47775. }
  47776. }
  47777. }
  47778. }
  47779. left_paddings[i] = left_padding;
  47780. }
  47781. x+=left_padding + font[ch]._width + padding_x;
  47782. o_ch = ch;
  47783. }
  47784. }
  47785. }
  47786. if (x!=0 || ch=='\n') { if (x>w) w = x; y+=font[0]._height; }
  47787. if (is_empty()) assign(x0 + w,y0 + y,1,is_native_font?1:font[0]._spectrum,(T)0);
  47788. }
  47789. // Draw font characters on image.
  47790. x = x0; y = y0;
  47791. for (unsigned int i = 0; i<text_length; ++i) {
  47792. ch = (unsigned char)text[i];
  47793. switch (ch) {
  47794. case '\n' :
  47795. if (font._width>10) y+=font[10]._height; else y+=font[0]._height;
  47796. x = x0;
  47797. break;
  47798. case '\t' :
  47799. case ' ' : {
  47800. const unsigned int
  47801. lw = (ch=='\t'?4:1)*font[font._width>32?32:0]._width,
  47802. lh = font[font._width>32?32:0]._height;
  47803. if (background_color) draw_rectangle(x,y,x + lw - 1,y + lh - 1,background_color,opacity);
  47804. x+=lw;
  47805. } break;
  47806. default : if (ch<font._width) {
  47807. if (font[ch]) {
  47808. CImg<T> letter = font[ch];
  47809. const CImg<t> &mask = ch + 256U<font._width?font[ch + 256]:empty;
  47810. const int posx = x + left_paddings[i] + padding_x;
  47811. for (unsigned int c = 0; c<_spectrum; c+=letter._spectrum) {
  47812. if (c) letter = font[ch];
  47813. const unsigned int cmin = std::min(_spectrum - c,letter._spectrum);
  47814. for (unsigned int d = 0; d<cmin; ++d)
  47815. if (foreground_color[c + d]!=255)
  47816. letter.get_shared_channel(d)*=foreground_color[c + d]/255.0f;
  47817. if (mask) { // Letter with alpha
  47818. if (background_color)
  47819. for (unsigned int d = 0; d<cmin; ++d)
  47820. draw_rectangle(x,y,0,c + d,posx + letter._width - 1,y + letter._height - 1,0,c + d,
  47821. background_color[c + d],opacity);
  47822. draw_image(posx,y,0,c,letter,mask,opacity,255.f);
  47823. } else // Letter without alpha
  47824. draw_image(posx,y,0,c,letter,opacity);
  47825. }
  47826. x = posx + letter._width;
  47827. }
  47828. }
  47829. }
  47830. }
  47831. return *this;
  47832. }
  47833. // [internal] Version used to display text in interactive viewers.
  47834. CImg<T>& __draw_text(const char *const text, unsigned int &font_size, const int is_down, ...) {
  47835. CImg<charT> tmp(2048);
  47836. std::va_list ap;
  47837. va_start(ap,is_down);
  47838. cimg_vsnprintf(tmp,tmp._width,text,ap); va_end(ap);
  47839. CImg<ucharT> a_label, a_labelmask;
  47840. const unsigned char a_labelcolor = 255;
  47841. unsigned int ofs = font_size, fs = ofs;
  47842. do { // Determine best font size
  47843. a_label.assign().draw_text(0,0,"%s",&a_labelcolor,0,1,fs,tmp._data);
  47844. if (a_label._width<7*_width/10 && a_label._height>_height/20 && a_label._height<_height/5) {
  47845. font_size = fs; break;
  47846. } else if ((a_label._width>7*_width/10 || a_label._height>_height/5) && fs>13 && ofs>=fs) {
  47847. ofs = fs; fs = std::max(13U,(unsigned int)cimg::round(fs/1.25f));
  47848. } else if (a_label._width<3*_width/10 && a_label._height<_height/20 && fs<64 && ofs<=fs) {
  47849. ofs = fs; fs = std::min(64U,(unsigned int)cimg::round(fs*1.25f));
  47850. } else { font_size = fs; break; }
  47851. } while (true);
  47852. a_label.normalize(0,255);
  47853. a_label+=(255 - a_label.get_dilate(3)).normalize(0,80);
  47854. a_label.resize(-100,-100,1,3,1);
  47855. return draw_image(0,is_down?height() - a_label.height():0,a_label,0.85f);
  47856. }
  47857. //! Draw a 2D vector field.
  47858. /**
  47859. \param flow Image of 2D vectors used as input data.
  47860. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  47861. \param opacity Drawing opacity.
  47862. \param sampling Length (in pixels) between each arrow.
  47863. \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length).
  47864. \param is_arrow Tells if arrows must be drawn, instead of oriented segments.
  47865. \param pattern Used pattern to draw lines.
  47866. \note Clipping is supported.
  47867. **/
  47868. template<typename t1, typename t2>
  47869. CImg<T>& draw_quiver(const CImg<t1>& flow,
  47870. const t2 *const color, const float opacity=1,
  47871. const unsigned int sampling=25, const float factor=-20,
  47872. const bool is_arrow=true, const unsigned int pattern=~0U) {
  47873. return draw_quiver(flow,CImg<t2>(color,_spectrum,1,1,1,true),opacity,sampling,factor,is_arrow,pattern);
  47874. }
  47875. //! Draw a 2D vector field, using a field of colors.
  47876. /**
  47877. \param flow Image of 2D vectors used as input data.
  47878. \param color Image of spectrum()-D vectors corresponding to the color of each arrow.
  47879. \param opacity Opacity of the drawing.
  47880. \param sampling Length (in pixels) between each arrow.
  47881. \param factor Length factor of each arrow (if <0, computed as a percentage of the maximum length).
  47882. \param is_arrow Tells if arrows must be drawn, instead of oriented segments.
  47883. \param pattern Used pattern to draw lines.
  47884. \note Clipping is supported.
  47885. **/
  47886. template<typename t1, typename t2>
  47887. CImg<T>& draw_quiver(const CImg<t1>& flow,
  47888. const CImg<t2>& color, const float opacity=1,
  47889. const unsigned int sampling=25, const float factor=-20,
  47890. const bool is_arrow=true, const unsigned int pattern=~0U) {
  47891. if (is_empty()) return *this;
  47892. if (!flow || flow._spectrum!=2)
  47893. throw CImgArgumentException(_cimg_instance
  47894. "draw_quiver(): Invalid dimensions of specified flow (%u,%u,%u,%u,%p).",
  47895. cimg_instance,
  47896. flow._width,flow._height,flow._depth,flow._spectrum,flow._data);
  47897. if (sampling<=0)
  47898. throw CImgArgumentException(_cimg_instance
  47899. "draw_quiver(): Invalid sampling value %g "
  47900. "(should be >0)",
  47901. cimg_instance,
  47902. sampling);
  47903. const bool colorfield = (color._width==flow._width && color._height==flow._height &&
  47904. color._depth==1 && color._spectrum==_spectrum);
  47905. if (is_overlapped(flow)) return draw_quiver(+flow,color,opacity,sampling,factor,is_arrow,pattern);
  47906. float vmax,fact;
  47907. if (factor<=0) {
  47908. float m, M = (float)flow.get_norm(2).max_min(m);
  47909. vmax = (float)std::max(cimg::abs(m),cimg::abs(M));
  47910. if (!vmax) vmax = 1;
  47911. fact = -factor;
  47912. } else { fact = factor; vmax = 1; }
  47913. for (unsigned int y = sampling/2; y<_height; y+=sampling)
  47914. for (unsigned int x = sampling/2; x<_width; x+=sampling) {
  47915. const unsigned int X = x*flow._width/_width, Y = y*flow._height/_height;
  47916. float u = (float)flow(X,Y,0,0)*fact/vmax, v = (float)flow(X,Y,0,1)*fact/vmax;
  47917. if (is_arrow) {
  47918. const int xx = (int)(x + u), yy = (int)(y + v);
  47919. if (colorfield) draw_arrow(x,y,xx,yy,color.get_vector_at(X,Y)._data,opacity,45,sampling/5.f,pattern);
  47920. else draw_arrow(x,y,xx,yy,color._data,opacity,45,sampling/5.f,pattern);
  47921. } else {
  47922. if (colorfield)
  47923. draw_line((int)(x - 0.5*u),(int)(y - 0.5*v),(int)(x + 0.5*u),(int)(y + 0.5*v),
  47924. color.get_vector_at(X,Y)._data,opacity,pattern);
  47925. else draw_line((int)(x - 0.5*u),(int)(y - 0.5*v),(int)(x + 0.5*u),(int)(y + 0.5*v),
  47926. color._data,opacity,pattern);
  47927. }
  47928. }
  47929. return *this;
  47930. }
  47931. //! Draw a labeled horizontal axis.
  47932. /**
  47933. \param values_x Values along the horizontal axis.
  47934. \param y Y-coordinate of the horizontal axis in the image instance.
  47935. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  47936. \param opacity Drawing opacity.
  47937. \param pattern Drawing pattern.
  47938. \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise).
  47939. \param allow_zero Enable/disable the drawing of label '0' if found.
  47940. **/
  47941. template<typename t, typename tc>
  47942. CImg<T>& draw_axis(const CImg<t>& values_x, const int y,
  47943. const tc *const color, const float opacity=1,
  47944. const unsigned int pattern=~0U, const unsigned int font_height=13,
  47945. const bool allow_zero=true, const float round_x=0) {
  47946. if (is_empty()) return *this;
  47947. const int yt = (y + 3 + font_height)<_height?y + 3:y - 2 - (int)font_height;
  47948. const int siz = (int)values_x.size() - 1;
  47949. CImg<charT> txt(32);
  47950. CImg<T> a_label;
  47951. if (siz<=0) { // Degenerated case
  47952. draw_line(0,y,_width - 1,y,color,opacity,pattern);
  47953. if (!siz) {
  47954. cimg_snprintf(txt,txt._width,"%g",round_x?cimg::round((double)*values_x,round_x):(double)*values_x);
  47955. a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
  47956. const int
  47957. _xt = (width() - a_label.width())/2,
  47958. xt = _xt<3?3:_xt + a_label.width()>=width() - 2?width() - 3 - a_label.width():_xt;
  47959. draw_point(width()/2,y - 1,color,opacity).draw_point(width()/2,y + 1,color,opacity);
  47960. if (allow_zero || *txt!='0' || txt[1]!=0)
  47961. draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
  47962. }
  47963. } else { // Regular case
  47964. if (values_x[0]<values_x[siz]) draw_arrow(0,y,_width - 1,y,color,opacity,30,5,pattern);
  47965. else draw_arrow(_width - 1,y,0,y,color,opacity,30,5,pattern);
  47966. cimg_foroff(values_x,x) {
  47967. cimg_snprintf(txt,txt._width,"%g",round_x?cimg::round((double)values_x(x),round_x):(double)values_x(x));
  47968. a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
  47969. const int
  47970. xi = (int)(x*(_width - 1)/siz),
  47971. _xt = xi - a_label.width()/2,
  47972. xt = _xt<3?3:_xt + a_label.width()>=width() - 2?width() - 3 - a_label.width():_xt;
  47973. draw_point(xi,y - 1,color,opacity).draw_point(xi,y + 1,color,opacity);
  47974. if (allow_zero || *txt!='0' || txt[1]!=0)
  47975. draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
  47976. }
  47977. }
  47978. return *this;
  47979. }
  47980. //! Draw a labeled vertical axis.
  47981. /**
  47982. \param x X-coordinate of the vertical axis in the image instance.
  47983. \param values_y Values along the Y-axis.
  47984. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  47985. \param opacity Drawing opacity.
  47986. \param pattern Drawing pattern.
  47987. \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise).
  47988. \param allow_zero Enable/disable the drawing of label '0' if found.
  47989. **/
  47990. template<typename t, typename tc>
  47991. CImg<T>& draw_axis(const int x, const CImg<t>& values_y,
  47992. const tc *const color, const float opacity=1,
  47993. const unsigned int pattern=~0U, const unsigned int font_height=13,
  47994. const bool allow_zero=true, const float round_y=0) {
  47995. if (is_empty()) return *this;
  47996. int siz = (int)values_y.size() - 1;
  47997. CImg<charT> txt(32);
  47998. CImg<T> a_label;
  47999. if (siz<=0) { // Degenerated case
  48000. draw_line(x,0,x,_height - 1,color,opacity,pattern);
  48001. if (!siz) {
  48002. cimg_snprintf(txt,txt._width,"%g",round_y?cimg::round((double)*values_y,round_y):(double)*values_y);
  48003. a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
  48004. const int
  48005. _yt = (height() - a_label.height())/2,
  48006. yt = _yt<0?0:_yt + a_label.height()>=height()?height() - 1 - a_label.height():_yt,
  48007. _xt = x - 2 - a_label.width(),
  48008. xt = _xt>=0?_xt:x + 3;
  48009. draw_point(x - 1,height()/2,color,opacity).draw_point(x + 1,height()/2,color,opacity);
  48010. if (allow_zero || *txt!='0' || txt[1]!=0)
  48011. draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
  48012. }
  48013. } else { // Regular case
  48014. if (values_y[0]<values_y[siz]) draw_arrow(x,0,x,_height - 1,color,opacity,30,5,pattern);
  48015. else draw_arrow(x,_height - 1,x,0,color,opacity,30,5,pattern);
  48016. cimg_foroff(values_y,y) {
  48017. cimg_snprintf(txt,txt._width,"%g",round_y?cimg::round((double)values_y(y),round_y):(double)values_y(y));
  48018. a_label.assign().draw_text(0,0,txt,color,(tc*)0,opacity,font_height);
  48019. const int
  48020. yi = (int)(y*(_height - 1)/siz),
  48021. _yt = yi - a_label.height()/2,
  48022. yt = _yt<0?0:_yt + a_label.height()>=height()?height() - 1 - a_label.height():_yt,
  48023. _xt = x - 2 - a_label.width(),
  48024. xt = _xt>=0?_xt:x + 3;
  48025. draw_point(x - 1,yi,color,opacity).draw_point(x + 1,yi,color,opacity);
  48026. if (allow_zero || *txt!='0' || txt[1]!=0)
  48027. draw_text(xt,yt,txt,color,(tc*)0,opacity,font_height);
  48028. }
  48029. }
  48030. return *this;
  48031. }
  48032. //! Draw labeled horizontal and vertical axes.
  48033. /**
  48034. \param values_x Values along the X-axis.
  48035. \param values_y Values along the Y-axis.
  48036. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  48037. \param opacity Drawing opacity.
  48038. \param pattern_x Drawing pattern for the X-axis.
  48039. \param pattern_y Drawing pattern for the Y-axis.
  48040. \param font_height Height of the labels (exact match for 13,23,53,103, interpolated otherwise).
  48041. \param allow_zero Enable/disable the drawing of label '0' if found.
  48042. **/
  48043. template<typename tx, typename ty, typename tc>
  48044. CImg<T>& draw_axes(const CImg<tx>& values_x, const CImg<ty>& values_y,
  48045. const tc *const color, const float opacity=1,
  48046. const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U,
  48047. const unsigned int font_height=13, const bool allow_zero=true,
  48048. const float round_x=0, const float round_y=0) {
  48049. if (is_empty()) return *this;
  48050. const CImg<tx> nvalues_x(values_x._data,values_x.size(),1,1,1,true);
  48051. const int sizx = (int)values_x.size() - 1, wm1 = width() - 1;
  48052. if (sizx>=0) {
  48053. float ox = (float)*nvalues_x;
  48054. for (unsigned int x = sizx?1U:0U; x<_width; ++x) {
  48055. const float nx = (float)nvalues_x._linear_atX((float)x*sizx/wm1);
  48056. if (nx*ox<=0) {
  48057. draw_axis(nx==0?x:x - 1,values_y,color,opacity,pattern_y,font_height,allow_zero,round_y);
  48058. break;
  48059. }
  48060. ox = nx;
  48061. }
  48062. }
  48063. const CImg<ty> nvalues_y(values_y._data,values_y.size(),1,1,1,true);
  48064. const int sizy = (int)values_y.size() - 1, hm1 = height() - 1;
  48065. if (sizy>0) {
  48066. float oy = (float)nvalues_y[0];
  48067. for (unsigned int y = sizy?1U:0U; y<_height; ++y) {
  48068. const float ny = (float)nvalues_y._linear_atX((float)y*sizy/hm1);
  48069. if (ny*oy<=0) {
  48070. draw_axis(values_x,ny==0?y:y - 1,color,opacity,pattern_x,font_height,allow_zero,round_x);
  48071. break;
  48072. }
  48073. oy = ny;
  48074. }
  48075. }
  48076. return *this;
  48077. }
  48078. //! Draw labeled horizontal and vertical axes \overloading.
  48079. template<typename tc>
  48080. CImg<T>& draw_axes(const float x0, const float x1, const float y0, const float y1,
  48081. const tc *const color, const float opacity=1,
  48082. const int subdivisionx=-60, const int subdivisiony=-60,
  48083. const float precisionx=0, const float precisiony=0,
  48084. const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U,
  48085. const unsigned int font_height=13) {
  48086. if (is_empty()) return *this;
  48087. const bool allow_zero = (x0*x1>0) || (y0*y1>0);
  48088. const float
  48089. dx = cimg::abs(x1 - x0), dy = cimg::abs(y1 - y0),
  48090. px = dx<=0?1:precisionx==0?(float)std::pow(10.,(int)std::log10(dx) - 2.):precisionx,
  48091. py = dy<=0?1:precisiony==0?(float)std::pow(10.,(int)std::log10(dy) - 2.):precisiony;
  48092. if (x0!=x1 && y0!=y1)
  48093. draw_axes(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1),
  48094. CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1),
  48095. color,opacity,pattern_x,pattern_y,font_height,allow_zero,px,py);
  48096. else if (x0==x1 && y0!=y1)
  48097. draw_axis((int)x0,CImg<floatT>::sequence(subdivisiony>0?subdivisiony:1-height()/subdivisiony,y0,y1),
  48098. color,opacity,pattern_y,font_height,py);
  48099. else if (x0!=x1 && y0==y1)
  48100. draw_axis(CImg<floatT>::sequence(subdivisionx>0?subdivisionx:1-width()/subdivisionx,x0,x1),(int)y0,
  48101. color,opacity,pattern_x,font_height,px);
  48102. return *this;
  48103. }
  48104. //! Draw 2D grid.
  48105. /**
  48106. \param values_x X-coordinates of the vertical lines.
  48107. \param values_y Y-coordinates of the horizontal lines.
  48108. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  48109. \param opacity Drawing opacity.
  48110. \param pattern_x Drawing pattern for vertical lines.
  48111. \param pattern_y Drawing pattern for horizontal lines.
  48112. **/
  48113. template<typename tx, typename ty, typename tc>
  48114. CImg<T>& draw_grid(const CImg<tx>& values_x, const CImg<ty>& values_y,
  48115. const tc *const color, const float opacity=1,
  48116. const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) {
  48117. if (is_empty()) return *this;
  48118. if (values_x) cimg_foroff(values_x,x) {
  48119. const int xi = (int)values_x[x];
  48120. if (xi>=0 && xi<width()) draw_line(xi,0,xi,_height - 1,color,opacity,pattern_x);
  48121. }
  48122. if (values_y) cimg_foroff(values_y,y) {
  48123. const int yi = (int)values_y[y];
  48124. if (yi>=0 && yi<height()) draw_line(0,yi,_width - 1,yi,color,opacity,pattern_y);
  48125. }
  48126. return *this;
  48127. }
  48128. //! Draw 2D grid \simplification.
  48129. template<typename tc>
  48130. CImg<T>& draw_grid(const float delta_x, const float delta_y,
  48131. const float offsetx, const float offsety,
  48132. const bool invertx, const bool inverty,
  48133. const tc *const color, const float opacity=1,
  48134. const unsigned int pattern_x=~0U, const unsigned int pattern_y=~0U) {
  48135. if (is_empty()) return *this;
  48136. CImg<uintT> seqx, seqy;
  48137. if (delta_x!=0) {
  48138. const float dx = delta_x>0?delta_x:_width*-delta_x/100;
  48139. const unsigned int nx = (unsigned int)(_width/dx);
  48140. seqx = CImg<uintT>::sequence(1 + nx,0,(unsigned int)(dx*nx));
  48141. if (offsetx) cimg_foroff(seqx,x) seqx(x) = (unsigned int)cimg::mod(seqx(x) + offsetx,(float)_width);
  48142. if (invertx) cimg_foroff(seqx,x) seqx(x) = _width - 1 - seqx(x);
  48143. }
  48144. if (delta_y!=0) {
  48145. const float dy = delta_y>0?delta_y:_height*-delta_y/100;
  48146. const unsigned int ny = (unsigned int)(_height/dy);
  48147. seqy = CImg<uintT>::sequence(1 + ny,0,(unsigned int)(dy*ny));
  48148. if (offsety) cimg_foroff(seqy,y) seqy(y) = (unsigned int)cimg::mod(seqy(y) + offsety,(float)_height);
  48149. if (inverty) cimg_foroff(seqy,y) seqy(y) = _height - 1 - seqy(y);
  48150. }
  48151. return draw_grid(seqx,seqy,color,opacity,pattern_x,pattern_y);
  48152. }
  48153. //! Draw 1D graph.
  48154. /**
  48155. \param data Image containing the graph values I = f(x).
  48156. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  48157. \param opacity Drawing opacity.
  48158. \param plot_type Define the type of the plot:
  48159. - 0 = No plot.
  48160. - 1 = Plot using segments.
  48161. - 2 = Plot using cubic splines.
  48162. - 3 = Plot with bars.
  48163. \param vertex_type Define the type of points:
  48164. - 0 = No points.
  48165. - 1 = Point.
  48166. - 2 = Straight cross.
  48167. - 3 = Diagonal cross.
  48168. - 4 = Filled circle.
  48169. - 5 = Outlined circle.
  48170. - 6 = Square.
  48171. - 7 = Diamond.
  48172. \param ymin Lower bound of the y-range.
  48173. \param ymax Upper bound of the y-range.
  48174. \param pattern Drawing pattern.
  48175. \note
  48176. - if \c ymin==ymax==0, the y-range is computed automatically from the input samples.
  48177. **/
  48178. template<typename t, typename tc>
  48179. CImg<T>& draw_graph(const CImg<t>& data,
  48180. const tc *const color, const float opacity=1,
  48181. const unsigned int plot_type=1, const int vertex_type=1,
  48182. const double ymin=0, const double ymax=0, const unsigned int pattern=~0U) {
  48183. if (is_empty() || _height<=1) return *this;
  48184. if (!color)
  48185. throw CImgArgumentException(_cimg_instance
  48186. "draw_graph(): Specified color is (null).",
  48187. cimg_instance);
  48188. // Create shaded colors for displaying bar plots.
  48189. CImg<tc> color1, color2;
  48190. if (plot_type==3) {
  48191. color1.assign(_spectrum); color2.assign(_spectrum);
  48192. cimg_forC(*this,c) {
  48193. color1[c] = (tc)std::min((float)cimg::type<tc>::max(),(float)color[c]*1.2f);
  48194. color2[c] = (tc)(color[c]*0.4f);
  48195. }
  48196. }
  48197. // Compute min/max and normalization factors.
  48198. const ulongT
  48199. siz = data.size(),
  48200. _siz1 = siz - (plot_type!=3),
  48201. siz1 = _siz1?_siz1:1;
  48202. const unsigned int
  48203. _width1 = _width - (plot_type!=3),
  48204. width1 = _width1?_width1:1;
  48205. double m = ymin, M = ymax;
  48206. if (ymin==ymax) m = (double)data.max_min(M);
  48207. if (m==M) { --m; ++M; }
  48208. const float ca = (float)(M-m)/(_height - 1);
  48209. bool init_hatch = true;
  48210. // Draw graph edges
  48211. switch (plot_type%4) {
  48212. case 1 : { // Segments
  48213. int oX = 0, oY = (int)cimg::round((data[0] - m)/ca);
  48214. if (siz==1) {
  48215. const int Y = (int)cimg::round((*data - m)/ca);
  48216. draw_line(0,Y,width() - 1,Y,color,opacity,pattern);
  48217. } else {
  48218. const float fx = (float)_width/siz1;
  48219. for (ulongT off = 1; off<siz; ++off) {
  48220. const int
  48221. X = (int)cimg::round(off*fx) - 1,
  48222. Y = (int)cimg::round((data[off]-m)/ca);
  48223. draw_line(oX,oY,X,Y,color,opacity,pattern,init_hatch);
  48224. oX = X; oY = Y;
  48225. init_hatch = false;
  48226. }
  48227. }
  48228. } break;
  48229. case 2 : { // Spline
  48230. const CImg<t> ndata(data._data,siz,1,1,1,true);
  48231. int oY = (int)cimg::round((data[0] - m)/ca);
  48232. cimg_forX(*this,x) {
  48233. const int Y = (int)cimg::round((ndata._cubic_atX((float)x*siz1/width1)-m)/ca);
  48234. if (x>0) draw_line(x,oY,x + 1,Y,color,opacity,pattern,init_hatch);
  48235. init_hatch = false;
  48236. oY = Y;
  48237. }
  48238. } break;
  48239. case 3 : { // Bars
  48240. const int Y0 = (int)cimg::round(-m/ca);
  48241. const float fx = (float)_width/siz1;
  48242. int oX = 0;
  48243. cimg_foroff(data,off) {
  48244. const int
  48245. X = (int)cimg::round((off + 1)*fx) - 1,
  48246. Y = (int)cimg::round((data[off] - m)/ca);
  48247. draw_rectangle(oX,Y0,X,Y,color,opacity).
  48248. draw_line(oX,Y,oX,Y0,color2.data(),opacity).
  48249. draw_line(oX,Y0,X,Y0,Y<=Y0?color2.data():color1.data(),opacity).
  48250. draw_line(X,Y,X,Y0,color1.data(),opacity).
  48251. draw_line(oX,Y,X,Y,Y<=Y0?color1.data():color2.data(),opacity);
  48252. oX = X + 1;
  48253. }
  48254. } break;
  48255. default : break; // No edges
  48256. }
  48257. // Draw graph points
  48258. const unsigned int wb2 = plot_type==3?_width1/(2*siz):0;
  48259. const float fx = (float)_width1/siz1;
  48260. switch (vertex_type%8) {
  48261. case 1 : { // Point
  48262. cimg_foroff(data,off) {
  48263. const int
  48264. X = (int)cimg::round(off*fx + wb2),
  48265. Y = (int)cimg::round((data[off]-m)/ca);
  48266. draw_point(X,Y,color,opacity);
  48267. }
  48268. } break;
  48269. case 2 : { // Straight Cross
  48270. cimg_foroff(data,off) {
  48271. const int
  48272. X = (int)cimg::round(off*fx + wb2),
  48273. Y = (int)cimg::round((data[off]-m)/ca);
  48274. draw_line(X - 3,Y,X + 3,Y,color,opacity).draw_line(X,Y - 3,X,Y + 3,color,opacity);
  48275. }
  48276. } break;
  48277. case 3 : { // Diagonal Cross
  48278. cimg_foroff(data,off) {
  48279. const int
  48280. X = (int)cimg::round(off*fx + wb2),
  48281. Y = (int)cimg::round((data[off]-m)/ca);
  48282. draw_line(X - 3,Y - 3,X + 3,Y + 3,color,opacity).draw_line(X - 3,Y + 3,X + 3,Y - 3,color,opacity);
  48283. }
  48284. } break;
  48285. case 4 : { // Filled Circle
  48286. cimg_foroff(data,off) {
  48287. const int
  48288. X = (int)cimg::round(off*fx + wb2),
  48289. Y = (int)cimg::round((data[off]-m)/ca);
  48290. draw_circle(X,Y,3,color,opacity);
  48291. }
  48292. } break;
  48293. case 5 : { // Outlined circle
  48294. cimg_foroff(data,off) {
  48295. const int
  48296. X = (int)cimg::round(off*fx + wb2),
  48297. Y = (int)cimg::round((data[off]-m)/ca);
  48298. draw_circle(X,Y,3,color,opacity,~0U);
  48299. }
  48300. } break;
  48301. case 6 : { // Square
  48302. cimg_foroff(data,off) {
  48303. const int
  48304. X = (int)cimg::round(off*fx + wb2),
  48305. Y = (int)cimg::round((data[off]-m)/ca);
  48306. draw_rectangle(X - 3,Y - 3,X + 3,Y + 3,color,opacity,~0U);
  48307. }
  48308. } break;
  48309. case 7 : { // Diamond
  48310. cimg_foroff(data,off) {
  48311. const int
  48312. X = (int)cimg::round(off*fx + wb2),
  48313. Y = (int)cimg::round((data[off]-m)/ca);
  48314. draw_line(X,Y - 4,X + 4,Y,color,opacity).
  48315. draw_line(X + 4,Y,X,Y + 4,color,opacity).
  48316. draw_line(X,Y + 4,X - 4,Y,color,opacity).
  48317. draw_line(X - 4,Y,X,Y - 4,color,opacity);
  48318. }
  48319. } break;
  48320. default : break; // No points
  48321. }
  48322. return *this;
  48323. }
  48324. bool _draw_fill(const int x, const int y, const int z,
  48325. const CImg<T>& ref, const float tolerance2) const {
  48326. const T *ptr1 = data(x,y,z), *ptr2 = ref._data;
  48327. const ulongT off = _width*_height*_depth;
  48328. float diff = 0;
  48329. cimg_forC(*this,c) { diff += cimg::sqr(*ptr1 - *(ptr2++)); ptr1+=off; }
  48330. return diff<=tolerance2;
  48331. }
  48332. //! Draw filled 3D region with the flood fill algorithm.
  48333. /**
  48334. \param x0 X-coordinate of the starting point of the region to fill.
  48335. \param y0 Y-coordinate of the starting point of the region to fill.
  48336. \param z0 Z-coordinate of the starting point of the region to fill.
  48337. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  48338. \param[out] region Image that will contain the mask of the filled region mask, as an output.
  48339. \param tolerance Tolerance concerning neighborhood values.
  48340. \param opacity Opacity of the drawing.
  48341. \param is_high_connectivity Tells if 8-connexity must be used.
  48342. \return \c region is initialized with the binary mask of the filled region.
  48343. **/
  48344. template<typename tc, typename t>
  48345. CImg<T>& draw_fill(const int x0, const int y0, const int z0,
  48346. const tc *const color, const float opacity,
  48347. CImg<t> &region,
  48348. const float tolerance = 0,
  48349. const bool is_high_connectivity = false) {
  48350. #define _draw_fill_push(x,y,z) if (N>=stack._width) stack.resize(2*N + 1,1,1,3,0); \
  48351. stack[N] = x; stack(N,1) = y; stack(N++,2) = z
  48352. #define _draw_fill_pop(x,y,z) x = stack[--N]; y = stack(N,1); z = stack(N,2)
  48353. #define _draw_fill_is_inside(x,y,z) !_region(x,y,z) && _draw_fill(x,y,z,ref,tolerance2)
  48354. if (!containsXYZC(x0,y0,z0,0)) return *this;
  48355. const float nopacity = cimg::abs((float)opacity), copacity = 1 - std::max((float)opacity,0.f);
  48356. const float tolerance2 = cimg::sqr(tolerance);
  48357. const CImg<T> ref = get_vector_at(x0,y0,z0);
  48358. CImg<uintT> stack(256,1,1,3);
  48359. CImg<ucharT> _region(_width,_height,_depth,1,0);
  48360. unsigned int N = 0;
  48361. int x, y, z;
  48362. _draw_fill_push(x0,y0,z0);
  48363. while (N>0) {
  48364. _draw_fill_pop(x,y,z);
  48365. if (!_region(x,y,z)) {
  48366. const int yp = y - 1, yn = y + 1, zp = z - 1, zn = z + 1;
  48367. int xl = x, xr = x;
  48368. // Using these booleans reduces the number of pushes drastically.
  48369. bool is_yp = false, is_yn = false, is_zp = false, is_zn = false;
  48370. for (int step = -1; step<2; step+=2) {
  48371. while (x>=0 && x<width() && _draw_fill_is_inside(x,y,z)) {
  48372. if (yp>=0 && _draw_fill_is_inside(x,yp,z)) {
  48373. if (!is_yp) { _draw_fill_push(x,yp,z); is_yp = true; }
  48374. } else is_yp = false;
  48375. if (yn<height() && _draw_fill_is_inside(x,yn,z)) {
  48376. if (!is_yn) { _draw_fill_push(x,yn,z); is_yn = true; }
  48377. } else is_yn = false;
  48378. if (depth()>1) {
  48379. if (zp>=0 && _draw_fill_is_inside(x,y,zp)) {
  48380. if (!is_zp) { _draw_fill_push(x,y,zp); is_zp = true; }
  48381. } else is_zp = false;
  48382. if (zn<depth() && _draw_fill_is_inside(x,y,zn)) {
  48383. if (!is_zn) { _draw_fill_push(x,y,zn); is_zn = true; }
  48384. } else is_zn = false;
  48385. }
  48386. if (is_high_connectivity) {
  48387. const int xp = x - 1, xn = x + 1;
  48388. if (yp>=0 && !is_yp) {
  48389. if (xp>=0 && _draw_fill_is_inside(xp,yp,z)) {
  48390. _draw_fill_push(xp,yp,z); if (step<0) is_yp = true;
  48391. }
  48392. if (xn<width() && _draw_fill_is_inside(xn,yp,z)) {
  48393. _draw_fill_push(xn,yp,z); if (step>0) is_yp = true;
  48394. }
  48395. }
  48396. if (yn<height() && !is_yn) {
  48397. if (xp>=0 && _draw_fill_is_inside(xp,yn,z)) {
  48398. _draw_fill_push(xp,yn,z); if (step<0) is_yn = true;
  48399. }
  48400. if (xn<width() && _draw_fill_is_inside(xn,yn,z)) {
  48401. _draw_fill_push(xn,yn,z); if (step>0) is_yn = true;
  48402. }
  48403. }
  48404. if (depth()>1) {
  48405. if (zp>=0 && !is_zp) {
  48406. if (xp>=0 && _draw_fill_is_inside(xp,y,zp)) {
  48407. _draw_fill_push(xp,y,zp); if (step<0) is_zp = true;
  48408. }
  48409. if (xn<width() && _draw_fill_is_inside(xn,y,zp)) {
  48410. _draw_fill_push(xn,y,zp); if (step>0) is_zp = true;
  48411. }
  48412. if (yp>=0 && !is_yp) {
  48413. if (_draw_fill_is_inside(x,yp,zp)) { _draw_fill_push(x,yp,zp); }
  48414. if (xp>=0 && _draw_fill_is_inside(xp,yp,zp)) { _draw_fill_push(xp,yp,zp); }
  48415. if (xn<width() && _draw_fill_is_inside(xn,yp,zp)) { _draw_fill_push(xn,yp,zp); }
  48416. }
  48417. if (yn<height() && !is_yn) {
  48418. if (_draw_fill_is_inside(x,yn,zp)) { _draw_fill_push(x,yn,zp); }
  48419. if (xp>=0 && _draw_fill_is_inside(xp,yn,zp)) { _draw_fill_push(xp,yn,zp); }
  48420. if (xn<width() && _draw_fill_is_inside(xn,yn,zp)) { _draw_fill_push(xn,yn,zp); }
  48421. }
  48422. }
  48423. if (zn<depth() && !is_zn) {
  48424. if (xp>=0 && _draw_fill_is_inside(xp,y,zn)) {
  48425. _draw_fill_push(xp,y,zn); if (step<0) is_zn = true;
  48426. }
  48427. if (xn<width() && _draw_fill_is_inside(xn,y,zn)) {
  48428. _draw_fill_push(xn,y,zn); if (step>0) is_zn = true;
  48429. }
  48430. if (yp>=0 && !is_yp) {
  48431. if (_draw_fill_is_inside(x,yp,zn)) { _draw_fill_push(x,yp,zn); }
  48432. if (xp>=0 && _draw_fill_is_inside(xp,yp,zn)) { _draw_fill_push(xp,yp,zn); }
  48433. if (xn<width() && _draw_fill_is_inside(xn,yp,zn)) { _draw_fill_push(xn,yp,zn); }
  48434. }
  48435. if (yn<height() && !is_yn) {
  48436. if (_draw_fill_is_inside(x,yn,zn)) { _draw_fill_push(x,yn,zn); }
  48437. if (xp>=0 && _draw_fill_is_inside(xp,yn,zn)) { _draw_fill_push(xp,yn,zn); }
  48438. if (xn<width() && _draw_fill_is_inside(xn,yn,zn)) { _draw_fill_push(xn,yn,zn); }
  48439. }
  48440. }
  48441. }
  48442. }
  48443. x+=step;
  48444. }
  48445. if (step<0) { xl = ++x; x = xr + 1; is_yp = is_yn = is_zp = is_zn = false; }
  48446. else xr = --x;
  48447. }
  48448. std::memset(_region.data(xl,y,z),1,xr - xl + 1);
  48449. if (opacity==1) {
  48450. if (sizeof(T)==1) {
  48451. const int dx = xr - xl + 1;
  48452. cimg_forC(*this,c) std::memset(data(xl,y,z,c),(int)color[c],dx);
  48453. } else cimg_forC(*this,c) {
  48454. const T val = (T)color[c];
  48455. T *ptri = data(xl,y,z,c); for (int k = xl; k<=xr; ++k) *(ptri++) = val;
  48456. }
  48457. } else cimg_forC(*this,c) {
  48458. const T val = (T)(color[c]*nopacity);
  48459. T *ptri = data(xl,y,z,c); for (int k = xl; k<=xr; ++k) { *ptri = (T)(val + *ptri*copacity); ++ptri; }
  48460. }
  48461. }
  48462. }
  48463. _region.move_to(region);
  48464. return *this;
  48465. }
  48466. //! Draw filled 3D region with the flood fill algorithm \simplification.
  48467. template<typename tc>
  48468. CImg<T>& draw_fill(const int x0, const int y0, const int z0,
  48469. const tc *const color, const float opacity=1,
  48470. const float tolerance=0, const bool is_high_connexity=false) {
  48471. CImg<ucharT> tmp;
  48472. return draw_fill(x0,y0,z0,color,opacity,tmp,tolerance,is_high_connexity);
  48473. }
  48474. //! Draw filled 2D region with the flood fill algorithm \simplification.
  48475. template<typename tc>
  48476. CImg<T>& draw_fill(const int x0, const int y0,
  48477. const tc *const color, const float opacity=1,
  48478. const float tolerance=0, const bool is_high_connexity=false) {
  48479. CImg<ucharT> tmp;
  48480. return draw_fill(x0,y0,0,color,opacity,tmp,tolerance,is_high_connexity);
  48481. }
  48482. //! Draw a random plasma texture.
  48483. /**
  48484. \param alpha Alpha-parameter.
  48485. \param beta Beta-parameter.
  48486. \param scale Scale-parameter.
  48487. \note Use the mid-point algorithm to render.
  48488. **/
  48489. CImg<T>& draw_plasma(const float alpha=1, const float beta=0, const unsigned int scale=8) {
  48490. if (is_empty()) return *this;
  48491. const int w = width(), h = height();
  48492. const Tfloat m = (Tfloat)cimg::type<T>::min(), M = (Tfloat)cimg::type<T>::max();
  48493. cimg_uint64 rng = (cimg::_rand(),cimg::rng());
  48494. cimg_forZC(*this,z,c) {
  48495. CImg<T> ref = get_shared_slice(z,c);
  48496. for (int delta = 1<<std::min(scale,31U); delta>1; delta>>=1) {
  48497. const int delta2 = delta>>1;
  48498. const float r = alpha*delta + beta;
  48499. // Square step.
  48500. for (int y0 = 0; y0<h; y0+=delta)
  48501. for (int x0 = 0; x0<w; x0+=delta) {
  48502. const int x1 = (x0 + delta)%w, y1 = (y0 + delta)%h, xc = (x0 + delta2)%w, yc = (y0 + delta2)%h;
  48503. const Tfloat val = (Tfloat)(0.25f*(ref(x0,y0) + ref(x0,y1) + ref(x0,y1) + ref(x1,y1)) +
  48504. r*cimg::rand(-1,1,&rng));
  48505. ref(xc,yc) = (T)(val<m?m:val>M?M:val);
  48506. }
  48507. // Diamond steps.
  48508. for (int y = -delta2; y<h; y+=delta)
  48509. for (int x0=0; x0<w; x0+=delta) {
  48510. const int y0 = cimg::mod(y,h), x1 = (x0 + delta)%w, y1 = (y + delta)%h,
  48511. xc = (x0 + delta2)%w, yc = (y + delta2)%h;
  48512. const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) +
  48513. r*cimg::rand(-1,1,&rng));
  48514. ref(xc,yc) = (T)(val<m?m:val>M?M:val);
  48515. }
  48516. for (int y0 = 0; y0<h; y0+=delta)
  48517. for (int x = -delta2; x<w; x+=delta) {
  48518. const int x0 = cimg::mod(x,w), x1 = (x + delta)%w, y1 = (y0 + delta)%h,
  48519. xc = (x + delta2)%w, yc = (y0 + delta2)%h;
  48520. const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) +
  48521. r*cimg::rand(-1,1,&rng));
  48522. ref(xc,yc) = (T)(val<m?m:val>M?M:val);
  48523. }
  48524. for (int y = -delta2; y<h; y+=delta)
  48525. for (int x = -delta2; x<w; x+=delta) {
  48526. const int x0 = cimg::mod(x,w), y0 = cimg::mod(y,h), x1 = (x + delta)%w, y1 = (y + delta)%h,
  48527. xc = (x + delta2)%w, yc = (y + delta2)%h;
  48528. const Tfloat val = (Tfloat)(0.25f*(ref(xc,y0) + ref(x0,yc) + ref(xc,y1) + ref(x1,yc)) +
  48529. r*cimg::rand(-1,1,&rng));
  48530. ref(xc,yc) = (T)(val<m?m:val>M?M:val);
  48531. }
  48532. }
  48533. }
  48534. cimg::srand(rng);
  48535. return *this;
  48536. }
  48537. //! Draw a quadratic Mandelbrot or Julia 2D fractal.
  48538. /**
  48539. \param x0 X-coordinate of the upper-left pixel.
  48540. \param y0 Y-coordinate of the upper-left pixel.
  48541. \param x1 X-coordinate of the lower-right pixel.
  48542. \param y1 Y-coordinate of the lower-right pixel.
  48543. \param palette Colormap.
  48544. \param opacity Drawing opacity.
  48545. \param z0r Real part of the upper-left fractal vertex.
  48546. \param z0i Imaginary part of the upper-left fractal vertex.
  48547. \param z1r Real part of the lower-right fractal vertex.
  48548. \param z1i Imaginary part of the lower-right fractal vertex.
  48549. \param iteration_max Maximum number of iterations for each estimated point.
  48550. \param is_normalized_iteration Tells if iterations are normalized.
  48551. \param is_julia_set Tells if the Mandelbrot or Julia set is rendered.
  48552. \param param_r Real part of the Julia set parameter.
  48553. \param param_i Imaginary part of the Julia set parameter.
  48554. \note Fractal rendering is done by the Escape Time Algorithm.
  48555. **/
  48556. template<typename tc>
  48557. CImg<T>& draw_mandelbrot(const int x0, const int y0, const int x1, const int y1,
  48558. const CImg<tc>& colormap, const float opacity=1,
  48559. const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2,
  48560. const unsigned int iteration_max=255,
  48561. const bool is_normalized_iteration=false,
  48562. const bool is_julia_set=false,
  48563. const double param_r=0, const double param_i=0) {
  48564. if (is_empty()) return *this;
  48565. CImg<tc> palette;
  48566. if (colormap) palette.assign(colormap._data,colormap.size()/colormap._spectrum,1,1,colormap._spectrum,true);
  48567. if (palette && palette._spectrum!=_spectrum)
  48568. throw CImgArgumentException(_cimg_instance
  48569. "draw_mandelbrot(): Instance and specified colormap (%u,%u,%u,%u,%p) have "
  48570. "incompatible dimensions.",
  48571. cimg_instance,
  48572. colormap._width,colormap._height,colormap._depth,colormap._spectrum,colormap._data);
  48573. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f), ln2 = (float)std::log(2.);
  48574. const int
  48575. _x0 = cimg::cut(x0,0,width() - 1),
  48576. _y0 = cimg::cut(y0,0,height() - 1),
  48577. _x1 = cimg::cut(x1,0,width() - 1),
  48578. _y1 = cimg::cut(y1,0,height() - 1);
  48579. cimg_pragma_openmp(parallel for cimg_openmp_collapse(2)
  48580. cimg_openmp_if((1 + _x1 - _x0)*(1 + _y1 - _y0)>=(cimg_openmp_sizefactor)*2048))
  48581. for (int q = _y0; q<=_y1; ++q)
  48582. for (int p = _x0; p<=_x1; ++p) {
  48583. unsigned int iteration = 0;
  48584. const double x = z0r + p*(z1r-z0r)/_width, y = z0i + q*(z1i-z0i)/_height;
  48585. double zr, zi, cr, ci;
  48586. if (is_julia_set) { zr = x; zi = y; cr = param_r; ci = param_i; }
  48587. else { zr = param_r; zi = param_i; cr = x; ci = y; }
  48588. for (iteration=1; zr*zr + zi*zi<=4 && iteration<=iteration_max; ++iteration) {
  48589. const double temp = zr*zr - zi*zi + cr;
  48590. zi = 2*zr*zi + ci;
  48591. zr = temp;
  48592. }
  48593. if (iteration>iteration_max) {
  48594. if (palette) {
  48595. if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette(0,c);
  48596. else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(0,c)*nopacity + (*this)(p,q,0,c)*copacity);
  48597. } else {
  48598. if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)0;
  48599. else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)((*this)(p,q,0,c)*copacity);
  48600. }
  48601. } else if (is_normalized_iteration) {
  48602. const float
  48603. normz = (float)cimg::abs(zr*zr + zi*zi),
  48604. niteration = (float)(iteration + 1 - std::log(std::log(normz))/ln2);
  48605. if (palette) {
  48606. if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._linear_atX(niteration,c);
  48607. else cimg_forC(*this,c)
  48608. (*this)(p,q,0,c) = (T)(palette._linear_atX(niteration,c)*nopacity + (*this)(p,q,0,c)*copacity);
  48609. } else {
  48610. if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)niteration;
  48611. else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(niteration*nopacity + (*this)(p,q,0,c)*copacity);
  48612. }
  48613. } else {
  48614. if (palette) {
  48615. if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)palette._atX(iteration,c);
  48616. else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(palette(iteration,c)*nopacity + (*this)(p,q,0,c)*copacity);
  48617. } else {
  48618. if (opacity>=1) cimg_forC(*this,c) (*this)(p,q,0,c) = (T)iteration;
  48619. else cimg_forC(*this,c) (*this)(p,q,0,c) = (T)(iteration*nopacity + (*this)(p,q,0,c)*copacity);
  48620. }
  48621. }
  48622. }
  48623. return *this;
  48624. }
  48625. //! Draw a quadratic Mandelbrot or Julia 2D fractal \overloading.
  48626. template<typename tc>
  48627. CImg<T>& draw_mandelbrot(const CImg<tc>& colormap, const float opacity=1,
  48628. const double z0r=-2, const double z0i=-2, const double z1r=2, const double z1i=2,
  48629. const unsigned int iteration_max=255,
  48630. const bool is_normalized_iteration=false,
  48631. const bool is_julia_set=false,
  48632. const double param_r=0, const double param_i=0) {
  48633. return draw_mandelbrot(0,0,_width - 1,_height - 1,colormap,opacity,
  48634. z0r,z0i,z1r,z1i,iteration_max,is_normalized_iteration,is_julia_set,param_r,param_i);
  48635. }
  48636. //! Draw a 1D gaussian function.
  48637. /**
  48638. \param xc X-coordinate of the gaussian center.
  48639. \param sigma Standard variation of the gaussian distribution.
  48640. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  48641. \param opacity Drawing opacity.
  48642. **/
  48643. template<typename tc>
  48644. CImg<T>& draw_gaussian(const float xc, const float sigma,
  48645. const tc *const color, const float opacity=1) {
  48646. if (is_empty()) return *this;
  48647. if (!color)
  48648. throw CImgArgumentException(_cimg_instance
  48649. "draw_gaussian(): Specified color is (null).",
  48650. cimg_instance);
  48651. const float sigma2 = 2*sigma*sigma, nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
  48652. const ulongT whd = (ulongT)_width*_height*_depth;
  48653. const tc *col = color;
  48654. cimg_forX(*this,x) {
  48655. const float dx = (x - xc), val = (float)std::exp(-dx*dx/sigma2);
  48656. T *ptrd = data(x,0,0,0);
  48657. if (opacity>=1) cimg_forC(*this,c) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
  48658. else cimg_forC(*this,c) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
  48659. col-=_spectrum;
  48660. }
  48661. return *this;
  48662. }
  48663. //! Draw a 2D gaussian function.
  48664. /**
  48665. \param xc X-coordinate of the gaussian center.
  48666. \param yc Y-coordinate of the gaussian center.
  48667. \param tensor Covariance matrix (must be 2x2).
  48668. \param color Pointer to \c spectrum() consecutive values, defining the drawing color.
  48669. \param opacity Drawing opacity.
  48670. **/
  48671. template<typename t, typename tc>
  48672. CImg<T>& draw_gaussian(const float xc, const float yc, const CImg<t>& tensor,
  48673. const tc *const color, const float opacity=1) {
  48674. if (is_empty()) return *this;
  48675. if (tensor._width!=2 || tensor._height!=2 || tensor._depth!=1 || tensor._spectrum!=1)
  48676. throw CImgArgumentException(_cimg_instance
  48677. "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 2x2 matrix.",
  48678. cimg_instance,
  48679. tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data);
  48680. if (!color)
  48681. throw CImgArgumentException(_cimg_instance
  48682. "draw_gaussian(): Specified color is (null).",
  48683. cimg_instance);
  48684. typedef typename CImg<t>::Tfloat tfloat;
  48685. const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/=-2.;
  48686. const tfloat a = invT2(0,0), b = 2*invT2(1,0), c = invT2(1,1);
  48687. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
  48688. const ulongT whd = (ulongT)_width*_height*_depth;
  48689. const tc *col = color;
  48690. float dy = -yc;
  48691. cimg_forY(*this,y) {
  48692. float dx = -xc;
  48693. cimg_forX(*this,x) {
  48694. const float val = (float)std::exp(a*dx*dx + b*dx*dy + c*dy*dy);
  48695. T *ptrd = data(x,y,0,0);
  48696. if (opacity>=1) cimg_forC(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
  48697. else cimg_forC(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
  48698. col-=_spectrum;
  48699. ++dx;
  48700. }
  48701. ++dy;
  48702. }
  48703. return *this;
  48704. }
  48705. //! Draw a 2D gaussian function \overloading.
  48706. template<typename tc>
  48707. CImg<T>& draw_gaussian(const int xc, const int yc, const float r1, const float r2, const float ru, const float rv,
  48708. const tc *const color, const float opacity=1) {
  48709. const double
  48710. a = r1*ru*ru + r2*rv*rv,
  48711. b = (r1-r2)*ru*rv,
  48712. c = r1*rv*rv + r2*ru*ru;
  48713. const CImg<Tfloat> tensor(2,2,1,1, a,b,b,c);
  48714. return draw_gaussian(xc,yc,tensor,color,opacity);
  48715. }
  48716. //! Draw a 2D gaussian function \overloading.
  48717. template<typename tc>
  48718. CImg<T>& draw_gaussian(const float xc, const float yc, const float sigma,
  48719. const tc *const color, const float opacity=1) {
  48720. return draw_gaussian(xc,yc,CImg<floatT>::diagonal(sigma,sigma),color,opacity);
  48721. }
  48722. //! Draw a 3D gaussian function \overloading.
  48723. template<typename t, typename tc>
  48724. CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const CImg<t>& tensor,
  48725. const tc *const color, const float opacity=1) {
  48726. if (is_empty()) return *this;
  48727. typedef typename CImg<t>::Tfloat tfloat;
  48728. if (tensor._width!=3 || tensor._height!=3 || tensor._depth!=1 || tensor._spectrum!=1)
  48729. throw CImgArgumentException(_cimg_instance
  48730. "draw_gaussian(): Specified tensor (%u,%u,%u,%u,%p) is not a 3x3 matrix.",
  48731. cimg_instance,
  48732. tensor._width,tensor._height,tensor._depth,tensor._spectrum,tensor._data);
  48733. const CImg<tfloat> invT = tensor.get_invert(), invT2 = (invT*invT)/=-2.;
  48734. 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);
  48735. const float nopacity = cimg::abs(opacity), copacity = 1 - std::max(opacity,0.f);
  48736. const ulongT whd = (ulongT)_width*_height*_depth;
  48737. const tc *col = color;
  48738. cimg_forXYZ(*this,x,y,z) {
  48739. const float
  48740. dx = (x - xc), dy = (y - yc), dz = (z - zc),
  48741. val = (float)std::exp(a*dx*dx + b*dx*dy + c*dx*dz + d*dy*dy + e*dy*dz + f*dz*dz);
  48742. T *ptrd = data(x,y,z,0);
  48743. if (opacity>=1) cimg_forC(*this,k) { *ptrd = (T)(val*(*col++)); ptrd+=whd; }
  48744. else cimg_forC(*this,k) { *ptrd = (T)(nopacity*val*(*col++) + *ptrd*copacity); ptrd+=whd; }
  48745. col-=_spectrum;
  48746. }
  48747. return *this;
  48748. }
  48749. //! Draw a 3D gaussian function \overloading.
  48750. template<typename tc>
  48751. CImg<T>& draw_gaussian(const float xc, const float yc, const float zc, const float sigma,
  48752. const tc *const color, const float opacity=1) {
  48753. return draw_gaussian(xc,yc,zc,CImg<floatT>::diagonal(sigma,sigma,sigma),color,opacity);
  48754. }
  48755. //! Draw a 3D object.
  48756. /**
  48757. \param x0 X-coordinate of the 3D object position
  48758. \param y0 Y-coordinate of the 3D object position
  48759. \param z0 Z-coordinate of the 3D object position
  48760. \param vertices Image Nx3 describing 3D point coordinates
  48761. \param primitives List of P primitives
  48762. \param colors List of P color (or textures)
  48763. \param opacities Image or list of P opacities
  48764. \param render_type d Render type (0=Points, 1=Lines, 2=Faces (no light), 3=Faces (flat), 4=Faces(Gouraud)
  48765. \param is_double_sided Tells if object faces have two sides or are oriented.
  48766. \param focale length of the focale (0 for parallel projection)
  48767. \param lightx X-coordinate of the light
  48768. \param lighty Y-coordinate of the light
  48769. \param lightz Z-coordinate of the light
  48770. \param specular_lightness Amount of specular light.
  48771. \param specular_shininess Shininess of the object
  48772. \param g_opacity Global opacity of the object.
  48773. **/
  48774. template<typename tp, typename tf, typename tc, typename to>
  48775. CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
  48776. const CImg<tp>& vertices, const CImgList<tf>& primitives,
  48777. const CImgList<tc>& colors, const CImg<to>& opacities,
  48778. const unsigned int render_type=4,
  48779. const bool is_double_sided=false, const float focale=700,
  48780. const float lightx=0, const float lighty=0, const float lightz=-5e8,
  48781. const float specular_lightness=0.2f, const float specular_shininess=0.1f,
  48782. const float g_opacity=1) {
  48783. return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,
  48784. is_double_sided,focale,lightx,lighty,lightz,
  48785. specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
  48786. }
  48787. //! Draw a 3D object \simplification.
  48788. template<typename tp, typename tf, typename tc, typename to, typename tz>
  48789. CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
  48790. const CImg<tp>& vertices, const CImgList<tf>& primitives,
  48791. const CImgList<tc>& colors, const CImg<to>& opacities,
  48792. const unsigned int render_type,
  48793. const bool is_double_sided, const float focale,
  48794. const float lightx, const float lighty, const float lightz,
  48795. const float specular_lightness, const float specular_shininess,
  48796. const float g_opacity, CImg<tz>& zbuffer) {
  48797. return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
  48798. render_type,is_double_sided,focale,lightx,lighty,lightz,
  48799. specular_lightness,specular_shininess,g_opacity,1);
  48800. }
  48801. #ifdef cimg_use_board
  48802. template<typename tp, typename tf, typename tc, typename to>
  48803. CImg<T>& draw_object3d(LibBoard::Board& board,
  48804. const float x0, const float y0, const float z0,
  48805. const CImg<tp>& vertices, const CImgList<tf>& primitives,
  48806. const CImgList<tc>& colors, const CImg<to>& opacities,
  48807. const unsigned int render_type=4,
  48808. const bool is_double_sided=false, const float focale=700,
  48809. const float lightx=0, const float lighty=0, const float lightz=-5e8,
  48810. const float specular_lightness=0.2f, const float specular_shininess=0.1f,
  48811. const float g_opacity=1) {
  48812. return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,
  48813. is_double_sided,focale,lightx,lighty,lightz,
  48814. specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
  48815. }
  48816. template<typename tp, typename tf, typename tc, typename to, typename tz>
  48817. CImg<T>& draw_object3d(LibBoard::Board& board,
  48818. const float x0, const float y0, const float z0,
  48819. const CImg<tp>& vertices, const CImgList<tf>& primitives,
  48820. const CImgList<tc>& colors, const CImg<to>& opacities,
  48821. const unsigned int render_type,
  48822. const bool is_double_sided, const float focale,
  48823. const float lightx, const float lighty, const float lightz,
  48824. const float specular_lightness, const float specular_shininess,
  48825. const float g_opacity, CImg<tz>& zbuffer) {
  48826. return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
  48827. render_type,is_double_sided,focale,lightx,lighty,lightz,
  48828. specular_lightness,specular_shininess,g_opacity,1);
  48829. }
  48830. #endif
  48831. //! Draw a 3D object \simplification.
  48832. template<typename tp, typename tf, typename tc, typename to>
  48833. CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
  48834. const CImg<tp>& vertices, const CImgList<tf>& primitives,
  48835. const CImgList<tc>& colors, const CImgList<to>& opacities,
  48836. const unsigned int render_type=4,
  48837. const bool is_double_sided=false, const float focale=700,
  48838. const float lightx=0, const float lighty=0, const float lightz=-5e8,
  48839. const float specular_lightness=0.2f, const float specular_shininess=0.1f,
  48840. const float g_opacity=1) {
  48841. return draw_object3d(x0,y0,z0,vertices,primitives,colors,opacities,render_type,
  48842. is_double_sided,focale,lightx,lighty,lightz,
  48843. specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
  48844. }
  48845. //! Draw a 3D object \simplification.
  48846. template<typename tp, typename tf, typename tc, typename to, typename tz>
  48847. CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
  48848. const CImg<tp>& vertices, const CImgList<tf>& primitives,
  48849. const CImgList<tc>& colors, const CImgList<to>& opacities,
  48850. const unsigned int render_type,
  48851. const bool is_double_sided, const float focale,
  48852. const float lightx, const float lighty, const float lightz,
  48853. const float specular_lightness, const float specular_shininess,
  48854. const float g_opacity, CImg<tz>& zbuffer) {
  48855. return _draw_object3d(0,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
  48856. render_type,is_double_sided,focale,lightx,lighty,lightz,
  48857. specular_lightness,specular_shininess,g_opacity,1);
  48858. }
  48859. #ifdef cimg_use_board
  48860. template<typename tp, typename tf, typename tc, typename to>
  48861. CImg<T>& draw_object3d(LibBoard::Board& board,
  48862. const float x0, const float y0, const float z0,
  48863. const CImg<tp>& vertices, const CImgList<tf>& primitives,
  48864. const CImgList<tc>& colors, const CImgList<to>& opacities,
  48865. const unsigned int render_type=4,
  48866. const bool is_double_sided=false, const float focale=700,
  48867. const float lightx=0, const float lighty=0, const float lightz=-5e8,
  48868. const float specular_lightness=0.2f, const float specular_shininess=0.1f,
  48869. const float g_opacity=1) {
  48870. return draw_object3d(board,x0,y0,z0,vertices,primitives,colors,opacities,render_type,
  48871. is_double_sided,focale,lightx,lighty,lightz,
  48872. specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
  48873. }
  48874. template<typename tp, typename tf, typename tc, typename to, typename tz>
  48875. CImg<T>& draw_object3d(LibBoard::Board& board,
  48876. const float x0, const float y0, const float z0,
  48877. const CImg<tp>& vertices, const CImgList<tf>& primitives,
  48878. const CImgList<tc>& colors, const CImgList<to>& opacities,
  48879. const unsigned int render_type,
  48880. const bool is_double_sided, const float focale,
  48881. const float lightx, const float lighty, const float lightz,
  48882. const float specular_lightness, const float specular_shininess,
  48883. const float g_opacity, CImg<tz>& zbuffer) {
  48884. return _draw_object3d((void*)&board,zbuffer,x0,y0,z0,vertices,primitives,colors,opacities,
  48885. render_type,is_double_sided,focale,lightx,lighty,lightz,
  48886. specular_lightness,specular_shininess,g_opacity,1);
  48887. }
  48888. #endif
  48889. //! Draw a 3D object \simplification.
  48890. template<typename tp, typename tf, typename tc>
  48891. CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
  48892. const CImg<tp>& vertices, const CImgList<tf>& primitives,
  48893. const CImgList<tc>& colors,
  48894. const unsigned int render_type=4,
  48895. const bool is_double_sided=false, const float focale=700,
  48896. const float lightx=0, const float lighty=0, const float lightz=-5e8,
  48897. const float specular_lightness=0.2f, const float specular_shininess=0.1f,
  48898. const float g_opacity=1) {
  48899. return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
  48900. render_type,is_double_sided,focale,lightx,lighty,lightz,
  48901. specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
  48902. }
  48903. //! Draw a 3D object \simplification.
  48904. template<typename tp, typename tf, typename tc, typename tz>
  48905. CImg<T>& draw_object3d(const float x0, const float y0, const float z0,
  48906. const CImg<tp>& vertices, const CImgList<tf>& primitives,
  48907. const CImgList<tc>& colors,
  48908. const unsigned int render_type,
  48909. const bool is_double_sided, const float focale,
  48910. const float lightx, const float lighty, const float lightz,
  48911. const float specular_lightness, const float specular_shininess,
  48912. const float g_opacity, CImg<tz>& zbuffer) {
  48913. return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
  48914. render_type,is_double_sided,focale,lightx,lighty,lightz,
  48915. specular_lightness,specular_shininess,g_opacity,zbuffer);
  48916. }
  48917. #ifdef cimg_use_board
  48918. template<typename tp, typename tf, typename tc, typename to>
  48919. CImg<T>& draw_object3d(LibBoard::Board& board,
  48920. const float x0, const float y0, const float z0,
  48921. const CImg<tp>& vertices, const CImgList<tf>& primitives,
  48922. const CImgList<tc>& colors,
  48923. const unsigned int render_type=4,
  48924. const bool is_double_sided=false, const float focale=700,
  48925. const float lightx=0, const float lighty=0, const float lightz=-5e8,
  48926. const float specular_lightness=0.2f, const float specular_shininess=0.1f,
  48927. const float g_opacity=1) {
  48928. return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
  48929. render_type,is_double_sided,focale,lightx,lighty,lightz,
  48930. specular_lightness,specular_shininess,g_opacity,CImg<floatT>::empty());
  48931. }
  48932. template<typename tp, typename tf, typename tc, typename to, typename tz>
  48933. CImg<T>& draw_object3d(LibBoard::Board& board,
  48934. const float x0, const float y0, const float z0,
  48935. const CImg<tp>& vertices, const CImgList<tf>& primitives,
  48936. const CImgList<tc>& colors,
  48937. const unsigned int render_type,
  48938. const bool is_double_sided, const float focale,
  48939. const float lightx, const float lighty, const float lightz,
  48940. const float specular_lightness, const float specular_shininess,
  48941. const float g_opacity, CImg<tz>& zbuffer) {
  48942. return draw_object3d(x0,y0,z0,vertices,primitives,colors,CImg<floatT>::const_empty(),
  48943. render_type,is_double_sided,focale,lightx,lighty,lightz,
  48944. specular_lightness,specular_shininess,g_opacity,zbuffer);
  48945. }
  48946. #endif
  48947. template<typename t, typename to>
  48948. static float __draw_object3d(const CImgList<t>& opacities, const unsigned int n_primitive, CImg<to>& opacity) {
  48949. if (n_primitive>=opacities._width || opacities[n_primitive].is_empty()) { opacity.assign(); return 1; }
  48950. if (opacities[n_primitive].size()==1) { opacity.assign(); return opacities(n_primitive,0); }
  48951. opacity.assign(opacities[n_primitive],true);
  48952. return 1.f;
  48953. }
  48954. template<typename t, typename to>
  48955. static float __draw_object3d(const CImg<t>& opacities, const unsigned int n_primitive, CImg<to>& opacity) {
  48956. opacity.assign();
  48957. return n_primitive>=opacities._width?1.f:(float)opacities[n_primitive];
  48958. }
  48959. template<typename t>
  48960. static float ___draw_object3d(const CImgList<t>& opacities, const unsigned int n_primitive) {
  48961. return n_primitive<opacities._width && opacities[n_primitive].size()==1?(float)opacities(n_primitive,0):1.f;
  48962. }
  48963. template<typename t>
  48964. static float ___draw_object3d(const CImg<t>& opacities, const unsigned int n_primitive) {
  48965. return n_primitive<opacities._width?(float)opacities[n_primitive]:1.f;
  48966. }
  48967. template<typename tz, typename tp, typename tf, typename tc, typename to>
  48968. CImg<T>& _draw_object3d(void *const pboard, CImg<tz>& zbuffer,
  48969. const float X, const float Y, const float Z,
  48970. const CImg<tp>& vertices,
  48971. const CImgList<tf>& primitives,
  48972. const CImgList<tc>& colors,
  48973. const to& opacities,
  48974. const unsigned int render_type,
  48975. const bool is_double_sided, const float focale,
  48976. const float lightx, const float lighty, const float lightz,
  48977. const float specular_lightness, const float specular_shininess,
  48978. const float g_opacity, const float sprite_scale) {
  48979. typedef typename cimg::superset2<tp,tz,float>::type tpfloat;
  48980. typedef typename to::value_type _to;
  48981. if (is_empty() || !vertices || !primitives) return *this;
  48982. CImg<char> error_message(1024);
  48983. if (!vertices.is_object3d(primitives,colors,opacities,false,error_message))
  48984. throw CImgArgumentException(_cimg_instance
  48985. "draw_object3d(): Invalid specified 3D object (%u,%u) (%s).",
  48986. cimg_instance,vertices._width,primitives._width,error_message.data());
  48987. #ifndef cimg_use_board
  48988. if (pboard) return *this;
  48989. #endif
  48990. if (render_type==5) cimg::mutex(10); // Static variable used in this case, breaks thread-safety
  48991. const float
  48992. nspec = 1 - (specular_lightness<0.f?0.f:(specular_lightness>1.f?1.f:specular_lightness)),
  48993. nspec2 = 1 + (specular_shininess<0.f?0.f:specular_shininess),
  48994. nsl1 = (nspec2 - 1)/cimg::sqr(nspec - 1),
  48995. nsl2 = 1 - 2*nsl1*nspec,
  48996. nsl3 = nspec2 - nsl1 - nsl2;
  48997. // Create light texture for phong-like rendering.
  48998. CImg<floatT> light_texture;
  48999. if (render_type==5) {
  49000. if (colors._width>primitives._width) {
  49001. static CImg<floatT> default_light_texture;
  49002. static const tc *lptr = 0;
  49003. static tc ref_values[64] = {};
  49004. const CImg<tc>& img = colors.back();
  49005. bool is_same_texture = (lptr==img._data);
  49006. if (is_same_texture)
  49007. for (unsigned int r = 0, j = 0; j<8; ++j)
  49008. for (unsigned int i = 0; i<8; ++i)
  49009. if (ref_values[r++]!=img(i*img._width/9,j*img._height/9,0,(i + j)%img._spectrum)) {
  49010. is_same_texture = false; break;
  49011. }
  49012. if (!is_same_texture || default_light_texture._spectrum<_spectrum) {
  49013. (default_light_texture.assign(img,false)/=255).resize(-100,-100,1,_spectrum);
  49014. lptr = colors.back().data();
  49015. for (unsigned int r = 0, j = 0; j<8; ++j)
  49016. for (unsigned int i = 0; i<8; ++i)
  49017. ref_values[r++] = img(i*img._width/9,j*img._height/9,0,(i + j)%img._spectrum);
  49018. }
  49019. light_texture.assign(default_light_texture,true);
  49020. } else {
  49021. static CImg<floatT> default_light_texture;
  49022. static float olightx = 0, olighty = 0, olightz = 0, ospecular_shininess = 0;
  49023. if (!default_light_texture ||
  49024. lightx!=olightx || lighty!=olighty || lightz!=olightz ||
  49025. specular_shininess!=ospecular_shininess || default_light_texture._spectrum<_spectrum) {
  49026. default_light_texture.assign(512,512);
  49027. const float
  49028. dlx = lightx - X,
  49029. dly = lighty - Y,
  49030. dlz = lightz - Z,
  49031. nl = cimg::hypot(dlx,dly,dlz),
  49032. nlx = (default_light_texture._width - 1)/2*(1 + dlx/nl),
  49033. nly = (default_light_texture._height - 1)/2*(1 + dly/nl),
  49034. white[] = { 1 };
  49035. default_light_texture.draw_gaussian(nlx,nly,default_light_texture._width/3.f,white);
  49036. cimg_forXY(default_light_texture,x,y) {
  49037. const float factor = default_light_texture(x,y);
  49038. if (factor>nspec) default_light_texture(x,y) = std::min(2.f,nsl1*factor*factor + nsl2*factor + nsl3);
  49039. }
  49040. default_light_texture.resize(-100,-100,1,_spectrum);
  49041. olightx = lightx; olighty = lighty; olightz = lightz; ospecular_shininess = specular_shininess;
  49042. }
  49043. light_texture.assign(default_light_texture,true);
  49044. }
  49045. }
  49046. // Compute 3D to 2D projection.
  49047. CImg<tpfloat> projections(vertices._width,2);
  49048. tpfloat parallzmin = cimg::type<tpfloat>::max();
  49049. const float absfocale = focale?cimg::abs(focale):0;
  49050. if (absfocale) {
  49051. cimg_pragma_openmp(parallel for cimg_openmp_if_size(projections.size(),4096))
  49052. cimg_forX(projections,l) { // Perspective projection
  49053. const tpfloat
  49054. x = (tpfloat)vertices(l,0),
  49055. y = (tpfloat)vertices(l,1),
  49056. z = (tpfloat)vertices(l,2);
  49057. const tpfloat projectedz = z + Z + absfocale;
  49058. projections(l,1) = Y + absfocale*y/projectedz;
  49059. projections(l,0) = X + absfocale*x/projectedz;
  49060. }
  49061. } else {
  49062. cimg_pragma_openmp(parallel for cimg_openmp_if_size(projections.size(),4096))
  49063. cimg_forX(projections,l) { // Parallel projection
  49064. const tpfloat
  49065. x = (tpfloat)vertices(l,0),
  49066. y = (tpfloat)vertices(l,1),
  49067. z = (tpfloat)vertices(l,2);
  49068. if (z<parallzmin) parallzmin = z;
  49069. projections(l,1) = Y + y;
  49070. projections(l,0) = X + x;
  49071. }
  49072. }
  49073. const float _focale = absfocale?absfocale:(1e5f-parallzmin);
  49074. float zmax = 0;
  49075. if (zbuffer) zmax = vertices.get_shared_row(2).max();
  49076. // Compute visible primitives.
  49077. CImg<uintT> visibles(primitives._width,1,1,1,~0U);
  49078. CImg<tpfloat> zrange(primitives._width);
  49079. const tpfloat zmin = absfocale?(tpfloat)(1.5f - absfocale):cimg::type<tpfloat>::min();
  49080. bool is_forward = zbuffer?true:false;
  49081. cimg_pragma_openmp(parallel for cimg_openmp_if_size(primitives.size(),4096))
  49082. cimglist_for(primitives,l) {
  49083. const CImg<tf>& primitive = primitives[l];
  49084. switch (primitive.size()) {
  49085. case 1 : { // Point
  49086. CImg<_to> _opacity;
  49087. __draw_object3d(opacities,l,_opacity);
  49088. if (l<=colors.width() && (colors[l].size()!=_spectrum || _opacity)) is_forward = false;
  49089. const unsigned int i0 = (unsigned int)primitive(0);
  49090. const tpfloat z0 = Z + vertices(i0,2);
  49091. if (z0>zmin) {
  49092. visibles(l) = (unsigned int)l;
  49093. zrange(l) = z0;
  49094. }
  49095. } break;
  49096. case 5 : { // Sphere
  49097. const unsigned int
  49098. i0 = (unsigned int)primitive(0),
  49099. i1 = (unsigned int)primitive(1);
  49100. const tpfloat
  49101. Xc = 0.5f*((float)vertices(i0,0) + (float)vertices(i1,0)),
  49102. Yc = 0.5f*((float)vertices(i0,1) + (float)vertices(i1,1)),
  49103. Zc = 0.5f*((float)vertices(i0,2) + (float)vertices(i1,2)),
  49104. _zc = Z + Zc,
  49105. zc = _zc + _focale,
  49106. xc = X + Xc*(absfocale?absfocale/zc:1),
  49107. yc = Y + Yc*(absfocale?absfocale/zc:1),
  49108. radius = 0.5f*cimg::hypot(vertices(i1,0) - vertices(i0,0),
  49109. vertices(i1,1) - vertices(i0,1),
  49110. vertices(i1,2) - vertices(i0,2))*(absfocale?absfocale/zc:1),
  49111. xm = xc - radius,
  49112. ym = yc - radius,
  49113. xM = xc + radius,
  49114. yM = yc + radius;
  49115. if (xM>=0 && xm<_width && yM>=0 && ym<_height && _zc>zmin) {
  49116. visibles(l) = (unsigned int)l;
  49117. zrange(l) = _zc;
  49118. }
  49119. is_forward = false;
  49120. } break;
  49121. case 2 : case 6 : { // Segment
  49122. const unsigned int
  49123. i0 = (unsigned int)primitive(0),
  49124. i1 = (unsigned int)primitive(1);
  49125. const tpfloat
  49126. x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
  49127. x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2);
  49128. tpfloat xm, xM, ym, yM;
  49129. if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
  49130. if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
  49131. if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin) {
  49132. visibles(l) = (unsigned int)l;
  49133. zrange(l) = (z0 + z1)/2;
  49134. }
  49135. } break;
  49136. case 3 : case 9 : { // Triangle
  49137. const unsigned int
  49138. i0 = (unsigned int)primitive(0),
  49139. i1 = (unsigned int)primitive(1),
  49140. i2 = (unsigned int)primitive(2);
  49141. const tpfloat
  49142. x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
  49143. x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2),
  49144. x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2);
  49145. tpfloat xm, xM, ym, yM;
  49146. if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
  49147. if (x2<xm) xm = x2;
  49148. if (x2>xM) xM = x2;
  49149. if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
  49150. if (y2<ym) ym = y2;
  49151. if (y2>yM) yM = y2;
  49152. if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin) {
  49153. const tpfloat d = (x1-x0)*(y2-y0) - (x2-x0)*(y1-y0);
  49154. if (is_double_sided || d<0) {
  49155. visibles(l) = (unsigned int)l;
  49156. zrange(l) = (z0 + z1 + z2)/3;
  49157. }
  49158. }
  49159. } break;
  49160. case 4 : case 12 : { // Quadrangle
  49161. const unsigned int
  49162. i0 = (unsigned int)primitive(0),
  49163. i1 = (unsigned int)primitive(1),
  49164. i2 = (unsigned int)primitive(2),
  49165. i3 = (unsigned int)primitive(3);
  49166. const tpfloat
  49167. x0 = projections(i0,0), y0 = projections(i0,1), z0 = Z + vertices(i0,2),
  49168. x1 = projections(i1,0), y1 = projections(i1,1), z1 = Z + vertices(i1,2),
  49169. x2 = projections(i2,0), y2 = projections(i2,1), z2 = Z + vertices(i2,2),
  49170. x3 = projections(i3,0), y3 = projections(i3,1), z3 = Z + vertices(i3,2);
  49171. tpfloat xm, xM, ym, yM;
  49172. if (x0<x1) { xm = x0; xM = x1; } else { xm = x1; xM = x0; }
  49173. if (x2<xm) xm = x2;
  49174. if (x2>xM) xM = x2;
  49175. if (x3<xm) xm = x3;
  49176. if (x3>xM) xM = x3;
  49177. if (y0<y1) { ym = y0; yM = y1; } else { ym = y1; yM = y0; }
  49178. if (y2<ym) ym = y2;
  49179. if (y2>yM) yM = y2;
  49180. if (y3<ym) ym = y3;
  49181. if (y3>yM) yM = y3;
  49182. if (xM>=0 && xm<_width && yM>=0 && ym<_height && z0>zmin && z1>zmin && z2>zmin && z3>zmin) {
  49183. const float d = (x1 - x0)*(y2 - y0) - (x2 - x0)*(y1 - y0);
  49184. if (is_double_sided || d<0) {
  49185. visibles(l) = (unsigned int)l;
  49186. zrange(l) = (z0 + z1 + z2 + z3)/4;
  49187. }
  49188. }
  49189. } break;
  49190. default :
  49191. if (render_type==5) cimg::mutex(10,0);
  49192. throw CImgArgumentException(_cimg_instance
  49193. "draw_object3d(): Invalid primitive[%u] with size %u "
  49194. "(should have size 1,2,3,4,5,6,9 or 12).",
  49195. cimg_instance,
  49196. l,primitive.size());
  49197. }
  49198. }
  49199. // Force transparent primitives to be drawn last when zbuffer is activated
  49200. // (and if object contains no spheres or sprites).
  49201. if (is_forward)
  49202. cimglist_for(primitives,l)
  49203. if (___draw_object3d(opacities,l)!=1) zrange(l) = 2*zmax - zrange(l);
  49204. // Sort only visibles primitives.
  49205. unsigned int *p_visibles = visibles._data;
  49206. tpfloat *p_zrange = zrange._data;
  49207. const tpfloat *ptrz = p_zrange;
  49208. cimg_for(visibles,ptr,unsigned int) {
  49209. if (*ptr!=~0U) { *(p_visibles++) = *ptr; *(p_zrange++) = *ptrz; }
  49210. ++ptrz;
  49211. }
  49212. const unsigned int nb_visibles = (unsigned int)(p_zrange - zrange._data);
  49213. if (!nb_visibles) {
  49214. if (render_type==5) cimg::mutex(10,0);
  49215. return *this;
  49216. }
  49217. CImg<uintT> permutations;
  49218. CImg<tpfloat>(zrange._data,nb_visibles,1,1,1,true).sort(permutations,is_forward);
  49219. // Compute light properties
  49220. CImg<floatT> lightprops;
  49221. switch (render_type) {
  49222. case 3 : { // Flat Shading
  49223. lightprops.assign(nb_visibles);
  49224. cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096))
  49225. cimg_forX(lightprops,l) {
  49226. const CImg<tf>& primitive = primitives(visibles(permutations(l)));
  49227. const unsigned int psize = (unsigned int)primitive.size();
  49228. if (psize==3 || psize==4 || psize==9 || psize==12) {
  49229. const unsigned int
  49230. i0 = (unsigned int)primitive(0),
  49231. i1 = (unsigned int)primitive(1),
  49232. i2 = (unsigned int)primitive(2);
  49233. const tpfloat
  49234. x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2),
  49235. x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2),
  49236. x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2),
  49237. dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
  49238. dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
  49239. nx = dy1*dz2 - dz1*dy2,
  49240. ny = dz1*dx2 - dx1*dz2,
  49241. nz = dx1*dy2 - dy1*dx2,
  49242. norm = 1e-5f + cimg::hypot(nx,ny,nz),
  49243. lx = X + (x0 + x1 + x2)/3 - lightx,
  49244. ly = Y + (y0 + y1 + y2)/3 - lighty,
  49245. lz = Z + (z0 + z1 + z2)/3 - lightz,
  49246. nl = 1e-5f + cimg::hypot(lx,ly,lz),
  49247. factor = std::max(cimg::abs(-lx*nx - ly*ny - lz*nz)/(norm*nl),(tpfloat)0);
  49248. lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
  49249. } else lightprops[l] = 1;
  49250. }
  49251. } break;
  49252. case 4 : // Gouraud Shading
  49253. case 5 : { // Phong-Shading
  49254. CImg<tpfloat> vertices_normals(vertices._width,6,1,1,0);
  49255. cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096))
  49256. for (int l = 0; l<(int)nb_visibles; ++l) {
  49257. const CImg<tf>& primitive = primitives[visibles(l)];
  49258. const unsigned int psize = (unsigned int)primitive.size();
  49259. const bool
  49260. triangle_flag = (psize==3) || (psize==9),
  49261. quadrangle_flag = (psize==4) || (psize==12);
  49262. if (triangle_flag || quadrangle_flag) {
  49263. const unsigned int
  49264. i0 = (unsigned int)primitive(0),
  49265. i1 = (unsigned int)primitive(1),
  49266. i2 = (unsigned int)primitive(2),
  49267. i3 = quadrangle_flag?(unsigned int)primitive(3):0;
  49268. const tpfloat
  49269. x0 = (tpfloat)vertices(i0,0), y0 = (tpfloat)vertices(i0,1), z0 = (tpfloat)vertices(i0,2),
  49270. x1 = (tpfloat)vertices(i1,0), y1 = (tpfloat)vertices(i1,1), z1 = (tpfloat)vertices(i1,2),
  49271. x2 = (tpfloat)vertices(i2,0), y2 = (tpfloat)vertices(i2,1), z2 = (tpfloat)vertices(i2,2),
  49272. dx1 = x1 - x0, dy1 = y1 - y0, dz1 = z1 - z0,
  49273. dx2 = x2 - x0, dy2 = y2 - y0, dz2 = z2 - z0,
  49274. nnx = dy1*dz2 - dz1*dy2,
  49275. nny = dz1*dx2 - dx1*dz2,
  49276. nnz = dx1*dy2 - dy1*dx2,
  49277. norm = 1e-5f + cimg::hypot(nnx,nny,nnz),
  49278. nx = nnx/norm,
  49279. ny = nny/norm,
  49280. nz = nnz/norm;
  49281. unsigned int ix = 0, iy = 1, iz = 2;
  49282. if (is_double_sided && nz>0) { ix = 3; iy = 4; iz = 5; }
  49283. vertices_normals(i0,ix)+=nx; vertices_normals(i0,iy)+=ny; vertices_normals(i0,iz)+=nz;
  49284. vertices_normals(i1,ix)+=nx; vertices_normals(i1,iy)+=ny; vertices_normals(i1,iz)+=nz;
  49285. vertices_normals(i2,ix)+=nx; vertices_normals(i2,iy)+=ny; vertices_normals(i2,iz)+=nz;
  49286. if (quadrangle_flag) {
  49287. vertices_normals(i3,ix)+=nx; vertices_normals(i3,iy)+=ny; vertices_normals(i3,iz)+=nz;
  49288. }
  49289. }
  49290. }
  49291. if (is_double_sided) cimg_forX(vertices_normals,p) {
  49292. const float
  49293. nx0 = vertices_normals(p,0), ny0 = vertices_normals(p,1), nz0 = vertices_normals(p,2),
  49294. nx1 = vertices_normals(p,3), ny1 = vertices_normals(p,4), nz1 = vertices_normals(p,5),
  49295. n0 = nx0*nx0 + ny0*ny0 + nz0*nz0, n1 = nx1*nx1 + ny1*ny1 + nz1*nz1;
  49296. if (n1>n0) {
  49297. vertices_normals(p,0) = -nx1;
  49298. vertices_normals(p,1) = -ny1;
  49299. vertices_normals(p,2) = -nz1;
  49300. }
  49301. }
  49302. if (render_type==4) {
  49303. lightprops.assign(vertices._width);
  49304. cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096))
  49305. cimg_forX(lightprops,l) {
  49306. const tpfloat
  49307. nx = vertices_normals(l,0),
  49308. ny = vertices_normals(l,1),
  49309. nz = vertices_normals(l,2),
  49310. norm = 1e-5f + cimg::hypot(nx,ny,nz),
  49311. lx = X + vertices(l,0) - lightx,
  49312. ly = Y + vertices(l,1) - lighty,
  49313. lz = Z + vertices(l,2) - lightz,
  49314. nl = 1e-5f + cimg::hypot(lx,ly,lz),
  49315. factor = std::max((-lx*nx - ly*ny - lz*nz)/(norm*nl),(tpfloat)0);
  49316. lightprops[l] = factor<=nspec?factor:(nsl1*factor*factor + nsl2*factor + nsl3);
  49317. }
  49318. } else {
  49319. const unsigned int
  49320. lw2 = light_texture._width/2 - 1,
  49321. lh2 = light_texture._height/2 - 1;
  49322. lightprops.assign(vertices._width,2);
  49323. cimg_pragma_openmp(parallel for cimg_openmp_if_size(nb_visibles,4096))
  49324. cimg_forX(lightprops,l) {
  49325. const tpfloat
  49326. nx = vertices_normals(l,0),
  49327. ny = vertices_normals(l,1),
  49328. nz = vertices_normals(l,2),
  49329. norm = 1e-5f + cimg::hypot(nx,ny,nz),
  49330. nnx = nx/norm,
  49331. nny = ny/norm;
  49332. lightprops(l,0) = lw2*(1 + nnx);
  49333. lightprops(l,1) = lh2*(1 + nny);
  49334. }
  49335. }
  49336. } break;
  49337. }
  49338. // Draw visible primitives
  49339. const CImg<tc> default_color(1,_spectrum,1,1,(tc)200);
  49340. CImg<_to> _opacity;
  49341. for (unsigned int l = 0; l<nb_visibles; ++l) {
  49342. const unsigned int n_primitive = visibles(permutations(l));
  49343. const CImg<tf>& primitive = primitives[n_primitive];
  49344. const CImg<tc>
  49345. &__color = n_primitive<colors._width?colors[n_primitive]:CImg<tc>(),
  49346. _color = (__color && __color.size()!=_spectrum && __color._spectrum<_spectrum)?
  49347. __color.get_resize(-100,-100,-100,_spectrum,0):CImg<tc>(),
  49348. &color = _color?_color:(__color?__color:default_color);
  49349. const tc *const pcolor = color._data;
  49350. float opacity = __draw_object3d(opacities,n_primitive,_opacity);
  49351. if (_opacity.is_empty()) opacity*=g_opacity;
  49352. #ifdef cimg_use_board
  49353. LibBoard::Board &board = *(LibBoard::Board*)pboard;
  49354. #endif
  49355. switch (primitive.size()) {
  49356. case 1 : { // Colored point or sprite
  49357. const unsigned int n0 = (unsigned int)primitive[0];
  49358. const int x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1));
  49359. if (_opacity.is_empty()) { // Scalar opacity
  49360. if (color.size()==_spectrum) { // Colored point
  49361. draw_point(x0,y0,pcolor,opacity);
  49362. #ifdef cimg_use_board
  49363. if (pboard) {
  49364. board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
  49365. board.drawDot((float)x0,height()-(float)y0);
  49366. }
  49367. #endif
  49368. } else { // Sprite
  49369. const tpfloat z = Z + vertices(n0,2);
  49370. const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1);
  49371. const unsigned int
  49372. _sw = (unsigned int)(color._width*factor),
  49373. _sh = (unsigned int)(color._height*factor),
  49374. sw = _sw?_sw:1, sh = _sh?_sh:1;
  49375. const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2;
  49376. if (sw<=3*_width/2 && sh<=3*_height/2 &&
  49377. (nx0 + (int)sw/2>=0 || nx0 - (int)sw/2<width() || ny0 + (int)sh/2>=0 || ny0 - (int)sh/2<height())) {
  49378. const CImg<tc>
  49379. _sprite = (sw!=color._width || sh!=color._height)?
  49380. color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<tc>(),
  49381. &sprite = _sprite?_sprite:color;
  49382. draw_image(nx0,ny0,sprite,opacity);
  49383. #ifdef cimg_use_board
  49384. if (pboard) {
  49385. board.setPenColorRGBi(128,128,128);
  49386. board.setFillColor(LibBoard::Color::Null);
  49387. board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh);
  49388. }
  49389. #endif
  49390. }
  49391. }
  49392. } else { // Opacity mask
  49393. const tpfloat z = Z + vertices(n0,2);
  49394. const float factor = focale<0?1:sprite_scale*(absfocale?absfocale/(z + absfocale):1);
  49395. const unsigned int
  49396. _sw = (unsigned int)(std::max(color._width,_opacity._width)*factor),
  49397. _sh = (unsigned int)(std::max(color._height,_opacity._height)*factor),
  49398. sw = _sw?_sw:1, sh = _sh?_sh:1;
  49399. const int nx0 = x0 - (int)sw/2, ny0 = y0 - (int)sh/2;
  49400. if (sw<=3*_width/2 && sh<=3*_height/2 &&
  49401. (nx0 + (int)sw/2>=0 || nx0 - (int)sw/2<width() || ny0 + (int)sh/2>=0 || ny0 - (int)sh/2<height())) {
  49402. const CImg<tc>
  49403. _sprite = (sw!=color._width || sh!=color._height)?
  49404. color.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<tc>(),
  49405. &sprite = _sprite?_sprite:color;
  49406. const CImg<_to>
  49407. _nopacity = (sw!=_opacity._width || sh!=_opacity._height)?
  49408. _opacity.get_resize(sw,sh,1,-100,render_type<=3?1:3):CImg<_to>(),
  49409. &nopacity = _nopacity?_nopacity:_opacity;
  49410. draw_image(nx0,ny0,sprite,nopacity,g_opacity);
  49411. #ifdef cimg_use_board
  49412. if (pboard) {
  49413. board.setPenColorRGBi(128,128,128);
  49414. board.setFillColor(LibBoard::Color::Null);
  49415. board.drawRectangle((float)nx0,height() - (float)ny0,sw,sh);
  49416. }
  49417. #endif
  49418. }
  49419. }
  49420. } break;
  49421. case 2 : { // Colored line
  49422. const unsigned int
  49423. n0 = (unsigned int)primitive[0],
  49424. n1 = (unsigned int)primitive[1];
  49425. const int
  49426. x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
  49427. x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1));
  49428. const float
  49429. z0 = vertices(n0,2) + Z + _focale,
  49430. z1 = vertices(n1,2) + Z + _focale;
  49431. if (render_type) {
  49432. if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity);
  49433. else draw_line(x0,y0,x1,y1,pcolor,opacity);
  49434. #ifdef cimg_use_board
  49435. if (pboard) {
  49436. board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
  49437. board.drawLine((float)x0,height() - (float)y0,x1,height() - (float)y1);
  49438. }
  49439. #endif
  49440. } else {
  49441. draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity);
  49442. #ifdef cimg_use_board
  49443. if (pboard) {
  49444. board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
  49445. board.drawDot((float)x0,height() - (float)y0);
  49446. board.drawDot((float)x1,height() - (float)y1);
  49447. }
  49448. #endif
  49449. }
  49450. } break;
  49451. case 5 : { // Colored sphere
  49452. const unsigned int
  49453. n0 = (unsigned int)primitive[0],
  49454. n1 = (unsigned int)primitive[1],
  49455. is_wireframe = (unsigned int)primitive[2],
  49456. is_radius = (unsigned int)primitive[3];
  49457. float Xc,Yc,Zc,radius;
  49458. if (is_radius) {
  49459. Xc = (float)vertices(n0,0);
  49460. Yc = (float)vertices(n0,1);
  49461. Zc = (float)vertices(n0,2);
  49462. radius = cimg::hypot(vertices(n1,0) - vertices(n0,0),
  49463. vertices(n1,1) - vertices(n0,1),
  49464. vertices(n1,2) - vertices(n0,2));
  49465. } else {
  49466. Xc = 0.5f*((float)vertices(n0,0) + (float)vertices(n1,0));
  49467. Yc = 0.5f*((float)vertices(n0,1) + (float)vertices(n1,1));
  49468. Zc = 0.5f*((float)vertices(n0,2) + (float)vertices(n1,2));
  49469. radius = 0.5f*cimg::hypot(vertices(n1,0) - vertices(n0,0),
  49470. vertices(n1,1) - vertices(n0,1),
  49471. vertices(n1,2) - vertices(n0,2));
  49472. }
  49473. const float
  49474. zc = Z + Zc + _focale,
  49475. af = absfocale?absfocale/zc:1,
  49476. xc = X + Xc*af,
  49477. yc = Y + Yc*af;
  49478. radius*=af;
  49479. switch (render_type) {
  49480. case 0 :
  49481. draw_point((int)xc,(int)yc,pcolor,opacity);
  49482. #ifdef cimg_use_board
  49483. if (pboard) {
  49484. board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
  49485. board.drawDot(xc,height() - yc);
  49486. }
  49487. #endif
  49488. break;
  49489. case 1 :
  49490. draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U);
  49491. #ifdef cimg_use_board
  49492. if (pboard) {
  49493. board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
  49494. board.setFillColor(LibBoard::Color::Null);
  49495. board.drawCircle(xc,height() - yc,radius);
  49496. }
  49497. #endif
  49498. break;
  49499. default :
  49500. if (is_wireframe) draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity,~0U);
  49501. else draw_circle((int)xc,(int)yc,(int)radius,pcolor,opacity);
  49502. #ifdef cimg_use_board
  49503. if (pboard) {
  49504. board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
  49505. if (!is_wireframe) board.fillCircle(xc,height() - yc,radius);
  49506. else {
  49507. board.setFillColor(LibBoard::Color::Null);
  49508. board.drawCircle(xc,height() - yc,radius);
  49509. }
  49510. }
  49511. #endif
  49512. break;
  49513. }
  49514. } break;
  49515. case 6 : { // Textured line
  49516. if (!__color) {
  49517. if (render_type==5) cimg::mutex(10,0);
  49518. throw CImgArgumentException(_cimg_instance
  49519. "draw_object3d(): Undefined texture for line primitive [%u].",
  49520. cimg_instance,n_primitive);
  49521. }
  49522. const unsigned int
  49523. n0 = (unsigned int)primitive[0],
  49524. n1 = (unsigned int)primitive[1];
  49525. const int
  49526. tx0 = (int)primitive[2], ty0 = (int)primitive[3],
  49527. tx1 = (int)primitive[4], ty1 = (int)primitive[5],
  49528. x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
  49529. x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1));
  49530. const float
  49531. z0 = vertices(n0,2) + Z + _focale,
  49532. z1 = vertices(n1,2) + Z + _focale;
  49533. if (render_type) {
  49534. if (zbuffer) draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity);
  49535. else draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity);
  49536. #ifdef cimg_use_board
  49537. if (pboard) {
  49538. board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
  49539. board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
  49540. }
  49541. #endif
  49542. } else {
  49543. draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0,
  49544. ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity).
  49545. draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1,
  49546. ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity);
  49547. #ifdef cimg_use_board
  49548. if (pboard) {
  49549. board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
  49550. board.drawDot((float)x0,height() - (float)y0);
  49551. board.drawDot((float)x1,height() - (float)y1);
  49552. }
  49553. #endif
  49554. }
  49555. } break;
  49556. case 3 : { // Colored triangle
  49557. const unsigned int
  49558. n0 = (unsigned int)primitive[0],
  49559. n1 = (unsigned int)primitive[1],
  49560. n2 = (unsigned int)primitive[2];
  49561. const int
  49562. x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
  49563. x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)),
  49564. x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1));
  49565. const float
  49566. z0 = vertices(n0,2) + Z + _focale,
  49567. z1 = vertices(n1,2) + Z + _focale,
  49568. z2 = vertices(n2,2) + Z + _focale;
  49569. switch (render_type) {
  49570. case 0 :
  49571. draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity).draw_point(x2,y2,pcolor,opacity);
  49572. #ifdef cimg_use_board
  49573. if (pboard) {
  49574. board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
  49575. board.drawDot((float)x0,height() - (float)y0);
  49576. board.drawDot((float)x1,height() - (float)y1);
  49577. board.drawDot((float)x2,height() - (float)y2);
  49578. }
  49579. #endif
  49580. break;
  49581. case 1 :
  49582. if (zbuffer)
  49583. draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x0,y0,z0,x2,y2,z2,pcolor,opacity).
  49584. draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity);
  49585. else
  49586. draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x0,y0,x2,y2,pcolor,opacity).
  49587. draw_line(x1,y1,x2,y2,pcolor,opacity);
  49588. #ifdef cimg_use_board
  49589. if (pboard) {
  49590. board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
  49591. board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
  49592. board.drawLine((float)x0,height() - (float)y0,(float)x2,height() - (float)y2);
  49593. board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
  49594. }
  49595. #endif
  49596. break;
  49597. case 2 :
  49598. if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity);
  49599. else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity);
  49600. #ifdef cimg_use_board
  49601. if (pboard) {
  49602. board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
  49603. board.fillTriangle((float)x0,height() - (float)y0,
  49604. (float)x1,height() - (float)y1,
  49605. (float)x2,height() - (float)y2);
  49606. }
  49607. #endif
  49608. break;
  49609. case 3 :
  49610. if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l));
  49611. else _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l));
  49612. #ifdef cimg_use_board
  49613. if (pboard) {
  49614. const float lp = std::min(lightprops(l),1.f);
  49615. board.setPenColorRGBi((unsigned char)(color[0]*lp),
  49616. (unsigned char)(color[1]*lp),
  49617. (unsigned char)(color[2]*lp),
  49618. (unsigned char)(opacity*255));
  49619. board.fillTriangle((float)x0,height() - (float)y0,
  49620. (float)x1,height() - (float)y1,
  49621. (float)x2,height() - (float)y2);
  49622. }
  49623. #endif
  49624. break;
  49625. case 4 :
  49626. if (zbuffer)
  49627. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,
  49628. lightprops(n0),lightprops(n1),lightprops(n2),opacity);
  49629. else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,lightprops(n0),lightprops(n1),lightprops(n2),opacity);
  49630. #ifdef cimg_use_board
  49631. if (pboard) {
  49632. board.setPenColorRGBi((unsigned char)(color[0]),
  49633. (unsigned char)(color[1]),
  49634. (unsigned char)(color[2]),
  49635. (unsigned char)(opacity*255));
  49636. board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprops(n0),
  49637. (float)x1,height() - (float)y1,lightprops(n1),
  49638. (float)x2,height() - (float)y2,lightprops(n2));
  49639. }
  49640. #endif
  49641. break;
  49642. case 5 : {
  49643. const unsigned int
  49644. lx0 = (unsigned int)cimg::uiround(lightprops(n0,0)), ly0 = (unsigned int)cimg::uiround(lightprops(n0,1)),
  49645. lx1 = (unsigned int)cimg::uiround(lightprops(n1,0)), ly1 = (unsigned int)cimg::uiround(lightprops(n1,1)),
  49646. lx2 = (unsigned int)cimg::uiround(lightprops(n2,0)), ly2 = (unsigned int)cimg::uiround(lightprops(n2,1));
  49647. if (zbuffer)
  49648. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
  49649. else draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity);
  49650. #ifdef cimg_use_board
  49651. if (pboard) {
  49652. const float
  49653. l0 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n0,0))),
  49654. (int)(light_texture.height()/2*(1 + lightprops(n0,1)))),
  49655. l1 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n1,0))),
  49656. (int)(light_texture.height()/2*(1 + lightprops(n1,1)))),
  49657. l2 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n2,0))),
  49658. (int)(light_texture.height()/2*(1 + lightprops(n2,1))));
  49659. board.setPenColorRGBi((unsigned char)(color[0]),
  49660. (unsigned char)(color[1]),
  49661. (unsigned char)(color[2]),
  49662. (unsigned char)(opacity*255));
  49663. board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
  49664. (float)x1,height() - (float)y1,l1,
  49665. (float)x2,height() - (float)y2,l2);
  49666. }
  49667. #endif
  49668. } break;
  49669. }
  49670. } break;
  49671. case 4 : { // Colored quadrangle
  49672. const unsigned int
  49673. n0 = (unsigned int)primitive[0],
  49674. n1 = (unsigned int)primitive[1],
  49675. n2 = (unsigned int)primitive[2],
  49676. n3 = (unsigned int)primitive[3];
  49677. const int
  49678. x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
  49679. x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)),
  49680. x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1)),
  49681. x3 = cimg::uiround(projections(n3,0)), y3 = cimg::uiround(projections(n3,1)),
  49682. xc = (x0 + x1 + x2 + x3)/4, yc = (y0 + y1 + y2 + y3)/4;
  49683. const float
  49684. z0 = vertices(n0,2) + Z + _focale,
  49685. z1 = vertices(n1,2) + Z + _focale,
  49686. z2 = vertices(n2,2) + Z + _focale,
  49687. z3 = vertices(n3,2) + Z + _focale,
  49688. zc = (z0 + z1 + z2 + z3)/4;
  49689. switch (render_type) {
  49690. case 0 :
  49691. draw_point(x0,y0,pcolor,opacity).draw_point(x1,y1,pcolor,opacity).
  49692. draw_point(x2,y2,pcolor,opacity).draw_point(x3,y3,pcolor,opacity);
  49693. #ifdef cimg_use_board
  49694. if (pboard) {
  49695. board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
  49696. board.drawDot((float)x0,height() - (float)y0);
  49697. board.drawDot((float)x1,height() - (float)y1);
  49698. board.drawDot((float)x2,height() - (float)y2);
  49699. board.drawDot((float)x3,height() - (float)y3);
  49700. }
  49701. #endif
  49702. break;
  49703. case 1 :
  49704. if (zbuffer)
  49705. draw_line(zbuffer,x0,y0,z0,x1,y1,z1,pcolor,opacity).draw_line(zbuffer,x1,y1,z1,x2,y2,z2,pcolor,opacity).
  49706. draw_line(zbuffer,x2,y2,z2,x3,y3,z3,pcolor,opacity).draw_line(zbuffer,x3,y3,z3,x0,y0,z0,pcolor,opacity);
  49707. else
  49708. draw_line(x0,y0,x1,y1,pcolor,opacity).draw_line(x1,y1,x2,y2,pcolor,opacity).
  49709. draw_line(x2,y2,x3,y3,pcolor,opacity).draw_line(x3,y3,x0,y0,pcolor,opacity);
  49710. #ifdef cimg_use_board
  49711. if (pboard) {
  49712. board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
  49713. board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
  49714. board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
  49715. board.drawLine((float)x2,height() - (float)y2,(float)x3,height() - (float)y3);
  49716. board.drawLine((float)x3,height() - (float)y3,(float)x0,height() - (float)y0);
  49717. }
  49718. #endif
  49719. break;
  49720. case 2 :
  49721. if (zbuffer)
  49722. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity).
  49723. draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity);
  49724. else
  49725. draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity).draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity);
  49726. #ifdef cimg_use_board
  49727. if (pboard) {
  49728. board.setPenColorRGBi(color[0],color[1],color[2],(unsigned char)(opacity*255));
  49729. board.fillTriangle((float)x0,height() - (float)y0,
  49730. (float)x1,height() - (float)y1,
  49731. (float)x2,height() - (float)y2);
  49732. board.fillTriangle((float)x0,height() - (float)y0,
  49733. (float)x2,height() - (float)y2,
  49734. (float)x3,height() - (float)y3);
  49735. }
  49736. #endif
  49737. break;
  49738. case 3 :
  49739. if (zbuffer)
  49740. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,pcolor,opacity,lightprops(l)).
  49741. draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,pcolor,opacity,lightprops(l));
  49742. else
  49743. _draw_triangle(x0,y0,x1,y1,x2,y2,pcolor,opacity,lightprops(l)).
  49744. _draw_triangle(x0,y0,x2,y2,x3,y3,pcolor,opacity,lightprops(l));
  49745. #ifdef cimg_use_board
  49746. if (pboard) {
  49747. const float lp = std::min(lightprops(l),1.f);
  49748. board.setPenColorRGBi((unsigned char)(color[0]*lp),
  49749. (unsigned char)(color[1]*lp),
  49750. (unsigned char)(color[2]*lp),(unsigned char)(opacity*255));
  49751. board.fillTriangle((float)x0,height() - (float)y0,
  49752. (float)x1,height() - (float)y1,
  49753. (float)x2,height() - (float)y2);
  49754. board.fillTriangle((float)x0,height() - (float)y0,
  49755. (float)x2,height() - (float)y2,
  49756. (float)x3,height() - (float)y3);
  49757. }
  49758. #endif
  49759. break;
  49760. case 4 : {
  49761. const float
  49762. lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
  49763. lightprop2 = lightprops(n2), lightprop3 = lightprops(n3),
  49764. lightpropc = (lightprop0 + lightprop1 + lightprop2 + lightprop2)/4;
  49765. if (zbuffer)
  49766. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,xc,yc,zc,pcolor,lightprop0,lightprop1,lightpropc,opacity).
  49767. draw_triangle(zbuffer,x1,y1,z1,x2,y2,z2,xc,yc,zc,pcolor,lightprop1,lightprop2,lightpropc,opacity).
  49768. draw_triangle(zbuffer,x2,y2,z2,x3,y3,z3,xc,yc,zc,pcolor,lightprop2,lightprop3,lightpropc,opacity).
  49769. draw_triangle(zbuffer,x3,y3,z3,x0,y0,z0,xc,yc,zc,pcolor,lightprop3,lightprop0,lightpropc,opacity);
  49770. else
  49771. draw_triangle(x0,y0,x1,y1,xc,yc,pcolor,lightprop0,lightprop1,lightpropc,opacity).
  49772. draw_triangle(x1,y1,x2,y2,xc,yc,pcolor,lightprop1,lightprop2,lightpropc,opacity).
  49773. draw_triangle(x2,y2,x3,y3,xc,yc,pcolor,lightprop2,lightprop3,lightpropc,opacity).
  49774. draw_triangle(x3,y3,x0,y0,xc,yc,pcolor,lightprop3,lightprop0,lightpropc,opacity);
  49775. #ifdef cimg_use_board
  49776. if (pboard) {
  49777. board.setPenColorRGBi((unsigned char)(color[0]),
  49778. (unsigned char)(color[1]),
  49779. (unsigned char)(color[2]),
  49780. (unsigned char)(opacity*255));
  49781. board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0,
  49782. (float)x1,height() - (float)y1,lightprop1,
  49783. (float)x2,height() - (float)y2,lightprop2);
  49784. board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0,
  49785. (float)x2,height() - (float)y2,lightprop2,
  49786. (float)x3,height() - (float)y3,lightprop3);
  49787. }
  49788. #endif
  49789. } break;
  49790. case 5 : {
  49791. const unsigned int
  49792. lx0 = (unsigned int)cimg::uiround(lightprops(n0,0)), ly0 = (unsigned int)cimg::uiround(lightprops(n0,1)),
  49793. lx1 = (unsigned int)cimg::uiround(lightprops(n1,0)), ly1 = (unsigned int)cimg::uiround(lightprops(n1,1)),
  49794. lx2 = (unsigned int)cimg::uiround(lightprops(n2,0)), ly2 = (unsigned int)cimg::uiround(lightprops(n2,1)),
  49795. lx3 = (unsigned int)cimg::uiround(lightprops(n3,0)), ly3 = (unsigned int)cimg::uiround(lightprops(n3,1)),
  49796. lxc = (lx0 + lx1 + lx2 + lx3)/4, lyc = (ly0 + ly1 + ly2 + ly3)/4;
  49797. if (zbuffer)
  49798. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,xc,yc,zc,pcolor,light_texture,lx0,ly0,lx1,ly1,lxc,lyc,opacity).
  49799. draw_triangle(zbuffer,x1,y1,z1,x2,y2,z2,xc,yc,zc,pcolor,light_texture,lx1,ly1,lx2,ly2,lxc,lyc,opacity).
  49800. draw_triangle(zbuffer,x2,y2,z2,x3,y3,z3,xc,yc,zc,pcolor,light_texture,lx2,ly2,lx3,ly3,lxc,lyc,opacity).
  49801. draw_triangle(zbuffer,x3,y3,z3,x0,y0,z0,xc,yc,zc,pcolor,light_texture,lx3,ly3,lx0,ly0,lxc,lyc,opacity);
  49802. else
  49803. draw_triangle(x0,y0,x1,y1,xc,yc,pcolor,light_texture,lx0,ly0,lx1,ly1,lxc,lyc,opacity).
  49804. draw_triangle(x1,y1,x2,y2,xc,yc,pcolor,light_texture,lx1,ly1,lx2,ly2,lxc,lyc,opacity).
  49805. draw_triangle(x2,y2,x3,y3,xc,yc,pcolor,light_texture,lx2,ly2,lx3,ly3,lxc,lyc,opacity).
  49806. draw_triangle(x3,y3,x0,y0,xc,yc,pcolor,light_texture,lx3,ly3,lx0,ly0,lxc,lyc,opacity);
  49807. #ifdef cimg_use_board
  49808. if (pboard) {
  49809. const float
  49810. l0 = light_texture((int)(light_texture.width()/2*(1 + lx0)), (int)(light_texture.height()/2*(1 + ly0))),
  49811. l1 = light_texture((int)(light_texture.width()/2*(1 + lx1)), (int)(light_texture.height()/2*(1 + ly1))),
  49812. l2 = light_texture((int)(light_texture.width()/2*(1 + lx2)), (int)(light_texture.height()/2*(1 + ly2))),
  49813. l3 = light_texture((int)(light_texture.width()/2*(1 + lx3)), (int)(light_texture.height()/2*(1 + ly3)));
  49814. board.setPenColorRGBi((unsigned char)(color[0]),
  49815. (unsigned char)(color[1]),
  49816. (unsigned char)(color[2]),
  49817. (unsigned char)(opacity*255));
  49818. board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
  49819. (float)x1,height() - (float)y1,l1,
  49820. (float)x2,height() - (float)y2,l2);
  49821. board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
  49822. (float)x2,height() - (float)y2,l2,
  49823. (float)x3,height() - (float)y3,l3);
  49824. }
  49825. #endif
  49826. } break;
  49827. }
  49828. } break;
  49829. case 9 : { // Textured triangle
  49830. if (!__color) {
  49831. if (render_type==5) cimg::mutex(10,0);
  49832. throw CImgArgumentException(_cimg_instance
  49833. "draw_object3d(): Undefined texture for triangle primitive [%u].",
  49834. cimg_instance,n_primitive);
  49835. }
  49836. const unsigned int
  49837. n0 = (unsigned int)primitive[0],
  49838. n1 = (unsigned int)primitive[1],
  49839. n2 = (unsigned int)primitive[2];
  49840. const int
  49841. tx0 = (int)primitive[3], ty0 = (int)primitive[4],
  49842. tx1 = (int)primitive[5], ty1 = (int)primitive[6],
  49843. tx2 = (int)primitive[7], ty2 = (int)primitive[8],
  49844. x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
  49845. x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)),
  49846. x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1));
  49847. const float
  49848. z0 = vertices(n0,2) + Z + _focale,
  49849. z1 = vertices(n1,2) + Z + _focale,
  49850. z2 = vertices(n2,2) + Z + _focale;
  49851. switch (render_type) {
  49852. case 0 :
  49853. draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0,
  49854. ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity).
  49855. draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1,
  49856. ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity).
  49857. draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2,
  49858. ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity);
  49859. #ifdef cimg_use_board
  49860. if (pboard) {
  49861. board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
  49862. board.drawDot((float)x0,height() - (float)y0);
  49863. board.drawDot((float)x1,height() - (float)y1);
  49864. board.drawDot((float)x2,height() - (float)y2);
  49865. }
  49866. #endif
  49867. break;
  49868. case 1 :
  49869. if (zbuffer)
  49870. draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
  49871. draw_line(zbuffer,x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity).
  49872. draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity);
  49873. else
  49874. draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
  49875. draw_line(x0,y0,z0,x2,y2,z2,color,tx0,ty0,tx2,ty2,opacity).
  49876. draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity);
  49877. #ifdef cimg_use_board
  49878. if (pboard) {
  49879. board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
  49880. board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
  49881. board.drawLine((float)x0,height() - (float)y0,(float)x2,height() - (float)y2);
  49882. board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
  49883. }
  49884. #endif
  49885. break;
  49886. case 2 :
  49887. if (zbuffer) draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity);
  49888. else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity);
  49889. #ifdef cimg_use_board
  49890. if (pboard) {
  49891. board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
  49892. board.fillTriangle((float)x0,height() - (float)y0,
  49893. (float)x1,height() - (float)y1,
  49894. (float)x2,height() - (float)y2);
  49895. }
  49896. #endif
  49897. break;
  49898. case 3 :
  49899. if (zbuffer)
  49900. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l));
  49901. else draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l));
  49902. #ifdef cimg_use_board
  49903. if (pboard) {
  49904. const float lp = std::min(lightprops(l),1.f);
  49905. board.setPenColorRGBi((unsigned char)(128*lp),
  49906. (unsigned char)(128*lp),
  49907. (unsigned char)(128*lp),
  49908. (unsigned char)(opacity*255));
  49909. board.fillTriangle((float)x0,height() - (float)y0,
  49910. (float)x1,height() - (float)y1,
  49911. (float)x2,height() - (float)y2);
  49912. }
  49913. #endif
  49914. break;
  49915. case 4 :
  49916. if (zbuffer)
  49917. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
  49918. lightprops(n0),lightprops(n1),lightprops(n2),opacity);
  49919. else
  49920. draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
  49921. lightprops(n0),lightprops(n1),lightprops(n2),opacity);
  49922. #ifdef cimg_use_board
  49923. if (pboard) {
  49924. board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
  49925. board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprops(n0),
  49926. (float)x1,height() - (float)y1,lightprops(n1),
  49927. (float)x2,height() - (float)y2,lightprops(n2));
  49928. }
  49929. #endif
  49930. break;
  49931. case 5 :
  49932. if (zbuffer)
  49933. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
  49934. (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
  49935. (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
  49936. (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
  49937. opacity);
  49938. else
  49939. draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,light_texture,
  49940. (unsigned int)lightprops(n0,0),(unsigned int)lightprops(n0,1),
  49941. (unsigned int)lightprops(n1,0),(unsigned int)lightprops(n1,1),
  49942. (unsigned int)lightprops(n2,0),(unsigned int)lightprops(n2,1),
  49943. opacity);
  49944. #ifdef cimg_use_board
  49945. if (pboard) {
  49946. const float
  49947. l0 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n0,0))),
  49948. (int)(light_texture.height()/2*(1 + lightprops(n0,1)))),
  49949. l1 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n1,0))),
  49950. (int)(light_texture.height()/2*(1 + lightprops(n1,1)))),
  49951. l2 = light_texture((int)(light_texture.width()/2*(1 + lightprops(n2,0))),
  49952. (int)(light_texture.height()/2*(1 + lightprops(n2,1))));
  49953. board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
  49954. board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
  49955. (float)x1,height() - (float)y1,l1,
  49956. (float)x2,height() - (float)y2,l2);
  49957. }
  49958. #endif
  49959. break;
  49960. }
  49961. } break;
  49962. case 12 : { // Textured quadrangle
  49963. if (!__color) {
  49964. if (render_type==5) cimg::mutex(10,0);
  49965. throw CImgArgumentException(_cimg_instance
  49966. "draw_object3d(): Undefined texture for quadrangle primitive [%u].",
  49967. cimg_instance,n_primitive);
  49968. }
  49969. const unsigned int
  49970. n0 = (unsigned int)primitive[0],
  49971. n1 = (unsigned int)primitive[1],
  49972. n2 = (unsigned int)primitive[2],
  49973. n3 = (unsigned int)primitive[3];
  49974. const int
  49975. tx0 = (int)primitive[4], ty0 = (int)primitive[5],
  49976. tx1 = (int)primitive[6], ty1 = (int)primitive[7],
  49977. tx2 = (int)primitive[8], ty2 = (int)primitive[9],
  49978. tx3 = (int)primitive[10], ty3 = (int)primitive[11],
  49979. x0 = cimg::uiround(projections(n0,0)), y0 = cimg::uiround(projections(n0,1)),
  49980. x1 = cimg::uiround(projections(n1,0)), y1 = cimg::uiround(projections(n1,1)),
  49981. x2 = cimg::uiround(projections(n2,0)), y2 = cimg::uiround(projections(n2,1)),
  49982. x3 = cimg::uiround(projections(n3,0)), y3 = cimg::uiround(projections(n3,1));
  49983. const float
  49984. z0 = vertices(n0,2) + Z + _focale,
  49985. z1 = vertices(n1,2) + Z + _focale,
  49986. z2 = vertices(n2,2) + Z + _focale,
  49987. z3 = vertices(n3,2) + Z + _focale;
  49988. switch (render_type) {
  49989. case 0 :
  49990. draw_point(x0,y0,color.get_vector_at(tx0<=0?0:tx0>=color.width()?color.width() - 1:tx0,
  49991. ty0<=0?0:ty0>=color.height()?color.height() - 1:ty0)._data,opacity).
  49992. draw_point(x1,y1,color.get_vector_at(tx1<=0?0:tx1>=color.width()?color.width() - 1:tx1,
  49993. ty1<=0?0:ty1>=color.height()?color.height() - 1:ty1)._data,opacity).
  49994. draw_point(x2,y2,color.get_vector_at(tx2<=0?0:tx2>=color.width()?color.width() - 1:tx2,
  49995. ty2<=0?0:ty2>=color.height()?color.height() - 1:ty2)._data,opacity).
  49996. draw_point(x3,y3,color.get_vector_at(tx3<=0?0:tx3>=color.width()?color.width() - 1:tx3,
  49997. ty3<=0?0:ty3>=color.height()?color.height() - 1:ty3)._data,opacity);
  49998. #ifdef cimg_use_board
  49999. if (pboard) {
  50000. board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
  50001. board.drawDot((float)x0,height() - (float)y0);
  50002. board.drawDot((float)x1,height() - (float)y1);
  50003. board.drawDot((float)x2,height() - (float)y2);
  50004. board.drawDot((float)x3,height() - (float)y3);
  50005. }
  50006. #endif
  50007. break;
  50008. case 1 :
  50009. if (zbuffer)
  50010. draw_line(zbuffer,x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
  50011. draw_line(zbuffer,x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity).
  50012. draw_line(zbuffer,x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity).
  50013. draw_line(zbuffer,x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity);
  50014. else
  50015. draw_line(x0,y0,z0,x1,y1,z1,color,tx0,ty0,tx1,ty1,opacity).
  50016. draw_line(x1,y1,z1,x2,y2,z2,color,tx1,ty1,tx2,ty2,opacity).
  50017. draw_line(x2,y2,z2,x3,y3,z3,color,tx2,ty2,tx3,ty3,opacity).
  50018. draw_line(x3,y3,z3,x0,y0,z0,color,tx3,ty3,tx0,ty0,opacity);
  50019. #ifdef cimg_use_board
  50020. if (pboard) {
  50021. board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
  50022. board.drawLine((float)x0,height() - (float)y0,(float)x1,height() - (float)y1);
  50023. board.drawLine((float)x1,height() - (float)y1,(float)x2,height() - (float)y2);
  50024. board.drawLine((float)x2,height() - (float)y2,(float)x3,height() - (float)y3);
  50025. board.drawLine((float)x3,height() - (float)y3,(float)x0,height() - (float)y0);
  50026. }
  50027. #endif
  50028. break;
  50029. case 2 :
  50030. if (zbuffer)
  50031. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity).
  50032. draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity);
  50033. else
  50034. draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity).
  50035. draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity);
  50036. #ifdef cimg_use_board
  50037. if (pboard) {
  50038. board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
  50039. board.fillTriangle((float)x0,height() - (float)y0,
  50040. (float)x1,height() - (float)y1,
  50041. (float)x2,height() - (float)y2);
  50042. board.fillTriangle((float)x0,height() - (float)y0,
  50043. (float)x2,height() - (float)y2,
  50044. (float)x3,height() - (float)y3);
  50045. }
  50046. #endif
  50047. break;
  50048. case 3 :
  50049. if (zbuffer)
  50050. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)).
  50051. draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l));
  50052. else
  50053. draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,opacity,lightprops(l)).
  50054. draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,opacity,lightprops(l));
  50055. #ifdef cimg_use_board
  50056. if (pboard) {
  50057. const float lp = std::min(lightprops(l),1.f);
  50058. board.setPenColorRGBi((unsigned char)(128*lp),
  50059. (unsigned char)(128*lp),
  50060. (unsigned char)(128*lp),
  50061. (unsigned char)(opacity*255));
  50062. board.fillTriangle((float)x0,height() - (float)y0,
  50063. (float)x1,height() - (float)y1,
  50064. (float)x2,height() - (float)y2);
  50065. board.fillTriangle((float)x0,height() - (float)y0,
  50066. (float)x2,height() - (float)y2,
  50067. (float)x3,height() - (float)y3);
  50068. }
  50069. #endif
  50070. break;
  50071. case 4 : {
  50072. const float
  50073. lightprop0 = lightprops(n0), lightprop1 = lightprops(n1),
  50074. lightprop2 = lightprops(n2), lightprop3 = lightprops(n3);
  50075. if (zbuffer)
  50076. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
  50077. lightprop0,lightprop1,lightprop2,opacity).
  50078. draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
  50079. lightprop0,lightprop2,lightprop3,opacity);
  50080. else
  50081. draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
  50082. lightprop0,lightprop1,lightprop2,opacity).
  50083. draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
  50084. lightprop0,lightprop2,lightprop3,opacity);
  50085. #ifdef cimg_use_board
  50086. if (pboard) {
  50087. board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
  50088. board.fillGouraudTriangle((float)x0,height() - (float)y0,lightprop0,
  50089. (float)x1,height() - (float)y1,lightprop1,
  50090. (float)x2,height() - (float)y2,lightprop2);
  50091. board.fillGouraudTriangle((float)x0,height() -(float)y0,lightprop0,
  50092. (float)x2,height() - (float)y2,lightprop2,
  50093. (float)x3,height() - (float)y3,lightprop3);
  50094. }
  50095. #endif
  50096. } break;
  50097. case 5 : {
  50098. const unsigned int
  50099. lx0 = (unsigned int)cimg::uiround(lightprops(n0,0)), ly0 = (unsigned int)cimg::uiround(lightprops(n0,1)),
  50100. lx1 = (unsigned int)cimg::uiround(lightprops(n1,0)), ly1 = (unsigned int)cimg::uiround(lightprops(n1,1)),
  50101. lx2 = (unsigned int)cimg::uiround(lightprops(n2,0)), ly2 = (unsigned int)cimg::uiround(lightprops(n2,1)),
  50102. lx3 = (unsigned int)cimg::uiround(lightprops(n3,0)), ly3 = (unsigned int)cimg::uiround(lightprops(n3,1));
  50103. if (zbuffer)
  50104. draw_triangle(zbuffer,x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
  50105. light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity).
  50106. draw_triangle(zbuffer,x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
  50107. light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity);
  50108. else
  50109. draw_triangle(x0,y0,z0,x1,y1,z1,x2,y2,z2,color,tx0,ty0,tx1,ty1,tx2,ty2,
  50110. light_texture,lx0,ly0,lx1,ly1,lx2,ly2,opacity).
  50111. draw_triangle(x0,y0,z0,x2,y2,z2,x3,y3,z3,color,tx0,ty0,tx2,ty2,tx3,ty3,
  50112. light_texture,lx0,ly0,lx2,ly2,lx3,ly3,opacity);
  50113. #ifdef cimg_use_board
  50114. if (pboard) {
  50115. const float
  50116. l0 = light_texture((int)(light_texture.width()/2*(1 + lx0)), (int)(light_texture.height()/2*(1 + ly0))),
  50117. l1 = light_texture((int)(light_texture.width()/2*(1 + lx1)), (int)(light_texture.height()/2*(1 + ly1))),
  50118. l2 = light_texture((int)(light_texture.width()/2*(1 + lx2)), (int)(light_texture.height()/2*(1 + ly2))),
  50119. l3 = light_texture((int)(light_texture.width()/2*(1 + lx3)), (int)(light_texture.height()/2*(1 + ly3)));
  50120. board.setPenColorRGBi(128,128,128,(unsigned char)(opacity*255));
  50121. board.fillGouraudTriangle((float)x0,height() - (float)y0,l0,
  50122. (float)x1,height() - (float)y1,l1,
  50123. (float)x2,height() - (float)y2,l2);
  50124. board.fillGouraudTriangle((float)x0,height() -(float)y0,l0,
  50125. (float)x2,height() - (float)y2,l2,
  50126. (float)x3,height() - (float)y3,l3);
  50127. }
  50128. #endif
  50129. } break;
  50130. }
  50131. } break;
  50132. }
  50133. }
  50134. if (render_type==5) cimg::mutex(10,0);
  50135. return *this;
  50136. }
  50137. //@}
  50138. //---------------------------
  50139. //
  50140. //! \name Data Input
  50141. //@{
  50142. //---------------------------
  50143. //! Launch simple interface to select a shape from an image.
  50144. /**
  50145. \param disp Display window to use.
  50146. \param feature_type Type of feature to select. Can be <tt>{ 0=point | 1=line | 2=rectangle | 3=ellipse }</tt>.
  50147. \param XYZ Pointer to 3 values X,Y,Z which tells about the projection point coordinates, for volumetric images.
  50148. \param exit_on_anykey Exit function when any key is pressed.
  50149. **/
  50150. CImg<T>& select(CImgDisplay &disp,
  50151. const unsigned int feature_type=2, unsigned int *const XYZ=0,
  50152. const bool exit_on_anykey=false,
  50153. const bool is_deep_selection_default=false) {
  50154. return get_select(disp,feature_type,XYZ,exit_on_anykey,is_deep_selection_default).move_to(*this);
  50155. }
  50156. //! Simple interface to select a shape from an image \overloading.
  50157. CImg<T>& select(const char *const title,
  50158. const unsigned int feature_type=2, unsigned int *const XYZ=0,
  50159. const bool exit_on_anykey=false,
  50160. const bool is_deep_selection_default=false) {
  50161. return get_select(title,feature_type,XYZ,exit_on_anykey,is_deep_selection_default).move_to(*this);
  50162. }
  50163. //! Simple interface to select a shape from an image \newinstance.
  50164. CImg<intT> get_select(CImgDisplay &disp,
  50165. const unsigned int feature_type=2, unsigned int *const XYZ=0,
  50166. const bool exit_on_anykey=false,
  50167. const bool is_deep_selection_default=false) const {
  50168. return _select(disp,0,feature_type,XYZ,0,0,0,exit_on_anykey,true,false,is_deep_selection_default);
  50169. }
  50170. //! Simple interface to select a shape from an image \newinstance.
  50171. CImg<intT> get_select(const char *const title,
  50172. const unsigned int feature_type=2, unsigned int *const XYZ=0,
  50173. const bool exit_on_anykey=false,
  50174. const bool is_deep_selection_default=false) const {
  50175. CImgDisplay disp;
  50176. return _select(disp,title,feature_type,XYZ,0,0,0,exit_on_anykey,true,false,is_deep_selection_default);
  50177. }
  50178. CImg<intT> _select(CImgDisplay &disp, const char *const title,
  50179. const unsigned int feature_type, unsigned int *const XYZ,
  50180. const int origX, const int origY, const int origZ,
  50181. const bool exit_on_anykey,
  50182. const bool reset_view3d,
  50183. const bool force_display_z_coord,
  50184. const bool is_deep_selection_default) const {
  50185. if (is_empty()) return CImg<intT>(1,feature_type==0?3:6,1,1,-1);
  50186. if (!disp) {
  50187. disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1);
  50188. if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
  50189. } else {
  50190. if (title) disp.set_title("%s",title);
  50191. disp.move_inside_screen();
  50192. }
  50193. CImg<T> thumb;
  50194. if (width()>disp.screen_width() || height()>disp.screen_height())
  50195. get_resize(cimg_fitscreen(width(),height(),depth()),depth(),-100).move_to(thumb);
  50196. const unsigned int old_normalization = disp.normalization();
  50197. bool old_is_resized = disp.is_resized();
  50198. disp._normalization = 0;
  50199. disp.show().set_key(0).set_wheel().show_mouse();
  50200. static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
  50201. int area = 0, area_started = 0, area_clicked = 0, phase = 0,
  50202. X0 = (int)((XYZ?XYZ[0]:_width/2)%_width),
  50203. Y0 = (int)((XYZ?XYZ[1]:_height/2)%_height),
  50204. Z0 = (int)((XYZ?XYZ[2]:_depth/2)%_depth),
  50205. X1 =-1, Y1 = -1, Z1 = -1,
  50206. X3d = -1, Y3d = -1,
  50207. oX3d = X3d, oY3d = -1,
  50208. omx = -1, omy = -1;
  50209. float X = -1, Y = -1, Z = -1;
  50210. unsigned int key = 0, font_size = 32;
  50211. bool is_deep_selection = is_deep_selection_default,
  50212. shape_selected = false, text_down = false, visible_cursor = true;
  50213. static CImg<floatT> pose3d;
  50214. static bool is_view3d = false, is_axes = true;
  50215. if (reset_view3d) { pose3d.assign(); is_view3d = false; }
  50216. CImg<floatT> points3d, opacities3d, sel_opacities3d;
  50217. CImgList<uintT> primitives3d, sel_primitives3d;
  50218. CImgList<ucharT> colors3d, sel_colors3d;
  50219. CImg<ucharT> visu, visu0, view3d;
  50220. CImg<charT> text(1024); *text = 0;
  50221. while (!key && !disp.is_closed() && !shape_selected) {
  50222. // Handle mouse motion and selection
  50223. int
  50224. mx = disp.mouse_x(),
  50225. my = disp.mouse_y();
  50226. const float
  50227. mX = mx<0?-1.f:(float)mx*(width() + (depth()>1?depth():0))/disp.width(),
  50228. mY = my<0?-1.f:(float)my*(height() + (depth()>1?depth():0))/disp.height();
  50229. area = 0;
  50230. if (mX>=0 && mY>=0 && mX<width() && mY<height()) { area = 1; X = mX; Y = mY; Z = (float)(phase?Z1:Z0); }
  50231. if (mX>=0 && mX<width() && mY>=height()) { area = 2; X = mX; Z = mY - _height; Y = (float)(phase?Y1:Y0); }
  50232. if (mY>=0 && mX>=width() && mY<height()) { area = 3; Y = mY; Z = mX - _width; X = (float)(phase?X1:X0); }
  50233. if (mX>=width() && mY>=height()) area = 4;
  50234. if (disp.button()) { if (!area_clicked) area_clicked = area; } else area_clicked = 0;
  50235. CImg<charT> filename(32);
  50236. switch (key = disp.key()) {
  50237. #if cimg_OS!=2
  50238. case cimg::keyCTRLRIGHT :
  50239. #endif
  50240. case 0 : case cimg::keyCTRLLEFT : key = 0; break;
  50241. case cimg::keyPAGEUP :
  50242. if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(1); key = 0; } break;
  50243. case cimg::keyPAGEDOWN :
  50244. if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { disp.set_wheel(-1); key = 0; } break;
  50245. case cimg::keyX : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  50246. is_axes = !is_axes; disp.set_key(key,false); key = 0; visu0.assign();
  50247. } break;
  50248. case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  50249. disp.set_fullscreen(false).
  50250. resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
  50251. CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
  50252. _is_resized = true;
  50253. disp.set_key(key,false); key = 0; visu0.assign();
  50254. } break;
  50255. case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  50256. disp.set_fullscreen(false).
  50257. resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
  50258. disp.set_key(key,false); key = 0; visu0.assign();
  50259. } break;
  50260. case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  50261. disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
  50262. disp.set_key(key,false); key = 0; visu0.assign();
  50263. } break;
  50264. case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  50265. disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
  50266. disp.set_key(key,false); key = 0; visu0.assign();
  50267. } break;
  50268. case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  50269. is_view3d = !is_view3d; disp.set_key(key,false); key = 0; visu0.assign();
  50270. } break;
  50271. case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  50272. static unsigned int snap_number = 0;
  50273. std::FILE *file;
  50274. do {
  50275. cimg_snprintf(filename,filename._width,cimg_appname "_%.6u."
  50276. #ifdef cimg_use_png
  50277. "png",
  50278. #else
  50279. "bmp",
  50280. #endif
  50281. snap_number++);
  50282. if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
  50283. } while (file);
  50284. if (visu0) {
  50285. (+visu0).__draw_text(" Saving snapshot...",font_size,(int)text_down).display(disp);
  50286. visu0.save(filename);
  50287. (+visu0).__draw_text(" Snapshot '%s' saved. ",font_size,(int)text_down,filename._data).display(disp);
  50288. }
  50289. disp.set_key(key,false); key = 0;
  50290. } break;
  50291. case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  50292. static unsigned int snap_number = 0;
  50293. std::FILE *file;
  50294. do {
  50295. #ifdef cimg_use_zlib
  50296. cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimgz",snap_number++);
  50297. #else
  50298. cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimg",snap_number++);
  50299. #endif
  50300. if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
  50301. } while (file);
  50302. (+visu0).__draw_text(" Saving instance... ",font_size,(int)text_down).display(disp);
  50303. save(filename);
  50304. (+visu0).__draw_text(" Instance '%s' saved. ",font_size,(int)text_down,filename._data).display(disp);
  50305. disp.set_key(key,false); key = 0;
  50306. } break;
  50307. }
  50308. switch (area) {
  50309. case 0 : // When mouse is out of image range
  50310. mx = my = -1; X = Y = Z = -1;
  50311. break;
  50312. case 1 : case 2 : case 3 : { // When mouse is over the XY,XZ or YZ projections
  50313. const unsigned int but = disp.button();
  50314. const bool b1 = (bool)(but&1), b2 = (bool)(but&2), b3 = (bool)(but&4);
  50315. if (b1 && phase==1 && area_clicked==area) { // When selection has been started (1st step)
  50316. if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign();
  50317. X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z;
  50318. }
  50319. if (!b1 && phase==2 && area_clicked!=area) { // When selection is at 2nd step (for volumes)
  50320. switch (area_started) {
  50321. case 1 : if (Z1!=(int)Z) visu0.assign(); Z1 = (int)Z; break;
  50322. case 2 : if (Y1!=(int)Y) visu0.assign(); Y1 = (int)Y; break;
  50323. case 3 : if (X1!=(int)X) visu0.assign(); X1 = (int)X; break;
  50324. }
  50325. }
  50326. if (b2 && area_clicked==area) { // When moving through the image/volume
  50327. if (phase) {
  50328. if (_depth>1 && (X1!=(int)X || Y1!=(int)Y || Z1!=(int)Z)) visu0.assign();
  50329. X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z;
  50330. } else {
  50331. if (_depth>1 && (X0!=(int)X || Y0!=(int)Y || Z0!=(int)Z)) visu0.assign();
  50332. X0 = (int)X; Y0 = (int)Y; Z0 = (int)Z;
  50333. }
  50334. }
  50335. if (b3) { // Reset selection
  50336. X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = area = area_clicked = area_started = 0;
  50337. visu0.assign();
  50338. }
  50339. if (disp.wheel()) { // When moving through the slices of the volume (with mouse wheel)
  50340. if (_depth>1 && !disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT() &&
  50341. !disp.is_keySHIFTLEFT() && !disp.is_keySHIFTRIGHT()) {
  50342. switch (area) {
  50343. case 1 :
  50344. if (phase) Z = (float)(Z1+=disp.wheel()); else Z = (float)(Z0+=disp.wheel());
  50345. visu0.assign(); break;
  50346. case 2 :
  50347. if (phase) Y = (float)(Y1+=disp.wheel()); else Y = (float)(Y0+=disp.wheel());
  50348. visu0.assign(); break;
  50349. case 3 :
  50350. if (phase) X = (float)(X1+=disp.wheel()); else X = (float)(X0+=disp.wheel());
  50351. visu0.assign(); break;
  50352. }
  50353. disp.set_wheel();
  50354. } else key = ~0U;
  50355. }
  50356. if ((phase==0 && b1) ||
  50357. (phase==1 && !b1) ||
  50358. (phase==2 && b1)) switch (phase) { // Detect change of phase
  50359. case 0 :
  50360. if (area==area_clicked) {
  50361. X0 = X1 = (int)X; Y0 = Y1 = (int)Y; Z0 = Z1 = (int)Z; area_started = area; ++phase;
  50362. } break;
  50363. case 1 :
  50364. if (area==area_started) {
  50365. X1 = (int)X; Y1 = (int)Y; Z1 = (int)Z; ++phase;
  50366. if (_depth>1) {
  50367. if (disp.is_keyCTRLLEFT()) is_deep_selection = !is_deep_selection_default;
  50368. if (is_deep_selection) ++phase;
  50369. }
  50370. } else if (!b1) { X = (float)X0; Y = (float)Y0; Z = (float)Z0; phase = 0; visu0.assign(); }
  50371. break;
  50372. case 2 : ++phase; break;
  50373. }
  50374. } break;
  50375. case 4 : // When mouse is over the 3D view
  50376. if (is_view3d && points3d) {
  50377. X3d = mx - width()*disp.width()/(width() + (depth()>1?depth():0));
  50378. Y3d = my - height()*disp.height()/(height() + (depth()>1?depth():0));
  50379. if (oX3d<0) { oX3d = X3d; oY3d = Y3d; }
  50380. // Left + right buttons: reset.
  50381. if ((disp.button()&3)==3) { pose3d.assign(); view3d.assign(); oX3d = oY3d = X3d = Y3d = -1; }
  50382. else if (disp.button()&1 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Left button: rotate
  50383. const float
  50384. R = 0.45f*std::min(view3d._width,view3d._height),
  50385. R2 = R*R,
  50386. u0 = (float)(oX3d - view3d.width()/2),
  50387. v0 = (float)(oY3d - view3d.height()/2),
  50388. u1 = (float)(X3d - view3d.width()/2),
  50389. v1 = (float)(Y3d - view3d.height()/2),
  50390. n0 = cimg::hypot(u0,v0),
  50391. n1 = cimg::hypot(u1,v1),
  50392. nu0 = n0>R?(u0*R/n0):u0,
  50393. nv0 = n0>R?(v0*R/n0):v0,
  50394. nw0 = (float)std::sqrt(std::max(0.f,R2 - nu0*nu0 - nv0*nv0)),
  50395. nu1 = n1>R?(u1*R/n1):u1,
  50396. nv1 = n1>R?(v1*R/n1):v1,
  50397. nw1 = (float)std::sqrt(std::max(0.f,R2 - nu1*nu1 - nv1*nv1)),
  50398. u = nv0*nw1 - nw0*nv1,
  50399. v = nw0*nu1 - nu0*nw1,
  50400. w = nv0*nu1 - nu0*nv1,
  50401. n = cimg::hypot(u,v,w),
  50402. alpha = (float)std::asin(n/R2)*180/cimg::PI;
  50403. pose3d.draw_image(CImg<floatT>::rotation_matrix(u,v,w,-alpha)*pose3d.get_crop(0,0,2,2));
  50404. view3d.assign();
  50405. } else if (disp.button()&2 && pose3d && oY3d!=Y3d) { // Right button: zoom
  50406. pose3d(3,2)+=(Y3d - oY3d)*1.5f; view3d.assign();
  50407. }
  50408. if (disp.wheel()) { // Wheel: zoom
  50409. pose3d(3,2)-=disp.wheel()*15; view3d.assign(); disp.set_wheel();
  50410. }
  50411. if (disp.button()&4 && pose3d && (oX3d!=X3d || oY3d!=Y3d)) { // Middle button: shift
  50412. pose3d(3,0)-=oX3d - X3d; pose3d(3,1)-=oY3d - Y3d; view3d.assign();
  50413. }
  50414. oX3d = X3d; oY3d = Y3d;
  50415. }
  50416. mx = my = -1; X = Y = Z = -1;
  50417. break;
  50418. }
  50419. if (phase) {
  50420. if (!feature_type) shape_selected = phase?true:false;
  50421. else {
  50422. if (_depth>1) shape_selected = (phase==3)?true:false;
  50423. else shape_selected = (phase==2)?true:false;
  50424. }
  50425. }
  50426. if (X0<0) X0 = 0;
  50427. if (X0>=width()) X0 = width() - 1;
  50428. if (Y0<0) Y0 = 0;
  50429. if (Y0>=height()) Y0 = height() - 1;
  50430. if (Z0<0) Z0 = 0;
  50431. if (Z0>=depth()) Z0 = depth() - 1;
  50432. if (X1<1) X1 = 0;
  50433. if (X1>=width()) X1 = width() - 1;
  50434. if (Y1<0) Y1 = 0;
  50435. if (Y1>=height()) Y1 = height() - 1;
  50436. if (Z1<0) Z1 = 0;
  50437. if (Z1>=depth()) Z1 = depth() - 1;
  50438. // Draw visualization image on the display
  50439. if (mx!=omx || my!=omy || !visu0 || (_depth>1 && !view3d)) {
  50440. if (!visu0) { // Create image of projected planes
  50441. if (thumb) thumb._get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0);
  50442. else _get_select(disp,old_normalization,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0).move_to(visu0);
  50443. visu0.resize(disp);
  50444. view3d.assign();
  50445. points3d.assign();
  50446. }
  50447. if (is_view3d && _depth>1 && !view3d) { // Create 3D view for volumetric images
  50448. const unsigned int
  50449. _x3d = (unsigned int)cimg::round((float)_width*visu0._width/(_width + _depth),1,1),
  50450. _y3d = (unsigned int)cimg::round((float)_height*visu0._height/(_height + _depth),1,1),
  50451. x3d = _x3d>=visu0._width?visu0._width - 1:_x3d,
  50452. y3d = _y3d>=visu0._height?visu0._height - 1:_y3d;
  50453. CImg<ucharT>(1,2,1,1,64,128).resize(visu0._width - x3d,visu0._height - y3d,1,visu0._spectrum,3).
  50454. move_to(view3d);
  50455. if (!points3d) {
  50456. get_projections3d(primitives3d,colors3d,phase?X1:X0,phase?Y1:Y0,phase?Z1:Z0,true).move_to(points3d);
  50457. points3d.append(CImg<floatT>(8,3,1,1,
  50458. 0,_width - 1,_width - 1,0,0,_width - 1,_width - 1,0,
  50459. 0,0,_height - 1,_height - 1,0,0,_height - 1,_height - 1,
  50460. 0,0,0,0,_depth - 1,_depth - 1,_depth - 1,_depth - 1),'x');
  50461. CImg<uintT>::vector(12,13).move_to(primitives3d); CImg<uintT>::vector(13,14).move_to(primitives3d);
  50462. CImg<uintT>::vector(14,15).move_to(primitives3d); CImg<uintT>::vector(15,12).move_to(primitives3d);
  50463. CImg<uintT>::vector(16,17).move_to(primitives3d); CImg<uintT>::vector(17,18).move_to(primitives3d);
  50464. CImg<uintT>::vector(18,19).move_to(primitives3d); CImg<uintT>::vector(19,16).move_to(primitives3d);
  50465. CImg<uintT>::vector(12,16).move_to(primitives3d); CImg<uintT>::vector(13,17).move_to(primitives3d);
  50466. CImg<uintT>::vector(14,18).move_to(primitives3d); CImg<uintT>::vector(15,19).move_to(primitives3d);
  50467. colors3d.insert(12,CImg<ucharT>::vector(255,255,255));
  50468. opacities3d.assign(primitives3d.width(),1,1,1,0.5f);
  50469. if (!phase) {
  50470. opacities3d[0] = opacities3d[1] = opacities3d[2] = 0.8f;
  50471. sel_primitives3d.assign();
  50472. sel_colors3d.assign();
  50473. sel_opacities3d.assign();
  50474. } else {
  50475. if (feature_type==2) {
  50476. points3d.append(CImg<floatT>(8,3,1,1,
  50477. X0,X1,X1,X0,X0,X1,X1,X0,
  50478. Y0,Y0,Y1,Y1,Y0,Y0,Y1,Y1,
  50479. Z0,Z0,Z0,Z0,Z1,Z1,Z1,Z1),'x');
  50480. sel_primitives3d.assign();
  50481. CImg<uintT>::vector(20,21).move_to(sel_primitives3d);
  50482. CImg<uintT>::vector(21,22).move_to(sel_primitives3d);
  50483. CImg<uintT>::vector(22,23).move_to(sel_primitives3d);
  50484. CImg<uintT>::vector(23,20).move_to(sel_primitives3d);
  50485. CImg<uintT>::vector(24,25).move_to(sel_primitives3d);
  50486. CImg<uintT>::vector(25,26).move_to(sel_primitives3d);
  50487. CImg<uintT>::vector(26,27).move_to(sel_primitives3d);
  50488. CImg<uintT>::vector(27,24).move_to(sel_primitives3d);
  50489. CImg<uintT>::vector(20,24).move_to(sel_primitives3d);
  50490. CImg<uintT>::vector(21,25).move_to(sel_primitives3d);
  50491. CImg<uintT>::vector(22,26).move_to(sel_primitives3d);
  50492. CImg<uintT>::vector(23,27).move_to(sel_primitives3d);
  50493. } else {
  50494. points3d.append(CImg<floatT>(2,3,1,1,
  50495. X0,X1,
  50496. Y0,Y1,
  50497. Z0,Z1),'x');
  50498. sel_primitives3d.assign(CImg<uintT>::vector(20,21));
  50499. }
  50500. sel_colors3d.assign(sel_primitives3d._width,CImg<ucharT>::vector(255,255,255));
  50501. sel_opacities3d.assign(sel_primitives3d._width,1,1,1,0.8f);
  50502. }
  50503. points3d.shift_object3d(-0.5f*(_width - 1),-0.5f*(_height - 1),-0.5f*(_depth - 1)).resize_object3d();
  50504. points3d*=0.75f*std::min(view3d._width,view3d._height);
  50505. }
  50506. if (!pose3d) CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose3d);
  50507. CImg<floatT> zbuffer3d(view3d._width,view3d._height,1,1,0);
  50508. const CImg<floatT> rotated_points3d = pose3d.get_crop(0,0,2,2)*points3d;
  50509. if (sel_primitives3d)
  50510. view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width,
  50511. pose3d(3,1) + 0.5f*view3d._height,
  50512. pose3d(3,2),
  50513. rotated_points3d,sel_primitives3d,sel_colors3d,sel_opacities3d,
  50514. 2,true,500,0,0,0,0,0,1,zbuffer3d);
  50515. view3d.draw_object3d(pose3d(3,0) + 0.5f*view3d._width,
  50516. pose3d(3,1) + 0.5f*view3d._height,
  50517. pose3d(3,2),
  50518. rotated_points3d,primitives3d,colors3d,opacities3d,
  50519. 2,true,500,0,0,0,0,0,1,zbuffer3d);
  50520. visu0.draw_image(x3d,y3d,view3d);
  50521. }
  50522. visu = visu0;
  50523. if (X<0 || Y<0 || Z<0) { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }}
  50524. else {
  50525. if (is_axes) { if (visible_cursor) { disp.hide_mouse(); visible_cursor = false; }}
  50526. else { if (!visible_cursor) { disp.show_mouse(); visible_cursor = true; }}
  50527. const int d = (depth()>1)?depth():0;
  50528. int _vX = (int)X, _vY = (int)Y, _vZ = (int)Z;
  50529. if (phase>=2) { _vX = X1; _vY = Y1; _vZ = Z1; }
  50530. int
  50531. w = disp.width(), W = width() + d,
  50532. h = disp.height(), H = height() + d,
  50533. _xp = (int)(_vX*(float)w/W), xp = _xp + ((int)(_xp*(float)W/w)!=_vX),
  50534. _yp = (int)(_vY*(float)h/H), yp = _yp + ((int)(_yp*(float)H/h)!=_vY),
  50535. _xn = (int)((_vX + 1.f)*w/W - 1), xn = _xn + ((int)((_xn + 1.f)*W/w)!=_vX + 1),
  50536. _yn = (int)((_vY + 1.f)*h/H - 1), yn = _yn + ((int)((_yn + 1.f)*H/h)!=_vY + 1),
  50537. _zxp = (int)((_vZ + width())*(float)w/W), zxp = _zxp + ((int)(_zxp*(float)W/w)!=_vZ + width()),
  50538. _zyp = (int)((_vZ + height())*(float)h/H), zyp = _zyp + ((int)(_zyp*(float)H/h)!=_vZ + height()),
  50539. _zxn = (int)((_vZ + width() + 1.f)*w/W - 1),
  50540. zxn = _zxn + ((int)((_zxn + 1.f)*W/w)!=_vZ + width() + 1),
  50541. _zyn = (int)((_vZ + height() + 1.f)*h/H - 1),
  50542. zyn = _zyn + ((int)((_zyn + 1.f)*H/h)!=_vZ + height() + 1),
  50543. _xM = (int)(width()*(float)w/W - 1), xM = _xM + ((int)((_xM + 1.f)*W/w)!=width()),
  50544. _yM = (int)(height()*(float)h/H - 1), yM = _yM + ((int)((_yM + 1.f)*H/h)!=height()),
  50545. xc = (xp + xn)/2,
  50546. yc = (yp + yn)/2,
  50547. zxc = (zxp + zxn)/2,
  50548. zyc = (zyp + zyn)/2,
  50549. xf = (int)(X*w/W),
  50550. yf = (int)(Y*h/H),
  50551. zxf = (int)((Z + width())*w/W),
  50552. zyf = (int)((Z + height())*h/H);
  50553. if (is_axes) { // Draw axes
  50554. visu.draw_line(0,yf,visu.width() - 1,yf,foreground_color,0.7f,0xFF00FF00).
  50555. draw_line(0,yf,visu.width() - 1,yf,background_color,0.7f,0x00FF00FF).
  50556. draw_line(xf,0,xf,visu.height() - 1,foreground_color,0.7f,0xFF00FF00).
  50557. draw_line(xf,0,xf,visu.height() - 1,background_color,0.7f,0x00FF00FF);
  50558. if (_depth>1)
  50559. visu.draw_line(zxf,0,zxf,yM,foreground_color,0.7f,0xFF00FF00).
  50560. draw_line(zxf,0,zxf,yM,background_color,0.7f,0x00FF00FF).
  50561. draw_line(0,zyf,xM,zyf,foreground_color,0.7f,0xFF00FF00).
  50562. draw_line(0,zyf,xM,zyf,background_color,0.7f,0x00FF00FF);
  50563. }
  50564. // Draw box cursor.
  50565. if (xn - xp>=4 && yn - yp>=4)
  50566. visu.draw_rectangle(xp,yp,xn,yn,foreground_color,0.2f).
  50567. draw_rectangle(xp,yp,xn,yn,foreground_color,1,0xAAAAAAAA).
  50568. draw_rectangle(xp,yp,xn,yn,background_color,1,0x55555555);
  50569. if (_depth>1) {
  50570. if (yn - yp>=4 && zxn - zxp>=4)
  50571. visu.draw_rectangle(zxp,yp,zxn,yn,background_color,0.2f).
  50572. draw_rectangle(zxp,yp,zxn,yn,foreground_color,1,0xAAAAAAAA).
  50573. draw_rectangle(zxp,yp,zxn,yn,background_color,1,0x55555555);
  50574. if (xn - xp>=4 && zyn - zyp>=4)
  50575. visu.draw_rectangle(xp,zyp,xn,zyn,background_color,0.2f).
  50576. draw_rectangle(xp,zyp,xn,zyn,foreground_color,1,0xAAAAAAAA).
  50577. draw_rectangle(xp,zyp,xn,zyn,background_color,1,0x55555555);
  50578. }
  50579. // Draw selection.
  50580. if (phase && (phase!=1 || area_started==area)) {
  50581. const int
  50582. _xp0 = (int)(X0*(float)w/W), xp0 = _xp0 + ((int)(_xp0*(float)W/w)!=X0),
  50583. _yp0 = (int)(Y0*(float)h/H), yp0 = _yp0 + ((int)(_yp0*(float)H/h)!=Y0),
  50584. _xn0 = (int)((X0 + 1.f)*w/W - 1), xn0 = _xn0 + ((int)((_xn0 + 1.f)*W/w)!=X0 + 1),
  50585. _yn0 = (int)((Y0 + 1.f)*h/H - 1), yn0 = _yn0 + ((int)((_yn0 + 1.f)*H/h)!=Y0 + 1),
  50586. _zxp0 = (int)((Z0 + width())*(float)w/W), zxp0 = _zxp0 + ((int)(_zxp0*(float)W/w)!=Z0 + width()),
  50587. _zyp0 = (int)((Z0 + height())*(float)h/H), zyp0 = _zyp0 + ((int)(_zyp0*(float)H/h)!=Z0 + height()),
  50588. _zxn0 = (int)((Z0 + width() + 1.f)*w/W - 1),
  50589. zxn0 = _zxn0 + ((int)((_zxn0 + 1.f)*W/w)!=Z0 + width() + 1),
  50590. _zyn0 = (int)((Z0 + height() + 1.f)*h/H - 1),
  50591. zyn0 = _zyn0 + ((int)((_zyn0 + 1.f)*H/h)!=Z0 + height() + 1),
  50592. xc0 = (xp0 + xn0)/2,
  50593. yc0 = (yp0 + yn0)/2,
  50594. zxc0 = (zxp0 + zxn0)/2,
  50595. zyc0 = (zyp0 + zyn0)/2;
  50596. switch (feature_type) {
  50597. case 1 : { // Vector
  50598. visu.draw_arrow(xc0,yc0,xc,yc,background_color,0.9f,30,5,0x33333333).
  50599. draw_arrow(xc0,yc0,xc,yc,foreground_color,0.9f,30,5,0xCCCCCCCC);
  50600. if (d) {
  50601. visu.draw_arrow(zxc0,yc0,zxc,yc,background_color,0.9f,30,5,0x33333333).
  50602. draw_arrow(zxc0,yc0,zxc,yc,foreground_color,0.9f,30,5,0xCCCCCCCC).
  50603. draw_arrow(xc0,zyc0,xc,zyc,background_color,0.9f,30,5,0x33333333).
  50604. draw_arrow(xc0,zyc0,xc,zyc,foreground_color,0.9f,30,5,0xCCCCCCCC);
  50605. }
  50606. } break;
  50607. case 2 : { // Box
  50608. visu.draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,background_color,0.2f).
  50609. draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,background_color,0.9f,0x55555555).
  50610. draw_rectangle(X0<X1?xp0:xp,Y0<Y1?yp0:yp,X0<X1?xn:xn0,Y0<Y1?yn:yn0,foreground_color,0.9f,0xAAAAAAAA);
  50611. if (xc0!=xc && yc0!=yc)
  50612. visu.draw_line(xc0,yc0,xc,yc,background_color,0.9f,0x33333333).
  50613. draw_line(xc0,yc0,xc,yc,foreground_color,0.9f,0xCCCCCCCC);
  50614. if (d) {
  50615. visu.draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,background_color,0.2f).
  50616. draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,
  50617. background_color,0.9f,0x55555555).
  50618. draw_rectangle(Z0<Z1?zxp0:zxp,Y0<Y1?yp0:yp,Z0<Z1?zxn:zxn0,Y0<Y1?yn:yn0,
  50619. foreground_color,0.9f,0xAAAAAAAA);
  50620. if (zxc0!=zxc && yc0!=yc)
  50621. visu.draw_line(zxc0,yc0,zxc,yc,background_color,0.9f,0x33333333).
  50622. draw_line(zxc0,yc0,zxc,yc,foreground_color,0.9f,0xCCCCCCCC);
  50623. visu.draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
  50624. background_color,0.2f).
  50625. draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
  50626. background_color,0.9f,0x55555555).
  50627. draw_rectangle(X0<X1?xp0:xp,Z0<Z1?zyp0:zyp,X0<X1?xn:xn0,Z0<Z1?zyn:zyn0,
  50628. foreground_color,0.9f,0xAAAAAAAA);
  50629. if (xp0!=xn && zyp0!=zyn)
  50630. visu.draw_line(xp0,zyp0,xn,zyn,background_color,0.9f,0x33333333).
  50631. draw_line(xp0,zyp0,xn,zyn,foreground_color,0.9f,0xCCCCCCCC);
  50632. }
  50633. } break;
  50634. case 3 : { // Ellipse
  50635. visu.draw_ellipse(xc0,yc0,
  50636. (float)cimg::abs(xc - xc0),
  50637. (float)cimg::abs(yc - yc0),0,background_color,0.2f).
  50638. draw_ellipse(xc0,yc0,
  50639. (float)cimg::abs(xc - xc0),
  50640. (float)cimg::abs(yc - yc0),0,foreground_color,0.9f,~0U).
  50641. draw_point(xc0,yc0,foreground_color,0.9f);
  50642. if (d) {
  50643. visu.draw_ellipse(zxc0,yc0,(float)cimg::abs(zxc - zxc0),(float)cimg::abs(yc - yc0),0,
  50644. background_color,0.2f).
  50645. draw_ellipse(zxc0,yc0,(float)cimg::abs(zxc - zxc0),(float)cimg::abs(yc - yc0),0,
  50646. foreground_color,0.9f,~0U).
  50647. draw_point(zxc0,yc0,foreground_color,0.9f).
  50648. draw_ellipse(xc0,zyc0,(float)cimg::abs(xc - xc0),(float)cimg::abs(zyc - zyc0),0,
  50649. background_color,0.2f).
  50650. draw_ellipse(xc0,zyc0,(float)cimg::abs(xc - xc0),(float)cimg::abs(zyc - zyc0),0,
  50651. foreground_color,0.9f,~0U).
  50652. draw_point(xc0,zyc0,foreground_color,0.9f);
  50653. }
  50654. } break;
  50655. }
  50656. }
  50657. // Draw text info.
  50658. if (my>=0 && my<13) text_down = true; else if (my>=visu.height() - 13) text_down = false;
  50659. if (!feature_type || !phase) {
  50660. if (X>=0 && Y>=0 && Z>=0 && X<width() && Y<height() && Z<depth()) {
  50661. if (_depth>1 || force_display_z_coord)
  50662. cimg_snprintf(text,text._width," Point (%d,%d,%d) = [ ",origX + (int)X,origY + (int)Y,origZ + (int)Z);
  50663. else cimg_snprintf(text,text._width," Point (%d,%d) = [ ",origX + (int)X,origY + (int)Y);
  50664. CImg<T> values = get_vector_at((int)X,(int)Y,(int)Z);
  50665. const bool is_large_spectrum = values._height>8;
  50666. if (is_large_spectrum)
  50667. values.draw_image(0,4,values.get_rows(values._height - 4,values._height - 1)).resize(1,8,1,1,0);
  50668. char *ctext = text._data + std::strlen(text), *const ltext = text._data + 512;
  50669. for (unsigned int c = 0; c<values._height && ctext<ltext; ++c) {
  50670. cimg_snprintf(ctext,24,cimg::type<T>::format_s(),
  50671. cimg::type<T>::format(values[c]));
  50672. ctext += std::strlen(ctext);
  50673. if (c==3 && is_large_spectrum) {
  50674. cimg_snprintf(ctext,24," ...");
  50675. ctext += std::strlen(ctext);
  50676. }
  50677. *(ctext++) = ' '; *ctext = 0;
  50678. }
  50679. std::strcpy(text._data + std::strlen(text),"] ");
  50680. }
  50681. } else switch (feature_type) {
  50682. case 1 : {
  50683. const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1),
  50684. length = cimg::round(cimg::hypot(dX,dY,dZ),0.1);
  50685. if (_depth>1 || force_display_z_coord)
  50686. cimg_snprintf(text,text._width," Vect (%d,%d,%d)-(%d,%d,%d), Length = %g ",
  50687. origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1,length);
  50688. else if (_width!=1 && _height!=1)
  50689. cimg_snprintf(text,text._width," Vect (%d,%d)-(%d,%d), Length = %g, Angle = %g\260 ",
  50690. origX + X0,origY + Y0,origX + X1,origY + Y1,length,
  50691. cimg::round(cimg::mod(180*std::atan2(-dY,-dX)/cimg::PI,360.),0.1));
  50692. else
  50693. cimg_snprintf(text,text._width," Vect (%d,%d)-(%d,%d), Length = %g ",
  50694. origX + X0,origY + Y0,origX + X1,origY + Y1,length);
  50695. } break;
  50696. case 2 : {
  50697. const double dX = (double)(X0 - X1), dY = (double)(Y0 - Y1), dZ = (double)(Z0 - Z1),
  50698. length = cimg::round(cimg::hypot(dX,dY,dZ),0.1);
  50699. if (_depth>1 || force_display_z_coord)
  50700. cimg_snprintf(text,text._width,
  50701. " Box ( %d,%d,%d ) - ( %d,%d,%d )\n Size = ( %d,%d,%d ), Length = %g ",
  50702. origX + (X0<X1?X0:X1),origY + (Y0<Y1?Y0:Y1),origZ + (Z0<Z1?Z0:Z1),
  50703. origX + (X0<X1?X1:X0),origY + (Y0<Y1?Y1:Y0),origZ + (Z0<Z1?Z1:Z0),
  50704. 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),1 + cimg::abs(Z0 - Z1),length);
  50705. else if (_width!=1 && _height!=1)
  50706. cimg_snprintf(text,text._width,
  50707. " Box ( %d,%d ) - ( %d,%d )\n Size = ( %d,%d ), Length = %g \n Angle = %g\260 ",
  50708. origX + (X0<X1?X0:X1),origY + (Y0<Y1?Y0:Y1),
  50709. origX + (X0<X1?X1:X0),origY + (Y0<Y1?Y1:Y0),
  50710. 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),length,
  50711. cimg::round(cimg::mod(180*std::atan2(-dY,-dX)/cimg::PI,360.),0.1));
  50712. else
  50713. cimg_snprintf(text,text._width,
  50714. " Box ( %d,%d ) - ( %d,%d )\n Size = (%d,%d), Length = %g ",
  50715. origX + (X0<X1?X0:X1),origY + (Y0<Y1?Y0:Y1),
  50716. origX + (X0<X1?X1:X0),origY + (Y0<Y1?Y1:Y0),
  50717. 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),length);
  50718. } break;
  50719. default :
  50720. if (_depth>1 || force_display_z_coord)
  50721. cimg_snprintf(text,text._width," Ellipse ( %d,%d,%d ) - ( %d,%d,%d ), Radii = ( %d,%d,%d ) ",
  50722. origX + X0,origY + Y0,origZ + Z0,origX + X1,origY + Y1,origZ + Z1,
  50723. 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1),1 + cimg::abs(Z0 - Z1));
  50724. else cimg_snprintf(text,text._width," Ellipse ( %d,%d ) - ( %d,%d ), Radii = ( %d,%d ) ",
  50725. origX + X0,origY + Y0,origX + X1,origY + Y1,
  50726. 1 + cimg::abs(X0 - X1),1 + cimg::abs(Y0 - Y1));
  50727. }
  50728. if (phase || (mx>=0 && my>=0)) visu.__draw_text("%s",font_size,(int)text_down,text._data);
  50729. }
  50730. disp.display(visu);
  50731. }
  50732. if (!shape_selected) disp.wait();
  50733. if (disp.is_resized()) { disp.resize(false)._is_resized = false; old_is_resized = true; visu0.assign(); }
  50734. omx = mx; omy = my;
  50735. if (!exit_on_anykey && key && key!=cimg::keyESC &&
  50736. (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
  50737. key = 0;
  50738. }
  50739. }
  50740. // Return result.
  50741. CImg<intT> res(1,feature_type==0?3:6,1,1,-1);
  50742. if (XYZ) { XYZ[0] = (unsigned int)X0; XYZ[1] = (unsigned int)Y0; XYZ[2] = (unsigned int)Z0; }
  50743. if (shape_selected) {
  50744. if (feature_type==2) {
  50745. if (is_deep_selection) switch (area_started) {
  50746. case 1 : Z0 = 0; Z1 = _depth - 1; break;
  50747. case 2 : Y0 = 0; Y1 = _height - 1; break;
  50748. case 3 : X0 = 0; X1 = _width - 1; break;
  50749. }
  50750. if (X0>X1) cimg::swap(X0,X1);
  50751. if (Y0>Y1) cimg::swap(Y0,Y1);
  50752. if (Z0>Z1) cimg::swap(Z0,Z1);
  50753. }
  50754. if (X1<0 || Y1<0 || Z1<0) X0 = Y0 = Z0 = X1 = Y1 = Z1 = -1;
  50755. switch (feature_type) {
  50756. case 1 : case 2 : res[0] = X0; res[1] = Y0; res[2] = Z0; res[3] = X1; res[4] = Y1; res[5] = Z1; break;
  50757. case 3 :
  50758. res[3] = cimg::abs(X1 - X0); res[4] = cimg::abs(Y1 - Y0); res[5] = cimg::abs(Z1 - Z0);
  50759. res[0] = X0; res[1] = Y0; res[2] = Z0;
  50760. break;
  50761. default : res[0] = X0; res[1] = Y0; res[2] = Z0;
  50762. }
  50763. }
  50764. if (!exit_on_anykey || !(disp.button()&4)) disp.set_button();
  50765. if (!visible_cursor) disp.show_mouse();
  50766. disp._normalization = old_normalization;
  50767. disp._is_resized = old_is_resized;
  50768. if (key!=~0U) disp.set_key(key);
  50769. return res;
  50770. }
  50771. // Return a visualizable 'uchar8' image for display routines.
  50772. CImg<ucharT> _get_select(const CImgDisplay& disp, const int normalization,
  50773. const int x, const int y, const int z) const {
  50774. if (is_empty()) return CImg<ucharT>(1,1,1,1,0);
  50775. const CImg<T> crop = get_shared_channels(0,std::min(2,spectrum() - 1));
  50776. CImg<Tuchar> img2d;
  50777. if (_depth>1) {
  50778. const int mdisp = std::min(disp.screen_width(),disp.screen_height());
  50779. if (depth()>mdisp) {
  50780. crop.get_resize(-100,-100,mdisp,-100,0).move_to(img2d);
  50781. img2d.projections2d(x,y,z*img2d._depth/_depth);
  50782. } else crop.get_projections2d(x,y,z).move_to(img2d);
  50783. } else CImg<Tuchar>(crop,false).move_to(img2d);
  50784. // Check for inf and NaN values.
  50785. if (cimg::type<T>::is_float() && normalization) {
  50786. bool is_inf = false, is_nan = false;
  50787. cimg_for(img2d,ptr,Tuchar)
  50788. if (cimg::type<T>::is_inf(*ptr)) { is_inf = true; break; }
  50789. else if (cimg::type<T>::is_nan(*ptr)) { is_nan = true; break; }
  50790. if (is_inf || is_nan) {
  50791. Tint m0 = (Tint)cimg::type<T>::max(), M0 = (Tint)cimg::type<T>::min();
  50792. if (!normalization) { m0 = 0; M0 = 255; }
  50793. else if (normalization==2) { m0 = (Tint)disp._min; M0 = (Tint)disp._max; }
  50794. else {
  50795. cimg_for(img2d,ptr,Tuchar)
  50796. if (!cimg::type<T>::is_inf(*ptr) && !cimg::type<T>::is_nan(*ptr)) {
  50797. if (*ptr<(Tuchar)m0) m0 = *ptr;
  50798. if (*ptr>(Tuchar)M0) M0 = *ptr;
  50799. }
  50800. }
  50801. const T
  50802. val_minf = (T)(normalization==1 || normalization==3?m0 - cimg::abs(m0):m0),
  50803. val_pinf = (T)(normalization==1 || normalization==3?M0 + cimg::abs(M0):M0);
  50804. if (is_nan)
  50805. cimg_for(img2d,ptr,Tuchar)
  50806. if (cimg::type<T>::is_nan(*ptr)) *ptr = val_minf; // Replace NaN values
  50807. if (is_inf)
  50808. cimg_for(img2d,ptr,Tuchar)
  50809. if (cimg::type<T>::is_inf(*ptr)) *ptr = (float)*ptr<0?val_minf:val_pinf; // Replace +-inf values
  50810. }
  50811. }
  50812. switch (normalization) {
  50813. case 1 : img2d.normalize((ucharT)0,(ucharT)255); break;
  50814. case 2 : {
  50815. const float m = disp._min, M = disp._max;
  50816. (img2d-=m)*=255.f/(M - m>0?M - m:1);
  50817. } break;
  50818. case 3 :
  50819. if (cimg::type<T>::is_float()) img2d.normalize((ucharT)0,(ucharT)255);
  50820. else {
  50821. const float
  50822. m = (float)cimg::type<T>::min(),
  50823. M = (float)cimg::type<T>::max();
  50824. (img2d-=m)*=255.f/(M - m>0?M - m:1);
  50825. } break;
  50826. }
  50827. if (img2d.spectrum()==2) img2d.channels(0,2);
  50828. return img2d;
  50829. }
  50830. //! Select sub-graph in a graph.
  50831. CImg<intT> get_select_graph(CImgDisplay &disp,
  50832. const unsigned int plot_type=1, const unsigned int vertex_type=1,
  50833. const char *const labelx=0, const double xmin=0, const double xmax=0,
  50834. const char *const labely=0, const double ymin=0, const double ymax=0,
  50835. const bool exit_on_anykey=false) const {
  50836. if (is_empty())
  50837. throw CImgInstanceException(_cimg_instance
  50838. "select_graph(): Empty instance.",
  50839. cimg_instance);
  50840. if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0).
  50841. set_title("CImg<%s>",pixel_type());
  50842. const ulongT siz = (ulongT)_width*_height*_depth;
  50843. const unsigned int old_normalization = disp.normalization();
  50844. disp.show().set_button().set_wheel()._normalization = 0;
  50845. double nymin = ymin, nymax = ymax, nxmin = xmin, nxmax = xmax;
  50846. if (nymin==nymax) { nymin = (Tfloat)min_max(nymax); const double dy = nymax - nymin; nymin-=dy/20; nymax+=dy/20; }
  50847. if (nymin==nymax) { --nymin; ++nymax; }
  50848. if (nxmin==nxmax && nxmin==0) { nxmin = 0; nxmax = siz - 1.; }
  50849. static const unsigned char black[] = { 0, 0, 0 }, white[] = { 255, 255, 255 }, gray[] = { 220, 220, 220 };
  50850. static const unsigned char gray2[] = { 110, 110, 110 }, ngray[] = { 35, 35, 35 };
  50851. CImg<ucharT> colormap(3,_spectrum);
  50852. if (_spectrum==1) { colormap[0] = colormap[1] = 120; colormap[2] = 200; }
  50853. else {
  50854. colormap(0,0) = 220; colormap(1,0) = 10; colormap(2,0) = 10;
  50855. if (_spectrum>1) { colormap(0,1) = 10; colormap(1,1) = 220; colormap(2,1) = 10; }
  50856. if (_spectrum>2) { colormap(0,2) = 10; colormap(1,2) = 10; colormap(2,2) = 220; }
  50857. if (_spectrum>3) { colormap(0,3) = 220; colormap(1,3) = 220; colormap(2,3) = 10; }
  50858. if (_spectrum>4) { colormap(0,4) = 220; colormap(1,4) = 10; colormap(2,4) = 220; }
  50859. if (_spectrum>5) { colormap(0,5) = 10; colormap(1,5) = 220; colormap(2,5) = 220; }
  50860. if (_spectrum>6) {
  50861. cimg_uint64 rng = 10;
  50862. cimg_for_inY(colormap,6,colormap.height()-1,k) {
  50863. colormap(0,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng));
  50864. colormap(1,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng));
  50865. colormap(2,k) = (unsigned char)(120 + cimg::rand(-100.f,100.f,&rng));
  50866. }
  50867. }
  50868. }
  50869. CImg<ucharT> visu0, visu, graph, text, axes;
  50870. int x0 = -1, x1 = -1, y0 = -1, y1 = -1, omouse_x = -2, omouse_y = -2;
  50871. const unsigned int one = plot_type==3?0U:1U;
  50872. unsigned int okey = 0, obutton = 0, font_size = 32;
  50873. CImg<charT> message(1024);
  50874. CImg_3x3(I,unsigned char);
  50875. for (bool selected = false; !selected && !disp.is_closed() && !okey && !disp.wheel(); ) {
  50876. const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
  50877. const unsigned int key = disp.key(), button = disp.button();
  50878. // Generate graph representation.
  50879. if (!visu0) {
  50880. visu0.assign(disp.width(),disp.height(),1,3,220);
  50881. const int gdimx = disp.width() - 32, gdimy = disp.height() - 32;
  50882. if (gdimx>0 && gdimy>0) {
  50883. graph.assign(gdimx,gdimy,1,3,255);
  50884. if (siz<32) {
  50885. if (siz>1) graph.draw_grid(gdimx/(float)(siz - one),gdimy/(float)(siz - one),0,0,
  50886. false,true,black,0.2f,0x33333333,0x33333333);
  50887. } else graph.draw_grid(-10,-10,0,0,false,true,black,0.2f,0x33333333,0x33333333);
  50888. cimg_forC(*this,c)
  50889. graph.draw_graph(get_shared_channel(c),&colormap(0,c),(plot_type!=3 || _spectrum==1)?1:0.6f,
  50890. plot_type,vertex_type,nymax,nymin);
  50891. axes.assign(gdimx,gdimy,1,1,0);
  50892. const float
  50893. dx = (float)cimg::abs(nxmax - nxmin), dy = (float)cimg::abs(nymax - nymin),
  50894. px = (float)std::pow(10.,(int)std::log10(dx?dx:1) - 2.),
  50895. py = (float)std::pow(10.,(int)std::log10(dy?dy:1) - 2.);
  50896. const CImg<Tdouble>
  50897. seqx = dx<=0?CImg<Tdouble>::vector(nxmin):
  50898. CImg<Tdouble>::sequence(1 + gdimx/60,nxmin,one?nxmax:nxmin + (nxmax - nxmin)*(siz + 1)/siz),
  50899. seqy = CImg<Tdouble>::sequence(1 + gdimy/60,nymax,nymin);
  50900. const bool allow_zero = (nxmin*nxmax>0) || (nymin*nymax>0);
  50901. axes.draw_axes(seqx,seqy,white,1,~0U,~0U,13,allow_zero,px,py);
  50902. if (nymin>0) axes.draw_axis(seqx,gdimy - 1,gray,1,~0U,13,allow_zero,px);
  50903. if (nymax<0) axes.draw_axis(seqx,0,gray,1,~0U,13,allow_zero,px);
  50904. if (nxmin>0) axes.draw_axis(0,seqy,gray,1,~0U,13,allow_zero,py);
  50905. if (nxmax<0) axes.draw_axis(gdimx - 1,seqy,gray,1,~0U,13,allow_zero,py);
  50906. cimg_for3x3(axes,x,y,0,0,I,unsigned char)
  50907. if (Icc) {
  50908. if (Icc==255) cimg_forC(graph,c) graph(x,y,c) = 0;
  50909. else cimg_forC(graph,c) graph(x,y,c) = (unsigned char)(2*graph(x,y,c)/3);
  50910. }
  50911. else if (Ipc || Inc || Icp || Icn || Ipp || Inn || Ipn || Inp)
  50912. cimg_forC(graph,c) graph(x,y,c) = (unsigned char)((graph(x,y,c) + 511)/3);
  50913. visu0.draw_image(16,16,graph);
  50914. visu0.draw_line(15,15,16 + gdimx,15,gray2).draw_line(16 + gdimx,15,16 + gdimx,16 + gdimy,gray2).
  50915. draw_line(16 + gdimx,16 + gdimy,15,16 + gdimy,white).draw_line(15,16 + gdimy,15,15,white);
  50916. } else graph.assign();
  50917. text.assign().draw_text(0,0,labelx?labelx:"X-axis",white,ngray,1,13).resize(-100,-100,1,3);
  50918. visu0.draw_image((visu0.width() - text.width())/2,visu0.height() - 14,~text);
  50919. text.assign().draw_text(0,0,labely?labely:"Y-axis",white,ngray,1,13).rotate(-90).resize(-100,-100,1,3);
  50920. visu0.draw_image(1,(visu0.height() - text.height())/2,~text);
  50921. visu.assign();
  50922. }
  50923. // Generate and display current view.
  50924. if (!visu) {
  50925. visu.assign(visu0);
  50926. if (graph && x0>=0 && x1>=0) {
  50927. const int
  50928. nx0 = x0<=x1?x0:x1,
  50929. nx1 = x0<=x1?x1:x0,
  50930. ny0 = y0<=y1?y0:y1,
  50931. ny1 = y0<=y1?y1:y0,
  50932. sx0 = (int)(16 + nx0*(visu.width() - 32)/std::max((ulongT)1,siz - one)),
  50933. sx1 = (int)(15 + (nx1 + 1)*(visu.width() - 32)/std::max((ulongT)1,siz - one)),
  50934. sy0 = 16 + ny0,
  50935. sy1 = 16 + ny1;
  50936. if (y0>=0 && y1>=0)
  50937. visu.draw_rectangle(sx0,sy0,sx1,sy1,gray,0.5f).draw_rectangle(sx0,sy0,sx1,sy1,black,0.5f,0xCCCCCCCCU);
  50938. else visu.draw_rectangle(sx0,0,sx1,visu.height() - 17,gray,0.5f).
  50939. draw_line(sx0,16,sx0,visu.height() - 17,black,0.5f,0xCCCCCCCCU).
  50940. draw_line(sx1,16,sx1,visu.height() - 17,black,0.5f,0xCCCCCCCCU);
  50941. }
  50942. if (mouse_x>=16 && mouse_y>=16 && mouse_x<visu.width() - 16 && mouse_y<visu.height() - 16) {
  50943. if (graph) visu.draw_line(mouse_x,16,mouse_x,visu.height() - 17,black,0.5f,0x55555555U);
  50944. const unsigned int
  50945. x = (unsigned int)cimg::round((mouse_x - 16.f)*(siz - one)/(disp.width() - 32),1,one?0:-1);
  50946. const double cx = nxmin + x*(nxmax - nxmin)/std::max((ulongT)1,siz - 1);
  50947. if (_spectrum>=7)
  50948. cimg_snprintf(message,message._width,"Value[%u:%g] = ( %g %g %g ... %g %g %g )",x,cx,
  50949. (double)(*this)(x,0,0,0),(double)(*this)(x,0,0,1),(double)(*this)(x,0,0,2),
  50950. (double)(*this)(x,0,0,_spectrum - 4),(double)(*this)(x,0,0,_spectrum - 3),
  50951. (double)(*this)(x,0,0,_spectrum - 1));
  50952. else {
  50953. cimg_snprintf(message,message._width,"Value[%u:%g] = ( ",x,cx);
  50954. unsigned int len = (unsigned int)std::strlen(message);
  50955. cimg_forC(*this,c)
  50956. cimg_snprintf(message._data + len,message._width - len,"%g ",(double)(*this)(x,0,0,c));
  50957. len = (unsigned int)std::strlen(message);
  50958. cimg_snprintf(message._data + len,message._width - len,")");
  50959. }
  50960. if (x0>=0 && x1>=0) {
  50961. const unsigned int
  50962. nx0 = (unsigned int)(x0<=x1?x0:x1),
  50963. nx1 = (unsigned int)(x0<=x1?x1:x0),
  50964. ny0 = (unsigned int)(y0<=y1?y0:y1),
  50965. ny1 = (unsigned int)(y0<=y1?y1:y0),
  50966. len = (unsigned int)std::strlen(message);
  50967. const double
  50968. cx0 = nxmin + nx0*(nxmax - nxmin)/std::max((ulongT)1,siz - 1),
  50969. cx1 = nxmin + (nx1 + one)*(nxmax - nxmin)/std::max((ulongT)1,siz - 1),
  50970. cy0 = nymax - ny0*(nymax - nymin)/(visu._height - 32),
  50971. cy1 = nymax - ny1*(nymax - nymin)/(visu._height - 32);
  50972. if (y0>=0 && y1>=0)
  50973. cimg_snprintf(message._data + len,message._width - len," - Range ( %u:%g, %g ) - ( %u:%g, %g )",
  50974. x0,cx0,cy0,x1 + one,cx1,cy1);
  50975. else
  50976. cimg_snprintf(message._data + len,message._width - len," - Range [ %u:%g - %u:%g ]",
  50977. x0,cx0,x1 + one,cx1);
  50978. }
  50979. text.assign().draw_text(0,0,message,white,ngray,1,13).resize(-100,-100,1,3);
  50980. visu.draw_image((visu.width() - text.width())/2,1,~text);
  50981. }
  50982. visu.display(disp);
  50983. }
  50984. // Test keys.
  50985. CImg<charT> filename(32);
  50986. switch (okey = key) {
  50987. #if cimg_OS!=2
  50988. case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT :
  50989. #endif
  50990. case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : okey = 0; break;
  50991. case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  50992. disp.set_fullscreen(false).
  50993. resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
  50994. CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
  50995. _is_resized = true;
  50996. disp.set_key(key,false); okey = 0;
  50997. } break;
  50998. case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  50999. disp.set_fullscreen(false).
  51000. resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
  51001. disp.set_key(key,false); okey = 0;
  51002. } break;
  51003. case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  51004. disp.set_fullscreen(false).
  51005. resize(cimg_fitscreen(CImgDisplay::screen_width()/2,
  51006. CImgDisplay::screen_height()/2,1),false)._is_resized = true;
  51007. disp.set_key(key,false); okey = 0;
  51008. } break;
  51009. case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  51010. disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
  51011. disp.set_key(key,false); okey = 0;
  51012. } break;
  51013. case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  51014. static unsigned int snap_number = 0;
  51015. if (visu || visu0) {
  51016. CImg<ucharT> &screen = visu?visu:visu0;
  51017. std::FILE *file;
  51018. do {
  51019. cimg_snprintf(filename,filename._width,cimg_appname "_%.6u."
  51020. #ifdef cimg_use_png
  51021. "png",
  51022. #else
  51023. "bmp",
  51024. #endif
  51025. snap_number++);
  51026. if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
  51027. } while (file);
  51028. (+screen).__draw_text(" Saving snapshot... ",font_size,0).display(disp);
  51029. screen.save(filename);
  51030. (+screen).__draw_text(" Snapshot '%s' saved. ",font_size,0,filename._data).display(disp);
  51031. }
  51032. disp.set_key(key,false); okey = 0;
  51033. } break;
  51034. case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  51035. static unsigned int snap_number = 0;
  51036. if (visu || visu0) {
  51037. CImg<ucharT> &screen = visu?visu:visu0;
  51038. std::FILE *file;
  51039. do {
  51040. #ifdef cimg_use_zlib
  51041. cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimgz",snap_number++);
  51042. #else
  51043. cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimg",snap_number++);
  51044. #endif
  51045. if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
  51046. } while (file);
  51047. (+screen).__draw_text(" Saving instance... ",font_size,0).display(disp);
  51048. save(filename);
  51049. (+screen).__draw_text(" Instance '%s' saved. ",font_size,0,filename._data).display(disp);
  51050. }
  51051. disp.set_key(key,false); okey = 0;
  51052. } break;
  51053. }
  51054. // Handle mouse motion and mouse buttons.
  51055. if (obutton!=button || omouse_x!=mouse_x || omouse_y!=mouse_y) {
  51056. visu.assign();
  51057. if (disp.mouse_x()>=0 && disp.mouse_y()>=0) {
  51058. const int
  51059. mx = (mouse_x - 16)*(int)(siz - one)/(disp.width() - 32),
  51060. cx = cimg::cut(mx,0,(int)(siz - 1 - one)),
  51061. my = mouse_y - 16,
  51062. cy = cimg::cut(my,0,disp.height() - 32);
  51063. if (button&1) {
  51064. if (!obutton) { x0 = cx; y0 = -1; } else { x1 = cx; y1 = -1; }
  51065. }
  51066. else if (button&2) {
  51067. if (!obutton) { x0 = cx; y0 = cy; } else { x1 = cx; y1 = cy; }
  51068. }
  51069. else if (obutton) { x1 = x1>=0?cx:-1; y1 = y1>=0?cy:-1; selected = true; }
  51070. } else if (!button && obutton) selected = true;
  51071. obutton = button; omouse_x = mouse_x; omouse_y = mouse_y;
  51072. }
  51073. if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
  51074. if (visu && visu0) disp.wait();
  51075. if (!exit_on_anykey && okey && okey!=cimg::keyESC &&
  51076. (okey!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
  51077. disp.set_key(key,false);
  51078. okey = 0;
  51079. }
  51080. }
  51081. disp._normalization = old_normalization;
  51082. if (x1>=0 && x1<x0) cimg::swap(x0,x1);
  51083. if (y1<y0) cimg::swap(y0,y1);
  51084. disp.set_key(okey);
  51085. return CImg<intT>(4,1,1,1,x0,y0,x1>=0?x1 + (int)one:-1,y1);
  51086. }
  51087. //! Load image from a file.
  51088. /**
  51089. \param filename Filename, as a C-string.
  51090. \note The extension of \c filename defines the file format. If no filename
  51091. extension is provided, CImg<T>::get_load() will try to load the file as a .cimg or .cimgz file.
  51092. **/
  51093. CImg<T>& load(const char *const filename) {
  51094. if (!filename)
  51095. throw CImgArgumentException(_cimg_instance
  51096. "load(): Specified filename is (null).",
  51097. cimg_instance);
  51098. if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) {
  51099. CImg<charT> filename_local(256);
  51100. load(cimg::load_network(filename,filename_local));
  51101. std::remove(filename_local);
  51102. return *this;
  51103. }
  51104. const char *const ext = cimg::split_filename(filename);
  51105. const unsigned int omode = cimg::exception_mode();
  51106. cimg::exception_mode(0);
  51107. bool is_loaded = true;
  51108. try {
  51109. #ifdef cimg_load_plugin
  51110. cimg_load_plugin(filename);
  51111. #endif
  51112. #ifdef cimg_load_plugin1
  51113. cimg_load_plugin1(filename);
  51114. #endif
  51115. #ifdef cimg_load_plugin2
  51116. cimg_load_plugin2(filename);
  51117. #endif
  51118. #ifdef cimg_load_plugin3
  51119. cimg_load_plugin3(filename);
  51120. #endif
  51121. #ifdef cimg_load_plugin4
  51122. cimg_load_plugin4(filename);
  51123. #endif
  51124. #ifdef cimg_load_plugin5
  51125. cimg_load_plugin5(filename);
  51126. #endif
  51127. #ifdef cimg_load_plugin6
  51128. cimg_load_plugin6(filename);
  51129. #endif
  51130. #ifdef cimg_load_plugin7
  51131. cimg_load_plugin7(filename);
  51132. #endif
  51133. #ifdef cimg_load_plugin8
  51134. cimg_load_plugin8(filename);
  51135. #endif
  51136. // Text formats
  51137. if (!cimg::strcasecmp(ext,"asc")) load_ascii(filename);
  51138. else if (!cimg::strcasecmp(ext,"csv") ||
  51139. !cimg::strcasecmp(ext,"dlm") ||
  51140. !cimg::strcasecmp(ext,"txt")) load_dlm(filename);
  51141. else if (!cimg::strcasecmp(ext,"pdf")) load_pdf_external(filename);
  51142. // 2D binary formats
  51143. else if (!cimg::strcasecmp(ext,"bmp")) load_bmp(filename);
  51144. else if (!cimg::strcasecmp(ext,"jpg") ||
  51145. !cimg::strcasecmp(ext,"jpeg") ||
  51146. !cimg::strcasecmp(ext,"jpe") ||
  51147. !cimg::strcasecmp(ext,"jfif") ||
  51148. !cimg::strcasecmp(ext,"jif")) load_jpeg(filename);
  51149. else if (!cimg::strcasecmp(ext,"png")) load_png(filename);
  51150. else if (!cimg::strcasecmp(ext,"ppm") ||
  51151. !cimg::strcasecmp(ext,"pgm") ||
  51152. !cimg::strcasecmp(ext,"pnm") ||
  51153. !cimg::strcasecmp(ext,"pbm") ||
  51154. !cimg::strcasecmp(ext,"pnk")) load_pnm(filename);
  51155. else if (!cimg::strcasecmp(ext,"pfm")) load_pfm(filename);
  51156. else if (!cimg::strcasecmp(ext,"tif") ||
  51157. !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
  51158. else if (!cimg::strcasecmp(ext,"exr")) load_exr(filename);
  51159. else if (!cimg::strcasecmp(ext,"arw") ||
  51160. !cimg::strcasecmp(ext,"cr2") ||
  51161. !cimg::strcasecmp(ext,"crw") ||
  51162. !cimg::strcasecmp(ext,"dcr") ||
  51163. !cimg::strcasecmp(ext,"dng") ||
  51164. !cimg::strcasecmp(ext,"mrw") ||
  51165. !cimg::strcasecmp(ext,"nef") ||
  51166. !cimg::strcasecmp(ext,"orf") ||
  51167. !cimg::strcasecmp(ext,"pix") ||
  51168. !cimg::strcasecmp(ext,"ptx") ||
  51169. !cimg::strcasecmp(ext,"raf") ||
  51170. !cimg::strcasecmp(ext,"srf")) load_dcraw_external(filename);
  51171. else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename);
  51172. else if (!cimg::strcasecmp(ext,"heic") ||
  51173. !cimg::strcasecmp(ext,"avif")) load_heif(filename);
  51174. // 3D binary formats
  51175. else if (!cimg::strcasecmp(ext,"dcm") ||
  51176. !cimg::strcasecmp(ext,"dicom")) load_medcon_external(filename);
  51177. else if (!cimg::strcasecmp(ext,"hdr") ||
  51178. !cimg::strcasecmp(ext,"nii")) load_analyze(filename);
  51179. else if (!cimg::strcasecmp(ext,"par") ||
  51180. !cimg::strcasecmp(ext,"rec")) load_parrec(filename);
  51181. else if (!cimg::strcasecmp(ext,"mnc")) load_minc2(filename);
  51182. else if (!cimg::strcasecmp(ext,"inr")) load_inr(filename);
  51183. else if (!cimg::strcasecmp(ext,"pan")) load_pandore(filename);
  51184. else if (!cimg::strcasecmp(ext,"cimg") ||
  51185. !cimg::strcasecmp(ext,"cimgz") ||
  51186. !*ext) return load_cimg(filename);
  51187. // Archive files
  51188. else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
  51189. // Image sequences
  51190. else if (!cimg::strcasecmp(ext,"avi") ||
  51191. !cimg::strcasecmp(ext,"mov") ||
  51192. !cimg::strcasecmp(ext,"asf") ||
  51193. !cimg::strcasecmp(ext,"divx") ||
  51194. !cimg::strcasecmp(ext,"flv") ||
  51195. !cimg::strcasecmp(ext,"mpg") ||
  51196. !cimg::strcasecmp(ext,"m1v") ||
  51197. !cimg::strcasecmp(ext,"m2v") ||
  51198. !cimg::strcasecmp(ext,"m4v") ||
  51199. !cimg::strcasecmp(ext,"mjp") ||
  51200. !cimg::strcasecmp(ext,"mp4") ||
  51201. !cimg::strcasecmp(ext,"mkv") ||
  51202. !cimg::strcasecmp(ext,"mpe") ||
  51203. !cimg::strcasecmp(ext,"movie") ||
  51204. !cimg::strcasecmp(ext,"ogm") ||
  51205. !cimg::strcasecmp(ext,"ogg") ||
  51206. !cimg::strcasecmp(ext,"ogv") ||
  51207. !cimg::strcasecmp(ext,"qt") ||
  51208. !cimg::strcasecmp(ext,"rm") ||
  51209. !cimg::strcasecmp(ext,"vob") ||
  51210. !cimg::strcasecmp(ext,"webm") ||
  51211. !cimg::strcasecmp(ext,"wmv") ||
  51212. !cimg::strcasecmp(ext,"xvid") ||
  51213. !cimg::strcasecmp(ext,"mpeg")) load_video(filename);
  51214. else is_loaded = false;
  51215. } catch (CImgIOException&) { is_loaded = false; }
  51216. // If nothing loaded, try to guess file format from magic number in file.
  51217. if (!is_loaded) {
  51218. std::FILE *file = cimg::std_fopen(filename,"rb");
  51219. if (!file) {
  51220. cimg::exception_mode(omode);
  51221. throw CImgIOException(_cimg_instance
  51222. "load(): Failed to open file '%s'.",
  51223. cimg_instance,
  51224. filename);
  51225. }
  51226. const char *const f_type = cimg::ftype(file,filename);
  51227. cimg::fclose(file);
  51228. is_loaded = true;
  51229. try {
  51230. if (!cimg::strcasecmp(f_type,"pnm")) load_pnm(filename);
  51231. else if (!cimg::strcasecmp(f_type,"pfm")) load_pfm(filename);
  51232. else if (!cimg::strcasecmp(f_type,"bmp")) load_bmp(filename);
  51233. else if (!cimg::strcasecmp(f_type,"inr")) load_inr(filename);
  51234. else if (!cimg::strcasecmp(f_type,"jpg")) load_jpeg(filename);
  51235. else if (!cimg::strcasecmp(f_type,"pan")) load_pandore(filename);
  51236. else if (!cimg::strcasecmp(f_type,"png")) load_png(filename);
  51237. else if (!cimg::strcasecmp(f_type,"tif")) load_tiff(filename);
  51238. else if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename);
  51239. else if (!cimg::strcasecmp(f_type,"dcm")) load_medcon_external(filename);
  51240. else is_loaded = false;
  51241. } catch (CImgIOException&) { is_loaded = false; }
  51242. }
  51243. // If nothing loaded, try to load file with other means.
  51244. if (!is_loaded) {
  51245. try {
  51246. load_other(filename);
  51247. } catch (CImgIOException&) {
  51248. cimg::exception_mode(omode);
  51249. throw CImgIOException(_cimg_instance
  51250. "load(): Failed to recognize format of file '%s'.",
  51251. cimg_instance,
  51252. filename);
  51253. }
  51254. }
  51255. cimg::exception_mode(omode);
  51256. return *this;
  51257. }
  51258. //! Load image from a file \newinstance.
  51259. static CImg<T> get_load(const char *const filename) {
  51260. return CImg<T>().load(filename);
  51261. }
  51262. //! Load image from an ascii file.
  51263. /**
  51264. \param filename Filename, as a C -string.
  51265. **/
  51266. CImg<T>& load_ascii(const char *const filename) {
  51267. return _load_ascii(0,filename);
  51268. }
  51269. //! Load image from an ascii file \inplace.
  51270. static CImg<T> get_load_ascii(const char *const filename) {
  51271. return CImg<T>().load_ascii(filename);
  51272. }
  51273. //! Load image from an ascii file \overloading.
  51274. CImg<T>& load_ascii(std::FILE *const file) {
  51275. return _load_ascii(file,0);
  51276. }
  51277. //! Loadimage from an ascii file \newinstance.
  51278. static CImg<T> get_load_ascii(std::FILE *const file) {
  51279. return CImg<T>().load_ascii(file);
  51280. }
  51281. CImg<T>& _load_ascii(std::FILE *const file, const char *const filename) {
  51282. if (!file && !filename)
  51283. throw CImgArgumentException(_cimg_instance
  51284. "load_ascii(): Specified filename is (null).",
  51285. cimg_instance);
  51286. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  51287. CImg<charT> line(256); *line = 0;
  51288. int err = std::fscanf(nfile,"%255[^\n]",line._data);
  51289. unsigned int dx = 0, dy = 1, dz = 1, dc = 1;
  51290. cimg_sscanf(line,"%u%*c%u%*c%u%*c%u",&dx,&dy,&dz,&dc);
  51291. err = std::fscanf(nfile,"%*[^0-9.eEinfa+-]");
  51292. if (!dx || !dy || !dz || !dc) {
  51293. if (!file) cimg::fclose(nfile);
  51294. throw CImgIOException(_cimg_instance
  51295. "load_ascii(): Invalid ascii header in file '%s', image dimensions are set "
  51296. "to (%u,%u,%u,%u).",
  51297. cimg_instance,
  51298. filename?filename:"(FILE*)",dx,dy,dz,dc);
  51299. }
  51300. assign(dx,dy,dz,dc);
  51301. const ulongT siz = size();
  51302. ulongT off = 0;
  51303. double val;
  51304. T *ptr = _data;
  51305. for (err = 1, off = 0; off<siz && err==1; ++off) {
  51306. err = std::fscanf(nfile,"%lf%*[^0-9.eEinfa+-]",&val);
  51307. *(ptr++) = (T)val;
  51308. }
  51309. if (err!=1)
  51310. cimg::warn(_cimg_instance
  51311. "load_ascii(): Only %lu/%lu values read from file '%s'.",
  51312. cimg_instance,
  51313. off - 1,siz,filename?filename:"(FILE*)");
  51314. if (!file) cimg::fclose(nfile);
  51315. return *this;
  51316. }
  51317. //! Load image from a DLM file.
  51318. /**
  51319. \param filename Filename, as a C-string.
  51320. **/
  51321. CImg<T>& load_dlm(const char *const filename) {
  51322. return _load_dlm(0,filename);
  51323. }
  51324. //! Load image from a DLM file \newinstance.
  51325. static CImg<T> get_load_dlm(const char *const filename) {
  51326. return CImg<T>().load_dlm(filename);
  51327. }
  51328. //! Load image from a DLM file \overloading.
  51329. CImg<T>& load_dlm(std::FILE *const file) {
  51330. return _load_dlm(file,0);
  51331. }
  51332. //! Load image from a DLM file \newinstance.
  51333. static CImg<T> get_load_dlm(std::FILE *const file) {
  51334. return CImg<T>().load_dlm(file);
  51335. }
  51336. CImg<T>& _load_dlm(std::FILE *const file, const char *const filename) {
  51337. if (!file && !filename)
  51338. throw CImgArgumentException(_cimg_instance
  51339. "load_dlm(): Specified filename is (null).",
  51340. cimg_instance);
  51341. std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
  51342. CImg<charT> delimiter(256), tmp(256); *delimiter = *tmp = 0;
  51343. unsigned int cdx = 0, dx = 0, dy = 0;
  51344. int err = 0;
  51345. double val;
  51346. assign(256,256,1,1,(T)0);
  51347. while ((err = std::fscanf(nfile,"%lf%255[^0-9eEinfa.+-]",&val,delimiter._data))>0) {
  51348. if (err>0) (*this)(cdx++,dy) = (T)val;
  51349. if (cdx>=_width) resize(3*_width/2,_height,1,1,0);
  51350. char c = 0;
  51351. if (!cimg_sscanf(delimiter,"%255[^\n]%c",tmp._data,&c) || c=='\n') {
  51352. dx = std::max(cdx,dx);
  51353. if (++dy>=_height) resize(_width,3*_height/2,1,1,0);
  51354. cdx = 0;
  51355. }
  51356. }
  51357. if (cdx && err==1) { dx = cdx; ++dy; }
  51358. if (!dx || !dy) {
  51359. if (!file) cimg::fclose(nfile);
  51360. throw CImgIOException(_cimg_instance
  51361. "load_dlm(): Invalid DLM file '%s'.",
  51362. cimg_instance,
  51363. filename?filename:"(FILE*)");
  51364. }
  51365. resize(dx,dy,1,1,0);
  51366. if (!file) cimg::fclose(nfile);
  51367. return *this;
  51368. }
  51369. //! Load image from a BMP file.
  51370. /**
  51371. \param filename Filename, as a C-string.
  51372. **/
  51373. CImg<T>& load_bmp(const char *const filename) {
  51374. return _load_bmp(0,filename);
  51375. }
  51376. //! Load image from a BMP file \newinstance.
  51377. static CImg<T> get_load_bmp(const char *const filename) {
  51378. return CImg<T>().load_bmp(filename);
  51379. }
  51380. //! Load image from a BMP file \overloading.
  51381. CImg<T>& load_bmp(std::FILE *const file) {
  51382. return _load_bmp(file,0);
  51383. }
  51384. //! Load image from a BMP file \newinstance.
  51385. static CImg<T> get_load_bmp(std::FILE *const file) {
  51386. return CImg<T>().load_bmp(file);
  51387. }
  51388. CImg<T>& _load_bmp(std::FILE *const file, const char *const filename) {
  51389. if (!file && !filename)
  51390. throw CImgArgumentException(_cimg_instance
  51391. "load_bmp(): Specified filename is (null).",
  51392. cimg_instance);
  51393. const ulongT fsiz = (ulongT)(file?cimg::fsize(file):cimg::fsize(filename));
  51394. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  51395. CImg<ucharT> header(54);
  51396. cimg::fread(header._data,54,nfile);
  51397. if (*header!='B' || header[1]!='M') {
  51398. if (!file) cimg::fclose(nfile);
  51399. throw CImgIOException(_cimg_instance
  51400. "load_bmp(): Invalid BMP file '%s'.",
  51401. cimg_instance,
  51402. filename?filename:"(FILE*)");
  51403. }
  51404. // Read header and pixel buffer
  51405. int
  51406. file_size = header[0x02] + (header[0x03]<<8) + (header[0x04]<<16) + (header[0x05]<<24),
  51407. offset = header[0x0A] + (header[0x0B]<<8) + (header[0x0C]<<16) + (header[0x0D]<<24),
  51408. header_size = header[0x0E] + (header[0x0F]<<8) + (header[0x10]<<16) + (header[0x11]<<24),
  51409. dx = header[0x12] + (header[0x13]<<8) + (header[0x14]<<16) + (header[0x15]<<24),
  51410. dy = header[0x16] + (header[0x17]<<8) + (header[0x18]<<16) + (header[0x19]<<24),
  51411. compression = header[0x1E] + (header[0x1F]<<8) + (header[0x20]<<16) + (header[0x21]<<24),
  51412. nb_colors = header[0x2E] + (header[0x2F]<<8) + (header[0x30]<<16) + (header[0x31]<<24),
  51413. bpp = header[0x1C] + (header[0x1D]<<8);
  51414. if ((ulongT)file_size!=fsiz)
  51415. throw CImgIOException(_cimg_instance
  51416. "load_bmp(): Invalid file_size %d specified in filename '%s' (expected %lu).",
  51417. cimg_instance,
  51418. file_size,filename?filename:"(FILE*)",fsiz);
  51419. if (header_size<0 || header_size>=file_size)
  51420. throw CImgIOException(_cimg_instance
  51421. "load_bmp(): Invalid header size %d specified in filename '%s'.",
  51422. cimg_instance,
  51423. header_size,filename?filename:"(FILE*)");
  51424. if (offset<0 || offset>=file_size)
  51425. throw CImgIOException(_cimg_instance
  51426. "load_bmp(): Invalid offset %d specified in filename '%s'.",
  51427. cimg_instance,
  51428. offset,filename?filename:"(FILE*)");
  51429. if (header_size>40) cimg::fseek(nfile,header_size - 40,SEEK_CUR);
  51430. const int
  51431. dx_bytes = (bpp==1)?(dx/8 + (dx%8?1:0)):((bpp==4)?(dx/2 + (dx%2)):(int)((longT)dx*bpp/8)),
  51432. align_bytes = (4 - dx_bytes%4)%4;
  51433. const ulongT
  51434. cimg_iobuffer = (ulongT)24*1024*1024,
  51435. buf_size = (ulongT)cimg::abs(dy)*(dx_bytes + align_bytes);
  51436. if (buf_size>=fsiz)
  51437. throw CImgIOException(_cimg_instance
  51438. "load_bmp(): File size %lu for filename '%s' does not match "
  51439. "encoded image dimensions (%d,%d).",
  51440. cimg_instance,
  51441. (long)fsiz,filename?filename:"(FILE*)",dx,dy);
  51442. CImg<intT> colormap;
  51443. if (bpp<16) { if (!nb_colors) nb_colors = 1<<bpp; } else nb_colors = 0;
  51444. if (nb_colors) { colormap.assign(nb_colors); cimg::fread(colormap._data,nb_colors,nfile); }
  51445. const int xoffset = offset - 14 - header_size - 4*nb_colors;
  51446. if (xoffset<0 || xoffset>=file_size)
  51447. throw CImgIOException(_cimg_instance
  51448. "load_bmp(): Malformed header in filename '%s'.",
  51449. cimg_instance,
  51450. filename?filename:"(FILE*)");
  51451. cimg::fseek(nfile,xoffset,SEEK_CUR);
  51452. CImg<ucharT> buffer;
  51453. if (buf_size<cimg_iobuffer) {
  51454. buffer.assign(buf_size,1,1,1,0);
  51455. cimg::fread(buffer._data,buf_size,nfile);
  51456. } else buffer.assign(dx_bytes + align_bytes);
  51457. unsigned char *ptrs = buffer;
  51458. // Decompress buffer (if necessary)
  51459. if (compression==1 || compression==2) {
  51460. if (file)
  51461. throw CImgIOException(_cimg_instance
  51462. "load_bmp(): Unable to load compressed data from '(*FILE)' inputs.",
  51463. cimg_instance);
  51464. else {
  51465. if (!file) cimg::fclose(nfile);
  51466. return load_other(filename);
  51467. }
  51468. }
  51469. // Read pixel data
  51470. assign(dx,cimg::abs(dy),1,3,0);
  51471. switch (bpp) {
  51472. case 1 : { // Monochrome
  51473. if (colormap._width>=2) for (int y = height() - 1; y>=0; --y) {
  51474. if (buf_size>=cimg_iobuffer) {
  51475. if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
  51476. cimg::fseek(nfile,align_bytes,SEEK_CUR);
  51477. }
  51478. unsigned char mask = 0x80, val = 0;
  51479. cimg_forX(*this,x) {
  51480. if (mask==0x80) val = *(ptrs++);
  51481. const unsigned char *col = (unsigned char*)(colormap._data + (val&mask?1:0));
  51482. (*this)(x,y,2) = (T)*(col++);
  51483. (*this)(x,y,1) = (T)*(col++);
  51484. (*this)(x,y,0) = (T)*(col++);
  51485. mask = cimg::ror(mask);
  51486. }
  51487. ptrs+=align_bytes;
  51488. }
  51489. } break;
  51490. case 4 : { // 16 colors
  51491. if (colormap._width>=16) for (int y = height() - 1; y>=0; --y) {
  51492. if (buf_size>=cimg_iobuffer) {
  51493. if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
  51494. cimg::fseek(nfile,align_bytes,SEEK_CUR);
  51495. }
  51496. unsigned char mask = 0xF0, val = 0;
  51497. cimg_forX(*this,x) {
  51498. if (mask==0xF0) val = *(ptrs++);
  51499. const unsigned char color = (unsigned char)((mask<16)?(val&mask):((val&mask)>>4));
  51500. const unsigned char *col = (unsigned char*)(colormap._data + color);
  51501. (*this)(x,y,2) = (T)*(col++);
  51502. (*this)(x,y,1) = (T)*(col++);
  51503. (*this)(x,y,0) = (T)*(col++);
  51504. mask = cimg::ror(mask,4);
  51505. }
  51506. ptrs+=align_bytes;
  51507. }
  51508. } break;
  51509. case 8 : { // 256 colors
  51510. if (colormap._width>=256) for (int y = height() - 1; y>=0; --y) {
  51511. if (buf_size>=cimg_iobuffer) {
  51512. if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
  51513. cimg::fseek(nfile,align_bytes,SEEK_CUR);
  51514. }
  51515. cimg_forX(*this,x) {
  51516. const unsigned char *col = (unsigned char*)(colormap._data + *(ptrs++));
  51517. (*this)(x,y,2) = (T)*(col++);
  51518. (*this)(x,y,1) = (T)*(col++);
  51519. (*this)(x,y,0) = (T)*(col++);
  51520. }
  51521. ptrs+=align_bytes;
  51522. }
  51523. } break;
  51524. case 16 : { // 16 bits colors (RGB565)
  51525. for (int y = height() - 1; y>=0; --y) {
  51526. if (buf_size>=cimg_iobuffer) {
  51527. if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
  51528. cimg::fseek(nfile,align_bytes,SEEK_CUR);
  51529. }
  51530. cimg_forX(*this,x) {
  51531. const unsigned char c1 = *(ptrs++), c2 = *(ptrs++);
  51532. const unsigned short col = (unsigned short)c2<<8 | c1;
  51533. (*this)(x,y,2) = (T)((col&0x1F)<<3);
  51534. (*this)(x,y,1) = (T)(((col>>5)&0x3F)<<3);
  51535. (*this)(x,y,0) = (T)(((col>>11)&0x1F)<<3);
  51536. }
  51537. ptrs+=align_bytes;
  51538. }
  51539. } break;
  51540. case 24 : { // 24 bits colors
  51541. for (int y = height() - 1; y>=0; --y) {
  51542. if (buf_size>=cimg_iobuffer) {
  51543. if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
  51544. cimg::fseek(nfile,align_bytes,SEEK_CUR);
  51545. }
  51546. cimg_forX(*this,x) {
  51547. (*this)(x,y,2) = (T)*(ptrs++);
  51548. (*this)(x,y,1) = (T)*(ptrs++);
  51549. (*this)(x,y,0) = (T)*(ptrs++);
  51550. }
  51551. ptrs+=align_bytes;
  51552. }
  51553. } break;
  51554. case 32 : { // 32 bits colors
  51555. for (int y = height() - 1; y>=0; --y) {
  51556. if (buf_size>=cimg_iobuffer) {
  51557. if (!cimg::fread(ptrs=buffer._data,dx_bytes,nfile)) break;
  51558. cimg::fseek(nfile,align_bytes,SEEK_CUR);
  51559. }
  51560. cimg_forX(*this,x) {
  51561. (*this)(x,y,2) = (T)*(ptrs++);
  51562. (*this)(x,y,1) = (T)*(ptrs++);
  51563. (*this)(x,y,0) = (T)*(ptrs++);
  51564. ++ptrs;
  51565. }
  51566. ptrs+=align_bytes;
  51567. }
  51568. } break;
  51569. }
  51570. if (dy<0) mirror('y');
  51571. if (!file) cimg::fclose(nfile);
  51572. return *this;
  51573. }
  51574. //! Load image from a JPEG file.
  51575. /**
  51576. \param filename Filename, as a C-string.
  51577. **/
  51578. CImg<T>& load_jpeg(const char *const filename) {
  51579. return _load_jpeg(0,filename);
  51580. }
  51581. //! Load image from a JPEG file \newinstance.
  51582. static CImg<T> get_load_jpeg(const char *const filename) {
  51583. return CImg<T>().load_jpeg(filename);
  51584. }
  51585. //! Load image from a JPEG file \overloading.
  51586. CImg<T>& load_jpeg(std::FILE *const file) {
  51587. return _load_jpeg(file,0);
  51588. }
  51589. //! Load image from a JPEG file \newinstance.
  51590. static CImg<T> get_load_jpeg(std::FILE *const file) {
  51591. return CImg<T>().load_jpeg(file);
  51592. }
  51593. // Custom error handler for libjpeg.
  51594. #ifdef cimg_use_jpeg
  51595. struct _cimg_error_mgr {
  51596. struct jpeg_error_mgr original;
  51597. jmp_buf setjmp_buffer;
  51598. char message[JMSG_LENGTH_MAX];
  51599. };
  51600. typedef struct _cimg_error_mgr *_cimg_error_ptr;
  51601. METHODDEF(void) _cimg_jpeg_error_exit(j_common_ptr cinfo) {
  51602. _cimg_error_ptr c_err = (_cimg_error_ptr) cinfo->err; // Return control to the setjmp point
  51603. (*cinfo->err->format_message)(cinfo,c_err->message);
  51604. jpeg_destroy(cinfo); // Clean memory and temp files
  51605. longjmp(c_err->setjmp_buffer,1);
  51606. }
  51607. #endif
  51608. CImg<T>& _load_jpeg(std::FILE *const file, const char *const filename) {
  51609. if (!file && !filename)
  51610. throw CImgArgumentException(_cimg_instance
  51611. "load_jpeg(): Specified filename is (null).",
  51612. cimg_instance);
  51613. #ifndef cimg_use_jpeg
  51614. if (file)
  51615. throw CImgIOException(_cimg_instance
  51616. "load_jpeg(): Unable to load data from '(FILE*)' unless libjpeg is enabled.",
  51617. cimg_instance);
  51618. else return load_other(filename);
  51619. #else
  51620. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  51621. struct jpeg_decompress_struct cinfo;
  51622. struct _cimg_error_mgr jerr;
  51623. cinfo.err = jpeg_std_error(&jerr.original);
  51624. jerr.original.error_exit = _cimg_jpeg_error_exit;
  51625. if (setjmp(jerr.setjmp_buffer)) { // JPEG error
  51626. if (!file) cimg::fclose(nfile);
  51627. throw CImgIOException(_cimg_instance
  51628. "load_jpeg(): Error message returned by libjpeg: %s.",
  51629. cimg_instance,jerr.message);
  51630. }
  51631. jpeg_create_decompress(&cinfo);
  51632. jpeg_stdio_src(&cinfo,nfile);
  51633. jpeg_read_header(&cinfo,TRUE);
  51634. jpeg_start_decompress(&cinfo);
  51635. if (cinfo.output_components!=1 && cinfo.output_components!=3 && cinfo.output_components!=4) {
  51636. if (!file) {
  51637. cimg::fclose(nfile);
  51638. return load_other(filename);
  51639. } else
  51640. throw CImgIOException(_cimg_instance
  51641. "load_jpeg(): Failed to load JPEG data from file '%s'.",
  51642. cimg_instance,filename?filename:"(FILE*)");
  51643. }
  51644. CImg<ucharT> buffer(cinfo.output_width*cinfo.output_components);
  51645. JSAMPROW row_pointer[1];
  51646. try { assign(cinfo.output_width,cinfo.output_height,1,cinfo.output_components); }
  51647. catch (...) { if (!file) cimg::fclose(nfile); throw; }
  51648. T *ptr_r = _data, *ptr_g = _data + 1UL*_width*_height, *ptr_b = _data + 2UL*_width*_height,
  51649. *ptr_a = _data + 3UL*_width*_height;
  51650. while (cinfo.output_scanline<cinfo.output_height) {
  51651. *row_pointer = buffer._data;
  51652. if (jpeg_read_scanlines(&cinfo,row_pointer,1)!=1) {
  51653. cimg::warn(_cimg_instance
  51654. "load_jpeg(): Incomplete data in file '%s'.",
  51655. cimg_instance,filename?filename:"(FILE*)");
  51656. break;
  51657. }
  51658. const unsigned char *ptrs = buffer._data;
  51659. switch (_spectrum) {
  51660. case 1 : {
  51661. cimg_forX(*this,x) *(ptr_r++) = (T)*(ptrs++);
  51662. } break;
  51663. case 3 : {
  51664. cimg_forX(*this,x) {
  51665. *(ptr_r++) = (T)*(ptrs++);
  51666. *(ptr_g++) = (T)*(ptrs++);
  51667. *(ptr_b++) = (T)*(ptrs++);
  51668. }
  51669. } break;
  51670. case 4 : {
  51671. cimg_forX(*this,x) {
  51672. *(ptr_r++) = (T)*(ptrs++);
  51673. *(ptr_g++) = (T)*(ptrs++);
  51674. *(ptr_b++) = (T)*(ptrs++);
  51675. *(ptr_a++) = (T)*(ptrs++);
  51676. }
  51677. } break;
  51678. }
  51679. }
  51680. jpeg_finish_decompress(&cinfo);
  51681. jpeg_destroy_decompress(&cinfo);
  51682. if (!file) cimg::fclose(nfile);
  51683. return *this;
  51684. #endif
  51685. }
  51686. //! Load image from a file, using Magick++ library.
  51687. /**
  51688. \param filename Filename, as a C-string.
  51689. **/
  51690. // Added April/may 2006 by Christoph Hormann <chris_hormann@gmx.de>
  51691. // This is experimental code, not much tested, use with care.
  51692. CImg<T>& load_magick(const char *const filename) {
  51693. if (!filename)
  51694. throw CImgArgumentException(_cimg_instance
  51695. "load_magick(): Specified filename is (null).",
  51696. cimg_instance);
  51697. #ifdef cimg_use_magick
  51698. Magick::Image image(filename);
  51699. const unsigned int W = image.size().width(), H = image.size().height();
  51700. switch (image.type()) {
  51701. case Magick::PaletteMatteType :
  51702. case Magick::TrueColorMatteType :
  51703. case Magick::ColorSeparationType : {
  51704. assign(W,H,1,4);
  51705. 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);
  51706. Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
  51707. for (ulongT off = (ulongT)W*H; off; --off) {
  51708. *(ptr_r++) = (T)(pixels->red);
  51709. *(ptr_g++) = (T)(pixels->green);
  51710. *(ptr_b++) = (T)(pixels->blue);
  51711. *(ptr_a++) = (T)(pixels->opacity);
  51712. ++pixels;
  51713. }
  51714. } break;
  51715. case Magick::PaletteType :
  51716. case Magick::TrueColorType : {
  51717. assign(W,H,1,3);
  51718. T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
  51719. Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
  51720. for (ulongT off = (ulongT)W*H; off; --off) {
  51721. *(ptr_r++) = (T)(pixels->red);
  51722. *(ptr_g++) = (T)(pixels->green);
  51723. *(ptr_b++) = (T)(pixels->blue);
  51724. ++pixels;
  51725. }
  51726. } break;
  51727. case Magick::GrayscaleMatteType : {
  51728. assign(W,H,1,2);
  51729. T *ptr_r = data(0,0,0,0), *ptr_a = data(0,0,0,1);
  51730. Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
  51731. for (ulongT off = (ulongT)W*H; off; --off) {
  51732. *(ptr_r++) = (T)(pixels->red);
  51733. *(ptr_a++) = (T)(pixels->opacity);
  51734. ++pixels;
  51735. }
  51736. } break;
  51737. default : {
  51738. assign(W,H,1,1);
  51739. T *ptr_r = data(0,0,0,0);
  51740. Magick::PixelPacket *pixels = image.getPixels(0,0,W,H);
  51741. for (ulongT off = (ulongT)W*H; off; --off) {
  51742. *(ptr_r++) = (T)(pixels->red);
  51743. ++pixels;
  51744. }
  51745. }
  51746. }
  51747. return *this;
  51748. #else
  51749. throw CImgIOException(_cimg_instance
  51750. "load_magick(): Unable to load file '%s' unless libMagick++ is enabled.",
  51751. cimg_instance,
  51752. filename);
  51753. #endif
  51754. }
  51755. //! Load image from a file, using Magick++ library \newinstance.
  51756. static CImg<T> get_load_magick(const char *const filename) {
  51757. return CImg<T>().load_magick(filename);
  51758. }
  51759. //! Load image from a PNG file.
  51760. /**
  51761. \param filename Filename, as a C-string.
  51762. \param[out] bits_per_value Number of bits used to store a scalar value in the image file.
  51763. **/
  51764. CImg<T>& load_png(const char *const filename, unsigned int *const bits_per_value=0) {
  51765. return _load_png(0,filename,bits_per_value);
  51766. }
  51767. //! Load image from a PNG file \newinstance.
  51768. static CImg<T> get_load_png(const char *const filename, unsigned int *const bits_per_value=0) {
  51769. return CImg<T>().load_png(filename,bits_per_value);
  51770. }
  51771. //! Load image from a PNG file \overloading.
  51772. CImg<T>& load_png(std::FILE *const file, unsigned int *const bits_per_value=0) {
  51773. return _load_png(file,0,bits_per_value);
  51774. }
  51775. //! Load image from a PNG file \newinstance.
  51776. static CImg<T> get_load_png(std::FILE *const file, unsigned int *const bits_per_value=0) {
  51777. return CImg<T>().load_png(file,bits_per_value);
  51778. }
  51779. // (Note: Most of this function has been written by Eric Fausett)
  51780. CImg<T>& _load_png(std::FILE *const file, const char *const filename, unsigned int *const bits_per_value) {
  51781. if (!file && !filename)
  51782. throw CImgArgumentException(_cimg_instance
  51783. "load_png(): Specified filename is (null).",
  51784. cimg_instance);
  51785. #ifndef cimg_use_png
  51786. cimg::unused(bits_per_value);
  51787. if (file)
  51788. throw CImgIOException(_cimg_instance
  51789. "load_png(): Unable to load data from '(FILE*)' unless libpng is enabled.",
  51790. cimg_instance);
  51791. else return load_other(filename);
  51792. #else
  51793. // Open file and check for PNG validity
  51794. #if defined __GNUC__
  51795. const char *volatile nfilename = filename; // Use 'volatile' to avoid (wrong) g++ warning
  51796. std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"rb");
  51797. #else
  51798. const char *nfilename = filename;
  51799. std::FILE *nfile = file?file:cimg::fopen(nfilename,"rb");
  51800. #endif
  51801. unsigned char pngCheck[8] = {};
  51802. cimg::fread(pngCheck,8,(std::FILE*)nfile);
  51803. if (png_sig_cmp(pngCheck,0,8)) {
  51804. if (!file) cimg::fclose(nfile);
  51805. throw CImgIOException(_cimg_instance
  51806. "load_png(): Invalid PNG file '%s'.",
  51807. cimg_instance,
  51808. nfilename?nfilename:"(FILE*)");
  51809. }
  51810. // Setup PNG structures for read
  51811. png_voidp user_error_ptr = 0;
  51812. png_error_ptr user_error_fn = 0, user_warning_fn = 0;
  51813. png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,user_error_ptr,user_error_fn,user_warning_fn);
  51814. if (!png_ptr) {
  51815. if (!file) cimg::fclose(nfile);
  51816. throw CImgIOException(_cimg_instance
  51817. "load_png(): Failed to initialize 'png_ptr' structure for file '%s'.",
  51818. cimg_instance,
  51819. nfilename?nfilename:"(FILE*)");
  51820. }
  51821. png_infop info_ptr = png_create_info_struct(png_ptr);
  51822. if (!info_ptr) {
  51823. if (!file) cimg::fclose(nfile);
  51824. png_destroy_read_struct(&png_ptr,(png_infopp)0,(png_infopp)0);
  51825. throw CImgIOException(_cimg_instance
  51826. "load_png(): Failed to initialize 'info_ptr' structure for file '%s'.",
  51827. cimg_instance,
  51828. nfilename?nfilename:"(FILE*)");
  51829. }
  51830. png_infop end_info = png_create_info_struct(png_ptr);
  51831. if (!end_info) {
  51832. if (!file) cimg::fclose(nfile);
  51833. png_destroy_read_struct(&png_ptr,&info_ptr,(png_infopp)0);
  51834. throw CImgIOException(_cimg_instance
  51835. "load_png(): Failed to initialize 'end_info' structure for file '%s'.",
  51836. cimg_instance,
  51837. nfilename?nfilename:"(FILE*)");
  51838. }
  51839. // Error handling callback for png file reading
  51840. if (setjmp(png_jmpbuf(png_ptr))) {
  51841. if (!file) cimg::fclose((std::FILE*)nfile);
  51842. png_destroy_read_struct(&png_ptr, &end_info, (png_infopp)0);
  51843. throw CImgIOException(_cimg_instance
  51844. "load_png(): Encountered unknown fatal error in libpng for file '%s'.",
  51845. cimg_instance,
  51846. nfilename?nfilename:"(FILE*)");
  51847. }
  51848. png_init_io(png_ptr, nfile);
  51849. png_set_sig_bytes(png_ptr, 8);
  51850. // Get PNG Header Info up to data block
  51851. png_read_info(png_ptr,info_ptr);
  51852. png_uint_32 W, H;
  51853. int bit_depth, color_type, interlace_type;
  51854. bool is_gray = false;
  51855. png_get_IHDR(png_ptr,info_ptr,&W,&H,&bit_depth,&color_type,&interlace_type,(int*)0,(int*)0);
  51856. png_set_interlace_handling(png_ptr);
  51857. if (bits_per_value) *bits_per_value = (unsigned int)bit_depth;
  51858. // Transforms to unify image data
  51859. if (color_type==PNG_COLOR_TYPE_PALETTE) {
  51860. png_set_palette_to_rgb(png_ptr);
  51861. color_type = PNG_COLOR_TYPE_RGB;
  51862. bit_depth = 8;
  51863. }
  51864. if (color_type==PNG_COLOR_TYPE_GRAY && bit_depth<8) {
  51865. png_set_expand_gray_1_2_4_to_8(png_ptr);
  51866. is_gray = true;
  51867. bit_depth = 8;
  51868. }
  51869. if (png_get_valid(png_ptr,info_ptr,PNG_INFO_tRNS)) {
  51870. png_set_tRNS_to_alpha(png_ptr);
  51871. color_type |= PNG_COLOR_MASK_ALPHA;
  51872. }
  51873. if (color_type==PNG_COLOR_TYPE_GRAY || color_type==PNG_COLOR_TYPE_GRAY_ALPHA) {
  51874. png_set_gray_to_rgb(png_ptr);
  51875. color_type |= PNG_COLOR_MASK_COLOR;
  51876. is_gray = true;
  51877. }
  51878. if (color_type==PNG_COLOR_TYPE_RGB)
  51879. png_set_filler(png_ptr,0xffffU,PNG_FILLER_AFTER);
  51880. png_read_update_info(png_ptr,info_ptr);
  51881. if (bit_depth!=8 && bit_depth!=16) {
  51882. if (!file) cimg::fclose(nfile);
  51883. png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
  51884. throw CImgIOException(_cimg_instance
  51885. "load_png(): Invalid bit depth %u in file '%s'.",
  51886. cimg_instance,
  51887. bit_depth,nfilename?nfilename:"(FILE*)");
  51888. }
  51889. const int byte_depth = bit_depth>>3;
  51890. // Allocate memory for image reading
  51891. png_bytep *const imgData = new png_bytep[H];
  51892. for (unsigned int row = 0; row<H; ++row) imgData[row] = new png_byte[(size_t)byte_depth*4*W];
  51893. png_read_image(png_ptr,imgData);
  51894. png_read_end(png_ptr,end_info);
  51895. // Read pixel data
  51896. if (color_type!=PNG_COLOR_TYPE_RGB && color_type!=PNG_COLOR_TYPE_RGB_ALPHA) {
  51897. if (!file) cimg::fclose(nfile);
  51898. png_destroy_read_struct(&png_ptr,&end_info,(png_infopp)0);
  51899. throw CImgIOException(_cimg_instance
  51900. "load_png(): Invalid color coding type %u in file '%s'.",
  51901. cimg_instance,
  51902. color_type,nfilename?nfilename:"(FILE*)");
  51903. }
  51904. const bool is_alpha = (color_type==PNG_COLOR_TYPE_RGBA);
  51905. try { assign(W,H,1,(is_gray?1:3) + (is_alpha?1:0)); }
  51906. catch (...) { if (!file) cimg::fclose(nfile); throw; }
  51907. T
  51908. *ptr_r = data(0,0,0,0),
  51909. *ptr_g = is_gray?0:data(0,0,0,1),
  51910. *ptr_b = is_gray?0:data(0,0,0,2),
  51911. *ptr_a = !is_alpha?0:data(0,0,0,is_gray?1:3);
  51912. switch (bit_depth) {
  51913. case 8 : {
  51914. cimg_forY(*this,y) {
  51915. const unsigned char *ptrs = (unsigned char*)imgData[y];
  51916. cimg_forX(*this,x) {
  51917. *(ptr_r++) = (T)*(ptrs++);
  51918. if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs;
  51919. if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs;
  51920. if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs;
  51921. }
  51922. }
  51923. } break;
  51924. case 16 : {
  51925. cimg_forY(*this,y) {
  51926. const unsigned short *ptrs = (unsigned short*)(imgData[y]);
  51927. if (!cimg::endianness()) cimg::invert_endianness(ptrs,4*_width);
  51928. cimg_forX(*this,x) {
  51929. *(ptr_r++) = (T)*(ptrs++);
  51930. if (ptr_g) *(ptr_g++) = (T)*(ptrs++); else ++ptrs;
  51931. if (ptr_b) *(ptr_b++) = (T)*(ptrs++); else ++ptrs;
  51932. if (ptr_a) *(ptr_a++) = (T)*(ptrs++); else ++ptrs;
  51933. }
  51934. }
  51935. } break;
  51936. }
  51937. png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
  51938. // Deallocate image read memory
  51939. cimg_forY(*this,n) delete[] imgData[n];
  51940. delete[] imgData;
  51941. if (!file) cimg::fclose(nfile);
  51942. return *this;
  51943. #endif
  51944. }
  51945. //! Load image from a PNM file.
  51946. /**
  51947. \param filename Filename, as a C-string.
  51948. **/
  51949. CImg<T>& load_pnm(const char *const filename) {
  51950. return _load_pnm(0,filename);
  51951. }
  51952. //! Load image from a PNM file \newinstance.
  51953. static CImg<T> get_load_pnm(const char *const filename) {
  51954. return CImg<T>().load_pnm(filename);
  51955. }
  51956. //! Load image from a PNM file \overloading.
  51957. CImg<T>& load_pnm(std::FILE *const file) {
  51958. return _load_pnm(file,0);
  51959. }
  51960. //! Load image from a PNM file \newinstance.
  51961. static CImg<T> get_load_pnm(std::FILE *const file) {
  51962. return CImg<T>().load_pnm(file);
  51963. }
  51964. CImg<T>& _load_pnm(std::FILE *const file, const char *const filename) {
  51965. if (!file && !filename)
  51966. throw CImgArgumentException(_cimg_instance
  51967. "load_pnm(): Specified filename is (null).",
  51968. cimg_instance);
  51969. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  51970. unsigned int ppm_type, W, H, D = 1, colormax = 255;
  51971. CImg<charT> item(16384,1,1,1,0);
  51972. int err, rval, gval, bval;
  51973. const longT cimg_iobuffer = (longT)24*1024*1024;
  51974. while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
  51975. if (cimg_sscanf(item," P%u",&ppm_type)!=1) {
  51976. if (!file) cimg::fclose(nfile);
  51977. throw CImgIOException(_cimg_instance
  51978. "load_pnm(): PNM header not found in file '%s'.",
  51979. cimg_instance,
  51980. filename?filename:"(FILE*)");
  51981. }
  51982. while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
  51983. if ((err=cimg_sscanf(item," %u %u %u %u",&W,&H,&D,&colormax))<2) {
  51984. if (!file) cimg::fclose(nfile);
  51985. throw CImgIOException(_cimg_instance
  51986. "load_pnm(): WIDTH and HEIGHT fields undefined in file '%s'.",
  51987. cimg_instance,
  51988. filename?filename:"(FILE*)");
  51989. }
  51990. if (ppm_type!=1 && ppm_type!=4) {
  51991. if (err==2 || (err==3 && (ppm_type==5 || ppm_type==7 || ppm_type==8 || ppm_type==9))) {
  51992. while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
  51993. if (cimg_sscanf(item,"%u",&colormax)!=1)
  51994. cimg::warn(_cimg_instance
  51995. "load_pnm(): COLORMAX field is undefined in file '%s'.",
  51996. cimg_instance,
  51997. filename?filename:"(FILE*)");
  51998. } else { colormax = D; D = 1; }
  51999. }
  52000. std::fgetc(nfile);
  52001. if (filename) { // Check that dimensions specified in file does not exceed the buffer dimension
  52002. const cimg_int64 siz = cimg::fsize(filename);
  52003. if (W*H*D>siz)
  52004. throw CImgIOException(_cimg_instance
  52005. "load_pnm(): Specified image dimensions in file '%s' exceed file size.",
  52006. cimg_instance,
  52007. filename);
  52008. }
  52009. switch (ppm_type) {
  52010. case 1 : { // 2D B&W ascii
  52011. assign(W,H,1,1);
  52012. T* ptrd = _data;
  52013. cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)(rval?0:255); else break; }
  52014. } break;
  52015. case 2 : { // 2D grey ascii
  52016. assign(W,H,1,1);
  52017. T* ptrd = _data;
  52018. cimg_foroff(*this,off) { if (std::fscanf(nfile,"%d",&rval)>0) *(ptrd++) = (T)rval; else break; }
  52019. } break;
  52020. case 3 : { // 2D color ascii
  52021. assign(W,H,1,3);
  52022. T *ptrd = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
  52023. cimg_forXY(*this,x,y) {
  52024. if (std::fscanf(nfile,"%d %d %d",&rval,&gval,&bval)==3) {
  52025. *(ptrd++) = (T)rval; *(ptr_g++) = (T)gval; *(ptr_b++) = (T)bval;
  52026. } else break;
  52027. }
  52028. } break;
  52029. case 4 : { // 2D b&w binary (support 3D PINK extension)
  52030. CImg<ucharT> raw;
  52031. assign(W,H,D,1);
  52032. T *ptrd = data(0,0,0,0);
  52033. unsigned int w = 0, h = 0, d = 0;
  52034. for (longT to_read = (longT)((W/8 + (W%8?1:0))*H*D); to_read>0; ) {
  52035. raw.assign(std::min(to_read,cimg_iobuffer));
  52036. cimg::fread(raw._data,raw._width,nfile);
  52037. to_read-=raw._width;
  52038. const unsigned char *ptrs = raw._data;
  52039. unsigned char mask = 0, val = 0;
  52040. for (ulongT off = (ulongT)raw._width; off || mask; mask>>=1) {
  52041. if (!mask) { if (off--) val = *(ptrs++); mask = 128; }
  52042. *(ptrd++) = (T)((val&mask)?0:255);
  52043. if (++w==W) { w = 0; mask = 0; if (++h==H) { h = 0; if (++d==D) break; }}
  52044. }
  52045. }
  52046. } break;
  52047. case 5 : case 7 : { // 2D/3D grey binary (support 3D PINK extension)
  52048. if (colormax<256) { // 8 bits
  52049. CImg<ucharT> raw;
  52050. assign(W,H,D,1);
  52051. T *ptrd = data(0,0,0,0);
  52052. for (longT to_read = (longT)size(); to_read>0; ) {
  52053. raw.assign(std::min(to_read,cimg_iobuffer));
  52054. cimg::fread(raw._data,raw._width,nfile);
  52055. to_read-=raw._width;
  52056. const unsigned char *ptrs = raw._data;
  52057. for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
  52058. }
  52059. } else { // 16 bits
  52060. CImg<ushortT> raw;
  52061. assign(W,H,D,1);
  52062. T *ptrd = data(0,0,0,0);
  52063. for (longT to_read = (longT)size(); to_read>0; ) {
  52064. raw.assign(std::min(to_read,cimg_iobuffer/2));
  52065. cimg::fread(raw._data,raw._width,nfile);
  52066. if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
  52067. to_read-=raw._width;
  52068. const unsigned short *ptrs = raw._data;
  52069. for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
  52070. }
  52071. }
  52072. } break;
  52073. case 6 : { // 2D color binary
  52074. if (colormax<256) { // 8 bits
  52075. CImg<ucharT> raw;
  52076. assign(W,H,1,3);
  52077. T
  52078. *ptr_r = data(0,0,0,0),
  52079. *ptr_g = data(0,0,0,1),
  52080. *ptr_b = data(0,0,0,2);
  52081. for (longT to_read = (longT)size(); to_read>0; ) {
  52082. raw.assign(std::min(to_read,cimg_iobuffer));
  52083. cimg::fread(raw._data,raw._width,nfile);
  52084. to_read-=raw._width;
  52085. const unsigned char *ptrs = raw._data;
  52086. for (ulongT off = (ulongT)raw._width/3; off; --off) {
  52087. *(ptr_r++) = (T)*(ptrs++);
  52088. *(ptr_g++) = (T)*(ptrs++);
  52089. *(ptr_b++) = (T)*(ptrs++);
  52090. }
  52091. }
  52092. } else { // 16 bits
  52093. CImg<ushortT> raw;
  52094. assign(W,H,1,3);
  52095. T
  52096. *ptr_r = data(0,0,0,0),
  52097. *ptr_g = data(0,0,0,1),
  52098. *ptr_b = data(0,0,0,2);
  52099. for (longT to_read = (longT)size(); to_read>0; ) {
  52100. raw.assign(std::min(to_read,cimg_iobuffer/2));
  52101. cimg::fread(raw._data,raw._width,nfile);
  52102. if (!cimg::endianness()) cimg::invert_endianness(raw._data,raw._width);
  52103. to_read-=raw._width;
  52104. const unsigned short *ptrs = raw._data;
  52105. for (ulongT off = (ulongT)raw._width/3; off; --off) {
  52106. *(ptr_r++) = (T)*(ptrs++);
  52107. *(ptr_g++) = (T)*(ptrs++);
  52108. *(ptr_b++) = (T)*(ptrs++);
  52109. }
  52110. }
  52111. }
  52112. } break;
  52113. case 8 : { // 2D/3D grey binary with int32 integers (PINK extension)
  52114. CImg<intT> raw;
  52115. assign(W,H,D,1);
  52116. T *ptrd = data(0,0,0,0);
  52117. for (longT to_read = (longT)size(); to_read>0; ) {
  52118. raw.assign(std::min(to_read,cimg_iobuffer));
  52119. cimg::fread(raw._data,raw._width,nfile);
  52120. to_read-=raw._width;
  52121. const int *ptrs = raw._data;
  52122. for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
  52123. }
  52124. } break;
  52125. case 9 : { // 2D/3D grey binary with float values (PINK extension)
  52126. CImg<floatT> raw;
  52127. assign(W,H,D,1);
  52128. T *ptrd = data(0,0,0,0);
  52129. for (longT to_read = (longT)size(); to_read>0; ) {
  52130. raw.assign(std::min(to_read,cimg_iobuffer));
  52131. cimg::fread(raw._data,raw._width,nfile);
  52132. to_read-=raw._width;
  52133. const float *ptrs = raw._data;
  52134. for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++);
  52135. }
  52136. } break;
  52137. default :
  52138. assign();
  52139. if (!file) cimg::fclose(nfile);
  52140. throw CImgIOException(_cimg_instance
  52141. "load_pnm(): PNM type 'P%d' found, but type is not supported.",
  52142. cimg_instance,
  52143. filename?filename:"(FILE*)",ppm_type);
  52144. }
  52145. if (!file) cimg::fclose(nfile);
  52146. return *this;
  52147. }
  52148. //! Load image from a PFM file.
  52149. /**
  52150. \param filename Filename, as a C-string.
  52151. **/
  52152. CImg<T>& load_pfm(const char *const filename) {
  52153. return _load_pfm(0,filename);
  52154. }
  52155. //! Load image from a PFM file \newinstance.
  52156. static CImg<T> get_load_pfm(const char *const filename) {
  52157. return CImg<T>().load_pfm(filename);
  52158. }
  52159. //! Load image from a PFM file \overloading.
  52160. CImg<T>& load_pfm(std::FILE *const file) {
  52161. return _load_pfm(file,0);
  52162. }
  52163. //! Load image from a PFM file \newinstance.
  52164. static CImg<T> get_load_pfm(std::FILE *const file) {
  52165. return CImg<T>().load_pfm(file);
  52166. }
  52167. CImg<T>& _load_pfm(std::FILE *const file, const char *const filename) {
  52168. if (!file && !filename)
  52169. throw CImgArgumentException(_cimg_instance
  52170. "load_pfm(): Specified filename is (null).",
  52171. cimg_instance);
  52172. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  52173. char pfm_type;
  52174. CImg<charT> item(16384,1,1,1,0);
  52175. int W = 0, H = 0, err = 0;
  52176. double scale = 0;
  52177. while ((err=std::fscanf(nfile,"%16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
  52178. if (cimg_sscanf(item," P%c",&pfm_type)!=1) {
  52179. if (!file) cimg::fclose(nfile);
  52180. throw CImgIOException(_cimg_instance
  52181. "load_pfm(): PFM header not found in file '%s'.",
  52182. cimg_instance,
  52183. filename?filename:"(FILE*)");
  52184. }
  52185. while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
  52186. if ((err=cimg_sscanf(item," %d %d",&W,&H))<2) {
  52187. if (!file) cimg::fclose(nfile);
  52188. throw CImgIOException(_cimg_instance
  52189. "load_pfm(): WIDTH and HEIGHT fields are undefined in file '%s'.",
  52190. cimg_instance,
  52191. filename?filename:"(FILE*)");
  52192. } else if (W<=0 || H<=0) {
  52193. if (!file) cimg::fclose(nfile);
  52194. throw CImgIOException(_cimg_instance
  52195. "load_pfm(): WIDTH (%d) and HEIGHT (%d) fields are invalid in file '%s'.",
  52196. cimg_instance,W,H,
  52197. filename?filename:"(FILE*)");
  52198. }
  52199. if (err==2) {
  52200. while ((err=std::fscanf(nfile," %16383[^\n]",item.data()))!=EOF && (*item=='#' || !err)) std::fgetc(nfile);
  52201. if (cimg_sscanf(item,"%lf",&scale)!=1)
  52202. cimg::warn(_cimg_instance
  52203. "load_pfm(): SCALE field is undefined in file '%s'.",
  52204. cimg_instance,
  52205. filename?filename:"(FILE*)");
  52206. }
  52207. std::fgetc(nfile);
  52208. const bool is_color = (pfm_type=='F'), is_inverted = (scale>0)!=cimg::endianness();
  52209. if (is_color) {
  52210. assign(W,H,1,3,(T)0);
  52211. CImg<floatT> buf(3*W);
  52212. T *ptr_r = data(0,0,0,0), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2);
  52213. cimg_forY(*this,y) {
  52214. cimg::fread(buf._data,3*W,nfile);
  52215. if (is_inverted) cimg::invert_endianness(buf._data,3*W);
  52216. const float *ptrs = buf._data;
  52217. cimg_forX(*this,x) {
  52218. *(ptr_r++) = (T)*(ptrs++);
  52219. *(ptr_g++) = (T)*(ptrs++);
  52220. *(ptr_b++) = (T)*(ptrs++);
  52221. }
  52222. }
  52223. } else {
  52224. assign(W,H,1,1,(T)0);
  52225. CImg<floatT> buf(W);
  52226. T *ptrd = data(0,0,0,0);
  52227. cimg_forY(*this,y) {
  52228. cimg::fread(buf._data,W,nfile);
  52229. if (is_inverted) cimg::invert_endianness(buf._data,W);
  52230. const float *ptrs = buf._data;
  52231. cimg_forX(*this,x) *(ptrd++) = (T)*(ptrs++);
  52232. }
  52233. }
  52234. if (!file) cimg::fclose(nfile);
  52235. return mirror('y'); // Most of the .pfm files are flipped along the y-axis
  52236. }
  52237. //! Load image from a RGB file.
  52238. /**
  52239. \param filename Filename, as a C-string.
  52240. \param dimw Width of the image buffer.
  52241. \param dimh Height of the image buffer.
  52242. **/
  52243. CImg<T>& load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
  52244. return _load_rgb(0,filename,dimw,dimh);
  52245. }
  52246. //! Load image from a RGB file \newinstance.
  52247. static CImg<T> get_load_rgb(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
  52248. return CImg<T>().load_rgb(filename,dimw,dimh);
  52249. }
  52250. //! Load image from a RGB file \overloading.
  52251. CImg<T>& load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
  52252. return _load_rgb(file,0,dimw,dimh);
  52253. }
  52254. //! Load image from a RGB file \newinstance.
  52255. static CImg<T> get_load_rgb(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
  52256. return CImg<T>().load_rgb(file,dimw,dimh);
  52257. }
  52258. CImg<T>& _load_rgb(std::FILE *const file, const char *const filename,
  52259. const unsigned int dimw, const unsigned int dimh) {
  52260. if (!file && !filename)
  52261. throw CImgArgumentException(_cimg_instance
  52262. "load_rgb(): Specified filename is (null).",
  52263. cimg_instance);
  52264. if (!dimw || !dimh) return assign();
  52265. const longT cimg_iobuffer = (longT)24*1024*1024;
  52266. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  52267. CImg<ucharT> raw;
  52268. assign(dimw,dimh,1,3);
  52269. T
  52270. *ptr_r = data(0,0,0,0),
  52271. *ptr_g = data(0,0,0,1),
  52272. *ptr_b = data(0,0,0,2);
  52273. for (longT to_read = (longT)size(); to_read>0; ) {
  52274. raw.assign(std::min(to_read,cimg_iobuffer));
  52275. cimg::fread(raw._data,raw._width,nfile);
  52276. to_read-=raw._width;
  52277. const unsigned char *ptrs = raw._data;
  52278. for (ulongT off = raw._width/3UL; off; --off) {
  52279. *(ptr_r++) = (T)*(ptrs++);
  52280. *(ptr_g++) = (T)*(ptrs++);
  52281. *(ptr_b++) = (T)*(ptrs++);
  52282. }
  52283. }
  52284. if (!file) cimg::fclose(nfile);
  52285. return *this;
  52286. }
  52287. //! Load image from a RGBA file.
  52288. /**
  52289. \param filename Filename, as a C-string.
  52290. \param dimw Width of the image buffer.
  52291. \param dimh Height of the image buffer.
  52292. **/
  52293. CImg<T>& load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
  52294. return _load_rgba(0,filename,dimw,dimh);
  52295. }
  52296. //! Load image from a RGBA file \newinstance.
  52297. static CImg<T> get_load_rgba(const char *const filename, const unsigned int dimw, const unsigned int dimh=1) {
  52298. return CImg<T>().load_rgba(filename,dimw,dimh);
  52299. }
  52300. //! Load image from a RGBA file \overloading.
  52301. CImg<T>& load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
  52302. return _load_rgba(file,0,dimw,dimh);
  52303. }
  52304. //! Load image from a RGBA file \newinstance.
  52305. static CImg<T> get_load_rgba(std::FILE *const file, const unsigned int dimw, const unsigned int dimh=1) {
  52306. return CImg<T>().load_rgba(file,dimw,dimh);
  52307. }
  52308. CImg<T>& _load_rgba(std::FILE *const file, const char *const filename,
  52309. const unsigned int dimw, const unsigned int dimh) {
  52310. if (!file && !filename)
  52311. throw CImgArgumentException(_cimg_instance
  52312. "load_rgba(): Specified filename is (null).",
  52313. cimg_instance);
  52314. if (!dimw || !dimh) return assign();
  52315. const longT cimg_iobuffer = (longT)24*1024*1024;
  52316. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  52317. CImg<ucharT> raw;
  52318. assign(dimw,dimh,1,4);
  52319. T
  52320. *ptr_r = data(0,0,0,0),
  52321. *ptr_g = data(0,0,0,1),
  52322. *ptr_b = data(0,0,0,2),
  52323. *ptr_a = data(0,0,0,3);
  52324. for (longT to_read = (longT)size(); to_read>0; ) {
  52325. raw.assign(std::min(to_read,cimg_iobuffer));
  52326. cimg::fread(raw._data,raw._width,nfile);
  52327. to_read-=raw._width;
  52328. const unsigned char *ptrs = raw._data;
  52329. for (ulongT off = raw._width/4UL; off; --off) {
  52330. *(ptr_r++) = (T)*(ptrs++);
  52331. *(ptr_g++) = (T)*(ptrs++);
  52332. *(ptr_b++) = (T)*(ptrs++);
  52333. *(ptr_a++) = (T)*(ptrs++);
  52334. }
  52335. }
  52336. if (!file) cimg::fclose(nfile);
  52337. return *this;
  52338. }
  52339. //! Load image from a TIFF file.
  52340. /**
  52341. \param filename Filename, as a C-string.
  52342. \param first_frame First frame to read (for multi-pages tiff).
  52343. \param last_frame Last frame to read (for multi-pages tiff).
  52344. \param step_frame Step value of frame reading.
  52345. \param[out] bits_per_value Number of bits used to store a scalar value in the image file.
  52346. \param[out] voxel_size Voxel size, as stored in the filename.
  52347. \param[out] description Description, as stored in the filename.
  52348. \note
  52349. - libtiff support is enabled by defining the precompilation
  52350. directive \c cimg_use_tiff.
  52351. - When libtiff is enabled, 2D and 3D (multipage) several
  52352. channel per pixel are supported for
  52353. <tt>char,uchar,short,ushort,float</tt> and \c double pixel types.
  52354. - If \c cimg_use_tiff is not defined at compile time the
  52355. function uses CImg<T>& load_other(const char*).
  52356. **/
  52357. CImg<T>& load_tiff(const char *const filename,
  52358. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  52359. const unsigned int step_frame=1, unsigned int *const bits_per_value=0,
  52360. float *const voxel_size=0, CImg<charT> *const description=0) {
  52361. if (!filename)
  52362. throw CImgArgumentException(_cimg_instance
  52363. "load_tiff(): Specified filename is (null).",
  52364. cimg_instance);
  52365. const unsigned int
  52366. nfirst_frame = first_frame<last_frame?first_frame:last_frame,
  52367. nstep_frame = step_frame?step_frame:1;
  52368. unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
  52369. #ifndef cimg_use_tiff
  52370. cimg::unused(bits_per_value,voxel_size,description);
  52371. if (nfirst_frame || nlast_frame!=~0U || nstep_frame>1)
  52372. throw CImgArgumentException(_cimg_instance
  52373. "load_tiff(): Unable to read sub-images from file '%s' unless libtiff is enabled.",
  52374. cimg_instance,
  52375. filename);
  52376. return load_other(filename);
  52377. #else
  52378. #if cimg_verbosity<3
  52379. TIFFSetWarningHandler(0);
  52380. TIFFSetErrorHandler(0);
  52381. #endif
  52382. TIFF *tif = TIFFOpen(filename,"r");
  52383. if (tif) {
  52384. unsigned int nb_images = 0;
  52385. do ++nb_images; while (TIFFReadDirectory(tif));
  52386. if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
  52387. cimg::warn(_cimg_instance
  52388. "load_tiff(): File '%s' contains %u image(s) while specified frame range is [%u,%u] (step %u).",
  52389. cimg_instance,
  52390. filename,nb_images,nfirst_frame,nlast_frame,nstep_frame);
  52391. if (nfirst_frame>=nb_images) return assign();
  52392. if (nlast_frame>=nb_images) nlast_frame = nb_images - 1;
  52393. TIFFSetDirectory(tif,0);
  52394. CImg<T> frame;
  52395. for (unsigned int l = nfirst_frame; l<=nlast_frame; l+=nstep_frame) {
  52396. frame._load_tiff(tif,l,bits_per_value,voxel_size,description);
  52397. if (l==nfirst_frame)
  52398. assign(frame._width,frame._height,1 + (nlast_frame - nfirst_frame)/nstep_frame,frame._spectrum);
  52399. if (frame._width>_width || frame._height>_height || frame._spectrum>_spectrum)
  52400. resize(std::max(frame._width,_width),
  52401. std::max(frame._height,_height),-100,
  52402. std::max(frame._spectrum,_spectrum),0);
  52403. draw_image(0,0,(l - nfirst_frame)/nstep_frame,frame);
  52404. }
  52405. TIFFClose(tif);
  52406. } else throw CImgIOException(_cimg_instance
  52407. "load_tiff(): Failed to open file '%s'.",
  52408. cimg_instance,
  52409. filename);
  52410. return *this;
  52411. #endif
  52412. }
  52413. //! Load image from a TIFF file \newinstance.
  52414. static CImg<T> get_load_tiff(const char *const filename,
  52415. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  52416. const unsigned int step_frame=1, unsigned int *const bits_per_value=0,
  52417. float *const voxel_size=0, CImg<charT> *const description=0) {
  52418. return CImg<T>().load_tiff(filename,first_frame,last_frame,step_frame,bits_per_value,voxel_size,description);
  52419. }
  52420. // (Original contribution by Jerome Boulanger).
  52421. #ifdef cimg_use_tiff
  52422. template<typename t>
  52423. void _load_tiff_tiled_contig(TIFF *const tif, const cimg_uint16 samplesperpixel,
  52424. const cimg_uint32 nx, const cimg_uint32 ny,
  52425. const cimg_uint32 tw, const cimg_uint32 th) {
  52426. t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
  52427. if (buf) {
  52428. for (unsigned int row = 0; row<ny; row+=th)
  52429. for (unsigned int col = 0; col<nx; col+=tw) {
  52430. if (TIFFReadTile(tif,buf,col,row,0,0)<0) {
  52431. _TIFFfree(buf); TIFFClose(tif);
  52432. throw CImgIOException(_cimg_instance
  52433. "load_tiff(): Invalid tile in file '%s'.",
  52434. cimg_instance,
  52435. TIFFFileName(tif));
  52436. }
  52437. const t *ptr = buf;
  52438. for (unsigned int rr = row; rr<std::min((unsigned int)(row + th),(unsigned int)ny); ++rr)
  52439. for (unsigned int cc = col; cc<std::min((unsigned int)(col + tw),(unsigned int)nx); ++cc)
  52440. for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
  52441. (*this)(cc,rr,vv) = (T)(ptr[(rr - row)*th*samplesperpixel + (cc - col)*samplesperpixel + vv]);
  52442. }
  52443. _TIFFfree(buf);
  52444. }
  52445. }
  52446. template<typename t>
  52447. void _load_tiff_tiled_separate(TIFF *const tif, const cimg_uint16 samplesperpixel,
  52448. const cimg_uint32 nx, const cimg_uint32 ny,
  52449. const cimg_uint32 tw, const cimg_uint32 th) {
  52450. t *const buf = (t*)_TIFFmalloc(TIFFTileSize(tif));
  52451. if (buf) {
  52452. for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
  52453. for (unsigned int row = 0; row<ny; row+=th)
  52454. for (unsigned int col = 0; col<nx; col+=tw) {
  52455. if (TIFFReadTile(tif,buf,col,row,0,vv)<0) {
  52456. _TIFFfree(buf); TIFFClose(tif);
  52457. throw CImgIOException(_cimg_instance
  52458. "load_tiff(): Invalid tile in file '%s'.",
  52459. cimg_instance,
  52460. TIFFFileName(tif));
  52461. }
  52462. const t *ptr = buf;
  52463. for (unsigned int rr = row; rr<std::min((unsigned int)(row + th),(unsigned int)ny); ++rr)
  52464. for (unsigned int cc = col; cc<std::min((unsigned int)(col + tw),(unsigned int)nx); ++cc)
  52465. (*this)(cc,rr,vv) = (T)*(ptr++);
  52466. }
  52467. _TIFFfree(buf);
  52468. }
  52469. }
  52470. template<typename t>
  52471. void _load_tiff_contig(TIFF *const tif, const cimg_uint16 samplesperpixel,
  52472. const cimg_uint32 nx, const cimg_uint32 ny) {
  52473. t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
  52474. if (buf) {
  52475. cimg_uint32 row, rowsperstrip = (cimg_uint32)-1;
  52476. TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
  52477. for (row = 0; row<ny; row+= rowsperstrip) {
  52478. cimg_uint32 nrow = (row + rowsperstrip>ny?ny - row:rowsperstrip);
  52479. tstrip_t strip = TIFFComputeStrip(tif, row, 0);
  52480. if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
  52481. _TIFFfree(buf); TIFFClose(tif);
  52482. throw CImgIOException(_cimg_instance
  52483. "load_tiff(): Invalid strip in file '%s'.",
  52484. cimg_instance,
  52485. TIFFFileName(tif));
  52486. }
  52487. const t *ptr = buf;
  52488. for (unsigned int rr = 0; rr<nrow; ++rr)
  52489. for (unsigned int cc = 0; cc<nx; ++cc)
  52490. for (unsigned int vv = 0; vv<samplesperpixel; ++vv) (*this)(cc,row + rr,vv) = (T)*(ptr++);
  52491. }
  52492. _TIFFfree(buf);
  52493. }
  52494. }
  52495. template<typename t>
  52496. void _load_tiff_separate(TIFF *const tif, const cimg_uint16 samplesperpixel,
  52497. const cimg_uint32 nx, const cimg_uint32 ny) {
  52498. t *buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
  52499. if (buf) {
  52500. cimg_uint32 row, rowsperstrip = (cimg_uint32)-1;
  52501. TIFFGetField(tif,TIFFTAG_ROWSPERSTRIP,&rowsperstrip);
  52502. for (unsigned int vv = 0; vv<samplesperpixel; ++vv)
  52503. for (row = 0; row<ny; row+= rowsperstrip) {
  52504. cimg_uint32 nrow = (row + rowsperstrip>ny?ny - row:rowsperstrip);
  52505. tstrip_t strip = TIFFComputeStrip(tif, row, vv);
  52506. if ((TIFFReadEncodedStrip(tif,strip,buf,-1))<0) {
  52507. _TIFFfree(buf); TIFFClose(tif);
  52508. throw CImgIOException(_cimg_instance
  52509. "load_tiff(): Invalid strip in file '%s'.",
  52510. cimg_instance,
  52511. TIFFFileName(tif));
  52512. }
  52513. const t *ptr = buf;
  52514. for (unsigned int rr = 0;rr<nrow; ++rr)
  52515. for (unsigned int cc = 0; cc<nx; ++cc)
  52516. (*this)(cc,row + rr,vv) = (T)*(ptr++);
  52517. }
  52518. _TIFFfree(buf);
  52519. }
  52520. }
  52521. CImg<T>& _load_tiff(TIFF *const tif, const unsigned int directory, unsigned int *const bits_per_value,
  52522. float *const voxel_size, CImg<charT> *const description) {
  52523. if (!TIFFSetDirectory(tif,directory)) return assign();
  52524. cimg_uint16 samplesperpixel = 1, bitspersample = 8, photo = 0;
  52525. cimg_uint16 sampleformat = 1;
  52526. cimg_uint32 nx = 1, ny = 1;
  52527. const char *const filename = TIFFFileName(tif);
  52528. const bool is_spp = (bool)TIFFGetField(tif,TIFFTAG_SAMPLESPERPIXEL,&samplesperpixel);
  52529. TIFFGetField(tif,TIFFTAG_IMAGEWIDTH,&nx);
  52530. TIFFGetField(tif,TIFFTAG_IMAGELENGTH,&ny);
  52531. TIFFGetField(tif, TIFFTAG_SAMPLEFORMAT, &sampleformat);
  52532. TIFFGetFieldDefaulted(tif,TIFFTAG_BITSPERSAMPLE,&bitspersample);
  52533. TIFFGetField(tif,TIFFTAG_PHOTOMETRIC,&photo);
  52534. if (bits_per_value) *bits_per_value = (unsigned int)bitspersample;
  52535. if (voxel_size) {
  52536. const char *s_description = 0;
  52537. float vx = 0, vy = 0, vz = 0;
  52538. if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description) {
  52539. const char *s_desc = std::strstr(s_description,"VX=");
  52540. if (s_desc && cimg_sscanf(s_desc,"VX=%f VY=%f VZ=%f",&vx,&vy,&vz)==3) { // CImg format
  52541. voxel_size[0] = vx; voxel_size[1] = vy; voxel_size[2] = vz;
  52542. }
  52543. s_desc = std::strstr(s_description,"spacing=");
  52544. if (s_desc && cimg_sscanf(s_desc,"spacing=%f",&vz)==1) { // Fiji format
  52545. voxel_size[2] = vz;
  52546. }
  52547. }
  52548. TIFFGetField(tif,TIFFTAG_XRESOLUTION,voxel_size);
  52549. TIFFGetField(tif,TIFFTAG_YRESOLUTION,voxel_size + 1);
  52550. voxel_size[0] = 1.f/voxel_size[0];
  52551. voxel_size[1] = 1.f/voxel_size[1];
  52552. }
  52553. if (description) {
  52554. const char *s_description = 0;
  52555. if (TIFFGetField(tif,TIFFTAG_IMAGEDESCRIPTION,&s_description) && s_description)
  52556. CImg<charT>::string(s_description).move_to(*description);
  52557. }
  52558. const unsigned int spectrum = !is_spp || photo>=3?(photo>1?3:1):samplesperpixel;
  52559. assign(nx,ny,1,spectrum);
  52560. if ((photo>=3 && sampleformat==1 &&
  52561. (bitspersample==4 || bitspersample==8) &&
  52562. (samplesperpixel==1 || samplesperpixel==3 || samplesperpixel==4)) ||
  52563. (bitspersample==1 && samplesperpixel==1)) {
  52564. // Special case for unsigned color images.
  52565. cimg_uint32 *const raster = (cimg_uint32*)_TIFFmalloc(nx*ny*sizeof(cimg_uint32));
  52566. if (!raster) {
  52567. _TIFFfree(raster); TIFFClose(tif);
  52568. throw CImgException(_cimg_instance
  52569. "load_tiff(): Failed to allocate memory (%s) for file '%s'.",
  52570. cimg_instance,
  52571. cimg::strbuffersize(nx*ny*sizeof(cimg_uint32)),filename);
  52572. }
  52573. TIFFReadRGBAImage(tif,nx,ny,raster,0);
  52574. switch (spectrum) {
  52575. case 1 :
  52576. cimg_forXY(*this,x,y)
  52577. (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 -y) + x]);
  52578. break;
  52579. case 3 :
  52580. cimg_forXY(*this,x,y) {
  52581. (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 - y) + x]);
  52582. (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 - y) + x]);
  52583. (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 - y) + x]);
  52584. }
  52585. break;
  52586. case 4 :
  52587. cimg_forXY(*this,x,y) {
  52588. (*this)(x,y,0) = (T)(float)TIFFGetR(raster[nx*(ny - 1 - y) + x]);
  52589. (*this)(x,y,1) = (T)(float)TIFFGetG(raster[nx*(ny - 1 - y) + x]);
  52590. (*this)(x,y,2) = (T)(float)TIFFGetB(raster[nx*(ny - 1 - y) + x]);
  52591. (*this)(x,y,3) = (T)(float)TIFFGetA(raster[nx*(ny - 1 - y) + x]);
  52592. }
  52593. break;
  52594. }
  52595. _TIFFfree(raster);
  52596. } else { // Other cases
  52597. cimg_uint16 config;
  52598. TIFFGetField(tif,TIFFTAG_PLANARCONFIG,&config);
  52599. if (TIFFIsTiled(tif)) {
  52600. cimg_uint32 tw = 1, th = 1;
  52601. TIFFGetField(tif,TIFFTAG_TILEWIDTH,&tw);
  52602. TIFFGetField(tif,TIFFTAG_TILELENGTH,&th);
  52603. if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
  52604. case 8 :
  52605. if (sampleformat==SAMPLEFORMAT_UINT)
  52606. _load_tiff_tiled_contig<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
  52607. else _load_tiff_tiled_contig<signed char>(tif,samplesperpixel,nx,ny,tw,th);
  52608. break;
  52609. case 16 :
  52610. if (sampleformat==SAMPLEFORMAT_UINT)
  52611. _load_tiff_tiled_contig<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
  52612. else _load_tiff_tiled_contig<short>(tif,samplesperpixel,nx,ny,tw,th);
  52613. break;
  52614. case 32 :
  52615. if (sampleformat==SAMPLEFORMAT_UINT)
  52616. _load_tiff_tiled_contig<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
  52617. else if (sampleformat==SAMPLEFORMAT_INT)
  52618. _load_tiff_tiled_contig<int>(tif,samplesperpixel,nx,ny,tw,th);
  52619. else _load_tiff_tiled_contig<float>(tif,samplesperpixel,nx,ny,tw,th);
  52620. break;
  52621. case 64 :
  52622. if (sampleformat==SAMPLEFORMAT_UINT)
  52623. _load_tiff_tiled_contig<uint64T>(tif,samplesperpixel,nx,ny,tw,th);
  52624. else if (sampleformat==SAMPLEFORMAT_INT)
  52625. _load_tiff_tiled_contig<int64T>(tif,samplesperpixel,nx,ny,tw,th);
  52626. else _load_tiff_tiled_contig<double>(tif,samplesperpixel,nx,ny,tw,th);
  52627. break;
  52628. } else switch (bitspersample) {
  52629. case 8 :
  52630. if (sampleformat==SAMPLEFORMAT_UINT)
  52631. _load_tiff_tiled_separate<unsigned char>(tif,samplesperpixel,nx,ny,tw,th);
  52632. else _load_tiff_tiled_separate<signed char>(tif,samplesperpixel,nx,ny,tw,th);
  52633. break;
  52634. case 16 :
  52635. if (sampleformat==SAMPLEFORMAT_UINT)
  52636. _load_tiff_tiled_separate<unsigned short>(tif,samplesperpixel,nx,ny,tw,th);
  52637. else _load_tiff_tiled_separate<short>(tif,samplesperpixel,nx,ny,tw,th);
  52638. break;
  52639. case 32 :
  52640. if (sampleformat==SAMPLEFORMAT_UINT)
  52641. _load_tiff_tiled_separate<unsigned int>(tif,samplesperpixel,nx,ny,tw,th);
  52642. else if (sampleformat==SAMPLEFORMAT_INT)
  52643. _load_tiff_tiled_separate<int>(tif,samplesperpixel,nx,ny,tw,th);
  52644. else _load_tiff_tiled_separate<float>(tif,samplesperpixel,nx,ny,tw,th);
  52645. break;
  52646. case 64 :
  52647. if (sampleformat==SAMPLEFORMAT_UINT)
  52648. _load_tiff_tiled_separate<uint64T>(tif,samplesperpixel,nx,ny,tw,th);
  52649. else if (sampleformat==SAMPLEFORMAT_INT)
  52650. _load_tiff_tiled_separate<int64T>(tif,samplesperpixel,nx,ny,tw,th);
  52651. else _load_tiff_tiled_separate<double>(tif,samplesperpixel,nx,ny,tw,th);
  52652. break;
  52653. }
  52654. } else {
  52655. if (config==PLANARCONFIG_CONTIG) switch (bitspersample) {
  52656. case 8 :
  52657. if (sampleformat==SAMPLEFORMAT_UINT)
  52658. _load_tiff_contig<unsigned char>(tif,samplesperpixel,nx,ny);
  52659. else _load_tiff_contig<signed char>(tif,samplesperpixel,nx,ny);
  52660. break;
  52661. case 16 :
  52662. if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned short>(tif,samplesperpixel,nx,ny);
  52663. else _load_tiff_contig<short>(tif,samplesperpixel,nx,ny);
  52664. break;
  52665. case 32 :
  52666. if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<unsigned int>(tif,samplesperpixel,nx,ny);
  52667. else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig<int>(tif,samplesperpixel,nx,ny);
  52668. else _load_tiff_contig<float>(tif,samplesperpixel,nx,ny);
  52669. break;
  52670. case 64 :
  52671. if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_contig<uint64T>(tif,samplesperpixel,nx,ny);
  52672. else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_contig<int64T>(tif,samplesperpixel,nx,ny);
  52673. else _load_tiff_contig<double>(tif,samplesperpixel,nx,ny);
  52674. break;
  52675. } else switch (bitspersample) {
  52676. case 8 :
  52677. if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned char>(tif,samplesperpixel,nx,ny);
  52678. else _load_tiff_separate<signed char>(tif,samplesperpixel,nx,ny);
  52679. break;
  52680. case 16 :
  52681. if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned short>(tif,samplesperpixel,nx,ny);
  52682. else _load_tiff_separate<short>(tif,samplesperpixel,nx,ny);
  52683. break;
  52684. case 32 :
  52685. if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<unsigned int>(tif,samplesperpixel,nx,ny);
  52686. else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate<int>(tif,samplesperpixel,nx,ny);
  52687. else _load_tiff_separate<float>(tif,samplesperpixel,nx,ny);
  52688. break;
  52689. case 64 :
  52690. if (sampleformat==SAMPLEFORMAT_UINT) _load_tiff_separate<uint64T>(tif,samplesperpixel,nx,ny);
  52691. else if (sampleformat==SAMPLEFORMAT_INT) _load_tiff_separate<int64T>(tif,samplesperpixel,nx,ny);
  52692. else _load_tiff_separate<double>(tif,samplesperpixel,nx,ny);
  52693. break;
  52694. }
  52695. }
  52696. }
  52697. return *this;
  52698. }
  52699. #endif
  52700. //! Load image from a MINC2 file.
  52701. /**
  52702. \param filename Filename, as a C-string.
  52703. **/
  52704. // (Original code by Haz-Edine Assemlal).
  52705. CImg<T>& load_minc2(const char *const filename) {
  52706. if (!filename)
  52707. throw CImgArgumentException(_cimg_instance
  52708. "load_minc2(): Specified filename is (null).",
  52709. cimg_instance);
  52710. #ifndef cimg_use_minc2
  52711. return load_other(filename);
  52712. #else
  52713. minc::minc_1_reader rdr;
  52714. rdr.open(filename);
  52715. assign(rdr.ndim(1)?rdr.ndim(1):1,
  52716. rdr.ndim(2)?rdr.ndim(2):1,
  52717. rdr.ndim(3)?rdr.ndim(3):1,
  52718. rdr.ndim(4)?rdr.ndim(4):1);
  52719. if (pixel_type()==cimg::type<unsigned char>::string())
  52720. rdr.setup_read_byte();
  52721. else if (pixel_type()==cimg::type<int>::string())
  52722. rdr.setup_read_int();
  52723. else if (pixel_type()==cimg::type<double>::string())
  52724. rdr.setup_read_double();
  52725. else
  52726. rdr.setup_read_float();
  52727. minc::load_standard_volume(rdr,this->_data);
  52728. return *this;
  52729. #endif
  52730. }
  52731. //! Load image from a MINC2 file \newinstance.
  52732. static CImg<T> get_load_minc2(const char *const filename) {
  52733. return CImg<T>().load_analyze(filename);
  52734. }
  52735. //! Load image from an ANALYZE7.5/NIFTI file.
  52736. /**
  52737. \param filename Filename, as a C-string.
  52738. \param[out] voxel_size Pointer to the three voxel sizes read from the file.
  52739. **/
  52740. CImg<T>& load_analyze(const char *const filename, float *const voxel_size=0) {
  52741. return _load_analyze(0,filename,voxel_size);
  52742. }
  52743. //! Load image from an ANALYZE7.5/NIFTI file \newinstance.
  52744. static CImg<T> get_load_analyze(const char *const filename, float *const voxel_size=0) {
  52745. return CImg<T>().load_analyze(filename,voxel_size);
  52746. }
  52747. //! Load image from an ANALYZE7.5/NIFTI file \overloading.
  52748. CImg<T>& load_analyze(std::FILE *const file, float *const voxel_size=0) {
  52749. return _load_analyze(file,0,voxel_size);
  52750. }
  52751. //! Load image from an ANALYZE7.5/NIFTI file \newinstance.
  52752. static CImg<T> get_load_analyze(std::FILE *const file, float *const voxel_size=0) {
  52753. return CImg<T>().load_analyze(file,voxel_size);
  52754. }
  52755. CImg<T>& _load_analyze(std::FILE *const file, const char *const filename, float *const voxel_size=0) {
  52756. if (!file && !filename)
  52757. throw CImgArgumentException(_cimg_instance
  52758. "load_analyze(): Specified filename is (null).",
  52759. cimg_instance);
  52760. std::FILE *nfile_header = 0, *nfile = 0;
  52761. if (!file) {
  52762. CImg<charT> body(1024);
  52763. const char *const ext = cimg::split_filename(filename,body);
  52764. const unsigned int len = (unsigned int)std::strlen(body);
  52765. if (!cimg::strcasecmp(ext,"hdr")) { // File is an Analyze header file
  52766. nfile_header = cimg::fopen(filename,"rb");
  52767. cimg_snprintf(body._data + len,body._width - len,".img");
  52768. nfile = cimg::fopen(body,"rb");
  52769. } else if (!cimg::strcasecmp(ext,"img")) { // File is an Analyze data file
  52770. nfile = cimg::fopen(filename,"rb");
  52771. cimg_snprintf(body._data + len,body._width - len,".hdr");
  52772. nfile_header = cimg::fopen(body,"rb");
  52773. } else nfile_header = nfile = cimg::fopen(filename,"rb"); // File is a Niftii file
  52774. } else nfile_header = nfile = file; // File is a Niftii file
  52775. if (!nfile || !nfile_header)
  52776. throw CImgIOException(_cimg_instance
  52777. "load_analyze(): Invalid Analyze7.5 or NIFTI header in file '%s'.",
  52778. cimg_instance,
  52779. filename?filename:"(FILE*)");
  52780. // Read header.
  52781. bool endian = false;
  52782. unsigned int header_size;
  52783. cimg::fread(&header_size,1,nfile_header);
  52784. if (!header_size)
  52785. throw CImgIOException(_cimg_instance
  52786. "load_analyze(): Invalid zero-size header in file '%s'.",
  52787. cimg_instance,
  52788. filename?filename:"(FILE*)");
  52789. if (header_size>=4096) { endian = true; cimg::invert_endianness(header_size); }
  52790. unsigned char *const header = new unsigned char[header_size];
  52791. cimg::fread(header + 4,header_size - 4,nfile_header);
  52792. if (!file && nfile_header!=nfile) cimg::fclose(nfile_header);
  52793. if (endian) {
  52794. cimg::invert_endianness((short*)(header + 40),5);
  52795. cimg::invert_endianness((short*)(header + 70),1);
  52796. cimg::invert_endianness((short*)(header + 72),1);
  52797. cimg::invert_endianness((float*)(header + 76),4);
  52798. cimg::invert_endianness((float*)(header + 108),1);
  52799. cimg::invert_endianness((float*)(header + 112),1);
  52800. }
  52801. if (nfile_header==nfile) {
  52802. const unsigned int vox_offset = (unsigned int)*(float*)(header + 108);
  52803. std::fseek(nfile,vox_offset,SEEK_SET);
  52804. }
  52805. unsigned short *dim = (unsigned short*)(header + 40), dimx = 1, dimy = 1, dimz = 1, dimv = 1;
  52806. if (!dim[0])
  52807. cimg::warn(_cimg_instance
  52808. "load_analyze(): File '%s' defines an image with zero dimensions.",
  52809. cimg_instance,
  52810. filename?filename:"(FILE*)");
  52811. if (dim[0]>4)
  52812. cimg::warn(_cimg_instance
  52813. "load_analyze(): File '%s' defines an image with %u dimensions, reading only the 4 first.",
  52814. cimg_instance,
  52815. filename?filename:"(FILE*)",dim[0]);
  52816. if (dim[0]>=1) dimx = dim[1];
  52817. if (dim[0]>=2) dimy = dim[2];
  52818. if (dim[0]>=3) dimz = dim[3];
  52819. if (dim[0]>=4) dimv = dim[4];
  52820. float scalefactor = *(float*)(header + 112); if (scalefactor==0) scalefactor = 1;
  52821. const unsigned short datatype = *(unsigned short*)(header + 70);
  52822. if (voxel_size) {
  52823. const float *vsize = (float*)(header + 76);
  52824. voxel_size[0] = vsize[1]; voxel_size[1] = vsize[2]; voxel_size[2] = vsize[3];
  52825. }
  52826. delete[] header;
  52827. // Read pixel data.
  52828. assign(dimx,dimy,dimz,dimv);
  52829. const size_t pdim = (size_t)dimx*dimy*dimz*dimv;
  52830. switch (datatype) {
  52831. case 2 : {
  52832. unsigned char *const buffer = new unsigned char[pdim];
  52833. cimg::fread(buffer,pdim,nfile);
  52834. cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
  52835. delete[] buffer;
  52836. } break;
  52837. case 4 : {
  52838. short *const buffer = new short[pdim];
  52839. cimg::fread(buffer,pdim,nfile);
  52840. if (endian) cimg::invert_endianness(buffer,pdim);
  52841. cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
  52842. delete[] buffer;
  52843. } break;
  52844. case 8 : {
  52845. int *const buffer = new int[pdim];
  52846. cimg::fread(buffer,pdim,nfile);
  52847. if (endian) cimg::invert_endianness(buffer,pdim);
  52848. cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
  52849. delete[] buffer;
  52850. } break;
  52851. case 16 : {
  52852. float *const buffer = new float[pdim];
  52853. cimg::fread(buffer,pdim,nfile);
  52854. if (endian) cimg::invert_endianness(buffer,pdim);
  52855. cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
  52856. delete[] buffer;
  52857. } break;
  52858. case 64 : {
  52859. double *const buffer = new double[pdim];
  52860. cimg::fread(buffer,pdim,nfile);
  52861. if (endian) cimg::invert_endianness(buffer,pdim);
  52862. cimg_foroff(*this,off) _data[off] = (T)(buffer[off]*scalefactor);
  52863. delete[] buffer;
  52864. } break;
  52865. default :
  52866. if (!file) cimg::fclose(nfile);
  52867. throw CImgIOException(_cimg_instance
  52868. "load_analyze(): Unable to load datatype %d in file '%s'",
  52869. cimg_instance,
  52870. datatype,filename?filename:"(FILE*)");
  52871. }
  52872. if (!file) cimg::fclose(nfile);
  52873. return *this;
  52874. }
  52875. //! Load image from a .cimg[z] file.
  52876. /**
  52877. \param filename Filename, as a C-string.
  52878. \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  52879. \param align Appending alignment.
  52880. **/
  52881. CImg<T>& load_cimg(const char *const filename, const char axis='z', const float align=0) {
  52882. CImgList<T> list;
  52883. list.load_cimg(filename);
  52884. if (list._width==1) return list[0].move_to(*this);
  52885. return assign(list.get_append(axis,align));
  52886. }
  52887. //! Load image from a .cimg[z] file \newinstance
  52888. static CImg<T> get_load_cimg(const char *const filename, const char axis='z', const float align=0) {
  52889. return CImg<T>().load_cimg(filename,axis,align);
  52890. }
  52891. //! Load image from a .cimg[z] file \overloading.
  52892. CImg<T>& load_cimg(std::FILE *const file, const char axis='z', const float align=0) {
  52893. CImgList<T> list;
  52894. list.load_cimg(file);
  52895. if (list._width==1) return list[0].move_to(*this);
  52896. return assign(list.get_append(axis,align));
  52897. }
  52898. //! Load image from a .cimg[z] file \newinstance
  52899. static CImg<T> get_load_cimg(std::FILE *const file, const char axis='z', const float align=0) {
  52900. return CImg<T>().load_cimg(file,axis,align);
  52901. }
  52902. //! Load sub-images of a .cimg file.
  52903. /**
  52904. \param filename Filename, as a C-string.
  52905. \param n0 Starting frame.
  52906. \param n1 Ending frame (~0U for max).
  52907. \param x0 X-coordinate of the starting sub-image vertex.
  52908. \param y0 Y-coordinate of the starting sub-image vertex.
  52909. \param z0 Z-coordinate of the starting sub-image vertex.
  52910. \param c0 C-coordinate of the starting sub-image vertex.
  52911. \param x1 X-coordinate of the ending sub-image vertex (~0U for max).
  52912. \param y1 Y-coordinate of the ending sub-image vertex (~0U for max).
  52913. \param z1 Z-coordinate of the ending sub-image vertex (~0U for max).
  52914. \param c1 C-coordinate of the ending sub-image vertex (~0U for max).
  52915. \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  52916. \param align Appending alignment.
  52917. **/
  52918. CImg<T>& load_cimg(const char *const filename,
  52919. const unsigned int n0, const unsigned int n1,
  52920. const unsigned int x0, const unsigned int y0,
  52921. const unsigned int z0, const unsigned int c0,
  52922. const unsigned int x1, const unsigned int y1,
  52923. const unsigned int z1, const unsigned int c1,
  52924. const char axis='z', const float align=0) {
  52925. CImgList<T> list;
  52926. list.load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
  52927. if (list._width==1) return list[0].move_to(*this);
  52928. return assign(list.get_append(axis,align));
  52929. }
  52930. //! Load sub-images of a .cimg file \newinstance.
  52931. static CImg<T> get_load_cimg(const char *const filename,
  52932. const unsigned int n0, const unsigned int n1,
  52933. const unsigned int x0, const unsigned int y0,
  52934. const unsigned int z0, const unsigned int c0,
  52935. const unsigned int x1, const unsigned int y1,
  52936. const unsigned int z1, const unsigned int c1,
  52937. const char axis='z', const float align=0) {
  52938. return CImg<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
  52939. }
  52940. //! Load sub-images of a .cimg file \overloading.
  52941. CImg<T>& load_cimg(std::FILE *const file,
  52942. const unsigned int n0, const unsigned int n1,
  52943. const unsigned int x0, const unsigned int y0,
  52944. const unsigned int z0, const unsigned int c0,
  52945. const unsigned int x1, const unsigned int y1,
  52946. const unsigned int z1, const unsigned int c1,
  52947. const char axis='z', const float align=0) {
  52948. CImgList<T> list;
  52949. list.load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
  52950. if (list._width==1) return list[0].move_to(*this);
  52951. return assign(list.get_append(axis,align));
  52952. }
  52953. //! Load sub-images of a .cimg file \newinstance.
  52954. static CImg<T> get_load_cimg(std::FILE *const file,
  52955. const unsigned int n0, const unsigned int n1,
  52956. const unsigned int x0, const unsigned int y0,
  52957. const unsigned int z0, const unsigned int c0,
  52958. const unsigned int x1, const unsigned int y1,
  52959. const unsigned int z1, const unsigned int c1,
  52960. const char axis='z', const float align=0) {
  52961. return CImg<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1,axis,align);
  52962. }
  52963. //! Load image from an INRIMAGE-4 file.
  52964. /**
  52965. \param filename Filename, as a C-string.
  52966. \param[out] voxel_size Pointer to the three voxel sizes read from the file.
  52967. **/
  52968. CImg<T>& load_inr(const char *const filename, float *const voxel_size=0) {
  52969. return _load_inr(0,filename,voxel_size);
  52970. }
  52971. //! Load image from an INRIMAGE-4 file \newinstance.
  52972. static CImg<T> get_load_inr(const char *const filename, float *const voxel_size=0) {
  52973. return CImg<T>().load_inr(filename,voxel_size);
  52974. }
  52975. //! Load image from an INRIMAGE-4 file \overloading.
  52976. CImg<T>& load_inr(std::FILE *const file, float *const voxel_size=0) {
  52977. return _load_inr(file,0,voxel_size);
  52978. }
  52979. //! Load image from an INRIMAGE-4 file \newinstance.
  52980. static CImg<T> get_load_inr(std::FILE *const file, float *voxel_size=0) {
  52981. return CImg<T>().load_inr(file,voxel_size);
  52982. }
  52983. static void _load_inr_header(std::FILE *file, int out[8], float *const voxel_size) {
  52984. CImg<charT> item(1024), tmp1(64), tmp2(64);
  52985. *item = *tmp1 = *tmp2 = 0;
  52986. out[0] = std::fscanf(file,"%63s",item._data);
  52987. out[0] = out[1] = out[2] = out[3] = out[5] = 1; out[4] = out[6] = out[7] = -1;
  52988. if (cimg::strncasecmp(item,"#INRIMAGE-4#{",13)!=0)
  52989. throw CImgIOException("CImg<%s>::load_inr(): INRIMAGE-4 header not found.",
  52990. pixel_type());
  52991. while (std::fscanf(file," %63[^\n]%*c",item._data)!=EOF && std::strncmp(item,"##}",3)) {
  52992. cimg_sscanf(item," XDIM%*[^0-9]%d",out);
  52993. cimg_sscanf(item," YDIM%*[^0-9]%d",out + 1);
  52994. cimg_sscanf(item," ZDIM%*[^0-9]%d",out + 2);
  52995. cimg_sscanf(item," VDIM%*[^0-9]%d",out + 3);
  52996. cimg_sscanf(item," PIXSIZE%*[^0-9]%d",out + 6);
  52997. if (voxel_size) {
  52998. cimg_sscanf(item," VX%*[^0-9.+-]%f",voxel_size);
  52999. cimg_sscanf(item," VY%*[^0-9.+-]%f",voxel_size + 1);
  53000. cimg_sscanf(item," VZ%*[^0-9.+-]%f",voxel_size + 2);
  53001. }
  53002. if (cimg_sscanf(item," CPU%*[ =]%s",tmp1._data)) out[7] = cimg::strncasecmp(tmp1,"sun",3)?0:1;
  53003. switch (cimg_sscanf(item," TYPE%*[ =]%s %s",tmp1._data,tmp2._data)) {
  53004. case 0 : break;
  53005. case 2 :
  53006. out[5] = cimg::strncasecmp(tmp1,"unsigned",8)?1:0;
  53007. std::strncpy(tmp1,tmp2,tmp1._width - 1); // Fallthrough
  53008. case 1 :
  53009. if (!cimg::strncasecmp(tmp1,"int",3) || !cimg::strncasecmp(tmp1,"fixed",5)) out[4] = 0;
  53010. if (!cimg::strncasecmp(tmp1,"float",5) || !cimg::strncasecmp(tmp1,"double",6)) out[4] = 1;
  53011. if (!cimg::strncasecmp(tmp1,"packed",6)) out[4] = 2;
  53012. if (out[4]>=0) break; // Fallthrough
  53013. default :
  53014. throw CImgIOException("CImg<%s>::load_inr(): Invalid pixel type '%s' defined in header.",
  53015. pixel_type(),
  53016. tmp2._data);
  53017. }
  53018. }
  53019. if (out[0]<0 || out[1]<0 || out[2]<0 || out[3]<0)
  53020. throw CImgIOException("CImg<%s>::load_inr(): Invalid dimensions (%d,%d,%d,%d) defined in header.",
  53021. pixel_type(),
  53022. out[0],out[1],out[2],out[3]);
  53023. if (out[4]<0 || out[5]<0)
  53024. throw CImgIOException("CImg<%s>::load_inr(): Incomplete pixel type defined in header.",
  53025. pixel_type());
  53026. if (out[6]<0)
  53027. throw CImgIOException("CImg<%s>::load_inr(): Incomplete PIXSIZE field defined in header.",
  53028. pixel_type());
  53029. if (out[7]<0)
  53030. throw CImgIOException("CImg<%s>::load_inr(): Big/Little Endian coding type undefined in header.",
  53031. pixel_type());
  53032. }
  53033. CImg<T>& _load_inr(std::FILE *const file, const char *const filename, float *const voxel_size) {
  53034. #define _cimg_load_inr_case(Tf,sign,pixsize,Ts) \
  53035. if (!loaded && fopt[6]==pixsize && fopt[4]==Tf && fopt[5]==sign) { \
  53036. Ts *xval, *const val = new Ts[(size_t)fopt[0]*fopt[3]]; \
  53037. cimg_forYZ(*this,y,z) { \
  53038. cimg::fread(val,fopt[0]*fopt[3],nfile); \
  53039. if (fopt[7]!=endian) cimg::invert_endianness(val,fopt[0]*fopt[3]); \
  53040. xval = val; cimg_forX(*this,x) cimg_forC(*this,c) (*this)(x,y,z,c) = (T)*(xval++); \
  53041. } \
  53042. delete[] val; \
  53043. loaded = true; \
  53044. }
  53045. if (!file && !filename)
  53046. throw CImgArgumentException(_cimg_instance
  53047. "load_inr(): Specified filename is (null).",
  53048. cimg_instance);
  53049. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  53050. int fopt[8], endian = cimg::endianness()?1:0;
  53051. bool loaded = false;
  53052. if (voxel_size) voxel_size[0] = voxel_size[1] = voxel_size[2] = 1;
  53053. _load_inr_header(nfile,fopt,voxel_size);
  53054. assign(fopt[0],fopt[1],fopt[2],fopt[3]);
  53055. _cimg_load_inr_case(0,0,8,unsigned char);
  53056. _cimg_load_inr_case(0,1,8,char);
  53057. _cimg_load_inr_case(0,0,16,unsigned short);
  53058. _cimg_load_inr_case(0,1,16,short);
  53059. _cimg_load_inr_case(0,0,32,unsigned int);
  53060. _cimg_load_inr_case(0,1,32,int);
  53061. _cimg_load_inr_case(1,0,32,float);
  53062. _cimg_load_inr_case(1,1,32,float);
  53063. _cimg_load_inr_case(1,0,64,double);
  53064. _cimg_load_inr_case(1,1,64,double);
  53065. if (!loaded) {
  53066. if (!file) cimg::fclose(nfile);
  53067. throw CImgIOException(_cimg_instance
  53068. "load_inr(): Unknown pixel type defined in file '%s'.",
  53069. cimg_instance,
  53070. filename?filename:"(FILE*)");
  53071. }
  53072. if (!file) cimg::fclose(nfile);
  53073. return *this;
  53074. }
  53075. //! Load image from a EXR file.
  53076. /**
  53077. \param filename Filename, as a C-string.
  53078. **/
  53079. CImg<T>& load_exr(const char *const filename) {
  53080. if (!filename)
  53081. throw CImgArgumentException(_cimg_instance
  53082. "load_exr(): Specified filename is (null).",
  53083. cimg_instance);
  53084. #if defined(cimg_use_openexr)
  53085. Imf::RgbaInputFile file(filename);
  53086. Imath::Box2i dw = file.dataWindow();
  53087. const int
  53088. inwidth = dw.max.x - dw.min.x + 1,
  53089. inheight = dw.max.y - dw.min.y + 1;
  53090. Imf::Array2D<Imf::Rgba> pixels;
  53091. pixels.resizeErase(inheight,inwidth);
  53092. file.setFrameBuffer(&pixels[0][0] - dw.min.x - dw.min.y*inwidth, 1, inwidth);
  53093. file.readPixels(dw.min.y, dw.max.y);
  53094. assign(inwidth,inheight,1,4);
  53095. 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);
  53096. cimg_forXY(*this,x,y) {
  53097. *(ptr_r++) = (T)pixels[y][x].r;
  53098. *(ptr_g++) = (T)pixels[y][x].g;
  53099. *(ptr_b++) = (T)pixels[y][x].b;
  53100. *(ptr_a++) = (T)pixels[y][x].a;
  53101. }
  53102. return *this;
  53103. #elif defined(cimg_use_tinyexr)
  53104. float *res;
  53105. const char *err = 0;
  53106. int width = 0, height = 0;
  53107. const int ret = LoadEXR(&res,&width,&height,filename,&err);
  53108. if (ret) throw CImgIOException(_cimg_instance
  53109. "load_exr(): Unable to load EXR file '%s'.",
  53110. cimg_instance,filename);
  53111. CImg<floatT>(res,4,width,height,1,true).get_permute_axes("yzcx").move_to(*this);
  53112. std::free(res);
  53113. return *this;
  53114. #else
  53115. return load_other(filename);
  53116. #endif
  53117. }
  53118. //! Load image from a EXR file \newinstance.
  53119. static CImg<T> get_load_exr(const char *const filename) {
  53120. return CImg<T>().load_exr(filename);
  53121. }
  53122. //! Load image from a PANDORE-5 file.
  53123. /**
  53124. \param filename Filename, as a C-string.
  53125. **/
  53126. CImg<T>& load_pandore(const char *const filename) {
  53127. return _load_pandore(0,filename);
  53128. }
  53129. //! Load image from a PANDORE-5 file \newinstance.
  53130. static CImg<T> get_load_pandore(const char *const filename) {
  53131. return CImg<T>().load_pandore(filename);
  53132. }
  53133. //! Load image from a PANDORE-5 file \overloading.
  53134. CImg<T>& load_pandore(std::FILE *const file) {
  53135. return _load_pandore(file,0);
  53136. }
  53137. //! Load image from a PANDORE-5 file \newinstance.
  53138. static CImg<T> get_load_pandore(std::FILE *const file) {
  53139. return CImg<T>().load_pandore(file);
  53140. }
  53141. CImg<T>& _load_pandore(std::FILE *const file, const char *const filename) {
  53142. #define __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,ndim,stype) \
  53143. cimg::fread(dims,nbdim,nfile); \
  53144. if (endian) cimg::invert_endianness(dims,nbdim); \
  53145. if ((ulongT)nwidth*nheight*ndepth*ndim>fsiz) \
  53146. throw CImgIOException(_cimg_instance \
  53147. "load_pandore(): File size %lu for filename '%s' does not match "\
  53148. "encoded image dimensions (%d,%d,%d,%d).",\
  53149. cimg_instance,\
  53150. (long)fsiz,filename?filename:"(FILE*)",\
  53151. (int)nwidth,(int)nheight,(int)ndepth,(int)ndim); \
  53152. assign(nwidth,nheight,ndepth,ndim); \
  53153. const size_t siz = size(); \
  53154. stype *buffer = new stype[siz]; \
  53155. cimg::fread(buffer,siz,nfile); \
  53156. if (endian) cimg::invert_endianness(buffer,siz); \
  53157. T *ptrd = _data; \
  53158. cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++); \
  53159. buffer-=siz; \
  53160. delete[] buffer
  53161. #define _cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1,stype2,stype3,ltype) { \
  53162. if (sizeof(stype1)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype1); } \
  53163. else if (sizeof(stype2)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype2); } \
  53164. else if (sizeof(stype3)==ltype) { __cimg_load_pandore_case(nbdim,nwidth,nheight,ndepth,dim,stype3); } \
  53165. else throw CImgIOException(_cimg_instance \
  53166. "load_pandore(): Unknown pixel datatype in file '%s'.", \
  53167. cimg_instance, \
  53168. filename?filename:"(FILE*)"); }
  53169. if (!file && !filename)
  53170. throw CImgArgumentException(_cimg_instance
  53171. "load_pandore(): Specified filename is (null).",
  53172. cimg_instance);
  53173. const ulongT fsiz = file?(ulongT)cimg_max_buf_size:(ulongT)cimg::fsize(filename);
  53174. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  53175. CImg<charT> header(32);
  53176. cimg::fread(header._data,12,nfile);
  53177. if (cimg::strncasecmp("PANDORE",header,7)) {
  53178. if (!file) cimg::fclose(nfile);
  53179. throw CImgIOException(_cimg_instance
  53180. "load_pandore(): PANDORE header not found in file '%s'.",
  53181. cimg_instance,
  53182. filename?filename:"(FILE*)");
  53183. }
  53184. unsigned int imageid, dims[8] = {};
  53185. int ptbuf[4] = {};
  53186. cimg::fread(&imageid,1,nfile);
  53187. const bool endian = imageid>255;
  53188. if (endian) cimg::invert_endianness(imageid);
  53189. cimg::fread(header._data,20,nfile);
  53190. switch (imageid) {
  53191. case 2 : _cimg_load_pandore_case(2,dims[1],1,1,1,unsigned char,unsigned char,unsigned char,1); break;
  53192. case 3 : _cimg_load_pandore_case(2,dims[1],1,1,1,long,int,short,4); break;
  53193. case 4 : _cimg_load_pandore_case(2,dims[1],1,1,1,double,float,float,4); break;
  53194. case 5 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,unsigned char,unsigned char,unsigned char,1); break;
  53195. case 6 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,long,int,short,4); break;
  53196. case 7 : _cimg_load_pandore_case(3,dims[2],dims[1],1,1,double,float,float,4); break;
  53197. case 8 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,unsigned char,unsigned char,unsigned char,1); break;
  53198. case 9 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,long,int,short,4); break;
  53199. case 10 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],1,double,float,float,4); break;
  53200. case 11 : { // Region 1D
  53201. cimg::fread(dims,3,nfile);
  53202. if (endian) cimg::invert_endianness(dims,3);
  53203. assign(dims[1],1,1,1);
  53204. const unsigned siz = size();
  53205. if (dims[2]<256) {
  53206. unsigned char *buffer = new unsigned char[siz];
  53207. cimg::fread(buffer,siz,nfile);
  53208. T *ptrd = _data;
  53209. cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
  53210. buffer-=siz;
  53211. delete[] buffer;
  53212. } else {
  53213. if (dims[2]<65536) {
  53214. unsigned short *buffer = new unsigned short[siz];
  53215. cimg::fread(buffer,siz,nfile);
  53216. if (endian) cimg::invert_endianness(buffer,siz);
  53217. T *ptrd = _data;
  53218. cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
  53219. buffer-=siz;
  53220. delete[] buffer;
  53221. } else {
  53222. unsigned int *buffer = new unsigned int[siz];
  53223. cimg::fread(buffer,siz,nfile);
  53224. if (endian) cimg::invert_endianness(buffer,siz);
  53225. T *ptrd = _data;
  53226. cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
  53227. buffer-=siz;
  53228. delete[] buffer;
  53229. }
  53230. }
  53231. }
  53232. break;
  53233. case 12 : { // Region 2D
  53234. cimg::fread(dims,4,nfile);
  53235. if (endian) cimg::invert_endianness(dims,4);
  53236. assign(dims[2],dims[1],1,1);
  53237. const size_t siz = size();
  53238. if (dims[3]<256) {
  53239. unsigned char *buffer = new unsigned char[siz];
  53240. cimg::fread(buffer,siz,nfile);
  53241. T *ptrd = _data;
  53242. cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
  53243. buffer-=siz;
  53244. delete[] buffer;
  53245. } else {
  53246. if (dims[3]<65536) {
  53247. unsigned short *buffer = new unsigned short[siz];
  53248. cimg::fread(buffer,siz,nfile);
  53249. if (endian) cimg::invert_endianness(buffer,siz);
  53250. T *ptrd = _data;
  53251. cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
  53252. buffer-=siz;
  53253. delete[] buffer;
  53254. } else {
  53255. unsigned int *buffer = new unsigned int[siz];
  53256. cimg::fread(buffer,siz,nfile);
  53257. if (endian) cimg::invert_endianness(buffer,siz);
  53258. T *ptrd = _data;
  53259. cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
  53260. buffer-=siz;
  53261. delete[] buffer;
  53262. }
  53263. }
  53264. }
  53265. break;
  53266. case 13 : { // Region 3D
  53267. cimg::fread(dims,5,nfile);
  53268. if (endian) cimg::invert_endianness(dims,5);
  53269. assign(dims[3],dims[2],dims[1],1);
  53270. const size_t siz = size();
  53271. if (dims[4]<256) {
  53272. unsigned char *buffer = new unsigned char[siz];
  53273. cimg::fread(buffer,siz,nfile);
  53274. T *ptrd = _data;
  53275. cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
  53276. buffer-=siz;
  53277. delete[] buffer;
  53278. } else {
  53279. if (dims[4]<65536) {
  53280. unsigned short *buffer = new unsigned short[siz];
  53281. cimg::fread(buffer,siz,nfile);
  53282. if (endian) cimg::invert_endianness(buffer,siz);
  53283. T *ptrd = _data;
  53284. cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
  53285. buffer-=siz;
  53286. delete[] buffer;
  53287. } else {
  53288. unsigned int *buffer = new unsigned int[siz];
  53289. cimg::fread(buffer,siz,nfile);
  53290. if (endian) cimg::invert_endianness(buffer,siz);
  53291. T *ptrd = _data;
  53292. cimg_foroff(*this,off) *(ptrd++) = (T)*(buffer++);
  53293. buffer-=siz;
  53294. delete[] buffer;
  53295. }
  53296. }
  53297. }
  53298. break;
  53299. case 16 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,unsigned char,unsigned char,unsigned char,1); break;
  53300. case 17 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,long,int,short,4); break;
  53301. case 18 : _cimg_load_pandore_case(4,dims[2],dims[1],1,3,double,float,float,4); break;
  53302. case 19 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,unsigned char,unsigned char,unsigned char,1); break;
  53303. case 20 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,long,int,short,4); break;
  53304. case 21 : _cimg_load_pandore_case(5,dims[3],dims[2],dims[1],3,double,float,float,4); break;
  53305. case 22 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
  53306. case 23 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],long,int,short,4); break;
  53307. case 24 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
  53308. case 25 : _cimg_load_pandore_case(2,dims[1],1,1,dims[0],double,float,float,4); break;
  53309. case 26 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned char,unsigned char,unsigned char,1); break;
  53310. case 27 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],long,int,short,4); break;
  53311. case 28 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],unsigned long,unsigned int,unsigned short,4); break;
  53312. case 29 : _cimg_load_pandore_case(3,dims[2],dims[1],1,dims[0],double,float,float,4); break;
  53313. case 30 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned char,unsigned char,unsigned char,1);
  53314. break;
  53315. case 31 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],long,int,short,4); break;
  53316. case 32 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],unsigned long,unsigned int,unsigned short,4);
  53317. break;
  53318. case 33 : _cimg_load_pandore_case(4,dims[3],dims[2],dims[1],dims[0],double,float,float,4); break;
  53319. case 34 : { // Points 1D
  53320. cimg::fread(ptbuf,1,nfile);
  53321. if (endian) cimg::invert_endianness(ptbuf,1);
  53322. assign(1); (*this)(0) = (T)ptbuf[0];
  53323. } break;
  53324. case 35 : { // Points 2D
  53325. cimg::fread(ptbuf,2,nfile);
  53326. if (endian) cimg::invert_endianness(ptbuf,2);
  53327. assign(2); (*this)(0) = (T)ptbuf[1]; (*this)(1) = (T)ptbuf[0];
  53328. } break;
  53329. case 36 : { // Points 3D
  53330. cimg::fread(ptbuf,3,nfile);
  53331. if (endian) cimg::invert_endianness(ptbuf,3);
  53332. assign(3); (*this)(0) = (T)ptbuf[2]; (*this)(1) = (T)ptbuf[1]; (*this)(2) = (T)ptbuf[0];
  53333. } break;
  53334. default :
  53335. if (!file) cimg::fclose(nfile);
  53336. throw CImgIOException(_cimg_instance
  53337. "load_pandore(): Unable to load data with ID_type %u in file '%s'.",
  53338. cimg_instance,
  53339. imageid,filename?filename:"(FILE*)");
  53340. }
  53341. if (!file) cimg::fclose(nfile);
  53342. return *this;
  53343. }
  53344. //! Load image from a PAR-REC (Philips) file.
  53345. /**
  53346. \param filename Filename, as a C-string.
  53347. \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  53348. \param align Appending alignment.
  53349. **/
  53350. CImg<T>& load_parrec(const char *const filename, const char axis='c', const float align=0) {
  53351. CImgList<T> list;
  53352. list.load_parrec(filename);
  53353. if (list._width==1) return list[0].move_to(*this);
  53354. return assign(list.get_append(axis,align));
  53355. }
  53356. //! Load image from a PAR-REC (Philips) file \newinstance.
  53357. static CImg<T> get_load_parrec(const char *const filename, const char axis='c', const float align=0) {
  53358. return CImg<T>().load_parrec(filename,axis,align);
  53359. }
  53360. //! Load image from a raw binary file.
  53361. /**
  53362. \param filename Filename, as a C-string.
  53363. \param size_x Width of the image buffer.
  53364. \param size_y Height of the image buffer.
  53365. \param size_z Depth of the image buffer.
  53366. \param size_c Spectrum of the image buffer.
  53367. \param is_multiplexed Tells if the image values are multiplexed along the C-axis.
  53368. \param invert_endianness Tells if the endianness of the image buffer must be inverted.
  53369. \param offset Starting offset of the read in the specified file.
  53370. **/
  53371. CImg<T>& load_raw(const char *const filename,
  53372. const unsigned int size_x=0, const unsigned int size_y=1,
  53373. const unsigned int size_z=1, const unsigned int size_c=1,
  53374. const bool is_multiplexed=false, const bool invert_endianness=false,
  53375. const ulongT offset=0) {
  53376. return _load_raw(0,filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
  53377. }
  53378. //! Load image from a raw binary file \newinstance.
  53379. static CImg<T> get_load_raw(const char *const filename,
  53380. const unsigned int size_x=0, const unsigned int size_y=1,
  53381. const unsigned int size_z=1, const unsigned int size_c=1,
  53382. const bool is_multiplexed=false, const bool invert_endianness=false,
  53383. const ulongT offset=0) {
  53384. return CImg<T>().load_raw(filename,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
  53385. }
  53386. //! Load image from a raw binary file \overloading.
  53387. CImg<T>& load_raw(std::FILE *const file,
  53388. const unsigned int size_x=0, const unsigned int size_y=1,
  53389. const unsigned int size_z=1, const unsigned int size_c=1,
  53390. const bool is_multiplexed=false, const bool invert_endianness=false,
  53391. const ulongT offset=0) {
  53392. return _load_raw(file,0,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
  53393. }
  53394. //! Load image from a raw binary file \newinstance.
  53395. static CImg<T> get_load_raw(std::FILE *const file,
  53396. const unsigned int size_x=0, const unsigned int size_y=1,
  53397. const unsigned int size_z=1, const unsigned int size_c=1,
  53398. const bool is_multiplexed=false, const bool invert_endianness=false,
  53399. const ulongT offset=0) {
  53400. return CImg<T>().load_raw(file,size_x,size_y,size_z,size_c,is_multiplexed,invert_endianness,offset);
  53401. }
  53402. CImg<T>& _load_raw(std::FILE *const file, const char *const filename,
  53403. const unsigned int size_x, const unsigned int size_y,
  53404. const unsigned int size_z, const unsigned int size_c,
  53405. const bool is_multiplexed, const bool invert_endianness,
  53406. const ulongT offset) {
  53407. if (!file && !filename)
  53408. throw CImgArgumentException(_cimg_instance
  53409. "load_raw(): Specified filename is (null).",
  53410. cimg_instance);
  53411. if (cimg::is_directory(filename))
  53412. throw CImgArgumentException(_cimg_instance
  53413. "load_raw(): Specified filename '%s' is a directory.",
  53414. cimg_instance,filename);
  53415. const bool is_bool = pixel_type()==cimg::type<bool>::string();
  53416. ulongT siz = (ulongT)size_x*size_y*size_z*size_c;
  53417. unsigned int
  53418. _size_x = size_x,
  53419. _size_y = size_y,
  53420. _size_z = size_z,
  53421. _size_c = size_c;
  53422. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  53423. if (!siz) { // Retrieve file size
  53424. const longT fpos = cimg::ftell(nfile);
  53425. if (fpos<0) throw CImgArgumentException(_cimg_instance
  53426. "load_raw(): Cannot determine size of input file '%s'.",
  53427. cimg_instance,filename?filename:"(FILE*)");
  53428. cimg::fseek(nfile,0,SEEK_END);
  53429. siz = (ulongT)cimg::ftell(nfile);
  53430. if (!is_bool) { siz/=sizeof(T); _size_y = (unsigned int)siz; }
  53431. else _size_y = (unsigned int)(siz*8);
  53432. _size_x = _size_z = _size_c = 1;
  53433. cimg::fseek(nfile,fpos,SEEK_SET);
  53434. }
  53435. cimg::fseek(nfile,(longT)offset,SEEK_SET);
  53436. assign(_size_x,_size_y,_size_z,_size_c,0);
  53437. if (is_bool) { // Boolean data (bitwise)
  53438. unsigned char *const buf = new unsigned char[siz];
  53439. cimg::fread(buf,siz,nfile);
  53440. _uchar2bool(buf,siz,is_multiplexed);
  53441. delete[] buf;
  53442. } else { // Non-boolean data
  53443. if (siz && (!is_multiplexed || size_c==1)) { // Non-multiplexed
  53444. cimg::fread(_data,siz,nfile);
  53445. if (invert_endianness) cimg::invert_endianness(_data,siz);
  53446. } else if (siz) { // Multiplexed
  53447. CImg<T> buf(1,1,1,_size_c);
  53448. cimg_forXYZ(*this,x,y,z) {
  53449. cimg::fread(buf._data,_size_c,nfile);
  53450. if (invert_endianness) cimg::invert_endianness(buf._data,_size_c);
  53451. set_vector_at(buf,x,y,z);
  53452. }
  53453. }
  53454. }
  53455. if (!file) cimg::fclose(nfile);
  53456. return *this;
  53457. }
  53458. //! Load image sequence from a YUV file.
  53459. /**
  53460. \param filename Filename, as a C-string.
  53461. \param size_x Width of the frames.
  53462. \param size_y Height of the frames.
  53463. \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
  53464. \param first_frame Index of the first frame to read.
  53465. \param last_frame Index of the last frame to read.
  53466. \param step_frame Step value for frame reading.
  53467. \param yuv2rgb Tells if the YUV to RGB transform must be applied.
  53468. \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  53469. **/
  53470. CImg<T>& load_yuv(const char *const filename,
  53471. const unsigned int size_x, const unsigned int size_y=1,
  53472. const unsigned int chroma_subsampling=444,
  53473. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  53474. const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
  53475. return get_load_yuv(filename,size_x,size_y,chroma_subsampling,
  53476. first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this);
  53477. }
  53478. //! Load image sequence from a YUV file \newinstance.
  53479. static CImg<T> get_load_yuv(const char *const filename,
  53480. const unsigned int size_x, const unsigned int size_y=1,
  53481. const unsigned int chroma_subsampling=444,
  53482. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  53483. const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
  53484. return CImgList<T>().load_yuv(filename,size_x,size_y,chroma_subsampling,
  53485. first_frame,last_frame,step_frame,yuv2rgb).get_append(axis);
  53486. }
  53487. //! Load image sequence from a YUV file \overloading.
  53488. CImg<T>& load_yuv(std::FILE *const file,
  53489. const unsigned int size_x, const unsigned int size_y=1,
  53490. const unsigned int chroma_subsampling=444,
  53491. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  53492. const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
  53493. return get_load_yuv(file,size_x,size_y,chroma_subsampling,
  53494. first_frame,last_frame,step_frame,yuv2rgb,axis).move_to(*this);
  53495. }
  53496. //! Load image sequence from a YUV file \newinstance.
  53497. static CImg<T> get_load_yuv(std::FILE *const file,
  53498. const unsigned int size_x, const unsigned int size_y=1,
  53499. const unsigned int chroma_subsampling=444,
  53500. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  53501. const unsigned int step_frame=1, const bool yuv2rgb=true, const char axis='z') {
  53502. return CImgList<T>().load_yuv(file,size_x,size_y,chroma_subsampling,
  53503. first_frame,last_frame,step_frame,yuv2rgb).get_append(axis);
  53504. }
  53505. //! Load 3D object from a .OFF file.
  53506. /**
  53507. \param[out] primitives Primitives data of the 3D object.
  53508. \param[out] colors Colors data of the 3D object.
  53509. \param filename Filename, as a C-string.
  53510. **/
  53511. template<typename tf, typename tc>
  53512. CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) {
  53513. return _load_off(primitives,colors,0,filename);
  53514. }
  53515. //! Load 3D object from a .OFF file \newinstance.
  53516. template<typename tf, typename tc>
  53517. static CImg<T> get_load_off(CImgList<tf>& primitives, CImgList<tc>& colors, const char *const filename) {
  53518. return CImg<T>().load_off(primitives,colors,filename);
  53519. }
  53520. //! Load 3D object from a .OFF file \overloading.
  53521. template<typename tf, typename tc>
  53522. CImg<T>& load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) {
  53523. return _load_off(primitives,colors,file,0);
  53524. }
  53525. //! Load 3D object from a .OFF file \newinstance.
  53526. template<typename tf, typename tc>
  53527. static CImg<T> get_load_off(CImgList<tf>& primitives, CImgList<tc>& colors, std::FILE *const file) {
  53528. return CImg<T>().load_off(primitives,colors,file);
  53529. }
  53530. template<typename tf, typename tc>
  53531. CImg<T>& _load_off(CImgList<tf>& primitives, CImgList<tc>& colors,
  53532. std::FILE *const file, const char *const filename) {
  53533. if (!file && !filename)
  53534. throw CImgArgumentException(_cimg_instance
  53535. "load_off(): Specified filename is (null).",
  53536. cimg_instance);
  53537. std::FILE *const nfile = file?file:cimg::fopen(filename,"r");
  53538. unsigned int nb_points = 0, nb_primitives = 0, nb_read = 0;
  53539. CImg<charT> line(256); *line = 0;
  53540. int err;
  53541. // Skip comments, and read magic string OFF
  53542. do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#'));
  53543. if (cimg::strncasecmp(line,"OFF",3) && cimg::strncasecmp(line,"COFF",4)) {
  53544. if (!file) cimg::fclose(nfile);
  53545. throw CImgIOException(_cimg_instance
  53546. "load_off(): OFF header not found in file '%s'.",
  53547. cimg_instance,
  53548. filename?filename:"(FILE*)");
  53549. }
  53550. do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#'));
  53551. if ((err = cimg_sscanf(line,"%u%u%*[^\n] ",&nb_points,&nb_primitives))!=2) {
  53552. if (!file) cimg::fclose(nfile);
  53553. throw CImgIOException(_cimg_instance
  53554. "load_off(): Invalid number of vertices or primitives specified in file '%s'.",
  53555. cimg_instance,
  53556. filename?filename:"(FILE*)");
  53557. }
  53558. // Read points data
  53559. assign(nb_points,3);
  53560. float X = 0, Y = 0, Z = 0;
  53561. cimg_forX(*this,l) {
  53562. do { err = std::fscanf(nfile,"%255[^\n] ",line._data); } while (!err || (err==1 && *line=='#'));
  53563. if ((err = cimg_sscanf(line,"%f%f%f%*[^\n] ",&X,&Y,&Z))!=3) {
  53564. if (!file) cimg::fclose(nfile);
  53565. throw CImgIOException(_cimg_instance
  53566. "load_off(): Failed to read vertex %u/%u in file '%s'.",
  53567. cimg_instance,
  53568. l + 1,nb_points,filename?filename:"(FILE*)");
  53569. }
  53570. (*this)(l,0) = (T)X; (*this)(l,1) = (T)Y; (*this)(l,2) = (T)Z;
  53571. }
  53572. // Read primitive data
  53573. primitives.assign();
  53574. colors.assign();
  53575. bool stop_flag = false;
  53576. while (!stop_flag) {
  53577. float c0 = 0.7f, c1 = 0.7f, c2 = 0.7f;
  53578. unsigned int prim = 0, i0 = 0, i1 = 0, i2 = 0, i3 = 0, i4 = 0, i5 = 0, i6 = 0, i7 = 0;
  53579. *line = 0;
  53580. if ((err = std::fscanf(nfile,"%u",&prim))!=1) stop_flag = true;
  53581. else {
  53582. ++nb_read;
  53583. switch (prim) {
  53584. case 1 : {
  53585. if ((err = std::fscanf(nfile,"%u%255[^\n] ",&i0,line._data))<2) {
  53586. cimg::warn(_cimg_instance
  53587. "load_off(): Failed to read primitive %u/%u from file '%s'.",
  53588. cimg_instance,
  53589. nb_read,nb_primitives,filename?filename:"(FILE*)");
  53590. err = std::fscanf(nfile,"%*[^\n] ");
  53591. } else {
  53592. err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
  53593. CImg<tf>::vector(i0).move_to(primitives);
  53594. CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
  53595. }
  53596. } break;
  53597. case 2 : {
  53598. if ((err = std::fscanf(nfile,"%u%u%255[^\n] ",&i0,&i1,line._data))<2) {
  53599. cimg::warn(_cimg_instance
  53600. "load_off(): Failed to read primitive %u/%u from file '%s'.",
  53601. cimg_instance,
  53602. nb_read,nb_primitives,filename?filename:"(FILE*)");
  53603. err = std::fscanf(nfile,"%*[^\n] ");
  53604. } else {
  53605. err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
  53606. CImg<tf>::vector(i0,i1).move_to(primitives);
  53607. CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
  53608. }
  53609. } break;
  53610. case 3 : {
  53611. if ((err = std::fscanf(nfile,"%u%u%u%255[^\n] ",&i0,&i1,&i2,line._data))<3) {
  53612. cimg::warn(_cimg_instance
  53613. "load_off(): Failed to read primitive %u/%u from file '%s'.",
  53614. cimg_instance,
  53615. nb_read,nb_primitives,filename?filename:"(FILE*)");
  53616. err = std::fscanf(nfile,"%*[^\n] ");
  53617. } else {
  53618. err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
  53619. CImg<tf>::vector(i0,i2,i1).move_to(primitives);
  53620. CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
  53621. }
  53622. } break;
  53623. case 4 : {
  53624. if ((err = std::fscanf(nfile,"%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,line._data))<4) {
  53625. cimg::warn(_cimg_instance
  53626. "load_off(): Failed to read primitive %u/%u from file '%s'.",
  53627. cimg_instance,
  53628. nb_read,nb_primitives,filename?filename:"(FILE*)");
  53629. err = std::fscanf(nfile,"%*[^\n] ");
  53630. } else {
  53631. err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
  53632. CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
  53633. CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)).move_to(colors);
  53634. }
  53635. } break;
  53636. case 5 : {
  53637. if ((err = std::fscanf(nfile,"%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,line._data))<5) {
  53638. cimg::warn(_cimg_instance
  53639. "load_off(): Failed to read primitive %u/%u from file '%s'.",
  53640. cimg_instance,
  53641. nb_read,nb_primitives,filename?filename:"(FILE*)");
  53642. err = std::fscanf(nfile,"%*[^\n] ");
  53643. } else {
  53644. err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
  53645. CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
  53646. CImg<tf>::vector(i0,i4,i3).move_to(primitives);
  53647. colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
  53648. ++nb_primitives;
  53649. }
  53650. } break;
  53651. case 6 : {
  53652. if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,line._data))<6) {
  53653. cimg::warn(_cimg_instance
  53654. "load_off(): Failed to read primitive %u/%u from file '%s'.",
  53655. cimg_instance,
  53656. nb_read,nb_primitives,filename?filename:"(FILE*)");
  53657. err = std::fscanf(nfile,"%*[^\n] ");
  53658. } else {
  53659. err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
  53660. CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
  53661. CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
  53662. colors.insert(2,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
  53663. ++nb_primitives;
  53664. }
  53665. } break;
  53666. case 7 : {
  53667. if ((err = std::fscanf(nfile,"%u%u%u%u%u%u%u%255[^\n] ",&i0,&i1,&i2,&i3,&i4,&i5,&i6,line._data))<7) {
  53668. cimg::warn(_cimg_instance
  53669. "load_off(): Failed to read primitive %u/%u from file '%s'.",
  53670. cimg_instance,
  53671. nb_read,nb_primitives,filename?filename:"(FILE*)");
  53672. err = std::fscanf(nfile,"%*[^\n] ");
  53673. } else {
  53674. err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
  53675. CImg<tf>::vector(i0,i4,i3,i1).move_to(primitives);
  53676. CImg<tf>::vector(i0,i6,i5,i4).move_to(primitives);
  53677. CImg<tf>::vector(i3,i2,i1).move_to(primitives);
  53678. colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
  53679. ++(++nb_primitives);
  53680. }
  53681. } break;
  53682. case 8 : {
  53683. 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) {
  53684. cimg::warn(_cimg_instance
  53685. "load_off(): Failed to read primitive %u/%u from file '%s'.",
  53686. cimg_instance,
  53687. nb_read,nb_primitives,filename?filename:"(FILE*)");
  53688. err = std::fscanf(nfile,"%*[^\n] ");
  53689. } else {
  53690. err = cimg_sscanf(line,"%f%f%f",&c0,&c1,&c2);
  53691. CImg<tf>::vector(i0,i3,i2,i1).move_to(primitives);
  53692. CImg<tf>::vector(i0,i5,i4,i3).move_to(primitives);
  53693. CImg<tf>::vector(i0,i7,i6,i5).move_to(primitives);
  53694. colors.insert(3,CImg<tc>::vector((tc)(c0*255),(tc)(c1*255),(tc)(c2*255)));
  53695. ++(++nb_primitives);
  53696. }
  53697. } break;
  53698. default :
  53699. cimg::warn(_cimg_instance
  53700. "load_off(): Failed to read primitive %u/%u (%u vertices) from file '%s'.",
  53701. cimg_instance,
  53702. nb_read,nb_primitives,prim,filename?filename:"(FILE*)");
  53703. err = std::fscanf(nfile,"%*[^\n] ");
  53704. }
  53705. }
  53706. }
  53707. if (!file) cimg::fclose(nfile);
  53708. if (primitives._width!=nb_primitives)
  53709. cimg::warn(_cimg_instance
  53710. "load_off(): Only %u/%u primitives read from file '%s'.",
  53711. cimg_instance,
  53712. primitives._width,nb_primitives,filename?filename:"(FILE*)");
  53713. return *this;
  53714. }
  53715. //! Load image sequence from a video file, using OpenCV library.
  53716. /**
  53717. \param filename Filename, as a C-string.
  53718. \param first_frame Index of the first frame to read.
  53719. \param last_frame Index of the last frame to read.
  53720. \param step_frame Step value for frame reading.
  53721. \param axis Alignment axis.
  53722. \param align Appending alignment.
  53723. **/
  53724. CImg<T>& load_video(const char *const filename,
  53725. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  53726. const unsigned int step_frame=1,
  53727. const char axis='z', const float align=0) {
  53728. return get_load_video(filename,first_frame,last_frame,step_frame,axis,align).move_to(*this);
  53729. }
  53730. //! Load image sequence from a video file, using OpenCV library \newinstance.
  53731. static CImg<T> get_load_video(const char *const filename,
  53732. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  53733. const unsigned int step_frame=1,
  53734. const char axis='z', const float align=0) {
  53735. return CImgList<T>().load_video(filename,first_frame,last_frame,step_frame).get_append(axis,align);
  53736. }
  53737. //! Load image sequence using FFMPEG's external tool 'ffmpeg'.
  53738. /**
  53739. \param filename Filename, as a C-string.
  53740. \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  53741. \param align Appending alignment.
  53742. **/
  53743. CImg<T>& load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) {
  53744. return get_load_ffmpeg_external(filename,axis,align).move_to(*this);
  53745. }
  53746. //! Load image sequence using FFMPEG's external tool 'ffmpeg' \newinstance.
  53747. static CImg<T> get_load_ffmpeg_external(const char *const filename, const char axis='z', const float align=0) {
  53748. return CImgList<T>().load_ffmpeg_external(filename).get_append(axis,align);
  53749. }
  53750. //! Load gif file, using Imagemagick or GraphicsMagicks's external tools.
  53751. /**
  53752. \param filename Filename, as a C-string.
  53753. \param axis Appending axis, if file contains multiple images. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  53754. \param align Appending alignment.
  53755. **/
  53756. CImg<T>& load_gif_external(const char *const filename,
  53757. const char axis='z', const float align=0) {
  53758. return get_load_gif_external(filename,axis,align).move_to(*this);
  53759. }
  53760. //! Load gif file, using ImageMagick or GraphicsMagick's external tool 'convert' \newinstance.
  53761. static CImg<T> get_load_gif_external(const char *const filename,
  53762. const char axis='z', const float align=0) {
  53763. return CImgList<T>().load_gif_external(filename).get_append(axis,align);
  53764. }
  53765. //! Load image from a HEIC file.
  53766. /**
  53767. \param filename Filename, as a C-string.
  53768. **/
  53769. CImg<T>& load_heif(const char *const filename) {
  53770. return _load_heif(filename);
  53771. }
  53772. //! Load image from a HEIC file \newinstance.
  53773. static CImg<T> get_load_heif(const char *const filename) {
  53774. return CImg<T>().load_heif(filename);
  53775. }
  53776. CImg<T>& _load_heif(const char *const filename) {
  53777. #ifndef cimg_use_heif
  53778. return load_other(filename);
  53779. #else
  53780. try {
  53781. heif::Context ctx;
  53782. ctx.read_from_file(filename);
  53783. heif::ImageHandle handle = ctx.get_primary_image_handle();
  53784. const heif::Image image =
  53785. handle.decode_image(heif_colorspace_RGB,handle.has_alpha_channel()?heif_chroma_interleaved_RGBA:
  53786. heif_chroma_interleaved_RGB);
  53787. const int
  53788. W = image.get_width(heif_channel_interleaved),
  53789. H = image.get_height(heif_channel_interleaved),
  53790. S = handle.has_alpha_channel()?4:3;
  53791. assign(W,H,1,S);
  53792. int stride;
  53793. const unsigned char *const buffer = image.get_plane(heif_channel_interleaved,&stride);
  53794. 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;
  53795. cimg_forY(*this,y) {
  53796. const unsigned char *ptrs = buffer + y*stride;
  53797. if (ptr_a) cimg_forX(*this,x) { // RGBA
  53798. *(ptr_r++) = (T)*(ptrs++);
  53799. *(ptr_g++) = (T)*(ptrs++);
  53800. *(ptr_b++) = (T)*(ptrs++);
  53801. *(ptr_a++) = (T)*(ptrs++);
  53802. }
  53803. else cimg_forX(*this,x) { // RGB
  53804. *(ptr_r++) = (T)*(ptrs++);
  53805. *(ptr_g++) = (T)*(ptrs++);
  53806. *(ptr_b++) = (T)*(ptrs++);
  53807. }
  53808. }
  53809. } catch (const heif::Error& e) {
  53810. throw CImgInstanceException(_cimg_instance
  53811. "load_heif(): Unable to decode image: %s",
  53812. cimg_instance,
  53813. e.get_message().c_str());
  53814. } catch (...) {
  53815. throw;
  53816. }
  53817. return *this;
  53818. #endif
  53819. }
  53820. //! Load image using GraphicsMagick's external tool 'gm'.
  53821. /**
  53822. \param filename Filename, as a C-string.
  53823. **/
  53824. CImg<T>& load_graphicsmagick_external(const char *const filename) {
  53825. if (!filename)
  53826. throw CImgArgumentException(_cimg_instance
  53827. "load_graphicsmagick_external(): Specified filename is (null).",
  53828. cimg_instance);
  53829. cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
  53830. CImg<charT> command(1024), filename_tmp(256);
  53831. std::FILE *file = 0;
  53832. const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
  53833. #if cimg_OS==1
  53834. if (!cimg::system("which gm")) {
  53835. cimg_snprintf(command,command._width,"%s convert \"%s\" %s:-",
  53836. cimg::graphicsmagick_path(),
  53837. s_filename.data(),
  53838. #ifdef cimg_use_png
  53839. "png"
  53840. #else
  53841. "pnm"
  53842. #endif
  53843. );
  53844. file = popen(command,"r");
  53845. if (file) {
  53846. const unsigned int omode = cimg::exception_mode();
  53847. cimg::exception_mode(0);
  53848. try {
  53849. #ifdef cimg_use_png
  53850. load_png(file);
  53851. #else
  53852. load_pnm(file);
  53853. #endif
  53854. } catch (...) {
  53855. pclose(file);
  53856. cimg::exception_mode(omode);
  53857. throw CImgIOException(_cimg_instance
  53858. "load_graphicsmagick_external(): Failed to load file '%s' "
  53859. "with external command 'gm'.",
  53860. cimg_instance,
  53861. filename);
  53862. }
  53863. pclose(file);
  53864. return *this;
  53865. }
  53866. }
  53867. #endif
  53868. do {
  53869. cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
  53870. cimg::temporary_path(),
  53871. cimg_file_separator,
  53872. cimg::filenamerand(),
  53873. #ifdef cimg_use_png
  53874. "png"
  53875. #else
  53876. "pnm"
  53877. #endif
  53878. );
  53879. if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
  53880. } while (file);
  53881. cimg_snprintf(command,command._width,"\"%s\" convert \"%s\" \"%s\"",
  53882. cimg::graphicsmagick_path(),
  53883. s_filename.data(),
  53884. CImg<charT>::string(filename_tmp)._system_strescape().data());
  53885. cimg::system(command,cimg::graphicsmagick_path());
  53886. if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
  53887. cimg::fclose(cimg::fopen(filename,"r"));
  53888. throw CImgIOException(_cimg_instance
  53889. "load_graphicsmagick_external(): Failed to load file '%s' with external command 'gm'.",
  53890. cimg_instance,
  53891. filename);
  53892. } else cimg::fclose(file);
  53893. #ifdef cimg_use_png
  53894. load_png(filename_tmp);
  53895. #else
  53896. load_pnm(filename_tmp);
  53897. #endif
  53898. std::remove(filename_tmp);
  53899. return *this;
  53900. }
  53901. //! Load image using GraphicsMagick's external tool 'gm' \newinstance.
  53902. static CImg<T> get_load_graphicsmagick_external(const char *const filename) {
  53903. return CImg<T>().load_graphicsmagick_external(filename);
  53904. }
  53905. //! Load gzipped image file, using external tool 'gunzip'.
  53906. /**
  53907. \param filename Filename, as a C-string.
  53908. **/
  53909. CImg<T>& load_gzip_external(const char *const filename) {
  53910. if (!filename)
  53911. throw CImgIOException(_cimg_instance
  53912. "load_gzip_external(): Specified filename is (null).",
  53913. cimg_instance);
  53914. cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
  53915. CImg<charT> command(1024), filename_tmp(256), body(256);
  53916. const char
  53917. *const ext = cimg::split_filename(filename,body),
  53918. *const ext2 = cimg::split_filename(body,0);
  53919. std::FILE *file = 0;
  53920. do {
  53921. if (!cimg::strcasecmp(ext,"gz")) {
  53922. if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
  53923. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
  53924. else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
  53925. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  53926. } else {
  53927. if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
  53928. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
  53929. else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
  53930. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  53931. }
  53932. if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
  53933. } while (file);
  53934. cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"",
  53935. cimg::gunzip_path(),
  53936. CImg<charT>::string(filename)._system_strescape().data(),
  53937. CImg<charT>::string(filename_tmp)._system_strescape().data());
  53938. cimg::system(command);
  53939. if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
  53940. cimg::fclose(cimg::fopen(filename,"r"));
  53941. throw CImgIOException(_cimg_instance
  53942. "load_gzip_external(): Failed to load file '%s' with external command 'gunzip'.",
  53943. cimg_instance,
  53944. filename);
  53945. } else cimg::fclose(file);
  53946. load(filename_tmp);
  53947. std::remove(filename_tmp);
  53948. return *this;
  53949. }
  53950. //! Load gzipped image file, using external tool 'gunzip' \newinstance.
  53951. static CImg<T> get_load_gzip_external(const char *const filename) {
  53952. return CImg<T>().load_gzip_external(filename);
  53953. }
  53954. //! Load image using ImageMagick's external tool 'convert'.
  53955. /**
  53956. \param filename Filename, as a C-string.
  53957. **/
  53958. CImg<T>& load_imagemagick_external(const char *const filename) {
  53959. if (!filename)
  53960. throw CImgArgumentException(_cimg_instance
  53961. "load_imagemagick_external(): Specified filename is (null).",
  53962. cimg_instance);
  53963. cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
  53964. CImg<charT> command(1024), filename_tmp(256);
  53965. std::FILE *file = 0;
  53966. const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
  53967. #if cimg_OS==1
  53968. if (!cimg::system("which convert")) {
  53969. cimg_snprintf(command,command._width,"%s%s \"%s\" %s:-",
  53970. cimg::imagemagick_path(),
  53971. !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"",
  53972. s_filename.data(),
  53973. #ifdef cimg_use_png
  53974. "png"
  53975. #else
  53976. "pnm"
  53977. #endif
  53978. );
  53979. file = popen(command,"r");
  53980. if (file) {
  53981. const unsigned int omode = cimg::exception_mode();
  53982. cimg::exception_mode(0);
  53983. try {
  53984. #ifdef cimg_use_png
  53985. load_png(file);
  53986. #else
  53987. load_pnm(file);
  53988. #endif
  53989. } catch (...) {
  53990. pclose(file);
  53991. cimg::exception_mode(omode);
  53992. throw CImgIOException(_cimg_instance
  53993. "load_imagemagick_external(): Failed to load file '%s' with "
  53994. "external command 'magick/convert'.",
  53995. cimg_instance,
  53996. filename);
  53997. }
  53998. pclose(file);
  53999. return *this;
  54000. }
  54001. }
  54002. #endif
  54003. do {
  54004. cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
  54005. cimg::temporary_path(),
  54006. cimg_file_separator,
  54007. cimg::filenamerand(),
  54008. #ifdef cimg_use_png
  54009. "png"
  54010. #else
  54011. "pnm"
  54012. #endif
  54013. );
  54014. if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
  54015. } while (file);
  54016. cimg_snprintf(command,command._width,"\"%s\"%s \"%s\" \"%s\"",
  54017. cimg::imagemagick_path(),
  54018. !cimg::strcasecmp(cimg::split_filename(filename),"pdf")?" -density 400x400":"",
  54019. s_filename.data(),
  54020. CImg<charT>::string(filename_tmp)._system_strescape().data());
  54021. cimg::system(command,cimg::imagemagick_path());
  54022. if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
  54023. cimg::fclose(cimg::fopen(filename,"r"));
  54024. throw CImgIOException(_cimg_instance
  54025. "load_imagemagick_external(): Failed to load file '%s' with "
  54026. "external command 'magick/convert'.",
  54027. cimg_instance,
  54028. filename);
  54029. } else cimg::fclose(file);
  54030. #ifdef cimg_use_png
  54031. load_png(filename_tmp);
  54032. #else
  54033. load_pnm(filename_tmp);
  54034. #endif
  54035. std::remove(filename_tmp);
  54036. return *this;
  54037. }
  54038. //! Load image using ImageMagick's external tool 'convert' \newinstance.
  54039. static CImg<T> get_load_imagemagick_external(const char *const filename) {
  54040. return CImg<T>().load_imagemagick_external(filename);
  54041. }
  54042. //! Load image from a DICOM file, using Medcon's external tool 'medcon'.
  54043. /**
  54044. \param filename Filename, as a C-string.
  54045. **/
  54046. CImg<T>& load_medcon_external(const char *const filename) {
  54047. if (!filename)
  54048. throw CImgArgumentException(_cimg_instance
  54049. "load_medcon_external(): Specified filename is (null).",
  54050. cimg_instance);
  54051. cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
  54052. CImg<charT> command(1024), filename_tmp(256), body(256);
  54053. cimg::fclose(cimg::fopen(filename,"r"));
  54054. std::FILE *file = 0;
  54055. do {
  54056. cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand());
  54057. if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
  54058. } while (file);
  54059. cimg_snprintf(command,command._width,"\"%s\" -w -c anlz -o \"%s\" -f \"%s\"",
  54060. cimg::medcon_path(),
  54061. CImg<charT>::string(filename_tmp)._system_strescape().data(),
  54062. CImg<charT>::string(filename)._system_strescape().data());
  54063. cimg::system(command,cimg::medcon_path());
  54064. cimg::split_filename(filename_tmp,body);
  54065. cimg_snprintf(command,command._width,"%s.hdr",body._data);
  54066. file = cimg::std_fopen(command,"rb");
  54067. if (!file) {
  54068. cimg_snprintf(command,command._width,"m000-%s.hdr",body._data);
  54069. file = cimg::std_fopen(command,"rb");
  54070. if (!file) {
  54071. throw CImgIOException(_cimg_instance
  54072. "load_medcon_external(): Failed to load file '%s' with external command 'medcon'.",
  54073. cimg_instance,
  54074. filename);
  54075. }
  54076. }
  54077. cimg::fclose(file);
  54078. load_analyze(command);
  54079. std::remove(command);
  54080. cimg::split_filename(command,body);
  54081. cimg_snprintf(command,command._width,"%s.img",body._data);
  54082. std::remove(command);
  54083. return *this;
  54084. }
  54085. //! Load image from a DICOM file, using Medcon's external tool 'medcon' \newinstance.
  54086. static CImg<T> get_load_medcon_external(const char *const filename) {
  54087. return CImg<T>().load_medcon_external(filename);
  54088. }
  54089. //! Load image from a .pdf file.
  54090. /**
  54091. \param filename Filename, as a C-string.
  54092. \param resolution Image resolution.
  54093. **/
  54094. CImg<T>& load_pdf_external(const char *const filename, const unsigned int resolution=400) {
  54095. if (!filename)
  54096. throw CImgArgumentException(_cimg_instance
  54097. "load_pdf_external(): Specified filename is (null).",
  54098. cimg_instance);
  54099. CImg<charT> command(1024), filename_tmp(256);
  54100. std::FILE *file = 0;
  54101. const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
  54102. #if cimg_OS==1
  54103. cimg_snprintf(command,command._width,"gs -q -dNOPAUSE -sDEVICE=ppmraw -o - -r%u \"%s\"",
  54104. resolution,s_filename.data());
  54105. file = popen(command,"r");
  54106. if (file) {
  54107. const unsigned int omode = cimg::exception_mode();
  54108. cimg::exception_mode(0);
  54109. try { load_pnm(file); } catch (...) {
  54110. pclose(file);
  54111. cimg::exception_mode(omode);
  54112. throw CImgIOException(_cimg_instance
  54113. "load_pdf_external(): Failed to load file '%s' with external command 'gs'.",
  54114. cimg_instance,
  54115. filename);
  54116. }
  54117. pclose(file);
  54118. return *this;
  54119. }
  54120. #endif
  54121. do {
  54122. cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.ppm",
  54123. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  54124. if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
  54125. } while (file);
  54126. cimg_snprintf(command,command._width,"gs -q -dNOPAUSE -sDEVICE=ppmraw -o \"%s\" -r%u \"%s\"",
  54127. CImg<charT>::string(filename_tmp)._system_strescape().data(),resolution,s_filename.data());
  54128. cimg::system(command,"gs");
  54129. if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
  54130. cimg::fclose(cimg::fopen(filename,"r"));
  54131. throw CImgIOException(_cimg_instance
  54132. "load_pdf_external(): Failed to load file '%s' with external command 'gs'.",
  54133. cimg_instance,
  54134. filename);
  54135. } else cimg::fclose(file);
  54136. load_pnm(filename_tmp);
  54137. std::remove(filename_tmp);
  54138. return *this;
  54139. }
  54140. //! Load image from a .pdf file \newinstance.
  54141. static CImg<T> get_load_pdf_external(const char *const filename, const unsigned int resolution=400) {
  54142. return CImg<T>().load_pdf_external(filename,resolution);
  54143. }
  54144. //! Load image from a RAW Color Camera file, using external tool 'dcraw'.
  54145. /**
  54146. \param filename Filename, as a C-string.
  54147. **/
  54148. CImg<T>& load_dcraw_external(const char *const filename) {
  54149. if (!filename)
  54150. throw CImgArgumentException(_cimg_instance
  54151. "load_dcraw_external(): Specified filename is (null).",
  54152. cimg_instance);
  54153. cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
  54154. CImg<charT> command(1024), filename_tmp(256);
  54155. std::FILE *file = 0;
  54156. const CImg<charT> s_filename = CImg<charT>::string(filename)._system_strescape();
  54157. #if cimg_OS==1
  54158. cimg_snprintf(command,command._width,"%s -w -4 -c \"%s\"",
  54159. cimg::dcraw_path(),s_filename.data());
  54160. file = popen(command,"r");
  54161. if (file) {
  54162. const unsigned int omode = cimg::exception_mode();
  54163. cimg::exception_mode(0);
  54164. try { load_pnm(file); } catch (...) {
  54165. pclose(file);
  54166. cimg::exception_mode(omode);
  54167. throw CImgIOException(_cimg_instance
  54168. "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.",
  54169. cimg_instance,
  54170. filename);
  54171. }
  54172. pclose(file);
  54173. return *this;
  54174. }
  54175. #endif
  54176. do {
  54177. cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.ppm",
  54178. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  54179. if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
  54180. } while (file);
  54181. cimg_snprintf(command,command._width,"\"%s\" -w -4 -c \"%s\" > \"%s\"",
  54182. cimg::dcraw_path(),s_filename.data(),CImg<charT>::string(filename_tmp)._system_strescape().data());
  54183. cimg::system(command,cimg::dcraw_path());
  54184. if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
  54185. cimg::fclose(cimg::fopen(filename,"r"));
  54186. throw CImgIOException(_cimg_instance
  54187. "load_dcraw_external(): Failed to load file '%s' with external command 'dcraw'.",
  54188. cimg_instance,
  54189. filename);
  54190. } else cimg::fclose(file);
  54191. load_pnm(filename_tmp);
  54192. std::remove(filename_tmp);
  54193. return *this;
  54194. }
  54195. //! Load image from a RAW Color Camera file, using external tool 'dcraw' \newinstance.
  54196. static CImg<T> get_load_dcraw_external(const char *const filename) {
  54197. return CImg<T>().load_dcraw_external(filename);
  54198. }
  54199. #ifdef cimg_use_opencv
  54200. // Convert a continuous cv::Mat<uchar> to a CImg<uchar>.
  54201. static CImg<ucharT> _cvmat2cimg(const cv::Mat &src) {
  54202. if (src.channels()==1) return CImg<ucharT>(src.ptr(),src.cols,src.rows,1,1);
  54203. else if (src.channels()==3) { // BGR
  54204. CImg<ucharT> res(src.cols,src.rows,1,src.channels());
  54205. const unsigned char *ptrs = src.ptr();
  54206. unsigned char *pR = res.data(), *pG = res.data(0,0,0,1), *pB = res.data(0,0,0,2);
  54207. cimg_forXY(res,x,y) { *(pB++) = *(ptrs++); *(pG++) = *(ptrs++); *(pR++) = *(ptrs++); }
  54208. return res;
  54209. }
  54210. return CImg<ucharT>(src.ptr(),src.channels(),src.cols,src.rows,1,true).get_permute_axes("yzcx");
  54211. }
  54212. // Convert a CImg<T> to a cv::Mat.
  54213. cv::Mat _cimg2cvmat() const {
  54214. if (is_empty())
  54215. throw CImgInstanceException(_cimg_instance
  54216. "_cimg2cvmat() : Instance image is empty.",
  54217. cimg_instance);
  54218. if (_spectrum==2)
  54219. throw CImgInstanceException(_cimg_instance
  54220. "_cimg2cvmat() : Invalid number of channels (should be '1' or '3+').",
  54221. cimg_instance);
  54222. if (_depth!=1)
  54223. throw CImgInstanceException(_cimg_instance
  54224. "_cimg2cvmat() : Invalid number of slices (should be '1').",
  54225. cimg_instance);
  54226. int mat_type = -1;
  54227. if (pixel_type()==cimg::type<unsigned char>::string()) mat_type = CV_8UC1;
  54228. if (pixel_type()==cimg::type<char>::string()) mat_type = CV_8SC1;
  54229. if (pixel_type()==cimg::type<unsigned short>::string()) mat_type = CV_16UC1;
  54230. if (pixel_type()==cimg::type<short>::string()) mat_type = CV_16SC1;
  54231. if (pixel_type()==cimg::type<int>::string()) mat_type = CV_32SC1;
  54232. if (pixel_type()==cimg::type<float>::string()) mat_type = CV_32FC1;
  54233. if (pixel_type()==cimg::type<double>::string()) mat_type = CV_64FC1;
  54234. if (mat_type<0)
  54235. throw CImgInstanceException(_cimg_instance
  54236. "_cvmat2cimg() : pixel type '%s' is not supported.",
  54237. cimg_instance,pixel_type());
  54238. cv::Mat res;
  54239. std::vector<cv::Mat> channels(_spectrum);
  54240. if (_spectrum>1) {
  54241. cimg_forC(*this,c)
  54242. channels[c] = cv::Mat(_height,_width,mat_type,_data + _width*_height*(_spectrum - 1 - c));
  54243. cv::merge(channels,res);
  54244. } else res = cv::Mat(_height,_width,mat_type,_data).clone();
  54245. return res;
  54246. }
  54247. #endif
  54248. //! Load image from a camera stream, using OpenCV.
  54249. /**
  54250. \param index Index of the camera to capture images from (from 0 to 63).
  54251. \param capture_width Width of the desired image ('0' stands for default value).
  54252. \param capture_height Height of the desired image ('0' stands for default value).
  54253. \param skip_frames Number of frames to skip before the capture.
  54254. \param release_camera Tells if the camera resource must be released at the end of the method.
  54255. **/
  54256. CImg<T>& load_camera(const unsigned int camera_index=0,
  54257. const unsigned int capture_width=0, const unsigned int capture_height=0,
  54258. const unsigned int skip_frames=0, const bool release_camera=true) {
  54259. #ifdef cimg_use_opencv
  54260. if (camera_index>=64)
  54261. throw CImgArgumentException(_cimg_instance
  54262. "load_camera(): Invalid request for camera #%u "
  54263. "(no more than 100 cameras can be managed simultaneously).",
  54264. cimg_instance,
  54265. camera_index);
  54266. static cv::VideoCapture *captures[64] = {};
  54267. static unsigned int captures_w[64], captures_h[64];
  54268. if (release_camera) {
  54269. cimg::mutex(9);
  54270. if (captures[camera_index]) captures[camera_index]->release();
  54271. delete captures[camera_index];
  54272. captures[camera_index] = 0;
  54273. captures_w[camera_index] = captures_h[camera_index] = 0;
  54274. cimg::mutex(9,0);
  54275. return *this;
  54276. }
  54277. if (!captures[camera_index]) {
  54278. cimg::mutex(9);
  54279. captures[camera_index] = new cv::VideoCapture(camera_index);
  54280. captures_w[camera_index] = captures_h[camera_index] = 0;
  54281. if (!captures[camera_index]->isOpened()) {
  54282. delete captures[camera_index];
  54283. captures[camera_index] = 0;
  54284. cimg::mutex(9,0);
  54285. throw CImgIOException(_cimg_instance
  54286. "load_camera(): Failed to initialize camera #%u.",
  54287. cimg_instance,
  54288. camera_index);
  54289. }
  54290. cimg::mutex(9,0);
  54291. }
  54292. cimg::mutex(9);
  54293. if (capture_width!=captures_w[camera_index]) {
  54294. captures[camera_index]->set(_cimg_cap_prop_frame_width,capture_width);
  54295. captures_w[camera_index] = capture_width;
  54296. }
  54297. if (capture_height!=captures_h[camera_index]) {
  54298. captures[camera_index]->set(_cimg_cap_prop_frame_height,capture_height);
  54299. captures_h[camera_index] = capture_height;
  54300. }
  54301. for (unsigned int i = 0; i<skip_frames; ++i) captures[camera_index]->grab();
  54302. cv::Mat cvimg;
  54303. captures[camera_index]->read(cvimg);
  54304. if (cvimg.empty()) {
  54305. cimg::mutex(9,0);
  54306. load_camera(camera_index,0,0,0,true); // Release camera
  54307. throw CImgIOException(_cimg_instance
  54308. "load_camera(): Failed to retrieve a %ux%u frame from camera #%u.",
  54309. cimg_instance,
  54310. capture_width,capture_height,camera_index);
  54311. } else _cvmat2cimg(cvimg).move_to(*this);
  54312. cimg::mutex(9,0);
  54313. return *this;
  54314. #else
  54315. cimg::unused(camera_index,skip_frames,release_camera,capture_width,capture_height);
  54316. throw CImgIOException(_cimg_instance
  54317. "load_camera(): This function requires features from the OpenCV library "
  54318. "('-Dcimg_use_opencv' must be defined).",
  54319. cimg_instance);
  54320. #endif
  54321. }
  54322. //! Load image from a camera stream, using OpenCV \newinstance.
  54323. static CImg<T> get_load_camera(const unsigned int camera_index=0,
  54324. const unsigned int capture_width=0, const unsigned int capture_height=0,
  54325. const unsigned int skip_frames=0, const bool release_camera=true) {
  54326. return CImg<T>().load_camera(camera_index,capture_width,capture_height,skip_frames,release_camera);
  54327. }
  54328. //! Load image using various non-native ways.
  54329. /**
  54330. \param filename Filename, as a C-string.
  54331. **/
  54332. CImg<T>& load_other(const char *const filename) {
  54333. if (!filename)
  54334. throw CImgArgumentException(_cimg_instance
  54335. "load_other(): Specified filename is (null).",
  54336. cimg_instance);
  54337. const unsigned int omode = cimg::exception_mode();
  54338. cimg::exception_mode(0);
  54339. try { load_magick(filename); }
  54340. catch (CImgException&) {
  54341. try { load_imagemagick_external(filename); }
  54342. catch (CImgException&) {
  54343. try { load_graphicsmagick_external(filename); }
  54344. catch (CImgException&) {
  54345. try { load_cimg(filename); }
  54346. catch (CImgException&) {
  54347. try {
  54348. cimg::fclose(cimg::fopen(filename,"rb"));
  54349. } catch (CImgException&) {
  54350. cimg::exception_mode(omode);
  54351. throw CImgIOException(_cimg_instance
  54352. "load_other(): Failed to open file '%s'.",
  54353. cimg_instance,
  54354. filename);
  54355. }
  54356. cimg::exception_mode(omode);
  54357. throw CImgIOException(_cimg_instance
  54358. "load_other(): Failed to recognize format of file '%s'.",
  54359. cimg_instance,
  54360. filename);
  54361. }
  54362. }
  54363. }
  54364. }
  54365. cimg::exception_mode(omode);
  54366. return *this;
  54367. }
  54368. //! Load image using various non-native ways \newinstance.
  54369. static CImg<T> get_load_other(const char *const filename) {
  54370. return CImg<T>().load_other(filename);
  54371. }
  54372. //@}
  54373. //---------------------------
  54374. //
  54375. //! \name Data Output
  54376. //@{
  54377. //---------------------------
  54378. //! Display information about the image data.
  54379. /**
  54380. \param title Name for the considered image.
  54381. \param display_stats Tells to compute and display image statistics.
  54382. **/
  54383. const CImg<T>& print(const char *const title=0, const bool display_stats=true) const {
  54384. int xm = 0, ym = 0, zm = 0, vm = 0, xM = 0, yM = 0, zM = 0, vM = 0;
  54385. CImg<doubleT> st;
  54386. if (!is_empty() && display_stats) {
  54387. st = get_stats();
  54388. xm = (int)st[4]; ym = (int)st[5], zm = (int)st[6], vm = (int)st[7];
  54389. xM = (int)st[8]; yM = (int)st[9], zM = (int)st[10], vM = (int)st[11];
  54390. }
  54391. const ulongT siz = size(), msiz = siz*sizeof(T), siz1 = siz - 1,
  54392. mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U, width1 = _width - 1;
  54393. CImg<charT> _title(64);
  54394. if (!title) cimg_snprintf(_title,_title._width,"CImg<%s>",pixel_type());
  54395. std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = (%u,%u,%u,%u) [%lu %s], %sdata%s = (%s*)%p",
  54396. cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal,
  54397. cimg::t_bold,cimg::t_normal,(void*)this,
  54398. cimg::t_bold,cimg::t_normal,_width,_height,_depth,_spectrum,
  54399. (unsigned long)(mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20))),
  54400. mdisp==0?"b":(mdisp==1?"Kio":"Mio"),
  54401. cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin());
  54402. if (_data)
  54403. std::fprintf(cimg::output(),"..%p (%s) = [ ",(void*)((char*)end() - 1),_is_shared?"shared":"non-shared");
  54404. else std::fprintf(cimg::output()," (%s) = [ ",_is_shared?"shared":"non-shared");
  54405. if (!is_empty()) cimg_foroff(*this,off) {
  54406. std::fprintf(cimg::output(),cimg::type<T>::format_s(),cimg::type<T>::format(_data[off]));
  54407. if (off!=siz1) std::fprintf(cimg::output(),"%s",off%_width==width1?" ; ":" ");
  54408. if (off==7 && siz>16) { off = siz1 - 8; std::fprintf(cimg::output(),"... "); }
  54409. }
  54410. if (!is_empty() && display_stats)
  54411. std::fprintf(cimg::output(),
  54412. " ], %smin%s = %g, %smax%s = %g, %smean%s = %g, %sstd%s = %g, %scoords_min%s = (%u,%u,%u,%u), "
  54413. "%scoords_max%s = (%u,%u,%u,%u).\n",
  54414. cimg::t_bold,cimg::t_normal,st[0],
  54415. cimg::t_bold,cimg::t_normal,st[1],
  54416. cimg::t_bold,cimg::t_normal,st[2],
  54417. cimg::t_bold,cimg::t_normal,std::sqrt(st[3]),
  54418. cimg::t_bold,cimg::t_normal,xm,ym,zm,vm,
  54419. cimg::t_bold,cimg::t_normal,xM,yM,zM,vM);
  54420. else std::fprintf(cimg::output(),"%s].\n",is_empty()?"":" ");
  54421. std::fflush(cimg::output());
  54422. return *this;
  54423. }
  54424. //! Display image into a CImgDisplay window.
  54425. /**
  54426. \param disp Display window.
  54427. **/
  54428. const CImg<T>& display(CImgDisplay& disp) const {
  54429. disp.display(*this);
  54430. return *this;
  54431. }
  54432. //! Display image into a CImgDisplay window, in an interactive way.
  54433. /**
  54434. \param disp Display window.
  54435. \param display_info Tells if image information are displayed on the standard output.
  54436. \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
  54437. \param exit_on_anykey Exit function when any key is pressed.
  54438. **/
  54439. const CImg<T>& display(CImgDisplay &disp, const bool display_info, unsigned int *const XYZ=0,
  54440. const bool exit_on_anykey=false) const {
  54441. return _display(disp,0,display_info,XYZ,exit_on_anykey,false);
  54442. }
  54443. //! Display image into an interactive window.
  54444. /**
  54445. \param title Window title
  54446. \param display_info Tells if image information are displayed on the standard output.
  54447. \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
  54448. \param exit_on_anykey Exit function when any key is pressed.
  54449. **/
  54450. const CImg<T>& display(const char *const title=0, const bool display_info=true, unsigned int *const XYZ=0,
  54451. const bool exit_on_anykey=false) const {
  54452. CImgDisplay disp;
  54453. return _display(disp,title,display_info,XYZ,exit_on_anykey,false);
  54454. }
  54455. const CImg<T>& _display(CImgDisplay &disp, const char *const title, const bool display_info,
  54456. unsigned int *const XYZ, const bool exit_on_anykey,
  54457. const bool exit_on_singleclick) const {
  54458. unsigned int oldw = 0, oldh = 0, _XYZ[3] = {}, key = 0;
  54459. int x0 = 0, y0 = 0, z0 = 0, x1 = width() - 1, y1 = height() - 1, z1 = depth() - 1,
  54460. old_mouse_x = -1, old_mouse_y = -1;
  54461. if (!disp) {
  54462. disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,1);
  54463. if (!title) disp.set_title("CImg<%s> (%ux%ux%ux%u)",pixel_type(),_width,_height,_depth,_spectrum);
  54464. else disp.set_title("%s",title);
  54465. } else if (title) disp.set_title("%s",title);
  54466. disp.show().flush();
  54467. const CImg<char> dtitle = CImg<char>::string(disp.title());
  54468. if (display_info) print(dtitle);
  54469. CImg<T> zoom;
  54470. for (bool reset_view = true, resize_disp = false, is_first_select = true; !key && !disp.is_closed(); ) {
  54471. if (reset_view) {
  54472. if (XYZ) { _XYZ[0] = XYZ[0]; _XYZ[1] = XYZ[1]; _XYZ[2] = XYZ[2]; }
  54473. else {
  54474. _XYZ[0] = (unsigned int)(x0 + x1 + 1)/2;
  54475. _XYZ[1] = (unsigned int)(y0 + y1 + 1)/2;
  54476. _XYZ[2] = (unsigned int)(z0 + z1 + 1)/2;
  54477. }
  54478. x0 = 0; y0 = 0; z0 = 0; x1 = width() - 1; y1 = height() - 1; z1 = depth() - 1;
  54479. disp.resize(cimg_fitscreen(_width,_height,_depth),false);
  54480. oldw = disp._width; oldh = disp._height;
  54481. resize_disp = true;
  54482. reset_view = false;
  54483. }
  54484. if (!x0 && !y0 && !z0 && x1==width() - 1 && y1==height() - 1 && z1==depth() - 1) {
  54485. if (is_empty()) zoom.assign(1,1,1,1,(T)0); else zoom.assign();
  54486. } else zoom = get_crop(x0,y0,z0,x1,y1,z1);
  54487. const CImg<T>& visu = zoom?zoom:*this;
  54488. const unsigned int
  54489. dx = 1U + x1 - x0, dy = 1U + y1 - y0, dz = 1U + z1 - z0,
  54490. tw = dx + (dz>1?dz:0U), th = dy + (dz>1?dz:0U);
  54491. if (!is_empty() && !disp.is_fullscreen() && resize_disp) {
  54492. const float
  54493. ttw = (float)tw*disp.width()/oldw, tth = (float)th*disp.height()/oldh,
  54494. dM = std::max(ttw,tth), diM = (float)std::max(disp.width(),disp.height());
  54495. const unsigned int
  54496. imgw = (unsigned int)(ttw*diM/dM), imgh = (unsigned int)(tth*diM/dM);
  54497. disp.set_fullscreen(false).resize(cimg_fitscreen(imgw,imgh,1),false);
  54498. resize_disp = false;
  54499. }
  54500. oldw = tw; oldh = th;
  54501. bool
  54502. go_up = false, go_down = false, go_left = false, go_right = false,
  54503. go_inc = false, go_dec = false, go_in = false, go_out = false,
  54504. go_in_center = false;
  54505. disp.set_title("%s",dtitle._data);
  54506. if (_width>1 && visu._width==1) disp.set_title("%s | x=%u",disp._title,x0);
  54507. if (_height>1 && visu._height==1) disp.set_title("%s | y=%u",disp._title,y0);
  54508. if (_depth>1 && visu._depth==1) disp.set_title("%s | z=%u",disp._title,z0);
  54509. disp._mouse_x = old_mouse_x; disp._mouse_y = old_mouse_y;
  54510. CImg<intT> selection = visu._select(disp,0,2,_XYZ,x0,y0,z0,true,is_first_select,_depth>1,true);
  54511. old_mouse_x = disp._mouse_x; old_mouse_y = disp._mouse_y;
  54512. is_first_select = false;
  54513. if (disp.wheel()) {
  54514. if ((disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) &&
  54515. (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT())) {
  54516. go_left = !(go_right = disp.wheel()>0);
  54517. } else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) {
  54518. go_down = !(go_up = disp.wheel()>0);
  54519. } else if (depth()==1 || disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  54520. go_out = !(go_in = disp.wheel()>0); go_in_center = false;
  54521. }
  54522. disp.set_wheel();
  54523. }
  54524. const int
  54525. sx0 = selection(0), sy0 = selection(1), sz0 = selection(2),
  54526. sx1 = selection(3), sy1 = selection(4), sz1 = selection(5);
  54527. if (sx0>=0 && sy0>=0 && sz0>=0 && sx1>=0 && sy1>=0 && sz1>=0) {
  54528. x1 = x0 + sx1; y1 = y0 + sy1; z1 = z0 + sz1;
  54529. x0+=sx0; y0+=sy0; z0+=sz0;
  54530. if ((sx0==sx1 && sy0==sy1) || (_depth>1 && sx0==sx1 && sz0==sz1) || (_depth>1 && sy0==sy1 && sz0==sz1)) {
  54531. if (exit_on_singleclick && (!zoom || is_empty())) break; else reset_view = true;
  54532. }
  54533. resize_disp = true;
  54534. } else switch (key = disp.key()) {
  54535. #if cimg_OS!=2
  54536. case cimg::keyCTRLRIGHT : case cimg::keySHIFTRIGHT :
  54537. #endif
  54538. case 0 : case cimg::keyCTRLLEFT : case cimg::keySHIFTLEFT : key = 0; break;
  54539. case cimg::keyP : if (visu._depth>1 && (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT())) {
  54540. // Special mode: play stack of frames
  54541. const unsigned int
  54542. w1 = visu._width*disp.width()/(visu._width + (visu._depth>1?visu._depth:0)),
  54543. h1 = visu._height*disp.height()/(visu._height + (visu._depth>1?visu._depth:0));
  54544. float frame_timing = 5;
  54545. bool is_stopped = false;
  54546. disp.set_key(key,false).set_wheel().resize(cimg_fitscreen(w1,h1,1),false); key = 0;
  54547. for (unsigned int timer = 0; !key && !disp.is_closed() && !disp.button(); ) {
  54548. if (disp.is_resized()) disp.resize(false);
  54549. if (!timer) {
  54550. visu.get_slice((int)_XYZ[2]).display(disp.set_title("%s | z=%d",dtitle.data(),_XYZ[2]));
  54551. (++_XYZ[2])%=visu._depth;
  54552. }
  54553. if (!is_stopped) { if (++timer>(unsigned int)frame_timing) timer = 0; } else timer = ~0U;
  54554. if (disp.wheel()) { frame_timing-=disp.wheel()/3.f; disp.set_wheel(); }
  54555. switch (key = disp.key()) {
  54556. #if cimg_OS!=2
  54557. case cimg::keyCTRLRIGHT :
  54558. #endif
  54559. case cimg::keyCTRLLEFT : key = 0; break;
  54560. case cimg::keyPAGEUP : frame_timing-=0.3f; key = 0; break;
  54561. case cimg::keyPAGEDOWN : frame_timing+=0.3f; key = 0; break;
  54562. case cimg::keySPACE : is_stopped = !is_stopped; disp.set_key(key,false); key = 0; break;
  54563. case cimg::keyARROWLEFT : case cimg::keyARROWUP : is_stopped = true; timer = 0; key = 0; break;
  54564. case cimg::keyARROWRIGHT : case cimg::keyARROWDOWN : is_stopped = true;
  54565. (_XYZ[2]+=visu._depth - 2)%=visu._depth; timer = 0; key = 0; break;
  54566. case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  54567. disp.set_fullscreen(false).
  54568. resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
  54569. CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false);
  54570. disp.set_key(key,false); key = 0;
  54571. } break;
  54572. case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  54573. disp.set_fullscreen(false).
  54574. resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false).set_key(key,false); key = 0;
  54575. } break;
  54576. case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  54577. disp.set_fullscreen(false).
  54578. resize(cimg_fitscreen(_width,_height,_depth),false).set_key(key,false); key = 0;
  54579. } break;
  54580. case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  54581. disp.resize(disp.screen_width(),disp.screen_height(),false).
  54582. toggle_fullscreen().set_key(key,false); key = 0;
  54583. } break;
  54584. }
  54585. frame_timing = frame_timing<1?1:(frame_timing>39?39:frame_timing);
  54586. disp.wait(20);
  54587. }
  54588. const unsigned int
  54589. w2 = (visu._width + (visu._depth>1?visu._depth:0))*disp.width()/visu._width,
  54590. h2 = (visu._height + (visu._depth>1?visu._depth:0))*disp.height()/visu._height;
  54591. disp.resize(cimg_fitscreen(w2,h2,1),false).set_title(dtitle.data()).set_key().set_button().set_wheel();
  54592. key = 0;
  54593. } break;
  54594. case cimg::keyHOME : reset_view = resize_disp = true; key = 0; break;
  54595. case cimg::keyPADADD : go_in = true; go_in_center = true; key = 0; break;
  54596. case cimg::keyPADSUB : go_out = true; key = 0; break;
  54597. case cimg::keyARROWLEFT : case cimg::keyPAD4: go_left = true; key = 0; break;
  54598. case cimg::keyARROWRIGHT : case cimg::keyPAD6: go_right = true; key = 0; break;
  54599. case cimg::keyARROWUP : case cimg::keyPAD8: go_up = true; key = 0; break;
  54600. case cimg::keyARROWDOWN : case cimg::keyPAD2: go_down = true; key = 0; break;
  54601. case cimg::keyPAD7 : go_up = go_left = true; key = 0; break;
  54602. case cimg::keyPAD9 : go_up = go_right = true; key = 0; break;
  54603. case cimg::keyPAD1 : go_down = go_left = true; key = 0; break;
  54604. case cimg::keyPAD3 : go_down = go_right = true; key = 0; break;
  54605. case cimg::keyPAGEUP : go_inc = true; key = 0; break;
  54606. case cimg::keyPAGEDOWN : go_dec = true; key = 0; break;
  54607. }
  54608. if (go_in) {
  54609. const int
  54610. mx = go_in_center?disp.width()/2:disp.mouse_x(),
  54611. my = go_in_center?disp.height()/2:disp.mouse_y(),
  54612. mX = mx*(width() + (depth()>1?depth():0))/disp.width(),
  54613. mY = my*(height() + (depth()>1?depth():0))/disp.height();
  54614. int X = (int)_XYZ[0], Y = (int)_XYZ[1], Z = (int)_XYZ[2];
  54615. if (mX<width() && mY<height()) {
  54616. X = x0 + mX*(1 + x1 - x0)/width(); Y = y0 + mY*(1 + y1 - y0)/height();
  54617. }
  54618. if (mX<width() && mY>=height()) {
  54619. X = x0 + mX*(1 + x1 - x0)/width(); Z = z0 + (mY - height())*(1 + z1 - z0)/depth();
  54620. }
  54621. if (mX>=width() && mY<height()) {
  54622. Y = y0 + mY*(1 + y1 - y0)/height(); Z = z0 + (mX - width())*(1 + z1 - z0)/depth();
  54623. }
  54624. if (x1 - x0>4) { x0 = X - 3*(X - x0)/4; x1 = X + 3*(x1 - X)/4; }
  54625. if (y1 - y0>4) { y0 = Y - 3*(Y - y0)/4; y1 = Y + 3*(y1 - Y)/4; }
  54626. if (z1 - z0>4) { z0 = Z - 3*(Z - z0)/4; z1 = Z + 3*(z1 - Z)/4; }
  54627. }
  54628. if (go_out) {
  54629. const int
  54630. delta_x = (x1 - x0)/8, delta_y = (y1 - y0)/8, delta_z = (z1 - z0)/8,
  54631. ndelta_x = delta_x?delta_x:(_width>1),
  54632. ndelta_y = delta_y?delta_y:(_height>1),
  54633. ndelta_z = delta_z?delta_z:(_depth>1);
  54634. x0-=ndelta_x; y0-=ndelta_y; z0-=ndelta_z;
  54635. x1+=ndelta_x; y1+=ndelta_y; z1+=ndelta_z;
  54636. if (x0<0) { x1-=x0; x0 = 0; if (x1>=width()) x1 = width() - 1; }
  54637. if (y0<0) { y1-=y0; y0 = 0; if (y1>=height()) y1 = height() - 1; }
  54638. if (z0<0) { z1-=z0; z0 = 0; if (z1>=depth()) z1 = depth() - 1; }
  54639. if (x1>=width()) { x0-=(x1 - width() + 1); x1 = width() - 1; if (x0<0) x0 = 0; }
  54640. if (y1>=height()) { y0-=(y1 - height() + 1); y1 = height() - 1; if (y0<0) y0 = 0; }
  54641. if (z1>=depth()) { z0-=(z1 - depth() + 1); z1 = depth() - 1; if (z0<0) z0 = 0; }
  54642. const float
  54643. ratio = (float)(x1-x0)/(y1-y0),
  54644. ratiow = (float)disp._width/disp._height,
  54645. sub = std::min(cimg::abs(ratio - ratiow),cimg::abs(1/ratio-1/ratiow));
  54646. if (sub>0.01) resize_disp = true;
  54647. }
  54648. if (go_left) {
  54649. const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1);
  54650. if (x0 - ndelta>=0) { x0-=ndelta; x1-=ndelta; }
  54651. else { x1-=x0; x0 = 0; }
  54652. }
  54653. if (go_right) {
  54654. const int delta = (x1 - x0)/4, ndelta = delta?delta:(_width>1);
  54655. if (x1+ndelta<width()) { x0+=ndelta; x1+=ndelta; }
  54656. else { x0+=(width() - 1 - x1); x1 = width() - 1; }
  54657. }
  54658. if (go_up) {
  54659. const int delta = (y1 - y0)/4, ndelta = delta?delta:(_height>1);
  54660. if (y0 - ndelta>=0) { y0-=ndelta; y1-=ndelta; }
  54661. else { y1-=y0; y0 = 0; }
  54662. }
  54663. if (go_down) {
  54664. const int delta = (y1 - y0)/4, ndelta = delta?delta:(_height>1);
  54665. if (y1+ndelta<height()) { y0+=ndelta; y1+=ndelta; }
  54666. else { y0+=(height() - 1 - y1); y1 = height() - 1; }
  54667. }
  54668. if (go_inc) {
  54669. const int delta = (z1 - z0)/4, ndelta = delta?delta:(_depth>1);
  54670. if (z0 - ndelta>=0) { z0-=ndelta; z1-=ndelta; }
  54671. else { z1-=z0; z0 = 0; }
  54672. }
  54673. if (go_dec) {
  54674. const int delta = (z1 - z0)/4, ndelta = delta?delta:(_depth>1);
  54675. if (z1+ndelta<depth()) { z0+=ndelta; z1+=ndelta; }
  54676. else { z0+=(depth() - 1 - z1); z1 = depth() - 1; }
  54677. }
  54678. disp.wait(100);
  54679. if (!exit_on_anykey && key && key!=cimg::keyESC &&
  54680. (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
  54681. key = 0;
  54682. }
  54683. }
  54684. disp.set_key(key);
  54685. if (XYZ) { XYZ[0] = _XYZ[0]; XYZ[1] = _XYZ[1]; XYZ[2] = _XYZ[2]; }
  54686. return *this;
  54687. }
  54688. //! Display object 3D in an interactive window.
  54689. /**
  54690. \param disp Display window.
  54691. \param vertices Vertices data of the 3D object.
  54692. \param primitives Primitives data of the 3D object.
  54693. \param colors Colors data of the 3D object.
  54694. \param opacities Opacities data of the 3D object.
  54695. \param centering Tells if the 3D object must be centered for the display.
  54696. \param render_static Rendering mode.
  54697. \param render_motion Rendering mode, when the 3D object is moved.
  54698. \param is_double_sided Tells if the object primitives are double-sided.
  54699. \param focale Focale
  54700. \param light_x X-coordinate of the light source.
  54701. \param light_y Y-coordinate of the light source.
  54702. \param light_z Z-coordinate of the light source.
  54703. \param specular_lightness Amount of specular light.
  54704. \param specular_shininess Shininess of the object material.
  54705. \param display_axes Tells if the 3D axes are displayed.
  54706. \param pose_matrix Pointer to 12 values, defining a 3D pose (as a 4x3 matrix).
  54707. \param exit_on_anykey Exit function when any key is pressed.
  54708. **/
  54709. template<typename tp, typename tf, typename tc, typename to>
  54710. const CImg<T>& display_object3d(CImgDisplay& disp,
  54711. const CImg<tp>& vertices,
  54712. const CImgList<tf>& primitives,
  54713. const CImgList<tc>& colors,
  54714. const to& opacities,
  54715. const bool centering=true,
  54716. const int render_static=4, const int render_motion=1,
  54717. const bool is_double_sided=true, const float focale=700,
  54718. const float light_x=0, const float light_y=0, const float light_z=-5e8f,
  54719. const float specular_lightness=0.2f, const float specular_shininess=0.1f,
  54720. const bool display_axes=true, float *const pose_matrix=0,
  54721. const bool exit_on_anykey=false) const {
  54722. return _display_object3d(disp,0,vertices,primitives,colors,opacities,centering,render_static,
  54723. render_motion,is_double_sided,focale,
  54724. light_x,light_y,light_z,specular_lightness,specular_shininess,
  54725. display_axes,pose_matrix,exit_on_anykey);
  54726. }
  54727. //! Display object 3D in an interactive window \simplification.
  54728. template<typename tp, typename tf, typename tc, typename to>
  54729. const CImg<T>& display_object3d(const char *const title,
  54730. const CImg<tp>& vertices,
  54731. const CImgList<tf>& primitives,
  54732. const CImgList<tc>& colors,
  54733. const to& opacities,
  54734. const bool centering=true,
  54735. const int render_static=4, const int render_motion=1,
  54736. const bool is_double_sided=true, const float focale=700,
  54737. const float light_x=0, const float light_y=0, const float light_z=-5e8f,
  54738. const float specular_lightness=0.2f, const float specular_shininess=0.1f,
  54739. const bool display_axes=true, float *const pose_matrix=0,
  54740. const bool exit_on_anykey=false) const {
  54741. CImgDisplay disp;
  54742. return _display_object3d(disp,title,vertices,primitives,colors,opacities,centering,render_static,
  54743. render_motion,is_double_sided,focale,
  54744. light_x,light_y,light_z,specular_lightness,specular_shininess,
  54745. display_axes,pose_matrix,exit_on_anykey);
  54746. }
  54747. //! Display object 3D in an interactive window \simplification.
  54748. template<typename tp, typename tf, typename tc>
  54749. const CImg<T>& display_object3d(CImgDisplay &disp,
  54750. const CImg<tp>& vertices,
  54751. const CImgList<tf>& primitives,
  54752. const CImgList<tc>& colors,
  54753. const bool centering=true,
  54754. const int render_static=4, const int render_motion=1,
  54755. const bool is_double_sided=true, const float focale=700,
  54756. const float light_x=0, const float light_y=0, const float light_z=-5e8f,
  54757. const float specular_lightness=0.2f, const float specular_shininess=0.1f,
  54758. const bool display_axes=true, float *const pose_matrix=0,
  54759. const bool exit_on_anykey=false) const {
  54760. return display_object3d(disp,vertices,primitives,colors,CImgList<floatT>(),centering,
  54761. render_static,render_motion,is_double_sided,focale,
  54762. light_x,light_y,light_z,specular_lightness,specular_shininess,
  54763. display_axes,pose_matrix,exit_on_anykey);
  54764. }
  54765. //! Display object 3D in an interactive window \simplification.
  54766. template<typename tp, typename tf, typename tc>
  54767. const CImg<T>& display_object3d(const char *const title,
  54768. const CImg<tp>& vertices,
  54769. const CImgList<tf>& primitives,
  54770. const CImgList<tc>& colors,
  54771. const bool centering=true,
  54772. const int render_static=4, const int render_motion=1,
  54773. const bool is_double_sided=true, const float focale=700,
  54774. const float light_x=0, const float light_y=0, const float light_z=-5e8f,
  54775. const float specular_lightness=0.2f, const float specular_shininess=0.1f,
  54776. const bool display_axes=true, float *const pose_matrix=0,
  54777. const bool exit_on_anykey=false) const {
  54778. return display_object3d(title,vertices,primitives,colors,CImgList<floatT>(),centering,
  54779. render_static,render_motion,is_double_sided,focale,
  54780. light_x,light_y,light_z,specular_lightness,specular_shininess,
  54781. display_axes,pose_matrix,exit_on_anykey);
  54782. }
  54783. //! Display object 3D in an interactive window \simplification.
  54784. template<typename tp, typename tf>
  54785. const CImg<T>& display_object3d(CImgDisplay &disp,
  54786. const CImg<tp>& vertices,
  54787. const CImgList<tf>& primitives,
  54788. const bool centering=true,
  54789. const int render_static=4, const int render_motion=1,
  54790. const bool is_double_sided=true, const float focale=700,
  54791. const float light_x=0, const float light_y=0, const float light_z=-5e8f,
  54792. const float specular_lightness=0.2f, const float specular_shininess=0.1f,
  54793. const bool display_axes=true, float *const pose_matrix=0,
  54794. const bool exit_on_anykey=false) const {
  54795. return display_object3d(disp,vertices,primitives,CImgList<T>(),centering,
  54796. render_static,render_motion,is_double_sided,focale,
  54797. light_x,light_y,light_z,specular_lightness,specular_shininess,
  54798. display_axes,pose_matrix,exit_on_anykey);
  54799. }
  54800. //! Display object 3D in an interactive window \simplification.
  54801. template<typename tp, typename tf>
  54802. const CImg<T>& display_object3d(const char *const title,
  54803. const CImg<tp>& vertices,
  54804. const CImgList<tf>& primitives,
  54805. const bool centering=true,
  54806. const int render_static=4, const int render_motion=1,
  54807. const bool is_double_sided=true, const float focale=700,
  54808. const float light_x=0, const float light_y=0, const float light_z=-5e8f,
  54809. const float specular_lightness=0.2f, const float specular_shininess=0.1f,
  54810. const bool display_axes=true, float *const pose_matrix=0,
  54811. const bool exit_on_anykey=false) const {
  54812. return display_object3d(title,vertices,primitives,CImgList<T>(),centering,
  54813. render_static,render_motion,is_double_sided,focale,
  54814. light_x,light_y,light_z,specular_lightness,specular_shininess,
  54815. display_axes,pose_matrix,exit_on_anykey);
  54816. }
  54817. //! Display object 3D in an interactive window \simplification.
  54818. template<typename tp>
  54819. const CImg<T>& display_object3d(CImgDisplay &disp,
  54820. const CImg<tp>& vertices,
  54821. const bool centering=true,
  54822. const int render_static=4, const int render_motion=1,
  54823. const bool is_double_sided=true, const float focale=700,
  54824. const float light_x=0, const float light_y=0, const float light_z=-5e8f,
  54825. const float specular_lightness=0.2f, const float specular_shininess=0.1f,
  54826. const bool display_axes=true, float *const pose_matrix=0,
  54827. const bool exit_on_anykey=false) const {
  54828. return display_object3d(disp,vertices,CImgList<uintT>(),centering,
  54829. render_static,render_motion,is_double_sided,focale,
  54830. light_x,light_y,light_z,specular_lightness,specular_shininess,
  54831. display_axes,pose_matrix,exit_on_anykey);
  54832. }
  54833. //! Display object 3D in an interactive window \simplification.
  54834. template<typename tp>
  54835. const CImg<T>& display_object3d(const char *const title,
  54836. const CImg<tp>& vertices,
  54837. const bool centering=true,
  54838. const int render_static=4, const int render_motion=1,
  54839. const bool is_double_sided=true, const float focale=700,
  54840. const float light_x=0, const float light_y=0, const float light_z=-5e8f,
  54841. const float specular_lightness=0.2f, const float specular_shininess=0.1f,
  54842. const bool display_axes=true, float *const pose_matrix=0,
  54843. const bool exit_on_anykey=false) const {
  54844. return display_object3d(title,vertices,CImgList<uintT>(),centering,
  54845. render_static,render_motion,is_double_sided,focale,
  54846. light_x,light_y,light_z,specular_lightness,specular_shininess,
  54847. display_axes,pose_matrix,exit_on_anykey);
  54848. }
  54849. template<typename tp, typename tf, typename tc, typename to>
  54850. const CImg<T>& _display_object3d(CImgDisplay& disp, const char *const title,
  54851. const CImg<tp>& vertices,
  54852. const CImgList<tf>& primitives,
  54853. const CImgList<tc>& colors,
  54854. const to& opacities,
  54855. const bool centering,
  54856. const int render_static, const int render_motion,
  54857. const bool is_double_sided, const float focale,
  54858. const float light_x, const float light_y, const float light_z,
  54859. const float specular_lightness, const float specular_shininess,
  54860. const bool display_axes, float *const pose_matrix,
  54861. const bool exit_on_anykey) const {
  54862. typedef typename cimg::superset<tp,float>::type tpfloat;
  54863. // Check input arguments
  54864. if (is_empty()) {
  54865. CImg<T> background;
  54866. if (colors && colors[0].size()==1) background.assign(1,2,1,1,64,128);
  54867. else background.assign(1,2,1,3,32,64,32,116,64,96);
  54868. if (disp) background.resize(disp.width(),disp.height(),1,-100,3);
  54869. else background.resize(cimg_fitscreen(CImgDisplay::screen_width()/2,
  54870. CImgDisplay::screen_height()/2,1),1,-100,3);
  54871. return background._display_object3d(disp,title,vertices,primitives,colors,opacities,centering,
  54872. render_static,render_motion,is_double_sided,focale,
  54873. light_x,light_y,light_z,specular_lightness,specular_shininess,
  54874. display_axes,pose_matrix,exit_on_anykey);
  54875. } else { if (disp) disp.resize(*this,false); }
  54876. CImg<charT> error_message(1024);
  54877. if (!vertices.is_object3d(primitives,colors,opacities,true,error_message))
  54878. throw CImgArgumentException(_cimg_instance
  54879. "display_object3d(): Invalid specified 3D object (%u,%u) (%s).",
  54880. cimg_instance,vertices._width,primitives._width,error_message.data());
  54881. if (vertices._width && !primitives) {
  54882. CImgList<tf> nprimitives(vertices._width,1,1,1,1);
  54883. cimglist_for(nprimitives,l) nprimitives(l,0) = (tf)l;
  54884. return _display_object3d(disp,title,vertices,nprimitives,colors,opacities,centering,
  54885. render_static,render_motion,is_double_sided,focale,
  54886. light_x,light_y,light_z,specular_lightness,specular_shininess,
  54887. display_axes,pose_matrix,exit_on_anykey);
  54888. }
  54889. if (!disp) {
  54890. disp.assign(cimg_fitscreen(_width,_height,_depth),title?title:0,3);
  54891. if (!title) disp.set_title("CImg<%s> (%u vertices, %u primitives)",
  54892. pixel_type(),vertices._width,primitives._width);
  54893. } else if (title) disp.set_title("%s",title);
  54894. // Init 3D objects and compute object statistics
  54895. CImg<floatT>
  54896. pose,
  54897. rotated_vertices(vertices._width,3),
  54898. bbox_vertices, rotated_bbox_vertices,
  54899. axes_vertices, rotated_axes_vertices,
  54900. bbox_opacities, axes_opacities;
  54901. CImgList<uintT> bbox_primitives, axes_primitives;
  54902. CImgList<tf> reverse_primitives;
  54903. CImgList<T> bbox_colors, bbox_colors2, axes_colors;
  54904. unsigned int ns_width = 0, ns_height = 0;
  54905. int _is_double_sided = (int)is_double_sided;
  54906. bool ndisplay_axes = display_axes;
  54907. const CImg<T>
  54908. background_color(1,1,1,_spectrum,0),
  54909. foreground_color(1,1,1,_spectrum,(T)std::min((int)cimg::type<T>::max(),255));
  54910. float
  54911. Xoff = 0, Yoff = 0, Zoff = 0, sprite_scale = 1,
  54912. xm = 0, xM = vertices?vertices.get_shared_row(0).max_min(xm):0,
  54913. ym = 0, yM = vertices?vertices.get_shared_row(1).max_min(ym):0,
  54914. zm = 0, zM = vertices?vertices.get_shared_row(2).max_min(zm):0;
  54915. const float delta = cimg::max(xM - xm,yM - ym,zM - zm);
  54916. rotated_bbox_vertices = bbox_vertices.assign(8,3,1,1,
  54917. xm,xM,xM,xm,xm,xM,xM,xm,
  54918. ym,ym,yM,yM,ym,ym,yM,yM,
  54919. zm,zm,zm,zm,zM,zM,zM,zM);
  54920. 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);
  54921. bbox_colors.assign(6,_spectrum,1,1,1,background_color[0]);
  54922. bbox_colors2.assign(6,_spectrum,1,1,1,foreground_color[0]);
  54923. bbox_opacities.assign(bbox_colors._width,1,1,1,0.3f);
  54924. rotated_axes_vertices = axes_vertices.assign(7,3,1,1,
  54925. 0,20,0,0,22,-6,-6,
  54926. 0,0,20,0,-6,22,-6,
  54927. 0,0,0,20,0,0,22);
  54928. axes_opacities.assign(3,1,1,1,1);
  54929. axes_colors.assign(3,_spectrum,1,1,1,foreground_color[0]);
  54930. axes_primitives.assign(3,1,2,1,1, 0,1, 0,2, 0,3);
  54931. // Begin user interaction loop
  54932. CImg<T> visu0(*this,false), visu;
  54933. CImg<tpfloat> zbuffer(visu0.width(),visu0.height(),1,1,0);
  54934. bool init_pose = true, clicked = false, redraw = true;
  54935. unsigned int key = 0, font_size = 32;
  54936. int
  54937. x0 = 0, y0 = 0, x1 = 0, y1 = 0,
  54938. nrender_static = render_static,
  54939. nrender_motion = render_motion;
  54940. disp.show().flush();
  54941. while (!disp.is_closed() && !key) {
  54942. // Init object pose
  54943. if (init_pose) {
  54944. const float
  54945. ratio = delta>0?(2.f*std::min(disp.width(),disp.height())/(3.f*delta)):1,
  54946. dx = (xM + xm)/2, dy = (yM + ym)/2, dz = (zM + zm)/2;
  54947. if (centering)
  54948. CImg<floatT>(4,3,1,1, ratio,0.,0.,-ratio*dx, 0.,ratio,0.,-ratio*dy, 0.,0.,ratio,-ratio*dz).move_to(pose);
  54949. else CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0).move_to(pose);
  54950. if (pose_matrix) {
  54951. CImg<floatT> pose0(pose_matrix,4,3,1,1,false);
  54952. pose0.resize(4,4,1,1,0); pose.resize(4,4,1,1,0);
  54953. pose0(3,3) = pose(3,3) = 1;
  54954. (pose0*pose).get_crop(0,0,3,2).move_to(pose);
  54955. Xoff = pose_matrix[12]; Yoff = pose_matrix[13]; Zoff = pose_matrix[14]; sprite_scale = pose_matrix[15];
  54956. } else { Xoff = Yoff = Zoff = 0; sprite_scale = 1; }
  54957. init_pose = false;
  54958. redraw = true;
  54959. }
  54960. // Rotate and draw 3D object
  54961. if (redraw) {
  54962. const float
  54963. r00 = pose(0,0), r10 = pose(1,0), r20 = pose(2,0), r30 = pose(3,0),
  54964. r01 = pose(0,1), r11 = pose(1,1), r21 = pose(2,1), r31 = pose(3,1),
  54965. r02 = pose(0,2), r12 = pose(1,2), r22 = pose(2,2), r32 = pose(3,2);
  54966. if ((clicked && nrender_motion>=0) || (!clicked && nrender_static>=0)) {
  54967. const tp *const pv0 = vertices.data(), *const pv1 = vertices.data(0,1), *const pv2 = vertices.data(0,2);
  54968. float
  54969. *const prv0 = rotated_vertices.data(),
  54970. *const prv1 = rotated_vertices.data(0,1),
  54971. *const prv2 = rotated_vertices.data(0,2);
  54972. cimg_pragma_openmp(parallel for cimg_openmp_if(vertices.width()>(cimg_openmp_sizefactor)*1024))
  54973. cimg_forX(vertices,l) {
  54974. const float x = (float)pv0[l], y = (float)pv1[l], z = (float)pv2[l];
  54975. prv0[l] = r00*x + r10*y + r20*z + r30;
  54976. prv1[l] = r01*x + r11*y + r21*z + r31;
  54977. prv2[l] = r02*x + r12*y + r22*z + r32;
  54978. }
  54979. }
  54980. else cimg_forX(bbox_vertices,l) {
  54981. const float x = bbox_vertices(l,0), y = bbox_vertices(l,1), z = bbox_vertices(l,2);
  54982. rotated_bbox_vertices(l,0) = r00*x + r10*y + r20*z + r30;
  54983. rotated_bbox_vertices(l,1) = r01*x + r11*y + r21*z + r31;
  54984. rotated_bbox_vertices(l,2) = r02*x + r12*y + r22*z + r32;
  54985. }
  54986. // Draw objects
  54987. const bool render_with_zbuffer = !clicked && nrender_static>0;
  54988. visu = visu0;
  54989. if ((clicked && nrender_motion<0) || (!clicked && nrender_static<0))
  54990. visu.draw_object3d(Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
  54991. rotated_bbox_vertices,bbox_primitives,bbox_colors,bbox_opacities,2,false,focale).
  54992. draw_object3d(Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
  54993. rotated_bbox_vertices,bbox_primitives,bbox_colors2,1,false,focale);
  54994. else visu._draw_object3d((void*)0,render_with_zbuffer?zbuffer.fill(0):CImg<tpfloat>::empty(),
  54995. Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
  54996. rotated_vertices,reverse_primitives?reverse_primitives:primitives,
  54997. colors,opacities,clicked?nrender_motion:nrender_static,_is_double_sided==1,focale,
  54998. width()/2.f + light_x,height()/2.f + light_y,light_z + Zoff,
  54999. specular_lightness,specular_shininess,1,sprite_scale);
  55000. // Draw axes
  55001. if (ndisplay_axes) {
  55002. const float
  55003. n = 1e-8f + cimg::hypot(r00,r01,r02),
  55004. _r00 = r00/n, _r10 = r10/n, _r20 = r20/n,
  55005. _r01 = r01/n, _r11 = r11/n, _r21 = r21/n,
  55006. _r02 = r01/n, _r12 = r12/n, _r22 = r22/n,
  55007. Xaxes = 25, Yaxes = visu._height - 38.f;
  55008. cimg_forX(axes_vertices,l) {
  55009. const float
  55010. x = axes_vertices(l,0),
  55011. y = axes_vertices(l,1),
  55012. z = axes_vertices(l,2);
  55013. rotated_axes_vertices(l,0) = _r00*x + _r10*y + _r20*z;
  55014. rotated_axes_vertices(l,1) = _r01*x + _r11*y + _r21*z;
  55015. rotated_axes_vertices(l,2) = _r02*x + _r12*y + _r22*z;
  55016. }
  55017. axes_opacities(0,0) = (rotated_axes_vertices(1,2)>0)?0.5f:1.f;
  55018. axes_opacities(1,0) = (rotated_axes_vertices(2,2)>0)?0.5f:1.f;
  55019. axes_opacities(2,0) = (rotated_axes_vertices(3,2)>0)?0.5f:1.f;
  55020. visu.draw_object3d(Xaxes,Yaxes,0,rotated_axes_vertices,axes_primitives,
  55021. axes_colors,axes_opacities,1,false,focale).
  55022. draw_text((int)(Xaxes + rotated_axes_vertices(4,0)),
  55023. (int)(Yaxes + rotated_axes_vertices(4,1)),
  55024. "X",axes_colors[0]._data,0,axes_opacities(0,0),13).
  55025. draw_text((int)(Xaxes + rotated_axes_vertices(5,0)),
  55026. (int)(Yaxes + rotated_axes_vertices(5,1)),
  55027. "Y",axes_colors[1]._data,0,axes_opacities(1,0),13).
  55028. draw_text((int)(Xaxes + rotated_axes_vertices(6,0)),
  55029. (int)(Yaxes + rotated_axes_vertices(6,1)),
  55030. "Z",axes_colors[2]._data,0,axes_opacities(2,0),13);
  55031. }
  55032. visu.display(disp);
  55033. if (!clicked || nrender_motion==nrender_static) redraw = false;
  55034. }
  55035. // Handle user interaction
  55036. if (!redraw) disp.wait();
  55037. if ((disp.button() || disp.wheel()) && disp.mouse_x()>=0 && disp.mouse_y()>=0) {
  55038. redraw = true;
  55039. if (!clicked) { x0 = x1 = disp.mouse_x(); y0 = y1 = disp.mouse_y(); if (!disp.wheel()) clicked = true; }
  55040. else { x1 = disp.mouse_x(); y1 = disp.mouse_y(); }
  55041. const bool is_keyCTRL = disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT();
  55042. if (disp.button()&1 && !is_keyCTRL) {
  55043. const float
  55044. R = 0.45f*std::min(disp.width(),disp.height()),
  55045. R2 = R*R,
  55046. u0 = (float)(x0 - disp.width()/2),
  55047. v0 = (float)(y0 - disp.height()/2),
  55048. u1 = (float)(x1 - disp.width()/2),
  55049. v1 = (float)(y1 - disp.height()/2),
  55050. n0 = cimg::hypot(u0,v0),
  55051. n1 = cimg::hypot(u1,v1),
  55052. nu0 = n0>R?(u0*R/n0):u0,
  55053. nv0 = n0>R?(v0*R/n0):v0,
  55054. nw0 = (float)std::sqrt(std::max(0.f,R2 - nu0*nu0 - nv0*nv0)),
  55055. nu1 = n1>R?(u1*R/n1):u1,
  55056. nv1 = n1>R?(v1*R/n1):v1,
  55057. nw1 = (float)std::sqrt(std::max(0.f,R2 - nu1*nu1 - nv1*nv1)),
  55058. u = nv0*nw1 - nw0*nv1,
  55059. v = nw0*nu1 - nu0*nw1,
  55060. w = nv0*nu1 - nu0*nv1,
  55061. n = cimg::hypot(u,v,w),
  55062. alpha = (float)std::asin(n/R2)*180/cimg::PI;
  55063. (CImg<floatT>::rotation_matrix(u,v,w,-alpha)*pose).move_to(pose);
  55064. x0 = x1; y0 = y1;
  55065. }
  55066. if (disp.button()&2 && !is_keyCTRL) {
  55067. if (focale>0) Zoff-=(y0 - y1)*focale/400;
  55068. else { const float s = std::exp((y0 - y1)/400.f); pose*=s; sprite_scale*=s; }
  55069. x0 = x1; y0 = y1;
  55070. }
  55071. if (disp.wheel()) {
  55072. if (focale>0) Zoff-=disp.wheel()*focale/20;
  55073. else { const float s = std::exp(disp.wheel()/20.f); pose*=s; sprite_scale*=s; }
  55074. disp.set_wheel();
  55075. }
  55076. if (disp.button()&4 || (disp.button()&1 && is_keyCTRL)) {
  55077. Xoff+=(x1 - x0); Yoff+=(y1 - y0); x0 = x1; y0 = y1;
  55078. }
  55079. if ((disp.button()&1) && (disp.button()&2) && !is_keyCTRL) {
  55080. init_pose = true; disp.set_button(); x0 = x1; y0 = y1;
  55081. pose = CImg<floatT>(4,3,1,1, 1,0,0,0, 0,1,0,0, 0,0,1,0);
  55082. }
  55083. } else if (clicked) { x0 = x1; y0 = y1; clicked = false; redraw = true; }
  55084. CImg<charT> filename(32);
  55085. switch (key = disp.key()) {
  55086. #if cimg_OS!=2
  55087. case cimg::keyCTRLRIGHT :
  55088. #endif
  55089. case 0 : case cimg::keyCTRLLEFT : key = 0; break;
  55090. case cimg::keyD: if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  55091. disp.set_fullscreen(false).
  55092. resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
  55093. CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
  55094. _is_resized = true;
  55095. disp.set_key(key,false); key = 0;
  55096. } break;
  55097. case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  55098. disp.set_fullscreen(false).
  55099. resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
  55100. disp.set_key(key,false); key = 0;
  55101. } break;
  55102. case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  55103. disp.set_fullscreen(false).resize(cimg_fitscreen(_width,_height,_depth),false)._is_resized = true;
  55104. disp.set_key(key,false); key = 0;
  55105. } break;
  55106. case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  55107. if (!ns_width || !ns_height ||
  55108. ns_width>(unsigned int)disp.screen_width() || ns_height>(unsigned int)disp.screen_height()) {
  55109. ns_width = disp.screen_width()*3U/4;
  55110. ns_height = disp.screen_height()*3U/4;
  55111. }
  55112. if (disp.is_fullscreen()) disp.resize(ns_width,ns_height,false);
  55113. else {
  55114. ns_width = disp._width; ns_height = disp._height;
  55115. disp.resize(disp.screen_width(),disp.screen_height(),false);
  55116. }
  55117. disp.toggle_fullscreen()._is_resized = true;
  55118. disp.set_key(key,false); key = 0;
  55119. } break;
  55120. case cimg::keyT : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  55121. // Switch single/double-sided primitives.
  55122. if (--_is_double_sided==-2) _is_double_sided = 1;
  55123. if (_is_double_sided>=0) reverse_primitives.assign();
  55124. else primitives.get_reverse_object3d().move_to(reverse_primitives);
  55125. disp.set_key(key,false); key = 0; redraw = true;
  55126. } break;
  55127. case cimg::keyZ : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Enable/disable Z-buffer
  55128. if (zbuffer) zbuffer.assign();
  55129. else zbuffer.assign(visu0.width(),visu0.height(),1,1,0);
  55130. disp.set_key(key,false); key = 0; redraw = true;
  55131. } break;
  55132. case cimg::keyX : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Show/hide 3D axes
  55133. ndisplay_axes = !ndisplay_axes;
  55134. disp.set_key(key,false); key = 0; redraw = true;
  55135. } break;
  55136. case cimg::keyF1 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to points
  55137. nrender_motion = (nrender_static==0 && nrender_motion!=0)?0:-1; nrender_static = 0;
  55138. disp.set_key(key,false); key = 0; redraw = true;
  55139. } break;
  55140. case cimg::keyF2 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to lines
  55141. nrender_motion = (nrender_static==1 && nrender_motion!=1)?1:-1; nrender_static = 1;
  55142. disp.set_key(key,false); key = 0; redraw = true;
  55143. } break;
  55144. case cimg::keyF3 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat
  55145. nrender_motion = (nrender_static==2 && nrender_motion!=2)?2:-1; nrender_static = 2;
  55146. disp.set_key(key,false); key = 0; redraw = true;
  55147. } break;
  55148. case cimg::keyF4 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to flat-shaded
  55149. nrender_motion = (nrender_static==3 && nrender_motion!=3)?3:-1; nrender_static = 3;
  55150. disp.set_key(key,false); key = 0; redraw = true;
  55151. } break;
  55152. case cimg::keyF5 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  55153. // Set rendering mode to gouraud-shaded.
  55154. nrender_motion = (nrender_static==4 && nrender_motion!=4)?4:-1; nrender_static = 4;
  55155. disp.set_key(key,false); key = 0; redraw = true;
  55156. } break;
  55157. case cimg::keyF6 : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Set rendering mode to phong-shaded
  55158. nrender_motion = (nrender_static==5 && nrender_motion!=5)?5:-1; nrender_static = 5;
  55159. disp.set_key(key,false); key = 0; redraw = true;
  55160. } break;
  55161. case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save snapshot
  55162. static unsigned int snap_number = 0;
  55163. std::FILE *file;
  55164. do {
  55165. cimg_snprintf(filename,filename._width,cimg_appname "_%.6u."
  55166. #ifdef cimg_use_png
  55167. "png",
  55168. #else
  55169. "bmp",
  55170. #endif
  55171. snap_number++);
  55172. if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
  55173. } while (file);
  55174. (+visu).__draw_text(" Saving snapshot... ",font_size,0).display(disp);
  55175. visu.save(filename);
  55176. (+visu).__draw_text(" Snapshot '%s' saved. ",font_size,0,filename._data).display(disp);
  55177. disp.set_key(key,false); key = 0;
  55178. } break;
  55179. case cimg::keyG : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .off file
  55180. static unsigned int snap_number = 0;
  55181. std::FILE *file;
  55182. do {
  55183. cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.off",snap_number++);
  55184. if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
  55185. } while (file);
  55186. (+visu).__draw_text(" Saving object... ",font_size,0).display(disp);
  55187. vertices.save_off(reverse_primitives?reverse_primitives:primitives,colors,filename);
  55188. (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp);
  55189. disp.set_key(key,false); key = 0;
  55190. } break;
  55191. case cimg::keyO : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .cimg file
  55192. static unsigned int snap_number = 0;
  55193. std::FILE *file;
  55194. do {
  55195. #ifdef cimg_use_zlib
  55196. cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimgz",snap_number++);
  55197. #else
  55198. cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimg",snap_number++);
  55199. #endif
  55200. if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
  55201. } while (file);
  55202. (+visu).__draw_text(" Saving object... ",font_size,0).display(disp);
  55203. vertices.get_object3dtoCImg3d(reverse_primitives?reverse_primitives:primitives,colors,opacities).
  55204. save(filename);
  55205. (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp);
  55206. disp.set_key(key,false); key = 0;
  55207. } break;
  55208. #ifdef cimg_use_board
  55209. case cimg::keyP : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .EPS file
  55210. static unsigned int snap_number = 0;
  55211. std::FILE *file;
  55212. do {
  55213. cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.eps",snap_number++);
  55214. if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
  55215. } while (file);
  55216. (+visu).__draw_text(" Saving EPS snapshot... ",font_size,0).display(disp);
  55217. LibBoard::Board board;
  55218. (+visu)._draw_object3d(&board,zbuffer.fill(0),
  55219. Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
  55220. rotated_vertices,reverse_primitives?reverse_primitives:primitives,
  55221. colors,opacities,clicked?nrender_motion:nrender_static,
  55222. _is_double_sided==1,focale,
  55223. visu.width()/2.f + light_x,visu.height()/2.f + light_y,light_z + Zoff,
  55224. specular_lightness,specular_shininess,1,
  55225. sprite_scale);
  55226. board.saveEPS(filename);
  55227. (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp);
  55228. disp.set_key(key,false); key = 0;
  55229. } break;
  55230. case cimg::keyV : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) { // Save object as a .SVG file
  55231. static unsigned int snap_number = 0;
  55232. std::FILE *file;
  55233. do {
  55234. cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.svg",snap_number++);
  55235. if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
  55236. } while (file);
  55237. (+visu).__draw_text(" Saving SVG snapshot... ",font_size,0).display(disp);
  55238. LibBoard::Board board;
  55239. (+visu)._draw_object3d(&board,zbuffer.fill(0),
  55240. Xoff + visu._width/2.f,Yoff + visu._height/2.f,Zoff,
  55241. rotated_vertices,reverse_primitives?reverse_primitives:primitives,
  55242. colors,opacities,clicked?nrender_motion:nrender_static,
  55243. _is_double_sided==1,focale,
  55244. visu.width()/2.f + light_x,visu.height()/2.f + light_y,light_z + Zoff,
  55245. specular_lightness,specular_shininess,1,
  55246. sprite_scale);
  55247. board.saveSVG(filename);
  55248. (+visu).__draw_text(" Object '%s' saved. ",font_size,0,filename._data).display(disp);
  55249. disp.set_key(key,false); key = 0;
  55250. } break;
  55251. #endif
  55252. }
  55253. if (disp.is_resized()) {
  55254. disp.resize(false); visu0 = get_resize(disp,1);
  55255. if (zbuffer) zbuffer.assign(disp.width(),disp.height());
  55256. redraw = true;
  55257. }
  55258. if (!exit_on_anykey && key && key!=cimg::keyESC &&
  55259. (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
  55260. key = 0;
  55261. }
  55262. }
  55263. if (pose_matrix) {
  55264. std::memcpy(pose_matrix,pose._data,12*sizeof(float));
  55265. pose_matrix[12] = Xoff; pose_matrix[13] = Yoff; pose_matrix[14] = Zoff; pose_matrix[15] = sprite_scale;
  55266. }
  55267. disp.set_button().set_key(key);
  55268. return *this;
  55269. }
  55270. //! Display 1D graph in an interactive window.
  55271. /**
  55272. \param disp Display window.
  55273. \param plot_type Plot type. Can be <tt>{ 0=points | 1=segments | 2=splines | 3=bars }</tt>.
  55274. \param vertex_type Vertex type.
  55275. \param labelx Title for the horizontal axis, as a C-string.
  55276. \param xmin Minimum value along the X-axis.
  55277. \param xmax Maximum value along the X-axis.
  55278. \param labely Title for the vertical axis, as a C-string.
  55279. \param ymin Minimum value along the X-axis.
  55280. \param ymax Maximum value along the X-axis.
  55281. \param exit_on_anykey Exit function when any key is pressed.
  55282. **/
  55283. const CImg<T>& display_graph(CImgDisplay &disp,
  55284. const unsigned int plot_type=1, const unsigned int vertex_type=1,
  55285. const char *const labelx=0, const double xmin=0, const double xmax=0,
  55286. const char *const labely=0, const double ymin=0, const double ymax=0,
  55287. const bool exit_on_anykey=false) const {
  55288. return _display_graph(disp,0,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey);
  55289. }
  55290. //! Display 1D graph in an interactive window \overloading.
  55291. const CImg<T>& display_graph(const char *const title=0,
  55292. const unsigned int plot_type=1, const unsigned int vertex_type=1,
  55293. const char *const labelx=0, const double xmin=0, const double xmax=0,
  55294. const char *const labely=0, const double ymin=0, const double ymax=0,
  55295. const bool exit_on_anykey=false) const {
  55296. CImgDisplay disp;
  55297. return _display_graph(disp,title,plot_type,vertex_type,labelx,xmin,xmax,labely,ymin,ymax,exit_on_anykey);
  55298. }
  55299. const CImg<T>& _display_graph(CImgDisplay &disp, const char *const title=0,
  55300. const unsigned int plot_type=1, const unsigned int vertex_type=1,
  55301. const char *const labelx=0, const double xmin=0, const double xmax=0,
  55302. const char *const labely=0, const double ymin=0, const double ymax=0,
  55303. const bool exit_on_anykey=false) const {
  55304. if (is_empty())
  55305. throw CImgInstanceException(_cimg_instance
  55306. "display_graph(): Empty instance.",
  55307. cimg_instance);
  55308. if (!disp) disp.assign(cimg_fitscreen(CImgDisplay::screen_width()/2,CImgDisplay::screen_height()/2,1),0,0).
  55309. set_title(title?"%s":"CImg<%s>",title?title:pixel_type());
  55310. const ulongT siz = (ulongT)_width*_height*_depth, siz1 = std::max((ulongT)1,siz - 1);
  55311. const unsigned int old_normalization = disp.normalization();
  55312. disp.show().flush()._normalization = 0;
  55313. double y0 = ymin, y1 = ymax, nxmin = xmin, nxmax = xmax;
  55314. if (nxmin==nxmax) { nxmin = 0; nxmax = siz1; }
  55315. int x0 = 0, x1 = width()*height()*depth() - 1, key = 0;
  55316. for (bool reset_view = true; !key && !disp.is_closed(); ) {
  55317. if (reset_view) { x0 = 0; x1 = width()*height()*depth() - 1; y0 = ymin; y1 = ymax; reset_view = false; }
  55318. CImg<T> zoom(x1 - x0 + 1,1,1,spectrum());
  55319. cimg_forC(*this,c) zoom.get_shared_channel(c) = CImg<T>(data(x0,0,0,c),x1 - x0 + 1,1,1,1,true);
  55320. if (y0==y1) { y0 = zoom.min_max(y1); const double dy = y1 - y0; y0-=dy/20; y1+=dy/20; }
  55321. if (y0==y1) { --y0; ++y1; }
  55322. const CImg<intT> selection = zoom.get_select_graph(disp,plot_type,vertex_type,
  55323. labelx,
  55324. nxmin + x0*(nxmax - nxmin)/siz1,
  55325. nxmin + x1*(nxmax - nxmin)/siz1,
  55326. labely,y0,y1,true);
  55327. const int mouse_x = disp.mouse_x(), mouse_y = disp.mouse_y();
  55328. if (selection[0]>=0) {
  55329. if (selection[2]<0) reset_view = true;
  55330. else {
  55331. x1 = x0 + selection[2]; x0+=selection[0];
  55332. if (selection[1]>=0 && selection[3]>=0) {
  55333. y0 = y1 - selection[3]*(y1 - y0)/(disp.height() - 32);
  55334. y1-=selection[1]*(y1 - y0)/(disp.height() - 32);
  55335. }
  55336. }
  55337. } else {
  55338. bool go_in = false, go_out = false, go_left = false, go_right = false, go_up = false, go_down = false;
  55339. switch (key = (int)disp.key()) {
  55340. case cimg::keyHOME : reset_view = true; key = 0; disp.set_key(); break;
  55341. case cimg::keyPADADD : go_in = true; go_out = false; key = 0; disp.set_key(); break;
  55342. case cimg::keyPADSUB : go_out = true; go_in = false; key = 0; disp.set_key(); break;
  55343. case cimg::keyARROWLEFT : case cimg::keyPAD4 : go_left = true; go_right = false; key = 0; disp.set_key();
  55344. break;
  55345. case cimg::keyARROWRIGHT : case cimg::keyPAD6 : go_right = true; go_left = false; key = 0; disp.set_key();
  55346. break;
  55347. case cimg::keyARROWUP : case cimg::keyPAD8 : go_up = true; go_down = false; key = 0; disp.set_key(); break;
  55348. case cimg::keyARROWDOWN : case cimg::keyPAD2 : go_down = true; go_up = false; key = 0; disp.set_key(); break;
  55349. case cimg::keyPAD7 : go_left = true; go_up = true; key = 0; disp.set_key(); break;
  55350. case cimg::keyPAD9 : go_right = true; go_up = true; key = 0; disp.set_key(); break;
  55351. case cimg::keyPAD1 : go_left = true; go_down = true; key = 0; disp.set_key(); break;
  55352. case cimg::keyPAD3 : go_right = true; go_down = true; key = 0; disp.set_key(); break;
  55353. }
  55354. if (disp.wheel()) {
  55355. if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) go_up = !(go_down = disp.wheel()<0);
  55356. else if (disp.is_keySHIFTLEFT() || disp.is_keySHIFTRIGHT()) go_left = !(go_right = disp.wheel()>0);
  55357. else go_out = !(go_in = disp.wheel()>0);
  55358. key = 0;
  55359. }
  55360. if (go_in) {
  55361. const int
  55362. xsiz = x1 - x0,
  55363. mx = (mouse_x - 16)*xsiz/(disp.width() - 32),
  55364. cx = x0 + cimg::cut(mx,0,xsiz);
  55365. if (x1 - x0>4) {
  55366. x0 = cx - 7*(cx - x0)/8; x1 = cx + 7*(x1 - cx)/8;
  55367. if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  55368. const double
  55369. ysiz = y1 - y0,
  55370. my = (mouse_y - 16)*ysiz/(disp.height() - 32),
  55371. cy = y1 - cimg::cut(my,0.,ysiz);
  55372. y0 = cy - 7*(cy - y0)/8; y1 = cy + 7*(y1 - cy)/8;
  55373. } else y0 = y1 = 0;
  55374. }
  55375. }
  55376. if (go_out) {
  55377. if (x0>0 || x1<(int)siz1) {
  55378. const int delta_x = (x1 - x0)/8, ndelta_x = delta_x?delta_x:(siz>1);
  55379. const double ndelta_y = (y1 - y0)/8;
  55380. x0-=ndelta_x; x1+=ndelta_x;
  55381. y0-=ndelta_y; y1+=ndelta_y;
  55382. if (x0<0) { x1-=x0; x0 = 0; if (x1>=(int)siz) x1 = (int)siz1; }
  55383. if (x1>=(int)siz) { x0-=(x1 - siz1); x1 = (int)siz1; if (x0<0) x0 = 0; }
  55384. }
  55385. }
  55386. if (go_left) {
  55387. const int delta = (x1 - x0)/5, ndelta = delta?delta:1;
  55388. if (x0 - ndelta>=0) { x0-=ndelta; x1-=ndelta; }
  55389. else { x1-=x0; x0 = 0; }
  55390. go_left = false;
  55391. }
  55392. if (go_right) {
  55393. const int delta = (x1 - x0)/5, ndelta = delta?delta:1;
  55394. if (x1 + ndelta<(int)siz) { x0+=ndelta; x1+=ndelta; }
  55395. else { x0+=(siz1 - x1); x1 = (int)siz1; }
  55396. go_right = false;
  55397. }
  55398. if (go_up) {
  55399. const double delta = (y1 - y0)/10, ndelta = delta?delta:1;
  55400. y0+=ndelta; y1+=ndelta;
  55401. go_up = false;
  55402. }
  55403. if (go_down) {
  55404. const double delta = (y1 - y0)/10, ndelta = delta?delta:1;
  55405. y0-=ndelta; y1-=ndelta;
  55406. go_down = false;
  55407. }
  55408. }
  55409. if (!exit_on_anykey && key && key!=(int)cimg::keyESC &&
  55410. (key!=(int)cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
  55411. disp.set_key(key,false);
  55412. key = 0;
  55413. }
  55414. }
  55415. disp._normalization = old_normalization;
  55416. return *this;
  55417. }
  55418. //! Save image as a file.
  55419. /**
  55420. \param filename Filename, as a C-string.
  55421. \param number When positive, represents an index added to the filename. Otherwise, no number is added.
  55422. \param digits Number of digits used for adding the number to the filename.
  55423. \note
  55424. - The used file format is defined by the file extension in the filename \p filename.
  55425. - Parameter \p number can be used to add a 6-digit number to the filename before saving.
  55426. **/
  55427. const CImg<T>& save(const char *const filename, const int number=-1, const unsigned int digits=6) const {
  55428. if (!filename)
  55429. throw CImgArgumentException(_cimg_instance
  55430. "save(): Specified filename is (null).",
  55431. cimg_instance);
  55432. // Do not test for empty instances, since .cimg format is able to manage empty instances.
  55433. const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.');
  55434. const char *const ext = cimg::split_filename(filename);
  55435. CImg<charT> nfilename(1024);
  55436. const char *const fn = is_stdout?filename:(number>=0)?cimg::number_filename(filename,number,digits,nfilename):
  55437. filename;
  55438. #ifdef cimg_save_plugin
  55439. cimg_save_plugin(fn);
  55440. #endif
  55441. #ifdef cimg_save_plugin1
  55442. cimg_save_plugin1(fn);
  55443. #endif
  55444. #ifdef cimg_save_plugin2
  55445. cimg_save_plugin2(fn);
  55446. #endif
  55447. #ifdef cimg_save_plugin3
  55448. cimg_save_plugin3(fn);
  55449. #endif
  55450. #ifdef cimg_save_plugin4
  55451. cimg_save_plugin4(fn);
  55452. #endif
  55453. #ifdef cimg_save_plugin5
  55454. cimg_save_plugin5(fn);
  55455. #endif
  55456. #ifdef cimg_save_plugin6
  55457. cimg_save_plugin6(fn);
  55458. #endif
  55459. #ifdef cimg_save_plugin7
  55460. cimg_save_plugin7(fn);
  55461. #endif
  55462. #ifdef cimg_save_plugin8
  55463. cimg_save_plugin8(fn);
  55464. #endif
  55465. // Text formats
  55466. if (!cimg::strcasecmp(ext,"asc")) return save_ascii(fn);
  55467. else if (!cimg::strcasecmp(ext,"csv") ||
  55468. !cimg::strcasecmp(ext,"dlm") ||
  55469. !cimg::strcasecmp(ext,"txt")) return save_dlm(fn);
  55470. else if (!cimg::strcasecmp(ext,"cpp") ||
  55471. !cimg::strcasecmp(ext,"hpp") ||
  55472. !cimg::strcasecmp(ext,"h") ||
  55473. !cimg::strcasecmp(ext,"c")) return save_cpp(fn);
  55474. // 2D binary formats
  55475. else if (!cimg::strcasecmp(ext,"bmp")) return save_bmp(fn);
  55476. else if (!cimg::strcasecmp(ext,"jpg") ||
  55477. !cimg::strcasecmp(ext,"jpeg") ||
  55478. !cimg::strcasecmp(ext,"jpe") ||
  55479. !cimg::strcasecmp(ext,"jfif") ||
  55480. !cimg::strcasecmp(ext,"jif")) return save_jpeg(fn);
  55481. else if (!cimg::strcasecmp(ext,"rgb")) return save_rgb(fn);
  55482. else if (!cimg::strcasecmp(ext,"rgba")) return save_rgba(fn);
  55483. else if (!cimg::strcasecmp(ext,"png")) return save_png(fn);
  55484. else if (!cimg::strcasecmp(ext,"pgm") ||
  55485. !cimg::strcasecmp(ext,"ppm") ||
  55486. !cimg::strcasecmp(ext,"pnm")) return save_pnm(fn);
  55487. else if (!cimg::strcasecmp(ext,"pnk")) return save_pnk(fn);
  55488. else if (!cimg::strcasecmp(ext,"pfm")) return save_pfm(fn);
  55489. else if (!cimg::strcasecmp(ext,"exr")) return save_exr(fn);
  55490. else if (!cimg::strcasecmp(ext,"tif") ||
  55491. !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
  55492. // 3D binary formats
  55493. else if (!*ext) {
  55494. #ifdef cimg_use_zlib
  55495. return save_cimg(fn,true);
  55496. #else
  55497. return save_cimg(fn,false);
  55498. #endif
  55499. } else if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
  55500. else if (!cimg::strcasecmp(ext,"cimg")) return save_cimg(fn,false);
  55501. else if (!cimg::strcasecmp(ext,"dcm")) return save_medcon_external(fn);
  55502. else if (!cimg::strcasecmp(ext,"hdr") ||
  55503. !cimg::strcasecmp(ext,"nii")) return save_analyze(fn);
  55504. else if (!cimg::strcasecmp(ext,"inr")) return save_inr(fn);
  55505. else if (!cimg::strcasecmp(ext,"mnc")) return save_minc2(fn);
  55506. else if (!cimg::strcasecmp(ext,"pan")) return save_pandore(fn);
  55507. else if (!cimg::strcasecmp(ext,"raw")) return save_raw(fn);
  55508. // Archive files
  55509. else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
  55510. // Image sequences
  55511. else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true);
  55512. else if (!cimg::strcasecmp(ext,"avi") ||
  55513. !cimg::strcasecmp(ext,"mov") ||
  55514. !cimg::strcasecmp(ext,"asf") ||
  55515. !cimg::strcasecmp(ext,"divx") ||
  55516. !cimg::strcasecmp(ext,"flv") ||
  55517. !cimg::strcasecmp(ext,"mpg") ||
  55518. !cimg::strcasecmp(ext,"m1v") ||
  55519. !cimg::strcasecmp(ext,"m2v") ||
  55520. !cimg::strcasecmp(ext,"m4v") ||
  55521. !cimg::strcasecmp(ext,"mjp") ||
  55522. !cimg::strcasecmp(ext,"mp4") ||
  55523. !cimg::strcasecmp(ext,"mkv") ||
  55524. !cimg::strcasecmp(ext,"mpe") ||
  55525. !cimg::strcasecmp(ext,"movie") ||
  55526. !cimg::strcasecmp(ext,"ogm") ||
  55527. !cimg::strcasecmp(ext,"ogg") ||
  55528. !cimg::strcasecmp(ext,"ogv") ||
  55529. !cimg::strcasecmp(ext,"qt") ||
  55530. !cimg::strcasecmp(ext,"rm") ||
  55531. !cimg::strcasecmp(ext,"vob") ||
  55532. !cimg::strcasecmp(ext,"webm") ||
  55533. !cimg::strcasecmp(ext,"wmv") ||
  55534. !cimg::strcasecmp(ext,"xvid") ||
  55535. !cimg::strcasecmp(ext,"mpeg")) return save_video(fn);
  55536. return save_other(fn);
  55537. }
  55538. //! Save image as an ascii file.
  55539. /**
  55540. \param filename Filename, as a C-string.
  55541. **/
  55542. const CImg<T>& save_ascii(const char *const filename) const {
  55543. return _save_ascii(0,filename);
  55544. }
  55545. //! Save image as an Ascii file \overloading.
  55546. const CImg<T>& save_ascii(std::FILE *const file) const {
  55547. return _save_ascii(file,0);
  55548. }
  55549. const CImg<T>& _save_ascii(std::FILE *const file, const char *const filename) const {
  55550. if (!file && !filename)
  55551. throw CImgArgumentException(_cimg_instance
  55552. "save_ascii(): Specified filename is (null).",
  55553. cimg_instance);
  55554. std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
  55555. std::fprintf(nfile,"%u %u %u %u\n",_width,_height,_depth,_spectrum);
  55556. const T* ptrs = _data;
  55557. cimg_forYZC(*this,y,z,c) {
  55558. cimg_forX(*this,x) std::fprintf(nfile,"%.17g ",(double)*(ptrs++));
  55559. std::fputc('\n',nfile);
  55560. }
  55561. if (!file) cimg::fclose(nfile);
  55562. return *this;
  55563. }
  55564. //! Save image as a .cpp source file.
  55565. /**
  55566. \param filename Filename, as a C-string.
  55567. **/
  55568. const CImg<T>& save_cpp(const char *const filename) const {
  55569. return _save_cpp(0,filename);
  55570. }
  55571. //! Save image as a .cpp source file \overloading.
  55572. const CImg<T>& save_cpp(std::FILE *const file) const {
  55573. return _save_cpp(file,0);
  55574. }
  55575. const CImg<T>& _save_cpp(std::FILE *const file, const char *const filename) const {
  55576. if (!file && !filename)
  55577. throw CImgArgumentException(_cimg_instance
  55578. "save_cpp(): Specified filename is (null).",
  55579. cimg_instance);
  55580. std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
  55581. CImg<charT> varname(1024); *varname = 0;
  55582. if (filename) cimg_sscanf(cimg::basename(filename),"%1023[a-zA-Z0-9_]",varname._data);
  55583. if (!*varname) cimg_snprintf(varname,varname._width,"unnamed");
  55584. std::fprintf(nfile,
  55585. "/* Define image '%s' of size %ux%ux%ux%u and type '%s' */\n"
  55586. "%s data_%s[] = { %s\n ",
  55587. varname._data,_width,_height,_depth,_spectrum,pixel_type(),pixel_type(),varname._data,
  55588. is_empty()?"};":"");
  55589. if (!is_empty()) for (ulongT off = 0, siz = size() - 1; off<=siz; ++off) {
  55590. std::fprintf(nfile,cimg::type<T>::format(),cimg::type<T>::format((*this)[off]));
  55591. if (off==siz) std::fprintf(nfile," };\n");
  55592. else if (!((off + 1)%16)) std::fprintf(nfile,",\n ");
  55593. else std::fprintf(nfile,", ");
  55594. }
  55595. if (!file) cimg::fclose(nfile);
  55596. return *this;
  55597. }
  55598. //! Save image as a DLM file.
  55599. /**
  55600. \param filename Filename, as a C-string.
  55601. **/
  55602. const CImg<T>& save_dlm(const char *const filename) const {
  55603. return _save_dlm(0,filename);
  55604. }
  55605. //! Save image as a DLM file \overloading.
  55606. const CImg<T>& save_dlm(std::FILE *const file) const {
  55607. return _save_dlm(file,0);
  55608. }
  55609. const CImg<T>& _save_dlm(std::FILE *const file, const char *const filename) const {
  55610. if (!file && !filename)
  55611. throw CImgArgumentException(_cimg_instance
  55612. "save_dlm(): Specified filename is (null).",
  55613. cimg_instance);
  55614. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  55615. if (_depth>1)
  55616. cimg::warn(_cimg_instance
  55617. "save_dlm(): Instance is volumetric, values along Z will be unrolled in file '%s'.",
  55618. cimg_instance,
  55619. filename?filename:"(FILE*)");
  55620. if (_spectrum>1)
  55621. cimg::warn(_cimg_instance
  55622. "save_dlm(): Instance is multispectral, values along C will be unrolled in file '%s'.",
  55623. cimg_instance,
  55624. filename?filename:"(FILE*)");
  55625. std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
  55626. const T* ptrs = _data;
  55627. cimg_forYZC(*this,y,z,c) {
  55628. cimg_forX(*this,x) std::fprintf(nfile,"%.17g%s",(double)*(ptrs++),(x==width() - 1)?"":",");
  55629. std::fputc('\n',nfile);
  55630. }
  55631. if (!file) cimg::fclose(nfile);
  55632. return *this;
  55633. }
  55634. //! Save image as a BMP file.
  55635. /**
  55636. \param filename Filename, as a C-string.
  55637. **/
  55638. const CImg<T>& save_bmp(const char *const filename) const {
  55639. return _save_bmp(0,filename);
  55640. }
  55641. //! Save image as a BMP file \overloading.
  55642. const CImg<T>& save_bmp(std::FILE *const file) const {
  55643. return _save_bmp(file,0);
  55644. }
  55645. const CImg<T>& _save_bmp(std::FILE *const file, const char *const filename) const {
  55646. if (!file && !filename)
  55647. throw CImgArgumentException(_cimg_instance
  55648. "save_bmp(): Specified filename is (null).",
  55649. cimg_instance);
  55650. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  55651. if (_depth>1)
  55652. cimg::warn(_cimg_instance
  55653. "save_bmp(): Instance is volumetric, only the first slice will be saved in file '%s'.",
  55654. cimg_instance,
  55655. filename?filename:"(FILE*)");
  55656. if (_spectrum>3)
  55657. cimg::warn(_cimg_instance
  55658. "save_bmp(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
  55659. cimg_instance,
  55660. filename?filename:"(FILE*)");
  55661. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  55662. CImg<ucharT> header(54,1,1,1,0);
  55663. unsigned char align_buf[4] = {};
  55664. const unsigned int
  55665. align = (4 - (3*_width)%4)%4,
  55666. buf_size = (3*_width + align)*height(),
  55667. file_size = 54 + buf_size;
  55668. header[0] = 'B'; header[1] = 'M';
  55669. header[0x02] = file_size&0xFF;
  55670. header[0x03] = (file_size>>8)&0xFF;
  55671. header[0x04] = (file_size>>16)&0xFF;
  55672. header[0x05] = (file_size>>24)&0xFF;
  55673. header[0x0A] = 0x36;
  55674. header[0x0E] = 0x28;
  55675. header[0x12] = _width&0xFF;
  55676. header[0x13] = (_width>>8)&0xFF;
  55677. header[0x14] = (_width>>16)&0xFF;
  55678. header[0x15] = (_width>>24)&0xFF;
  55679. header[0x16] = _height&0xFF;
  55680. header[0x17] = (_height>>8)&0xFF;
  55681. header[0x18] = (_height>>16)&0xFF;
  55682. header[0x19] = (_height>>24)&0xFF;
  55683. header[0x1A] = 1;
  55684. header[0x1B] = 0;
  55685. header[0x1C] = 24;
  55686. header[0x1D] = 0;
  55687. header[0x22] = buf_size&0xFF;
  55688. header[0x23] = (buf_size>>8)&0xFF;
  55689. header[0x24] = (buf_size>>16)&0xFF;
  55690. header[0x25] = (buf_size>>24)&0xFF;
  55691. header[0x27] = 0x1;
  55692. header[0x2B] = 0x1;
  55693. cimg::fwrite(header._data,54,nfile);
  55694. const T
  55695. *ptr_r = data(0,_height - 1,0,0),
  55696. *ptr_g = (_spectrum>=2)?data(0,_height - 1,0,1):0,
  55697. *ptr_b = (_spectrum>=3)?data(0,_height - 1,0,2):0;
  55698. switch (_spectrum) {
  55699. case 1 : {
  55700. cimg_forY(*this,y) {
  55701. cimg_forX(*this,x) {
  55702. const unsigned char val = (unsigned char)*(ptr_r++);
  55703. std::fputc(val,nfile); std::fputc(val,nfile); std::fputc(val,nfile);
  55704. }
  55705. cimg::fwrite(align_buf,align,nfile);
  55706. ptr_r-=2*_width;
  55707. }
  55708. } break;
  55709. case 2 : {
  55710. cimg_forY(*this,y) {
  55711. cimg_forX(*this,x) {
  55712. std::fputc(0,nfile);
  55713. std::fputc((unsigned char)(*(ptr_g++)),nfile);
  55714. std::fputc((unsigned char)(*(ptr_r++)),nfile);
  55715. }
  55716. cimg::fwrite(align_buf,align,nfile);
  55717. ptr_r-=2*_width; ptr_g-=2*_width;
  55718. }
  55719. } break;
  55720. default : {
  55721. cimg_forY(*this,y) {
  55722. cimg_forX(*this,x) {
  55723. std::fputc((unsigned char)(*(ptr_b++)),nfile);
  55724. std::fputc((unsigned char)(*(ptr_g++)),nfile);
  55725. std::fputc((unsigned char)(*(ptr_r++)),nfile);
  55726. }
  55727. cimg::fwrite(align_buf,align,nfile);
  55728. ptr_r-=2*_width; ptr_g-=2*_width; ptr_b-=2*_width;
  55729. }
  55730. }
  55731. }
  55732. if (!file) cimg::fclose(nfile);
  55733. return *this;
  55734. }
  55735. //! Save image as a JPEG file.
  55736. /**
  55737. \param filename Filename, as a C-string.
  55738. \param quality Image quality (in %)
  55739. **/
  55740. const CImg<T>& save_jpeg(const char *const filename, const unsigned int quality=100) const {
  55741. return _save_jpeg(0,filename,quality);
  55742. }
  55743. //! Save image as a JPEG file \overloading.
  55744. const CImg<T>& save_jpeg(std::FILE *const file, const unsigned int quality=100) const {
  55745. return _save_jpeg(file,0,quality);
  55746. }
  55747. const CImg<T>& _save_jpeg(std::FILE *const file, const char *const filename, const unsigned int quality) const {
  55748. if (!file && !filename)
  55749. throw CImgArgumentException(_cimg_instance
  55750. "save_jpeg(): Specified filename is (null).",
  55751. cimg_instance);
  55752. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  55753. if (_depth>1)
  55754. cimg::warn(_cimg_instance
  55755. "save_jpeg(): Instance is volumetric, only the first slice will be saved in file '%s'.",
  55756. cimg_instance,
  55757. filename?filename:"(FILE*)");
  55758. #ifndef cimg_use_jpeg
  55759. if (!file) return save_other(filename,quality);
  55760. else throw CImgIOException(_cimg_instance
  55761. "save_jpeg(): Unable to save data in '(*FILE)' unless libjpeg is enabled.",
  55762. cimg_instance);
  55763. #else
  55764. unsigned int dimbuf = 0;
  55765. J_COLOR_SPACE colortype = JCS_RGB;
  55766. switch (_spectrum) {
  55767. case 1 : dimbuf = 1; colortype = JCS_GRAYSCALE; break;
  55768. case 2 : dimbuf = 3; colortype = JCS_RGB; break;
  55769. case 3 : dimbuf = 3; colortype = JCS_RGB; break;
  55770. default : dimbuf = 4; colortype = JCS_CMYK; break;
  55771. }
  55772. // Call libjpeg functions
  55773. struct jpeg_compress_struct cinfo;
  55774. struct jpeg_error_mgr jerr;
  55775. cinfo.err = jpeg_std_error(&jerr);
  55776. jpeg_create_compress(&cinfo);
  55777. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  55778. jpeg_stdio_dest(&cinfo,nfile);
  55779. cinfo.image_width = _width;
  55780. cinfo.image_height = _height;
  55781. cinfo.input_components = dimbuf;
  55782. cinfo.in_color_space = colortype;
  55783. jpeg_set_defaults(&cinfo);
  55784. jpeg_set_quality(&cinfo,quality<100?quality:100,TRUE);
  55785. jpeg_start_compress(&cinfo,TRUE);
  55786. JSAMPROW row_pointer[1];
  55787. CImg<ucharT> buffer(_width*dimbuf);
  55788. while (cinfo.next_scanline<cinfo.image_height) {
  55789. unsigned char *ptrd = buffer._data;
  55790. // Fill pixel buffer
  55791. switch (_spectrum) {
  55792. case 1 : { // Greyscale images
  55793. const T *ptr_g = data(0, cinfo.next_scanline);
  55794. for (unsigned int b = 0; b<cinfo.image_width; b++)
  55795. *(ptrd++) = (unsigned char)*(ptr_g++);
  55796. } break;
  55797. case 2 : { // RG images
  55798. const T *ptr_r = data(0,cinfo.next_scanline,0,0),
  55799. *ptr_g = data(0,cinfo.next_scanline,0,1);
  55800. for (unsigned int b = 0; b<cinfo.image_width; ++b) {
  55801. *(ptrd++) = (unsigned char)*(ptr_r++);
  55802. *(ptrd++) = (unsigned char)*(ptr_g++);
  55803. *(ptrd++) = 0;
  55804. }
  55805. } break;
  55806. case 3 : { // RGB images
  55807. const T *ptr_r = data(0,cinfo.next_scanline,0,0),
  55808. *ptr_g = data(0,cinfo.next_scanline,0,1),
  55809. *ptr_b = data(0,cinfo.next_scanline,0,2);
  55810. for (unsigned int b = 0; b<cinfo.image_width; ++b) {
  55811. *(ptrd++) = (unsigned char)*(ptr_r++);
  55812. *(ptrd++) = (unsigned char)*(ptr_g++);
  55813. *(ptrd++) = (unsigned char)*(ptr_b++);
  55814. }
  55815. } break;
  55816. default : { // CMYK images
  55817. const T *ptr_r = data(0,cinfo.next_scanline,0,0),
  55818. *ptr_g = data(0,cinfo.next_scanline,0,1),
  55819. *ptr_b = data(0,cinfo.next_scanline,0,2),
  55820. *ptr_a = data(0,cinfo.next_scanline,0,3);
  55821. for (unsigned int b = 0; b<cinfo.image_width; ++b) {
  55822. *(ptrd++) = (unsigned char)*(ptr_r++);
  55823. *(ptrd++) = (unsigned char)*(ptr_g++);
  55824. *(ptrd++) = (unsigned char)*(ptr_b++);
  55825. *(ptrd++) = (unsigned char)*(ptr_a++);
  55826. }
  55827. }
  55828. }
  55829. *row_pointer = buffer._data;
  55830. jpeg_write_scanlines(&cinfo,row_pointer,1);
  55831. }
  55832. jpeg_finish_compress(&cinfo);
  55833. if (!file) cimg::fclose(nfile);
  55834. jpeg_destroy_compress(&cinfo);
  55835. return *this;
  55836. #endif
  55837. }
  55838. //! Save image, using built-in ImageMagick++ library.
  55839. /**
  55840. \param filename Filename, as a C-string.
  55841. \param bytes_per_pixel Force the number of bytes per pixel for the saving, when possible.
  55842. **/
  55843. const CImg<T>& save_magick(const char *const filename, const unsigned int bytes_per_pixel=0) const {
  55844. if (!filename)
  55845. throw CImgArgumentException(_cimg_instance
  55846. "save_magick(): Specified filename is (null).",
  55847. cimg_instance);
  55848. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  55849. #ifdef cimg_use_magick
  55850. double stmin, stmax = (double)max_min(stmin);
  55851. if (_depth>1)
  55852. cimg::warn(_cimg_instance
  55853. "save_magick(): Instance is volumetric, only the first slice will be saved in file '%s'.",
  55854. cimg_instance,
  55855. filename);
  55856. if (_spectrum>3)
  55857. cimg::warn(_cimg_instance
  55858. "save_magick(): Instance is multispectral, only the three first channels will be "
  55859. "saved in file '%s'.",
  55860. cimg_instance,
  55861. filename);
  55862. if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
  55863. cimg::warn(_cimg_instance
  55864. "save_magick(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
  55865. cimg_instance,
  55866. stmin,stmax,filename);
  55867. Magick::Image image(Magick::Geometry(_width,_height),"black");
  55868. image.type(Magick::TrueColorType);
  55869. image.depth(bytes_per_pixel?(8*bytes_per_pixel):(stmax>=256?16:8));
  55870. const T
  55871. *ptr_r = data(0,0,0,0),
  55872. *ptr_g = _spectrum>1?data(0,0,0,1):0,
  55873. *ptr_b = _spectrum>2?data(0,0,0,2):0;
  55874. Magick::PixelPacket *pixels = image.getPixels(0,0,_width,_height);
  55875. switch (_spectrum) {
  55876. case 1 : // Scalar images
  55877. for (ulongT off = (ulongT)_width*_height; off; --off) {
  55878. pixels->red = pixels->green = pixels->blue = (Magick::Quantum)*(ptr_r++);
  55879. ++pixels;
  55880. }
  55881. break;
  55882. case 2 : // RG images
  55883. for (ulongT off = (ulongT)_width*_height; off; --off) {
  55884. pixels->red = (Magick::Quantum)*(ptr_r++);
  55885. pixels->green = (Magick::Quantum)*(ptr_g++);
  55886. pixels->blue = 0; ++pixels;
  55887. }
  55888. break;
  55889. default : // RGB images
  55890. for (ulongT off = (ulongT)_width*_height; off; --off) {
  55891. pixels->red = (Magick::Quantum)*(ptr_r++);
  55892. pixels->green = (Magick::Quantum)*(ptr_g++);
  55893. pixels->blue = (Magick::Quantum)*(ptr_b++);
  55894. ++pixels;
  55895. }
  55896. }
  55897. image.syncPixels();
  55898. image.write(filename);
  55899. return *this;
  55900. #else
  55901. cimg::unused(bytes_per_pixel);
  55902. throw CImgIOException(_cimg_instance
  55903. "save_magick(): Unable to save file '%s' unless libMagick++ is enabled.",
  55904. cimg_instance,
  55905. filename);
  55906. #endif
  55907. }
  55908. //! Save image as a PNG file.
  55909. /**
  55910. \param filename Filename, as a C-string.
  55911. \param bytes_per_pixel Force the number of bytes per pixels for the saving, when possible.
  55912. **/
  55913. const CImg<T>& save_png(const char *const filename, const unsigned int bytes_per_pixel=0) const {
  55914. return _save_png(0,filename,bytes_per_pixel);
  55915. }
  55916. //! Save image as a PNG file \overloading.
  55917. const CImg<T>& save_png(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
  55918. return _save_png(file,0,bytes_per_pixel);
  55919. }
  55920. const CImg<T>& _save_png(std::FILE *const file, const char *const filename,
  55921. const unsigned int bytes_per_pixel=0) const {
  55922. if (!file && !filename)
  55923. throw CImgArgumentException(_cimg_instance
  55924. "save_png(): Specified filename is (null).",
  55925. cimg_instance);
  55926. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  55927. #ifndef cimg_use_png
  55928. cimg::unused(bytes_per_pixel);
  55929. if (!file) return save_other(filename);
  55930. else throw CImgIOException(_cimg_instance
  55931. "save_png(): Unable to save data in '(*FILE)' unless libpng is enabled.",
  55932. cimg_instance);
  55933. #else
  55934. #if defined __GNUC__
  55935. const char *volatile nfilename = filename; // Use 'volatile' to avoid (wrong) g++ warning
  55936. std::FILE *volatile nfile = file?file:cimg::fopen(nfilename,"wb");
  55937. volatile double stmin, stmax = (double)max_min(stmin);
  55938. #else
  55939. const char *nfilename = filename;
  55940. std::FILE *nfile = file?file:cimg::fopen(nfilename,"wb");
  55941. double stmin, stmax = (double)max_min(stmin);
  55942. #endif
  55943. if (_depth>1)
  55944. cimg::warn(_cimg_instance
  55945. "save_png(): Instance is volumetric, only the first slice will be saved in file '%s'.",
  55946. cimg_instance,
  55947. filename);
  55948. if (_spectrum>4)
  55949. cimg::warn(_cimg_instance
  55950. "save_png(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
  55951. cimg_instance,
  55952. filename);
  55953. if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
  55954. cimg::warn(_cimg_instance
  55955. "save_png(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
  55956. cimg_instance,
  55957. stmin,stmax,filename);
  55958. // Setup PNG structures for write
  55959. png_voidp user_error_ptr = 0;
  55960. png_error_ptr user_error_fn = 0, user_warning_fn = 0;
  55961. png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,user_error_ptr, user_error_fn,
  55962. user_warning_fn);
  55963. if (!png_ptr){
  55964. if (!file) cimg::fclose(nfile);
  55965. throw CImgIOException(_cimg_instance
  55966. "save_png(): Failed to initialize 'png_ptr' structure when saving file '%s'.",
  55967. cimg_instance,
  55968. nfilename?nfilename:"(FILE*)");
  55969. }
  55970. png_infop info_ptr = png_create_info_struct(png_ptr);
  55971. if (!info_ptr) {
  55972. png_destroy_write_struct(&png_ptr,(png_infopp)0);
  55973. if (!file) cimg::fclose(nfile);
  55974. throw CImgIOException(_cimg_instance
  55975. "save_png(): Failed to initialize 'info_ptr' structure when saving file '%s'.",
  55976. cimg_instance,
  55977. nfilename?nfilename:"(FILE*)");
  55978. }
  55979. if (setjmp(png_jmpbuf(png_ptr))) {
  55980. png_destroy_write_struct(&png_ptr, &info_ptr);
  55981. if (!file) cimg::fclose(nfile);
  55982. throw CImgIOException(_cimg_instance
  55983. "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.",
  55984. cimg_instance,
  55985. nfilename?nfilename:"(FILE*)");
  55986. }
  55987. png_init_io(png_ptr, nfile);
  55988. const int bit_depth = bytes_per_pixel?(bytes_per_pixel*8):(stmax>=256?16:8);
  55989. int color_type;
  55990. switch (spectrum()) {
  55991. case 1 : color_type = PNG_COLOR_TYPE_GRAY; break;
  55992. case 2 : color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break;
  55993. case 3 : color_type = PNG_COLOR_TYPE_RGB; break;
  55994. default : color_type = PNG_COLOR_TYPE_RGB_ALPHA;
  55995. }
  55996. const int interlace_type = PNG_INTERLACE_NONE;
  55997. const int compression_type = PNG_COMPRESSION_TYPE_DEFAULT;
  55998. const int filter_method = PNG_FILTER_TYPE_DEFAULT;
  55999. png_set_IHDR(png_ptr,info_ptr,_width,_height,bit_depth,color_type,interlace_type,compression_type,filter_method);
  56000. png_write_info(png_ptr,info_ptr);
  56001. const int byte_depth = bit_depth>>3;
  56002. const int numChan = spectrum()>4?4:spectrum();
  56003. const int pixel_bit_depth_flag = numChan * (bit_depth - 1);
  56004. // Allocate Memory for Image Save and Fill pixel data
  56005. png_bytep *const imgData = new png_byte*[_height];
  56006. for (unsigned int row = 0; row<_height; ++row) imgData[row] = new png_byte[byte_depth*numChan*_width];
  56007. const T *pC0 = data(0,0,0,0);
  56008. switch (pixel_bit_depth_flag) {
  56009. case 7 : { // Gray 8-bit
  56010. cimg_forY(*this,y) {
  56011. unsigned char *ptrd = imgData[y];
  56012. cimg_forX(*this,x) *(ptrd++) = (unsigned char)*(pC0++);
  56013. }
  56014. } break;
  56015. case 14 : { // Gray w/ Alpha 8-bit
  56016. const T *pC1 = data(0,0,0,1);
  56017. cimg_forY(*this,y) {
  56018. unsigned char *ptrd = imgData[y];
  56019. cimg_forX(*this,x) {
  56020. *(ptrd++) = (unsigned char)*(pC0++);
  56021. *(ptrd++) = (unsigned char)*(pC1++);
  56022. }
  56023. }
  56024. } break;
  56025. case 21 : { // RGB 8-bit
  56026. const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
  56027. cimg_forY(*this,y) {
  56028. unsigned char *ptrd = imgData[y];
  56029. cimg_forX(*this,x) {
  56030. *(ptrd++) = (unsigned char)*(pC0++);
  56031. *(ptrd++) = (unsigned char)*(pC1++);
  56032. *(ptrd++) = (unsigned char)*(pC2++);
  56033. }
  56034. }
  56035. } break;
  56036. case 28 : { // RGB x/ Alpha 8-bit
  56037. const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
  56038. cimg_forY(*this,y){
  56039. unsigned char *ptrd = imgData[y];
  56040. cimg_forX(*this,x){
  56041. *(ptrd++) = (unsigned char)*(pC0++);
  56042. *(ptrd++) = (unsigned char)*(pC1++);
  56043. *(ptrd++) = (unsigned char)*(pC2++);
  56044. *(ptrd++) = (unsigned char)*(pC3++);
  56045. }
  56046. }
  56047. } break;
  56048. case 15 : { // Gray 16-bit
  56049. cimg_forY(*this,y){
  56050. unsigned short *ptrd = (unsigned short*)(imgData[y]);
  56051. cimg_forX(*this,x) *(ptrd++) = (unsigned short)*(pC0++);
  56052. if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],_width);
  56053. }
  56054. } break;
  56055. case 30 : { // Gray w/ Alpha 16-bit
  56056. const T *pC1 = data(0,0,0,1);
  56057. cimg_forY(*this,y){
  56058. unsigned short *ptrd = (unsigned short*)(imgData[y]);
  56059. cimg_forX(*this,x) {
  56060. *(ptrd++) = (unsigned short)*(pC0++);
  56061. *(ptrd++) = (unsigned short)*(pC1++);
  56062. }
  56063. if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],2*_width);
  56064. }
  56065. } break;
  56066. case 45 : { // RGB 16-bit
  56067. const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2);
  56068. cimg_forY(*this,y) {
  56069. unsigned short *ptrd = (unsigned short*)(imgData[y]);
  56070. cimg_forX(*this,x) {
  56071. *(ptrd++) = (unsigned short)*(pC0++);
  56072. *(ptrd++) = (unsigned short)*(pC1++);
  56073. *(ptrd++) = (unsigned short)*(pC2++);
  56074. }
  56075. if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],3*_width);
  56076. }
  56077. } break;
  56078. case 60 : { // RGB w/ Alpha 16-bit
  56079. const T *pC1 = data(0,0,0,1), *pC2 = data(0,0,0,2), *pC3 = data(0,0,0,3);
  56080. cimg_forY(*this,y) {
  56081. unsigned short *ptrd = (unsigned short*)(imgData[y]);
  56082. cimg_forX(*this,x) {
  56083. *(ptrd++) = (unsigned short)*(pC0++);
  56084. *(ptrd++) = (unsigned short)*(pC1++);
  56085. *(ptrd++) = (unsigned short)*(pC2++);
  56086. *(ptrd++) = (unsigned short)*(pC3++);
  56087. }
  56088. if (!cimg::endianness()) cimg::invert_endianness((unsigned short*)imgData[y],4*_width);
  56089. }
  56090. } break;
  56091. default :
  56092. if (!file) cimg::fclose(nfile);
  56093. throw CImgIOException(_cimg_instance
  56094. "save_png(): Encountered unknown fatal error in libpng when saving file '%s'.",
  56095. cimg_instance,
  56096. nfilename?nfilename:"(FILE*)");
  56097. }
  56098. png_write_image(png_ptr,imgData);
  56099. png_write_end(png_ptr,info_ptr);
  56100. png_destroy_write_struct(&png_ptr, &info_ptr);
  56101. // Deallocate Image Write Memory
  56102. cimg_forY(*this,n) delete[] imgData[n];
  56103. delete[] imgData;
  56104. if (!file) cimg::fclose(nfile);
  56105. return *this;
  56106. #endif
  56107. }
  56108. //! Save image as a PNM file.
  56109. /**
  56110. \param filename Filename, as a C-string.
  56111. \param bytes_per_pixel Force the number of bytes per pixels for the saving.
  56112. **/
  56113. const CImg<T>& save_pnm(const char *const filename, const unsigned int bytes_per_pixel=0) const {
  56114. return _save_pnm(0,filename,bytes_per_pixel);
  56115. }
  56116. //! Save image as a PNM file \overloading.
  56117. const CImg<T>& save_pnm(std::FILE *const file, const unsigned int bytes_per_pixel=0) const {
  56118. return _save_pnm(file,0,bytes_per_pixel);
  56119. }
  56120. const CImg<T>& _save_pnm(std::FILE *const file, const char *const filename,
  56121. const unsigned int bytes_per_pixel=0) const {
  56122. if (!file && !filename)
  56123. throw CImgArgumentException(_cimg_instance
  56124. "save_pnm(): Specified filename is (null).",
  56125. cimg_instance);
  56126. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  56127. double stmin, stmax = (double)max_min(stmin);
  56128. if (_depth>1)
  56129. cimg::warn(_cimg_instance
  56130. "save_pnm(): Instance is volumetric, only the first slice will be saved in file '%s'.",
  56131. cimg_instance,
  56132. filename?filename:"(FILE*)");
  56133. if (_spectrum>3)
  56134. cimg::warn(_cimg_instance
  56135. "save_pnm(): Instance is multispectral, only the three first channels will be saved in file '%s'.",
  56136. cimg_instance,
  56137. filename?filename:"(FILE*)");
  56138. if (stmin<0 || (bytes_per_pixel==1 && stmax>=256) || stmax>=65536)
  56139. cimg::warn(_cimg_instance
  56140. "save_pnm(): Instance has pixel values in [%g,%g], probable type overflow in file '%s'.",
  56141. cimg_instance,
  56142. stmin,stmax,filename?filename:"(FILE*)");
  56143. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  56144. const T
  56145. *ptr_r = data(0,0,0,0),
  56146. *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
  56147. *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
  56148. const ulongT buf_size = std::min((ulongT)(1024*1024),(ulongT)(_width*_height*(_spectrum==1?1UL:3UL)));
  56149. std::fprintf(nfile,"P%c\n%u %u\n%u\n",
  56150. (_spectrum==1?'5':'6'),_width,_height,stmax<256?255:(stmax<4096?4095:65535));
  56151. switch (_spectrum) {
  56152. case 1 : { // Scalar image
  56153. if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PGM 8 bits
  56154. CImg<ucharT> buf((unsigned int)buf_size);
  56155. for (longT to_write = (longT)width()*height(); to_write>0; ) {
  56156. const ulongT N = std::min((ulongT)to_write,buf_size);
  56157. unsigned char *ptrd = buf._data;
  56158. for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr_r++);
  56159. cimg::fwrite(buf._data,N,nfile);
  56160. to_write-=N;
  56161. }
  56162. } else { // Binary PGM 16 bits
  56163. CImg<ushortT> buf((unsigned int)buf_size);
  56164. for (longT to_write = (longT)width()*height(); to_write>0; ) {
  56165. const ulongT N = std::min((ulongT)to_write,buf_size);
  56166. unsigned short *ptrd = buf._data;
  56167. for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned short)*(ptr_r++);
  56168. if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
  56169. cimg::fwrite(buf._data,N,nfile);
  56170. to_write-=N;
  56171. }
  56172. }
  56173. } break;
  56174. case 2 : { // RG image
  56175. if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
  56176. CImg<ucharT> buf((unsigned int)buf_size);
  56177. for (longT to_write = (longT)width()*height(); to_write>0; ) {
  56178. const ulongT N = std::min((ulongT)to_write,buf_size/3);
  56179. unsigned char *ptrd = buf._data;
  56180. for (ulongT i = N; i>0; --i) {
  56181. *(ptrd++) = (unsigned char)*(ptr_r++);
  56182. *(ptrd++) = (unsigned char)*(ptr_g++);
  56183. *(ptrd++) = 0;
  56184. }
  56185. cimg::fwrite(buf._data,3*N,nfile);
  56186. to_write-=N;
  56187. }
  56188. } else { // Binary PPM 16 bits
  56189. CImg<ushortT> buf((unsigned int)buf_size);
  56190. for (longT to_write = (longT)width()*height(); to_write>0; ) {
  56191. const ulongT N = std::min((ulongT)to_write,buf_size/3);
  56192. unsigned short *ptrd = buf._data;
  56193. for (ulongT i = N; i>0; --i) {
  56194. *(ptrd++) = (unsigned short)*(ptr_r++);
  56195. *(ptrd++) = (unsigned short)*(ptr_g++);
  56196. *(ptrd++) = 0;
  56197. }
  56198. if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
  56199. cimg::fwrite(buf._data,3*N,nfile);
  56200. to_write-=N;
  56201. }
  56202. }
  56203. } break;
  56204. default : { // RGB image
  56205. if (bytes_per_pixel==1 || (!bytes_per_pixel && stmax<256)) { // Binary PPM 8 bits
  56206. CImg<ucharT> buf((unsigned int)buf_size);
  56207. for (longT to_write = (longT)width()*height(); to_write>0; ) {
  56208. const ulongT N = std::min((ulongT)to_write,buf_size/3);
  56209. unsigned char *ptrd = buf._data;
  56210. for (ulongT i = N; i>0; --i) {
  56211. *(ptrd++) = (unsigned char)*(ptr_r++);
  56212. *(ptrd++) = (unsigned char)*(ptr_g++);
  56213. *(ptrd++) = (unsigned char)*(ptr_b++);
  56214. }
  56215. cimg::fwrite(buf._data,3*N,nfile);
  56216. to_write-=N;
  56217. }
  56218. } else { // Binary PPM 16 bits
  56219. CImg<ushortT> buf((unsigned int)buf_size);
  56220. for (longT to_write = (longT)width()*height(); to_write>0; ) {
  56221. const ulongT N = std::min((ulongT)to_write,buf_size/3);
  56222. unsigned short *ptrd = buf._data;
  56223. for (ulongT i = N; i>0; --i) {
  56224. *(ptrd++) = (unsigned short)*(ptr_r++);
  56225. *(ptrd++) = (unsigned short)*(ptr_g++);
  56226. *(ptrd++) = (unsigned short)*(ptr_b++);
  56227. }
  56228. if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
  56229. cimg::fwrite(buf._data,3*N,nfile);
  56230. to_write-=N;
  56231. }
  56232. }
  56233. }
  56234. }
  56235. if (!file) cimg::fclose(nfile);
  56236. return *this;
  56237. }
  56238. //! Save image as a PNK file.
  56239. /**
  56240. \param filename Filename, as a C-string.
  56241. **/
  56242. const CImg<T>& save_pnk(const char *const filename) const {
  56243. return _save_pnk(0,filename);
  56244. }
  56245. //! Save image as a PNK file \overloading.
  56246. const CImg<T>& save_pnk(std::FILE *const file) const {
  56247. return _save_pnk(file,0);
  56248. }
  56249. const CImg<T>& _save_pnk(std::FILE *const file, const char *const filename) const {
  56250. if (!file && !filename)
  56251. throw CImgArgumentException(_cimg_instance
  56252. "save_pnk(): Specified filename is (null).",
  56253. cimg_instance);
  56254. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  56255. if (_spectrum>1)
  56256. cimg::warn(_cimg_instance
  56257. "save_pnk(): Instance is multispectral, only the first channel will be saved in file '%s'.",
  56258. cimg_instance,
  56259. filename?filename:"(FILE*)");
  56260. const ulongT buf_size = std::min((ulongT)1024*1024,(ulongT)_width*_height*_depth);
  56261. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  56262. const T *ptr = data(0,0,0,0);
  56263. if (!cimg::type<T>::is_float() && sizeof(T)==1 && _depth<2) // Can be saved as regular PNM file
  56264. _save_pnm(file,filename,0);
  56265. else if (!cimg::type<T>::is_float() && sizeof(T)==1) { // Save as extended P5 file: Binary byte-valued 3D
  56266. std::fprintf(nfile,"P5\n%u %u %u\n255\n",_width,_height,_depth);
  56267. CImg<ucharT> buf((unsigned int)buf_size);
  56268. for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) {
  56269. const ulongT N = std::min((ulongT)to_write,buf_size);
  56270. unsigned char *ptrd = buf._data;
  56271. for (ulongT i = N; i>0; --i) *(ptrd++) = (unsigned char)*(ptr++);
  56272. cimg::fwrite(buf._data,N,nfile);
  56273. to_write-=N;
  56274. }
  56275. } else if (!cimg::type<T>::is_float()) { // Save as P8: Binary int32-valued 3D
  56276. if (_depth>1) std::fprintf(nfile,"P8\n%u %u %u\n%d\n",_width,_height,_depth,(int)max());
  56277. else std::fprintf(nfile,"P8\n%u %u\n%d\n",_width,_height,(int)max());
  56278. CImg<intT> buf((unsigned int)buf_size);
  56279. for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) {
  56280. const ulongT N = std::min((ulongT)to_write,buf_size);
  56281. int *ptrd = buf._data;
  56282. for (ulongT i = N; i>0; --i) *(ptrd++) = (int)*(ptr++);
  56283. cimg::fwrite(buf._data,N,nfile);
  56284. to_write-=N;
  56285. }
  56286. } else { // Save as P9: Binary float-valued 3D
  56287. if (_depth>1) std::fprintf(nfile,"P9\n%u %u %u\n%g\n",_width,_height,_depth,(double)max());
  56288. else std::fprintf(nfile,"P9\n%u %u\n%g\n",_width,_height,(double)max());
  56289. CImg<floatT> buf((unsigned int)buf_size);
  56290. for (longT to_write = (longT)width()*height()*depth(); to_write>0; ) {
  56291. const ulongT N = std::min((ulongT)to_write,buf_size);
  56292. float *ptrd = buf._data;
  56293. for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr++);
  56294. cimg::fwrite(buf._data,N,nfile);
  56295. to_write-=N;
  56296. }
  56297. }
  56298. if (!file) cimg::fclose(nfile);
  56299. return *this;
  56300. }
  56301. //! Save image as a PFM file.
  56302. /**
  56303. \param filename Filename, as a C-string.
  56304. **/
  56305. const CImg<T>& save_pfm(const char *const filename) const {
  56306. get_mirror('y')._save_pfm(0,filename);
  56307. return *this;
  56308. }
  56309. //! Save image as a PFM file \overloading.
  56310. const CImg<T>& save_pfm(std::FILE *const file) const {
  56311. get_mirror('y')._save_pfm(file,0);
  56312. return *this;
  56313. }
  56314. const CImg<T>& _save_pfm(std::FILE *const file, const char *const filename) const {
  56315. if (!file && !filename)
  56316. throw CImgArgumentException(_cimg_instance
  56317. "save_pfm(): Specified filename is (null).",
  56318. cimg_instance);
  56319. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  56320. if (_depth>1)
  56321. cimg::warn(_cimg_instance
  56322. "save_pfm(): Instance is volumetric, only the first slice will be saved in file '%s'.",
  56323. cimg_instance,
  56324. filename?filename:"(FILE*)");
  56325. if (_spectrum>3)
  56326. cimg::warn(_cimg_instance
  56327. "save_pfm(): image instance is multispectral, only the three first channels will be saved "
  56328. "in file '%s'.",
  56329. cimg_instance,
  56330. filename?filename:"(FILE*)");
  56331. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  56332. const T
  56333. *ptr_r = data(0,0,0,0),
  56334. *ptr_g = (_spectrum>=2)?data(0,0,0,1):0,
  56335. *ptr_b = (_spectrum>=3)?data(0,0,0,2):0;
  56336. const unsigned int buf_size = std::min(1024*1024U,_width*_height*(_spectrum==1?1:3));
  56337. std::fprintf(nfile,"P%c\n%u %u\n1.0\n",
  56338. (_spectrum==1?'f':'F'),_width,_height);
  56339. switch (_spectrum) {
  56340. case 1 : { // Scalar image
  56341. CImg<floatT> buf(buf_size);
  56342. for (longT to_write = (longT)width()*height(); to_write>0; ) {
  56343. const ulongT N = std::min((ulongT)to_write,(ulongT)buf_size);
  56344. float *ptrd = buf._data;
  56345. for (ulongT i = N; i>0; --i) *(ptrd++) = (float)*(ptr_r++);
  56346. if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
  56347. cimg::fwrite(buf._data,N,nfile);
  56348. to_write-=N;
  56349. }
  56350. } break;
  56351. case 2 : { // RG image
  56352. CImg<floatT> buf(buf_size);
  56353. for (longT to_write = (longT)width()*height(); to_write>0; ) {
  56354. const unsigned int N = std::min((unsigned int)to_write,buf_size/3);
  56355. float *ptrd = buf._data;
  56356. for (ulongT i = N; i>0; --i) {
  56357. *(ptrd++) = (float)*(ptr_r++);
  56358. *(ptrd++) = (float)*(ptr_g++);
  56359. *(ptrd++) = 0;
  56360. }
  56361. if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
  56362. cimg::fwrite(buf._data,3*N,nfile);
  56363. to_write-=N;
  56364. }
  56365. } break;
  56366. default : { // RGB image
  56367. CImg<floatT> buf(buf_size);
  56368. for (longT to_write = (longT)width()*height(); to_write>0; ) {
  56369. const unsigned int N = std::min((unsigned int)to_write,buf_size/3);
  56370. float *ptrd = buf._data;
  56371. for (ulongT i = N; i>0; --i) {
  56372. *(ptrd++) = (float)*(ptr_r++);
  56373. *(ptrd++) = (float)*(ptr_g++);
  56374. *(ptrd++) = (float)*(ptr_b++);
  56375. }
  56376. if (!cimg::endianness()) cimg::invert_endianness(buf._data,buf_size);
  56377. cimg::fwrite(buf._data,3*N,nfile);
  56378. to_write-=N;
  56379. }
  56380. }
  56381. }
  56382. if (!file) cimg::fclose(nfile);
  56383. return *this;
  56384. }
  56385. //! Save image as a RGB file.
  56386. /**
  56387. \param filename Filename, as a C-string.
  56388. **/
  56389. const CImg<T>& save_rgb(const char *const filename) const {
  56390. return _save_rgb(0,filename);
  56391. }
  56392. //! Save image as a RGB file \overloading.
  56393. const CImg<T>& save_rgb(std::FILE *const file) const {
  56394. return _save_rgb(file,0);
  56395. }
  56396. const CImg<T>& _save_rgb(std::FILE *const file, const char *const filename) const {
  56397. if (!file && !filename)
  56398. throw CImgArgumentException(_cimg_instance
  56399. "save_rgb(): Specified filename is (null).",
  56400. cimg_instance);
  56401. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  56402. if (_spectrum!=3)
  56403. cimg::warn(_cimg_instance
  56404. "save_rgb(): image instance has not exactly 3 channels, for file '%s'.",
  56405. cimg_instance,
  56406. filename?filename:"(FILE*)");
  56407. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  56408. const ulongT wh = (ulongT)_width*_height;
  56409. unsigned char *const buffer = new unsigned char[3*wh], *nbuffer = buffer;
  56410. const T
  56411. *ptr1 = data(0,0,0,0),
  56412. *ptr2 = _spectrum>1?data(0,0,0,1):0,
  56413. *ptr3 = _spectrum>2?data(0,0,0,2):0;
  56414. switch (_spectrum) {
  56415. case 1 : { // Scalar image
  56416. for (ulongT k = 0; k<wh; ++k) {
  56417. const unsigned char val = (unsigned char)*(ptr1++);
  56418. *(nbuffer++) = val;
  56419. *(nbuffer++) = val;
  56420. *(nbuffer++) = val;
  56421. }
  56422. } break;
  56423. case 2 : { // RG image
  56424. for (ulongT k = 0; k<wh; ++k) {
  56425. *(nbuffer++) = (unsigned char)(*(ptr1++));
  56426. *(nbuffer++) = (unsigned char)(*(ptr2++));
  56427. *(nbuffer++) = 0;
  56428. }
  56429. } break;
  56430. default : { // RGB image
  56431. for (ulongT k = 0; k<wh; ++k) {
  56432. *(nbuffer++) = (unsigned char)(*(ptr1++));
  56433. *(nbuffer++) = (unsigned char)(*(ptr2++));
  56434. *(nbuffer++) = (unsigned char)(*(ptr3++));
  56435. }
  56436. }
  56437. }
  56438. cimg::fwrite(buffer,3*wh,nfile);
  56439. if (!file) cimg::fclose(nfile);
  56440. delete[] buffer;
  56441. return *this;
  56442. }
  56443. //! Save image as a RGBA file.
  56444. /**
  56445. \param filename Filename, as a C-string.
  56446. **/
  56447. const CImg<T>& save_rgba(const char *const filename) const {
  56448. return _save_rgba(0,filename);
  56449. }
  56450. //! Save image as a RGBA file \overloading.
  56451. const CImg<T>& save_rgba(std::FILE *const file) const {
  56452. return _save_rgba(file,0);
  56453. }
  56454. const CImg<T>& _save_rgba(std::FILE *const file, const char *const filename) const {
  56455. if (!file && !filename)
  56456. throw CImgArgumentException(_cimg_instance
  56457. "save_rgba(): Specified filename is (null).",
  56458. cimg_instance);
  56459. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  56460. if (_spectrum!=4)
  56461. cimg::warn(_cimg_instance
  56462. "save_rgba(): image instance has not exactly 4 channels, for file '%s'.",
  56463. cimg_instance,
  56464. filename?filename:"(FILE*)");
  56465. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  56466. const ulongT wh = (ulongT)_width*_height;
  56467. unsigned char *const buffer = new unsigned char[4*wh], *nbuffer = buffer;
  56468. const T
  56469. *ptr1 = data(0,0,0,0),
  56470. *ptr2 = _spectrum>1?data(0,0,0,1):0,
  56471. *ptr3 = _spectrum>2?data(0,0,0,2):0,
  56472. *ptr4 = _spectrum>3?data(0,0,0,3):0;
  56473. switch (_spectrum) {
  56474. case 1 : { // Scalar images
  56475. for (ulongT k = 0; k<wh; ++k) {
  56476. const unsigned char val = (unsigned char)*(ptr1++);
  56477. *(nbuffer++) = val;
  56478. *(nbuffer++) = val;
  56479. *(nbuffer++) = val;
  56480. *(nbuffer++) = 255;
  56481. }
  56482. } break;
  56483. case 2 : { // RG images
  56484. for (ulongT k = 0; k<wh; ++k) {
  56485. *(nbuffer++) = (unsigned char)(*(ptr1++));
  56486. *(nbuffer++) = (unsigned char)(*(ptr2++));
  56487. *(nbuffer++) = 0;
  56488. *(nbuffer++) = 255;
  56489. }
  56490. } break;
  56491. case 3 : { // RGB images
  56492. for (ulongT k = 0; k<wh; ++k) {
  56493. *(nbuffer++) = (unsigned char)(*(ptr1++));
  56494. *(nbuffer++) = (unsigned char)(*(ptr2++));
  56495. *(nbuffer++) = (unsigned char)(*(ptr3++));
  56496. *(nbuffer++) = 255;
  56497. }
  56498. } break;
  56499. default : { // RGBA images
  56500. for (ulongT k = 0; k<wh; ++k) {
  56501. *(nbuffer++) = (unsigned char)(*(ptr1++));
  56502. *(nbuffer++) = (unsigned char)(*(ptr2++));
  56503. *(nbuffer++) = (unsigned char)(*(ptr3++));
  56504. *(nbuffer++) = (unsigned char)(*(ptr4++));
  56505. }
  56506. }
  56507. }
  56508. cimg::fwrite(buffer,4*wh,nfile);
  56509. if (!file) cimg::fclose(nfile);
  56510. delete[] buffer;
  56511. return *this;
  56512. }
  56513. //! Save image as a TIFF file.
  56514. /**
  56515. \param filename Filename, as a C-string.
  56516. \param compression_type Type of data compression. Can be <tt>{ 0=None | 1=LZW | 2=JPEG }</tt>.
  56517. \param[out] voxel_size Voxel size, to be stored in the filename.
  56518. \param[out] description Description, to be stored in the filename.
  56519. \param use_bigtiff Allow to save big tiff files (>4Gb).
  56520. \note
  56521. - libtiff support is enabled by defining the precompilation
  56522. directive \c cimg_use_tiff.
  56523. - When libtiff is enabled, 2D and 3D (multipage) several
  56524. channel per pixel are supported for
  56525. <tt>char,uchar,short,ushort,float</tt> and \c double pixel types.
  56526. - If \c cimg_use_tiff is not defined at compile time the
  56527. function uses CImg<T>&save_other(const char*).
  56528. **/
  56529. const CImg<T>& save_tiff(const char *const filename, const unsigned int compression_type=0,
  56530. const float *const voxel_size=0, const char *const description=0,
  56531. const bool use_bigtiff=true) const {
  56532. if (!filename)
  56533. throw CImgArgumentException(_cimg_instance
  56534. "save_tiff(): Specified filename is (null).",
  56535. cimg_instance);
  56536. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  56537. #ifdef cimg_use_tiff
  56538. const bool
  56539. _use_bigtiff = use_bigtiff && sizeof(ulongT)>=8 && size()*sizeof(T)>=1UL<<31; // No bigtiff for small images
  56540. TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4");
  56541. if (tif) {
  56542. cimg_forZ(*this,z) _save_tiff(tif,z,z,compression_type,voxel_size,description);
  56543. TIFFClose(tif);
  56544. } else throw CImgIOException(_cimg_instance
  56545. "save_tiff(): Failed to open file '%s' for writing.",
  56546. cimg_instance,
  56547. filename);
  56548. return *this;
  56549. #else
  56550. cimg::unused(compression_type,voxel_size,description,use_bigtiff);
  56551. return save_other(filename);
  56552. #endif
  56553. }
  56554. #ifdef cimg_use_tiff
  56555. #define _cimg_save_tiff(types,typed) if (!std::strcmp(types,pixel_type())) { \
  56556. const typed foo = (typed)0; return _save_tiff(tif,directory,z,foo,compression_type,voxel_size,description); }
  56557. // [internal] Save a plane into a tiff file
  56558. template<typename t>
  56559. const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z, const t& pixel_t,
  56560. const unsigned int compression_type, const float *const voxel_size,
  56561. const char *const description) const {
  56562. if (is_empty() || !tif || pixel_t) return *this;
  56563. const char *const filename = TIFFFileName(tif);
  56564. cimg_uint32 rowsperstrip = (cimg_uint32)-1;
  56565. cimg_uint16 spp = _spectrum, bpp = sizeof(t)*8, photometric;
  56566. if (spp==3 || spp==4) photometric = PHOTOMETRIC_RGB;
  56567. else photometric = PHOTOMETRIC_MINISBLACK;
  56568. TIFFSetDirectory(tif,directory);
  56569. TIFFSetField(tif,TIFFTAG_IMAGEWIDTH,_width);
  56570. TIFFSetField(tif,TIFFTAG_IMAGELENGTH,_height);
  56571. if (voxel_size) {
  56572. const float vx = voxel_size[0], vy = voxel_size[1], vz = voxel_size[2];
  56573. TIFFSetField(tif,TIFFTAG_RESOLUTIONUNIT,RESUNIT_NONE);
  56574. TIFFSetField(tif,TIFFTAG_XRESOLUTION,1.f/vx);
  56575. TIFFSetField(tif,TIFFTAG_YRESOLUTION,1.f/vy);
  56576. CImg<charT> s_description(256);
  56577. cimg_snprintf(s_description,s_description._width,"VX=%g VY=%g VZ=%g spacing=%g",vx,vy,vz,vz);
  56578. TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,s_description.data());
  56579. }
  56580. if (description) TIFFSetField(tif,TIFFTAG_IMAGEDESCRIPTION,description);
  56581. TIFFSetField(tif,TIFFTAG_ORIENTATION,ORIENTATION_TOPLEFT);
  56582. TIFFSetField(tif,TIFFTAG_SAMPLESPERPIXEL,spp);
  56583. if (cimg::type<t>::is_float()) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,3);
  56584. else if (cimg::type<t>::min()==0) TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,1);
  56585. else TIFFSetField(tif,TIFFTAG_SAMPLEFORMAT,2);
  56586. double valm, valM = max_min(valm);
  56587. TIFFSetField(tif,TIFFTAG_SMINSAMPLEVALUE,valm);
  56588. TIFFSetField(tif,TIFFTAG_SMAXSAMPLEVALUE,valM);
  56589. TIFFSetField(tif,TIFFTAG_BITSPERSAMPLE,bpp);
  56590. TIFFSetField(tif,TIFFTAG_PLANARCONFIG,PLANARCONFIG_CONTIG);
  56591. TIFFSetField(tif,TIFFTAG_PHOTOMETRIC,photometric);
  56592. TIFFSetField(tif,TIFFTAG_COMPRESSION,compression_type==2?COMPRESSION_JPEG:
  56593. compression_type==1?COMPRESSION_LZW:COMPRESSION_NONE);
  56594. rowsperstrip = TIFFDefaultStripSize(tif,rowsperstrip);
  56595. TIFFSetField(tif,TIFFTAG_ROWSPERSTRIP,rowsperstrip);
  56596. TIFFSetField(tif,TIFFTAG_FILLORDER,FILLORDER_MSB2LSB);
  56597. TIFFSetField(tif,TIFFTAG_SOFTWARE,cimg_appname);
  56598. t *const buf = (t*)_TIFFmalloc(TIFFStripSize(tif));
  56599. if (buf) {
  56600. for (unsigned int row = 0; row<_height; row+=rowsperstrip) {
  56601. cimg_uint32 nrow = (row + rowsperstrip>_height?_height - row:rowsperstrip);
  56602. tstrip_t strip = TIFFComputeStrip(tif,row,0);
  56603. tsize_t i = 0;
  56604. for (unsigned int rr = 0; rr<nrow; ++rr)
  56605. for (unsigned int cc = 0; cc<_width; ++cc)
  56606. for (unsigned int vv = 0; vv<spp; ++vv)
  56607. buf[i++] = (t)(*this)(cc,row + rr,z,vv);
  56608. if (TIFFWriteEncodedStrip(tif,strip,buf,i*sizeof(t))<0)
  56609. throw CImgIOException(_cimg_instance
  56610. "save_tiff(): Invalid strip writing when saving file '%s'.",
  56611. cimg_instance,
  56612. filename?filename:"(FILE*)");
  56613. }
  56614. _TIFFfree(buf);
  56615. }
  56616. TIFFWriteDirectory(tif);
  56617. return *this;
  56618. }
  56619. const CImg<T>& _save_tiff(TIFF *tif, const unsigned int directory, const unsigned int z,
  56620. const unsigned int compression_type, const float *const voxel_size,
  56621. const char *const description) const {
  56622. _cimg_save_tiff("uint8",cimg_uint8);
  56623. _cimg_save_tiff("int8",cimg_int8);
  56624. _cimg_save_tiff("uint16",cimg_uint16);
  56625. _cimg_save_tiff("int16",cimg_int16);
  56626. _cimg_save_tiff("uint32",cimg_uint32);
  56627. _cimg_save_tiff("int32",cimg_int32);
  56628. _cimg_save_tiff("uint64",cimg_uint32); // 'int64' as 'int32'
  56629. _cimg_save_tiff("int64",cimg_int32);
  56630. _cimg_save_tiff("float32",cimg_float32);
  56631. _cimg_save_tiff("float64",cimg_float32); // 'float64' as 'float32'
  56632. const char *const filename = TIFFFileName(tif);
  56633. throw CImgInstanceException(_cimg_instance
  56634. "save_tiff(): Unsupported pixel type '%s' for file '%s'.",
  56635. cimg_instance,
  56636. pixel_type(),filename?filename:"(FILE*)");
  56637. return *this;
  56638. }
  56639. #endif
  56640. //! Save image as a MINC2 file.
  56641. /**
  56642. \param filename Filename, as a C-string.
  56643. \param imitate_file If non-zero, reference filename, as a C-string, to borrow header from.
  56644. **/
  56645. const CImg<T>& save_minc2(const char *const filename,
  56646. const char *const imitate_file=0) const {
  56647. if (!filename)
  56648. throw CImgArgumentException(_cimg_instance
  56649. "save_minc2(): Specified filename is (null).",
  56650. cimg_instance);
  56651. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  56652. #ifndef cimg_use_minc2
  56653. cimg::unused(imitate_file);
  56654. return save_other(filename);
  56655. #else
  56656. minc::minc_1_writer wtr;
  56657. if (imitate_file)
  56658. wtr.open(filename, imitate_file);
  56659. else {
  56660. minc::minc_info di;
  56661. if (width()) di.push_back(minc::dim_info(width(),width()*0.5,-1,minc::dim_info::DIM_X));
  56662. if (height()) di.push_back(minc::dim_info(height(),height()*0.5,-1,minc::dim_info::DIM_Y));
  56663. if (depth()) di.push_back(minc::dim_info(depth(),depth()*0.5,-1,minc::dim_info::DIM_Z));
  56664. if (spectrum()) di.push_back(minc::dim_info(spectrum(),spectrum()*0.5,-1,minc::dim_info::DIM_TIME));
  56665. wtr.open(filename,di,1,NC_FLOAT,0);
  56666. }
  56667. if (pixel_type()==cimg::type<unsigned char>::string())
  56668. wtr.setup_write_byte();
  56669. else if (pixel_type()==cimg::type<int>::string())
  56670. wtr.setup_write_int();
  56671. else if (pixel_type()==cimg::type<double>::string())
  56672. wtr.setup_write_double();
  56673. else
  56674. wtr.setup_write_float();
  56675. minc::save_standard_volume(wtr, this->_data);
  56676. return *this;
  56677. #endif
  56678. }
  56679. //! Save image as an ANALYZE7.5 or NIFTI file.
  56680. /**
  56681. \param filename Filename, as a C-string.
  56682. \param voxel_size Pointer to 3 consecutive values that tell about the voxel sizes along the X,Y and Z dimensions.
  56683. **/
  56684. const CImg<T>& save_analyze(const char *const filename, const float *const voxel_size=0) const {
  56685. if (!filename)
  56686. throw CImgArgumentException(_cimg_instance
  56687. "save_analyze(): Specified filename is (null).",
  56688. cimg_instance);
  56689. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  56690. std::FILE *file;
  56691. CImg<charT> hname(1024), iname(1024);
  56692. const char *const ext = cimg::split_filename(filename);
  56693. short datatype = -1;
  56694. if (!*ext) {
  56695. cimg_snprintf(hname,hname._width,"%s.hdr",filename);
  56696. cimg_snprintf(iname,iname._width,"%s.img",filename);
  56697. }
  56698. if (!cimg::strncasecmp(ext,"hdr",3)) {
  56699. std::strcpy(hname,filename);
  56700. std::strncpy(iname,filename,iname._width - 1);
  56701. cimg_snprintf(iname._data + std::strlen(iname) - 3,4,"img");
  56702. }
  56703. if (!cimg::strncasecmp(ext,"img",3)) {
  56704. std::strcpy(hname,filename);
  56705. std::strncpy(iname,filename,iname._width - 1);
  56706. cimg_snprintf(hname._data + std::strlen(iname) - 3,4,"hdr");
  56707. }
  56708. if (!cimg::strncasecmp(ext,"nii",3)) {
  56709. std::strncpy(hname,filename,hname._width - 1); *iname = 0;
  56710. }
  56711. CImg<charT> header(*iname?348:352,1,1,1,0);
  56712. int *const iheader = (int*)header._data;
  56713. *iheader = 348;
  56714. std::strcpy(header._data + 4,"CImg");
  56715. std::strcpy(header._data + 14," ");
  56716. ((short*)&(header[36]))[0] = 4096;
  56717. ((char*)&(header[38]))[0] = 114;
  56718. ((short*)&(header[40]))[0] = 4;
  56719. ((short*)&(header[40]))[1] = (short)_width;
  56720. ((short*)&(header[40]))[2] = (short)_height;
  56721. ((short*)&(header[40]))[3] = (short)_depth;
  56722. ((short*)&(header[40]))[4] = (short)_spectrum;
  56723. if (!cimg::strcasecmp(pixel_type(),"bool") ||
  56724. !cimg::strcasecmp(pixel_type(),"uint8") ||
  56725. !cimg::strcasecmp(pixel_type(),"int8")) datatype = 2;
  56726. if (!cimg::strcasecmp(pixel_type(),"uint16") ||
  56727. !cimg::strcasecmp(pixel_type(),"int16")) datatype = 4;
  56728. if (!cimg::strcasecmp(pixel_type(),"uint32") ||
  56729. !cimg::strcasecmp(pixel_type(),"int32")) datatype = 8;
  56730. if (!cimg::strcasecmp(pixel_type(),"uint64") ||
  56731. !cimg::strcasecmp(pixel_type(),"int64")) datatype = 8;
  56732. if (!cimg::strcasecmp(pixel_type(),"float32")) datatype = 16;
  56733. if (!cimg::strcasecmp(pixel_type(),"float64")) datatype = 64;
  56734. if (datatype<0)
  56735. throw CImgIOException(_cimg_instance
  56736. "save_analyze(): Unsupported pixel type '%s' for file '%s'.",
  56737. cimg_instance,
  56738. pixel_type(),filename);
  56739. ((short*)&(header[70]))[0] = datatype;
  56740. ((short*)&(header[72]))[0] = sizeof(T);
  56741. ((float*)&(header[108]))[0] = (float)(*iname?0:header.width());
  56742. ((float*)&(header[112]))[0] = 1;
  56743. ((float*)&(header[76]))[0] = 0;
  56744. if (voxel_size) {
  56745. ((float*)&(header[76]))[1] = voxel_size[0];
  56746. ((float*)&(header[76]))[2] = voxel_size[1];
  56747. ((float*)&(header[76]))[3] = voxel_size[2];
  56748. } else ((float*)&(header[76]))[1] = ((float*)&(header[76]))[2] = ((float*)&(header[76]))[3] = 1;
  56749. file = cimg::fopen(hname,"wb");
  56750. cimg::fwrite(header._data,header.width(),file);
  56751. if (*iname) { cimg::fclose(file); file = cimg::fopen(iname,"wb"); }
  56752. cimg::fwrite(_data,size(),file);
  56753. cimg::fclose(file);
  56754. return *this;
  56755. }
  56756. //! Save image as a .cimg file.
  56757. /**
  56758. \param filename Filename, as a C-string.
  56759. \param is_compressed Tells if the file contains compressed image data.
  56760. **/
  56761. const CImg<T>& save_cimg(const char *const filename, const bool is_compressed=false) const {
  56762. CImgList<T>(*this,true).save_cimg(filename,is_compressed);
  56763. return *this;
  56764. }
  56765. //! Save image as a .cimg file \overloading.
  56766. const CImg<T>& save_cimg(std::FILE *const file, const bool is_compressed=false) const {
  56767. CImgList<T>(*this,true).save_cimg(file,is_compressed);
  56768. return *this;
  56769. }
  56770. //! Save image as a sub-image into an existing .cimg file.
  56771. /**
  56772. \param filename Filename, as a C-string.
  56773. \param n0 Index of the image inside the file.
  56774. \param x0 X-coordinate of the sub-image location.
  56775. \param y0 Y-coordinate of the sub-image location.
  56776. \param z0 Z-coordinate of the sub-image location.
  56777. \param c0 C-coordinate of the sub-image location.
  56778. **/
  56779. const CImg<T>& save_cimg(const char *const filename,
  56780. const unsigned int n0,
  56781. const unsigned int x0, const unsigned int y0,
  56782. const unsigned int z0, const unsigned int c0) const {
  56783. CImgList<T>(*this,true).save_cimg(filename,n0,x0,y0,z0,c0);
  56784. return *this;
  56785. }
  56786. //! Save image as a sub-image into an existing .cimg file \overloading.
  56787. const CImg<T>& save_cimg(std::FILE *const file,
  56788. const unsigned int n0,
  56789. const unsigned int x0, const unsigned int y0,
  56790. const unsigned int z0, const unsigned int c0) const {
  56791. CImgList<T>(*this,true).save_cimg(file,n0,x0,y0,z0,c0);
  56792. return *this;
  56793. }
  56794. //! Save blank image as a .cimg file.
  56795. /**
  56796. \param filename Filename, as a C-string.
  56797. \param dx Width of the image.
  56798. \param dy Height of the image.
  56799. \param dz Depth of the image.
  56800. \param dc Number of channels of the image.
  56801. \note
  56802. - All pixel values of the saved image are set to \c 0.
  56803. - Use this method to save large images without having to instantiate and allocate them.
  56804. **/
  56805. static void save_empty_cimg(const char *const filename,
  56806. const unsigned int dx, const unsigned int dy=1,
  56807. const unsigned int dz=1, const unsigned int dc=1) {
  56808. return CImgList<T>::save_empty_cimg(filename,1,dx,dy,dz,dc);
  56809. }
  56810. //! Save blank image as a .cimg file \overloading.
  56811. /**
  56812. Same as save_empty_cimg(const char *,unsigned int,unsigned int,unsigned int,unsigned int)
  56813. with a file stream argument instead of a filename string.
  56814. **/
  56815. static void save_empty_cimg(std::FILE *const file,
  56816. const unsigned int dx, const unsigned int dy=1,
  56817. const unsigned int dz=1, const unsigned int dc=1) {
  56818. return CImgList<T>::save_empty_cimg(file,1,dx,dy,dz,dc);
  56819. }
  56820. //! Save image as an INRIMAGE-4 file.
  56821. /**
  56822. \param filename Filename, as a C-string.
  56823. \param voxel_size Pointer to 3 values specifying the voxel sizes along the X,Y and Z dimensions.
  56824. **/
  56825. const CImg<T>& save_inr(const char *const filename, const float *const voxel_size=0) const {
  56826. return _save_inr(0,filename,voxel_size);
  56827. }
  56828. //! Save image as an INRIMAGE-4 file \overloading.
  56829. const CImg<T>& save_inr(std::FILE *const file, const float *const voxel_size=0) const {
  56830. return _save_inr(file,0,voxel_size);
  56831. }
  56832. const CImg<T>& _save_inr(std::FILE *const file, const char *const filename, const float *const voxel_size) const {
  56833. if (!file && !filename)
  56834. throw CImgArgumentException(_cimg_instance
  56835. "save_inr(): Specified filename is (null).",
  56836. cimg_instance);
  56837. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  56838. int inrpixsize = -1;
  56839. const char *inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0";
  56840. if (!cimg::strcasecmp(pixel_type(),"uint8")) {
  56841. inrtype = "unsigned fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1;
  56842. }
  56843. if (!cimg::strcasecmp(pixel_type(),"int8")) {
  56844. inrtype = "fixed\nPIXSIZE=8 bits\nSCALE=2**0"; inrpixsize = 1;
  56845. }
  56846. if (!cimg::strcasecmp(pixel_type(),"uint16")) {
  56847. inrtype = "unsigned fixed\nPIXSIZE=16 bits\nSCALE=2**0";inrpixsize = 2;
  56848. }
  56849. if (!cimg::strcasecmp(pixel_type(),"int16")) {
  56850. inrtype = "fixed\nPIXSIZE=16 bits\nSCALE=2**0"; inrpixsize = 2;
  56851. }
  56852. if (!cimg::strcasecmp(pixel_type(),"uint32")) {
  56853. inrtype = "unsigned fixed\nPIXSIZE=32 bits\nSCALE=2**0";inrpixsize = 4;
  56854. }
  56855. if (!cimg::strcasecmp(pixel_type(),"int32")) {
  56856. inrtype = "fixed\nPIXSIZE=32 bits\nSCALE=2**0"; inrpixsize = 4;
  56857. }
  56858. if (!cimg::strcasecmp(pixel_type(),"float32")) {
  56859. inrtype = "float\nPIXSIZE=32 bits"; inrpixsize = 4;
  56860. }
  56861. if (!cimg::strcasecmp(pixel_type(),"float64")) {
  56862. inrtype = "float\nPIXSIZE=64 bits"; inrpixsize = 8;
  56863. }
  56864. if (inrpixsize<=0)
  56865. throw CImgIOException(_cimg_instance
  56866. "save_inr(): Unsupported pixel type '%s' for file '%s'",
  56867. cimg_instance,
  56868. pixel_type(),filename?filename:"(FILE*)");
  56869. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  56870. CImg<charT> header(257);
  56871. int err = cimg_snprintf(header,header._width,"#INRIMAGE-4#{\nXDIM=%u\nYDIM=%u\nZDIM=%u\nVDIM=%u\n",
  56872. _width,_height,_depth,_spectrum);
  56873. if (voxel_size)
  56874. err+=cimg_snprintf(header._data + err,128,"VX=%g\nVY=%g\nVZ=%g\n",
  56875. voxel_size[0],voxel_size[1],voxel_size[2]);
  56876. err+=cimg_snprintf(header._data + err,128,"TYPE=%s\nCPU=%s\n",
  56877. inrtype,cimg::endianness()?"sun":"decm");
  56878. std::memset(header._data + err,'\n',252 - err);
  56879. std::memcpy(header._data + 252,"##}\n",4);
  56880. cimg::fwrite(header._data,256,nfile);
  56881. cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) cimg::fwrite(&((*this)(x,y,z,c)),1,nfile);
  56882. if (!file) cimg::fclose(nfile);
  56883. return *this;
  56884. }
  56885. //! Save image as an OpenEXR file.
  56886. /**
  56887. \param filename Filename, as a C-string.
  56888. \note The OpenEXR file format is <a href="http://en.wikipedia.org/wiki/OpenEXR">described here</a>.
  56889. **/
  56890. const CImg<T>& save_exr(const char *const filename) const {
  56891. if (!filename)
  56892. throw CImgArgumentException(_cimg_instance
  56893. "save_exr(): Specified filename is (null).",
  56894. cimg_instance);
  56895. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  56896. if (_depth>1)
  56897. cimg::warn(_cimg_instance
  56898. "save_exr(): Instance is volumetric, only the first slice will be saved in file '%s'.",
  56899. cimg_instance,
  56900. filename);
  56901. #ifndef cimg_use_openexr
  56902. return save_other(filename);
  56903. #else
  56904. Imf::Rgba *const ptrd0 = new Imf::Rgba[(size_t)_width*_height], *ptrd = ptrd0, rgba;
  56905. switch (_spectrum) {
  56906. case 1 : { // Grayscale image
  56907. for (const T *ptr_r = data(), *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) {
  56908. rgba.r = (half)(*(ptr_r));
  56909. rgba.g = (half)(*(ptr_r));
  56910. rgba.b = (half)(*(ptr_r++));
  56911. rgba.a = (half)1;
  56912. *(ptrd++) = rgba;
  56913. }
  56914. } break;
  56915. case 2 : { // RG image
  56916. for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1),
  56917. *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e; ) {
  56918. rgba.r = (half)(*(ptr_r++));
  56919. rgba.g = (half)(*(ptr_g++));
  56920. rgba.b = (half)0;
  56921. rgba.a = (half)1;
  56922. *(ptrd++) = rgba;
  56923. }
  56924. } break;
  56925. case 3 : { // RGB image
  56926. for (const T *ptr_r = data(), *ptr_g = data(0,0,0,1), *ptr_b = data(0,0,0,2),
  56927. *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) {
  56928. rgba.r = (half)(*(ptr_r++));
  56929. rgba.g = (half)(*(ptr_g++));
  56930. rgba.b = (half)(*(ptr_b++));
  56931. rgba.a = (half)1;
  56932. *(ptrd++) = rgba;
  56933. }
  56934. } break;
  56935. default : { // RGBA image
  56936. 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),
  56937. *const ptr_e = ptr_r + (ulongT)_width*_height; ptr_r<ptr_e;) {
  56938. rgba.r = (half)(*(ptr_r++));
  56939. rgba.g = (half)(*(ptr_g++));
  56940. rgba.b = (half)(*(ptr_b++));
  56941. rgba.a = (half)(*(ptr_a++));
  56942. *(ptrd++) = rgba;
  56943. }
  56944. } break;
  56945. }
  56946. Imf::RgbaOutputFile outFile(filename,_width,_height,
  56947. _spectrum==1?Imf::WRITE_Y:_spectrum==2?Imf::WRITE_YA:_spectrum==3?
  56948. Imf::WRITE_RGB:Imf::WRITE_RGBA);
  56949. outFile.setFrameBuffer(ptrd0,1,_width);
  56950. outFile.writePixels(_height);
  56951. delete[] ptrd0;
  56952. return *this;
  56953. #endif
  56954. }
  56955. //! Save image as a Pandore-5 file.
  56956. /**
  56957. \param filename Filename, as a C-string.
  56958. \param colorspace Colorspace data field in output file
  56959. (see <a href="http://www.greyc.ensicaen.fr/~regis/Pandore">Pandore file specifications</a>
  56960. for more information).
  56961. **/
  56962. const CImg<T>& save_pandore(const char *const filename, const unsigned int colorspace=0) const {
  56963. return _save_pandore(0,filename,colorspace);
  56964. }
  56965. //! Save image as a Pandore-5 file \overloading.
  56966. /**
  56967. Same as save_pandore(const char *,unsigned int) const
  56968. with a file stream argument instead of a filename string.
  56969. **/
  56970. const CImg<T>& save_pandore(std::FILE *const file, const unsigned int colorspace=0) const {
  56971. return _save_pandore(file,0,colorspace);
  56972. }
  56973. unsigned int _save_pandore_header_length(unsigned int id, unsigned int *dims, const unsigned int colorspace) const {
  56974. unsigned int nbdims = 0;
  56975. if (id==2 || id==3 || id==4) {
  56976. dims[0] = 1; dims[1] = _width; nbdims = 2;
  56977. }
  56978. if (id==5 || id==6 || id==7) {
  56979. dims[0] = 1; dims[1] = _height; dims[2] = _width; nbdims=3;
  56980. }
  56981. if (id==8 || id==9 || id==10) {
  56982. dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4;
  56983. }
  56984. if (id==16 || id==17 || id==18) {
  56985. dims[0] = 3; dims[1] = _height; dims[2] = _width; dims[3] = colorspace; nbdims = 4;
  56986. }
  56987. if (id==19 || id==20 || id==21) {
  56988. dims[0] = 3; dims[1] = _depth; dims[2] = _height; dims[3] = _width; dims[4] = colorspace; nbdims = 5;
  56989. }
  56990. if (id==22 || id==23 || id==25) {
  56991. dims[0] = _spectrum; dims[1] = _width; nbdims = 2;
  56992. }
  56993. if (id==26 || id==27 || id==29) {
  56994. dims[0] = _spectrum; dims[1] = _height; dims[2] = _width; nbdims=3;
  56995. }
  56996. if (id==30 || id==31 || id==33) {
  56997. dims[0] = _spectrum; dims[1] = _depth; dims[2] = _height; dims[3] = _width; nbdims = 4;
  56998. }
  56999. return nbdims;
  57000. }
  57001. const CImg<T>& _save_pandore(std::FILE *const file, const char *const filename,
  57002. const unsigned int colorspace) const {
  57003. #define __cimg_save_pandore_case(dtype) \
  57004. dtype *buffer = new dtype[size()]; \
  57005. const T *ptrs = _data; \
  57006. cimg_foroff(*this,off) *(buffer++) = (dtype)(*(ptrs++)); \
  57007. buffer-=size(); \
  57008. cimg::fwrite(buffer,size(),nfile); \
  57009. delete[] buffer
  57010. #define _cimg_save_pandore_case(sy,sz,sv,stype,id) \
  57011. if (!saved && (sy?(sy==_height):true) && (sz?(sz==_depth):true) && \
  57012. (sv?(sv==_spectrum):true) && !std::strcmp(stype,pixel_type())) { \
  57013. unsigned int *iheader = (unsigned int*)(header + 12); \
  57014. nbdims = _save_pandore_header_length((*iheader=id),dims,colorspace); \
  57015. cimg::fwrite(header,36,nfile); \
  57016. if (sizeof(unsigned long)==4) { CImg<ulongT> ndims(5); \
  57017. for (int d = 0; d<5; ++d) ndims[d] = (unsigned long)dims[d]; \
  57018. cimg::fwrite(ndims._data,nbdims,nfile); } \
  57019. else if (sizeof(unsigned int)==4) { CImg<uintT> ndims(5); \
  57020. for (int d = 0; d<5; ++d) ndims[d] = (unsigned int)dims[d]; \
  57021. cimg::fwrite(ndims._data,nbdims,nfile); } \
  57022. else if (sizeof(unsigned short)==4) { CImg<ushortT> ndims(5); \
  57023. for (int d = 0; d<5; ++d) ndims[d] = (unsigned short)dims[d]; \
  57024. cimg::fwrite(ndims._data,nbdims,nfile); } \
  57025. else throw CImgIOException(_cimg_instance \
  57026. "save_pandore(): Unsupported datatype for file '%s'.",\
  57027. cimg_instance, \
  57028. filename?filename:"(FILE*)"); \
  57029. if (id==2 || id==5 || id==8 || id==16 || id==19 || id==22 || id==26 || id==30) { \
  57030. __cimg_save_pandore_case(unsigned char); \
  57031. } else if (id==3 || id==6 || id==9 || id==17 || id==20 || id==23 || id==27 || id==31) { \
  57032. if (sizeof(unsigned long)==4) { __cimg_save_pandore_case(unsigned long); } \
  57033. else if (sizeof(unsigned int)==4) { __cimg_save_pandore_case(unsigned int); } \
  57034. else if (sizeof(unsigned short)==4) { __cimg_save_pandore_case(unsigned short); } \
  57035. else throw CImgIOException(_cimg_instance \
  57036. "save_pandore(): Unsupported datatype for file '%s'.",\
  57037. cimg_instance, \
  57038. filename?filename:"(FILE*)"); \
  57039. } else if (id==4 || id==7 || id==10 || id==18 || id==21 || id==25 || id==29 || id==33) { \
  57040. if (sizeof(double)==4) { __cimg_save_pandore_case(double); } \
  57041. else if (sizeof(float)==4) { __cimg_save_pandore_case(float); } \
  57042. else throw CImgIOException(_cimg_instance \
  57043. "save_pandore(): Unsupported datatype for file '%s'.",\
  57044. cimg_instance, \
  57045. filename?filename:"(FILE*)"); \
  57046. } \
  57047. saved = true; \
  57048. }
  57049. if (!file && !filename)
  57050. throw CImgArgumentException(_cimg_instance
  57051. "save_pandore(): Specified filename is (null).",
  57052. cimg_instance);
  57053. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  57054. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  57055. unsigned char header[36] = { 'P','A','N','D','O','R','E','0','4',0,0,0,
  57056. 0,0,0,0,'C','I','m','g',0,0,0,0,0,
  57057. 'N','o',' ','d','a','t','e',0,0,0,0 };
  57058. unsigned int nbdims, dims[5] = {};
  57059. bool saved = false;
  57060. _cimg_save_pandore_case(1,1,1,"uint8",2);
  57061. _cimg_save_pandore_case(1,1,1,"int8",3);
  57062. _cimg_save_pandore_case(1,1,1,"uint16",3);
  57063. _cimg_save_pandore_case(1,1,1,"int16",3);
  57064. _cimg_save_pandore_case(1,1,1,"uint32",3);
  57065. _cimg_save_pandore_case(1,1,1,"int32",3);
  57066. _cimg_save_pandore_case(1,1,1,"uint64",3);
  57067. _cimg_save_pandore_case(1,1,1,"int64",3);
  57068. _cimg_save_pandore_case(1,1,1,"float32",4);
  57069. _cimg_save_pandore_case(1,1,1,"float64",4);
  57070. _cimg_save_pandore_case(0,1,1,"uint8",5);
  57071. _cimg_save_pandore_case(0,1,1,"int8",6);
  57072. _cimg_save_pandore_case(0,1,1,"uint16",6);
  57073. _cimg_save_pandore_case(0,1,1,"int16",6);
  57074. _cimg_save_pandore_case(0,1,1,"uint32",6);
  57075. _cimg_save_pandore_case(0,1,1,"int32",6);
  57076. _cimg_save_pandore_case(0,1,1,"uint64",6);
  57077. _cimg_save_pandore_case(0,1,1,"int64",6);
  57078. _cimg_save_pandore_case(0,1,1,"float32",7);
  57079. _cimg_save_pandore_case(0,1,1,"float64",7);
  57080. _cimg_save_pandore_case(0,0,1,"uint8",8);
  57081. _cimg_save_pandore_case(0,0,1,"int8",9);
  57082. _cimg_save_pandore_case(0,0,1,"uint16",9);
  57083. _cimg_save_pandore_case(0,0,1,"int16",9);
  57084. _cimg_save_pandore_case(0,0,1,"uint32",9);
  57085. _cimg_save_pandore_case(0,0,1,"int32",9);
  57086. _cimg_save_pandore_case(0,0,1,"uint64",9);
  57087. _cimg_save_pandore_case(0,0,1,"int64",9);
  57088. _cimg_save_pandore_case(0,0,1,"float32",10);
  57089. _cimg_save_pandore_case(0,0,1,"float64",10);
  57090. _cimg_save_pandore_case(0,1,3,"uint8",16);
  57091. _cimg_save_pandore_case(0,1,3,"int8",17);
  57092. _cimg_save_pandore_case(0,1,3,"uint16",17);
  57093. _cimg_save_pandore_case(0,1,3,"int16",17);
  57094. _cimg_save_pandore_case(0,1,3,"uint32",17);
  57095. _cimg_save_pandore_case(0,1,3,"int32",17);
  57096. _cimg_save_pandore_case(0,1,3,"uint64",17);
  57097. _cimg_save_pandore_case(0,1,3,"int64",17);
  57098. _cimg_save_pandore_case(0,1,3,"float32",18);
  57099. _cimg_save_pandore_case(0,1,3,"float64",18);
  57100. _cimg_save_pandore_case(0,0,3,"uint8",19);
  57101. _cimg_save_pandore_case(0,0,3,"int8",20);
  57102. _cimg_save_pandore_case(0,0,3,"uint16",20);
  57103. _cimg_save_pandore_case(0,0,3,"int16",20);
  57104. _cimg_save_pandore_case(0,0,3,"uint32",20);
  57105. _cimg_save_pandore_case(0,0,3,"int32",20);
  57106. _cimg_save_pandore_case(0,0,3,"uint64",20);
  57107. _cimg_save_pandore_case(0,0,3,"int64",20);
  57108. _cimg_save_pandore_case(0,0,3,"float32",21);
  57109. _cimg_save_pandore_case(0,0,3,"float64",21);
  57110. _cimg_save_pandore_case(1,1,0,"uint8",22);
  57111. _cimg_save_pandore_case(1,1,0,"int8",23);
  57112. _cimg_save_pandore_case(1,1,0,"uint16",23);
  57113. _cimg_save_pandore_case(1,1,0,"int16",23);
  57114. _cimg_save_pandore_case(1,1,0,"uint32",23);
  57115. _cimg_save_pandore_case(1,1,0,"int32",23);
  57116. _cimg_save_pandore_case(1,1,0,"uint64",23);
  57117. _cimg_save_pandore_case(1,1,0,"int64",23);
  57118. _cimg_save_pandore_case(1,1,0,"float32",25);
  57119. _cimg_save_pandore_case(1,1,0,"float64",25);
  57120. _cimg_save_pandore_case(0,1,0,"uint8",26);
  57121. _cimg_save_pandore_case(0,1,0,"int8",27);
  57122. _cimg_save_pandore_case(0,1,0,"uint16",27);
  57123. _cimg_save_pandore_case(0,1,0,"int16",27);
  57124. _cimg_save_pandore_case(0,1,0,"uint32",27);
  57125. _cimg_save_pandore_case(0,1,0,"int32",27);
  57126. _cimg_save_pandore_case(0,1,0,"uint64",27);
  57127. _cimg_save_pandore_case(0,1,0,"int64",27);
  57128. _cimg_save_pandore_case(0,1,0,"float32",29);
  57129. _cimg_save_pandore_case(0,1,0,"float64",29);
  57130. _cimg_save_pandore_case(0,0,0,"uint8",30);
  57131. _cimg_save_pandore_case(0,0,0,"int8",31);
  57132. _cimg_save_pandore_case(0,0,0,"uint16",31);
  57133. _cimg_save_pandore_case(0,0,0,"int16",31);
  57134. _cimg_save_pandore_case(0,0,0,"uint32",31);
  57135. _cimg_save_pandore_case(0,0,0,"int32",31);
  57136. _cimg_save_pandore_case(0,0,0,"uint64",31);
  57137. _cimg_save_pandore_case(0,0,0,"int64",31);
  57138. _cimg_save_pandore_case(0,0,0,"float32",33);
  57139. _cimg_save_pandore_case(0,0,0,"float64",33);
  57140. if (!file) cimg::fclose(nfile);
  57141. return *this;
  57142. }
  57143. //! Save image as a raw data file.
  57144. /**
  57145. \param filename Filename, as a C-string.
  57146. \param is_multiplexed Tells if the image channels are stored in a multiplexed way (\c true) or not (\c false).
  57147. \note The .raw format does not store the image dimensions in the output file,
  57148. so you have to keep track of them somewhere to be able to read the file correctly afterwards.
  57149. **/
  57150. const CImg<T>& save_raw(const char *const filename, const bool is_multiplexed=false) const {
  57151. return _save_raw(0,filename,is_multiplexed);
  57152. }
  57153. //! Save image as a raw data file \overloading.
  57154. /**
  57155. Same as save_raw(const char *,bool) const
  57156. with a file stream argument instead of a filename string.
  57157. **/
  57158. const CImg<T>& save_raw(std::FILE *const file, const bool is_multiplexed=false) const {
  57159. return _save_raw(file,0,is_multiplexed);
  57160. }
  57161. const CImg<T>& _save_raw(std::FILE *const file, const char *const filename, const bool is_multiplexed) const {
  57162. if (!file && !filename)
  57163. throw CImgArgumentException(_cimg_instance
  57164. "save_raw(): Specified filename is (null).",
  57165. cimg_instance);
  57166. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  57167. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  57168. if (pixel_type()==cimg::type<bool>::string()) { // Boolean data (bitwise)
  57169. ulongT siz;
  57170. const unsigned char *const buf = _bool2uchar(siz,is_multiplexed);
  57171. cimg::fwrite(buf,siz,nfile);
  57172. delete[] buf;
  57173. } else { // Non boolean data
  57174. if (!is_multiplexed || _spectrum==1) cimg::fwrite(_data,size(),nfile); // Non-multiplexed
  57175. else { // Multiplexed
  57176. CImg<T> buf(_spectrum);
  57177. cimg_forXYZ(*this,x,y,z) {
  57178. cimg_forC(*this,c) buf[c] = (*this)(x,y,z,c);
  57179. cimg::fwrite(buf._data,_spectrum,nfile);
  57180. }
  57181. }
  57182. }
  57183. if (!file) cimg::fclose(nfile);
  57184. return *this;
  57185. }
  57186. // Return unsigned char buffer that encodes data of a CImg<bool> instance bitwise.
  57187. // (buffer needs to be deallocated afterwards, with delete[]).
  57188. const unsigned char *_bool2uchar(ulongT &siz, const bool is_multiplexed) const {
  57189. const ulongT _siz = size();
  57190. siz = _siz/8 + (_siz%8?1:0);
  57191. unsigned char *const buf = new unsigned char[siz], *ptrd = buf, val = 0, bit = 0;
  57192. if (!is_multiplexed || _spectrum==1) // Non-multiplexed
  57193. cimg_for(*this,ptrs,T) { (val<<=1)|=(*ptrs?1:0); if (++bit==8) { *(ptrd++) = val; val = bit = 0; }}
  57194. else // Multiplexed
  57195. cimg_forXYZ(*this,x,y,z) cimg_forC(*this,c) {
  57196. (val<<=1)|=((*this)(x,y,z,c)?1:0); if (++bit==8) { *(ptrd++) = val; val = bit = 0; }
  57197. }
  57198. if (bit) *ptrd = val;
  57199. return buf;
  57200. }
  57201. // Fill CImg<T> instance from bitwise data encoded in an unsigned char buffer.
  57202. void _uchar2bool(const unsigned char *buf, const ulongT siz, const bool is_multiplexed) {
  57203. const ulongT S = std::min(siz*8,size());
  57204. const unsigned char *ptrs = buf;
  57205. unsigned char val = 0, mask = 0;
  57206. T *ptrd = _data;
  57207. if (S && (!is_multiplexed || _spectrum==1)) // Non-multiplexed
  57208. for (ulongT off = 0; off<S; ++off) {
  57209. if (!(mask>>=1)) { val = *(ptrs++); mask = 128; }
  57210. *(ptrd++) = (T)((val&mask)?1:0);
  57211. }
  57212. else if (S) { // Multiplexed
  57213. ulongT off = 0;
  57214. for (int z = 0; z<depth() && off<=S; ++z)
  57215. for (int y = 0; y<height() && off<=S; ++y)
  57216. for (int x = 0; x<width() && off<=S; ++x)
  57217. for (int c = 0; c<spectrum() && off<=S; ++c) {
  57218. if (!(mask>>=1)) { val = *(ptrs++); ++off; mask = 128; }
  57219. (*this)(x,y,z,c) = (T)((val&mask)?1:0);
  57220. }
  57221. }
  57222. }
  57223. //! Save image as a .yuv video file.
  57224. /**
  57225. \param filename Filename, as a C-string.
  57226. \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
  57227. \param is_rgb Tells if pixel values of the instance image are RGB-coded (\c true) or YUV-coded (\c false).
  57228. \note Each slice of the instance image is considered to be a single frame of the output video file.
  57229. **/
  57230. const CImg<T>& save_yuv(const char *const filename,
  57231. const unsigned int chroma_subsampling=444,
  57232. const bool is_rgb=true) const {
  57233. CImgList<T>(*this,true).save_yuv(filename,chroma_subsampling,is_rgb);
  57234. return *this;
  57235. }
  57236. //! Save image as a .yuv video file \overloading.
  57237. /**
  57238. Same as save_yuv(const char*,const unsigned int,const bool) const
  57239. with a file stream argument instead of a filename string.
  57240. **/
  57241. const CImg<T>& save_yuv(std::FILE *const file,
  57242. const unsigned int chroma_subsampling=444,
  57243. const bool is_rgb=true) const {
  57244. CImgList<T>(*this,true).save_yuv(file,chroma_subsampling,is_rgb);
  57245. return *this;
  57246. }
  57247. //! Save 3D object as an Object File Format (.off) file.
  57248. /**
  57249. \param filename Filename, as a C-string.
  57250. \param primitives List of 3D object primitives.
  57251. \param colors List of 3D object colors.
  57252. \note
  57253. - Instance image contains the vertices data of the 3D object.
  57254. - Textured, transparent or sphere-shaped primitives cannot be managed by the .off file format.
  57255. Such primitives will be lost or simplified during file saving.
  57256. - The .off file format is <a href="http://people.sc.fsu.edu/~jburkardt/html/off_format.html">described here</a>.
  57257. **/
  57258. template<typename tf, typename tc>
  57259. const CImg<T>& save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
  57260. const char *const filename) const {
  57261. return _save_off(primitives,colors,0,filename);
  57262. }
  57263. //! Save 3D object as an Object File Format (.off) file \overloading.
  57264. /**
  57265. Same as save_off(const CImgList<tf>&,const CImgList<tc>&,const char*) const
  57266. with a file stream argument instead of a filename string.
  57267. **/
  57268. template<typename tf, typename tc>
  57269. const CImg<T>& save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
  57270. std::FILE *const file) const {
  57271. return _save_off(primitives,colors,file,0);
  57272. }
  57273. template<typename tf, typename tc>
  57274. const CImg<T>& _save_off(const CImgList<tf>& primitives, const CImgList<tc>& colors,
  57275. std::FILE *const file, const char *const filename) const {
  57276. if (!file && !filename)
  57277. throw CImgArgumentException(_cimg_instance
  57278. "save_off(): Specified filename is (null).",
  57279. cimg_instance);
  57280. if (is_empty())
  57281. throw CImgInstanceException(_cimg_instance
  57282. "save_off(): Empty instance, for file '%s'.",
  57283. cimg_instance,
  57284. filename?filename:"(FILE*)");
  57285. CImgList<T> opacities;
  57286. CImg<charT> error_message(1024);
  57287. if (!is_object3d(primitives,colors,opacities,true,error_message))
  57288. throw CImgInstanceException(_cimg_instance
  57289. "save_off(): Invalid specified 3D object, for file '%s' (%s).",
  57290. cimg_instance,
  57291. filename?filename:"(FILE*)",error_message.data());
  57292. const CImg<tc> default_color(1,3,1,1,(tc)std::min((int)cimg::type<tc>::max(),200));
  57293. std::FILE *const nfile = file?file:cimg::fopen(filename,"w");
  57294. unsigned int supported_primitives = 0;
  57295. cimglist_for(primitives,l) if (primitives[l].size()!=5) ++supported_primitives;
  57296. std::fprintf(nfile,"OFF\n%u %u %u\n",_width,supported_primitives,3*primitives._width);
  57297. cimg_forX(*this,i) std::fprintf(nfile,"%f %f %f\n",
  57298. (float)((*this)(i,0)),(float)((*this)(i,1)),(float)((*this)(i,2)));
  57299. cimglist_for(primitives,l) {
  57300. const CImg<tc>& color = l<colors.width()?colors[l]:default_color;
  57301. const unsigned int psiz = primitives[l].size(), csiz = color.size();
  57302. const float r = color[0]/255.f, g = (csiz>1?color[1]:r)/255.f, b = (csiz>2?color[2]:g)/255.f;
  57303. switch (psiz) {
  57304. case 1 : std::fprintf(nfile,"1 %u %f %f %f\n",
  57305. (unsigned int)primitives(l,0),r,g,b); break;
  57306. case 2 : std::fprintf(nfile,"2 %u %u %f %f %f\n",
  57307. (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break;
  57308. case 3 : std::fprintf(nfile,"3 %u %u %u %f %f %f\n",
  57309. (unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
  57310. (unsigned int)primitives(l,1),r,g,b); break;
  57311. case 4 : std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",
  57312. (unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
  57313. (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),r,g,b); break;
  57314. case 5 : std::fprintf(nfile,"2 %u %u %f %f %f\n",
  57315. (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),r,g,b); break;
  57316. case 6 : {
  57317. const unsigned int xt = (unsigned int)primitives(l,2), yt = (unsigned int)primitives(l,3);
  57318. const float
  57319. rt = color.atXY(xt,yt,0)/255.f,
  57320. gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f,
  57321. bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f;
  57322. std::fprintf(nfile,"2 %u %u %f %f %f\n",
  57323. (unsigned int)primitives(l,0),(unsigned int)primitives(l,1),rt,gt,bt);
  57324. } break;
  57325. case 9 : {
  57326. const unsigned int xt = (unsigned int)primitives(l,3), yt = (unsigned int)primitives(l,4);
  57327. const float
  57328. rt = color.atXY(xt,yt,0)/255.f,
  57329. gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f,
  57330. bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f;
  57331. std::fprintf(nfile,"3 %u %u %u %f %f %f\n",
  57332. (unsigned int)primitives(l,0),(unsigned int)primitives(l,2),
  57333. (unsigned int)primitives(l,1),rt,gt,bt);
  57334. } break;
  57335. case 12 : {
  57336. const unsigned int xt = (unsigned int)primitives(l,4), yt = (unsigned int)primitives(l,5);
  57337. const float
  57338. rt = color.atXY(xt,yt,0)/255.f,
  57339. gt = (csiz>1?color.atXY(xt,yt,1):r)/255.f,
  57340. bt = (csiz>2?color.atXY(xt,yt,2):g)/255.f;
  57341. std::fprintf(nfile,"4 %u %u %u %u %f %f %f\n",
  57342. (unsigned int)primitives(l,0),(unsigned int)primitives(l,3),
  57343. (unsigned int)primitives(l,2),(unsigned int)primitives(l,1),rt,gt,bt);
  57344. } break;
  57345. }
  57346. }
  57347. if (!file) cimg::fclose(nfile);
  57348. return *this;
  57349. }
  57350. //! Save volumetric image as a video (using the OpenCV library when available).
  57351. /**
  57352. \param filename Filename to write data to.
  57353. \param fps Number of frames per second.
  57354. \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs).
  57355. \param keep_open Tells if the video writer associated to the specified filename
  57356. must be kept open or not (to allow frames to be added in the same file afterwards).
  57357. **/
  57358. const CImg<T>& save_video(const char *const filename, const unsigned int fps=25,
  57359. const char *codec=0, const bool keep_open=false) const {
  57360. if (is_empty()) { CImgList<T>().save_video(filename,fps,codec,keep_open); return *this; }
  57361. CImgList<T> list;
  57362. get_split('z').move_to(list);
  57363. list.save_video(filename,fps,codec,keep_open);
  57364. return *this;
  57365. }
  57366. //! Save volumetric image as a video, using ffmpeg external binary.
  57367. /**
  57368. \param filename Filename, as a C-string.
  57369. \param fps Video framerate.
  57370. \param codec Video codec, as a C-string.
  57371. \param bitrate Video bitrate.
  57372. \note
  57373. - Each slice of the instance image is considered to be a single frame of the output video file.
  57374. - This method uses \c ffmpeg, an external executable binary provided by
  57375. <a href="http://www.ffmpeg.org">FFmpeg</a>.
  57376. It must be installed for the method to succeed.
  57377. **/
  57378. const CImg<T>& save_ffmpeg_external(const char *const filename, const unsigned int fps=25,
  57379. const char *const codec=0, const unsigned int bitrate=2048) const {
  57380. if (!filename)
  57381. throw CImgArgumentException(_cimg_instance
  57382. "save_ffmpeg_external(): Specified filename is (null).",
  57383. cimg_instance);
  57384. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  57385. CImgList<T> list;
  57386. get_split('z').move_to(list);
  57387. list.save_ffmpeg_external(filename,fps,codec,bitrate);
  57388. return *this;
  57389. }
  57390. //! Save image using gzip external binary.
  57391. /**
  57392. \param filename Filename, as a C-string.
  57393. \note This method uses \c gzip, an external executable binary provided by
  57394. <a href="//http://www.gzip.org">gzip</a>.
  57395. It must be installed for the method to succeed.
  57396. **/
  57397. const CImg<T>& save_gzip_external(const char *const filename) const {
  57398. if (!filename)
  57399. throw CImgArgumentException(_cimg_instance
  57400. "save_gzip_external(): Specified filename is (null).",
  57401. cimg_instance);
  57402. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  57403. CImg<charT> command(1024), filename_tmp(256), body(256);
  57404. const char
  57405. *ext = cimg::split_filename(filename,body),
  57406. *ext2 = cimg::split_filename(body,0);
  57407. std::FILE *file;
  57408. do {
  57409. if (!cimg::strcasecmp(ext,"gz")) {
  57410. if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
  57411. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
  57412. else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
  57413. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  57414. } else {
  57415. if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
  57416. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
  57417. else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
  57418. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  57419. }
  57420. if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
  57421. } while (file);
  57422. save(filename_tmp);
  57423. cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"",
  57424. cimg::gzip_path(),
  57425. CImg<charT>::string(filename_tmp)._system_strescape().data(),
  57426. CImg<charT>::string(filename)._system_strescape().data());
  57427. cimg::system(command,cimg::gzip_path());
  57428. file = cimg::std_fopen(filename,"rb");
  57429. if (!file)
  57430. throw CImgIOException(_cimg_instance
  57431. "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.",
  57432. cimg_instance,
  57433. filename);
  57434. else cimg::fclose(file);
  57435. std::remove(filename_tmp);
  57436. return *this;
  57437. }
  57438. //! Save image using GraphicsMagick's external binary.
  57439. /**
  57440. \param filename Filename, as a C-string.
  57441. \param quality Image quality (expressed in percent), when the file format supports it.
  57442. \note This method uses \c gm, an external executable binary provided by
  57443. <a href="http://www.graphicsmagick.org">GraphicsMagick</a>.
  57444. It must be installed for the method to succeed.
  57445. **/
  57446. const CImg<T>& save_graphicsmagick_external(const char *const filename, const unsigned int quality=100) const {
  57447. if (!filename)
  57448. throw CImgArgumentException(_cimg_instance
  57449. "save_graphicsmagick_external(): Specified filename is (null).",
  57450. cimg_instance);
  57451. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  57452. if (_depth>1)
  57453. cimg::warn(_cimg_instance
  57454. "save_other(): File '%s', saving a volumetric image with an external call to "
  57455. "GraphicsMagick only writes the first image slice.",
  57456. cimg_instance,filename);
  57457. #ifdef cimg_use_png
  57458. #define _cimg_sge_extension1 "png"
  57459. #define _cimg_sge_extension2 "png"
  57460. #else
  57461. #define _cimg_sge_extension1 "pgm"
  57462. #define _cimg_sge_extension2 "ppm"
  57463. #endif
  57464. CImg<charT> command(1024), filename_tmp(256);
  57465. std::FILE *file;
  57466. do {
  57467. cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
  57468. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),
  57469. _spectrum==1?_cimg_sge_extension1:_cimg_sge_extension2);
  57470. if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
  57471. } while (file);
  57472. #ifdef cimg_use_png
  57473. save_png(filename_tmp);
  57474. #else
  57475. save_pnm(filename_tmp);
  57476. #endif
  57477. cimg_snprintf(command,command._width,"\"%s\" convert -quality %u \"%s\" \"%s\"",
  57478. cimg::graphicsmagick_path(),quality,
  57479. CImg<charT>::string(filename_tmp)._system_strescape().data(),
  57480. CImg<charT>::string(filename)._system_strescape().data());
  57481. cimg::system(command,cimg::graphicsmagick_path());
  57482. file = cimg::std_fopen(filename,"rb");
  57483. if (!file)
  57484. throw CImgIOException(_cimg_instance
  57485. "save_graphicsmagick_external(): Failed to save file '%s' with external command 'gm'.",
  57486. cimg_instance,
  57487. filename);
  57488. if (file) cimg::fclose(file);
  57489. std::remove(filename_tmp);
  57490. return *this;
  57491. }
  57492. //! Save image using ImageMagick's external binary.
  57493. /**
  57494. \param filename Filename, as a C-string.
  57495. \param quality Image quality (expressed in percent), when the file format supports it.
  57496. \note This method uses \c convert, an external executable binary provided by
  57497. <a href="http://www.imagemagick.org">ImageMagick</a>.
  57498. It must be installed for the method to succeed.
  57499. **/
  57500. const CImg<T>& save_imagemagick_external(const char *const filename, const unsigned int quality=100) const {
  57501. if (!filename)
  57502. throw CImgArgumentException(_cimg_instance
  57503. "save_imagemagick_external(): Specified filename is (null).",
  57504. cimg_instance);
  57505. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  57506. if (_depth>1)
  57507. cimg::warn(_cimg_instance
  57508. "save_other(): File '%s', saving a volumetric image with an external call to "
  57509. "ImageMagick only writes the first image slice.",
  57510. cimg_instance,filename);
  57511. #ifdef cimg_use_png
  57512. #define _cimg_sie_extension1 "png"
  57513. #define _cimg_sie_extension2 "png"
  57514. #else
  57515. #define _cimg_sie_extension1 "pgm"
  57516. #define _cimg_sie_extension2 "ppm"
  57517. #endif
  57518. CImg<charT> command(1024), filename_tmp(256);
  57519. std::FILE *file;
  57520. do {
  57521. cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",cimg::temporary_path(),
  57522. cimg_file_separator,cimg::filenamerand(),_spectrum==1?_cimg_sie_extension1:_cimg_sie_extension2);
  57523. if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
  57524. } while (file);
  57525. #ifdef cimg_use_png
  57526. save_png(filename_tmp);
  57527. #else
  57528. save_pnm(filename_tmp);
  57529. #endif
  57530. cimg_snprintf(command,command._width,"\"%s\" -quality %u \"%s\" \"%s\"",
  57531. cimg::imagemagick_path(),quality,
  57532. CImg<charT>::string(filename_tmp)._system_strescape().data(),
  57533. CImg<charT>::string(filename)._system_strescape().data());
  57534. cimg::system(command,cimg::imagemagick_path());
  57535. file = cimg::std_fopen(filename,"rb");
  57536. if (!file)
  57537. throw CImgIOException(_cimg_instance
  57538. "save_imagemagick_external(): Failed to save file '%s' with "
  57539. "external command 'magick/convert'.",
  57540. cimg_instance,
  57541. filename);
  57542. if (file) cimg::fclose(file);
  57543. std::remove(filename_tmp);
  57544. return *this;
  57545. }
  57546. //! Save image as a Dicom file.
  57547. /**
  57548. \param filename Filename, as a C-string.
  57549. \note This method uses \c medcon, an external executable binary provided by
  57550. <a href="http://xmedcon.sourceforge.net">(X)Medcon</a>.
  57551. It must be installed for the method to succeed.
  57552. **/
  57553. const CImg<T>& save_medcon_external(const char *const filename) const {
  57554. if (!filename)
  57555. throw CImgArgumentException(_cimg_instance
  57556. "save_medcon_external(): Specified filename is (null).",
  57557. cimg_instance);
  57558. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  57559. CImg<charT> command(1024), filename_tmp(256), body(256);
  57560. std::FILE *file;
  57561. do {
  57562. cimg_snprintf(filename_tmp,filename_tmp._width,"%s.hdr",cimg::filenamerand());
  57563. if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
  57564. } while (file);
  57565. save_analyze(filename_tmp);
  57566. cimg_snprintf(command,command._width,"\"%s\" -w -c dicom -o \"%s\" -f \"%s\"",
  57567. cimg::medcon_path(),
  57568. CImg<charT>::string(filename)._system_strescape().data(),
  57569. CImg<charT>::string(filename_tmp)._system_strescape().data());
  57570. cimg::system(command,cimg::medcon_path());
  57571. std::remove(filename_tmp);
  57572. cimg::split_filename(filename_tmp,body);
  57573. cimg_snprintf(filename_tmp,filename_tmp._width,"%s.img",body._data);
  57574. std::remove(filename_tmp);
  57575. file = cimg::std_fopen(filename,"rb");
  57576. if (!file) {
  57577. cimg_snprintf(command,command._width,"m000-%s",filename);
  57578. file = cimg::std_fopen(command,"rb");
  57579. if (!file) {
  57580. cimg::fclose(cimg::fopen(filename,"r"));
  57581. throw CImgIOException(_cimg_instance
  57582. "save_medcon_external(): Failed to save file '%s' with external command 'medcon'.",
  57583. cimg_instance,
  57584. filename);
  57585. }
  57586. }
  57587. cimg::fclose(file);
  57588. std::rename(command,filename);
  57589. return *this;
  57590. }
  57591. // Save image for non natively supported formats.
  57592. /**
  57593. \param filename Filename, as a C-string.
  57594. \param quality Image quality (expressed in percent), when the file format supports it.
  57595. \note
  57596. - The filename extension tells about the desired file format.
  57597. - This method tries to save the instance image as a file, using external tools from
  57598. <a href="http://www.imagemagick.org">ImageMagick</a> or
  57599. <a href="http://www.graphicsmagick.org">GraphicsMagick</a>.
  57600. At least one of these tool must be installed for the method to succeed.
  57601. - It is recommended to use the generic method save(const char*, int) const instead,
  57602. as it can handle some file formats natively.
  57603. **/
  57604. const CImg<T>& save_other(const char *const filename, const unsigned int quality=100) const {
  57605. if (!filename)
  57606. throw CImgArgumentException(_cimg_instance
  57607. "save_other(): Specified filename is (null).",
  57608. cimg_instance);
  57609. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  57610. if (_depth>1)
  57611. cimg::warn(_cimg_instance
  57612. "save_other(): File '%s', saving a volumetric image with an external call to "
  57613. "ImageMagick or GraphicsMagick only writes the first image slice.",
  57614. cimg_instance,filename);
  57615. const unsigned int omode = cimg::exception_mode();
  57616. bool is_saved = true;
  57617. cimg::exception_mode(0);
  57618. try { save_magick(filename); }
  57619. catch (CImgException&) {
  57620. try { save_imagemagick_external(filename,quality); }
  57621. catch (CImgException&) {
  57622. try { save_graphicsmagick_external(filename,quality); }
  57623. catch (CImgException&) {
  57624. is_saved = false;
  57625. }
  57626. }
  57627. }
  57628. cimg::exception_mode(omode);
  57629. if (!is_saved)
  57630. throw CImgIOException(_cimg_instance
  57631. "save_other(): Failed to save file '%s'. Format is not natively supported, "
  57632. "and no external commands succeeded.",
  57633. cimg_instance,
  57634. filename);
  57635. return *this;
  57636. }
  57637. //! Serialize a CImg<T> instance into a raw CImg<unsigned char> buffer.
  57638. /**
  57639. \param is_compressed tells if zlib compression must be used for serialization
  57640. (this requires 'cimg_use_zlib' been enabled).
  57641. \param header_size Reserve empty bytes as a starting header.
  57642. **/
  57643. CImg<ucharT> get_serialize(const bool is_compressed=false, const unsigned int header_size=0) const {
  57644. return CImgList<T>(*this,true).get_serialize(is_compressed,header_size);
  57645. }
  57646. // [internal] Return a 40x38 color logo of a 'danger' item.
  57647. static CImg<T> _logo40x38() {
  57648. CImg<T> res(40,38,1,3);
  57649. const unsigned char *ptrs = cimg::logo40x38;
  57650. T *ptr1 = res.data(0,0,0,0), *ptr2 = res.data(0,0,0,1), *ptr3 = res.data(0,0,0,2);
  57651. for (ulongT off = 0; off<(ulongT)res._width*res._height;) {
  57652. const unsigned char n = *(ptrs++), r = *(ptrs++), g = *(ptrs++), b = *(ptrs++);
  57653. for (unsigned int l = 0; l<n; ++off, ++l) { *(ptr1++) = (T)r; *(ptr2++) = (T)g; *(ptr3++) = (T)b; }
  57654. }
  57655. return res;
  57656. }
  57657. //@}
  57658. }; // struct CImg { ...
  57659. /*
  57660. #-----------------------------------------
  57661. #
  57662. #
  57663. #
  57664. # Definition of the CImgList<T> structure
  57665. #
  57666. #
  57667. #
  57668. #------------------------------------------
  57669. */
  57670. //! Represent a list of images CImg<T>.
  57671. template<typename T>
  57672. struct CImgList {
  57673. unsigned int _width, _allocated_width;
  57674. CImg<T> *_data;
  57675. //! Simple iterator type, to loop through each image of a list.
  57676. /**
  57677. \note
  57678. - The \c CImgList<T>::iterator type is defined as a <tt>CImg<T>*</tt>.
  57679. - You may use it like this:
  57680. \code
  57681. CImgList<> list; // Assuming this image list is not empty
  57682. for (CImgList<>::iterator it = list.begin(); it<list.end(); ++it) (*it).mirror('x');
  57683. \endcode
  57684. - Using the loop macro \c cimglist_for is another (more concise) alternative:
  57685. \code
  57686. cimglist_for(list,l) list[l].mirror('x');
  57687. \endcode
  57688. **/
  57689. typedef CImg<T>* iterator;
  57690. //! Simple const iterator type, to loop through each image of a \c const list instance.
  57691. /**
  57692. \note
  57693. - The \c CImgList<T>::const_iterator type is defined to be a <tt>const CImg<T>*</tt>.
  57694. - Similar to CImgList<T>::iterator, but for constant list instances.
  57695. **/
  57696. typedef const CImg<T>* const_iterator;
  57697. //! Pixel value type.
  57698. /**
  57699. Refer to the pixels value type of the images in the list.
  57700. \note
  57701. - The \c CImgList<T>::value_type type of a \c CImgList<T> is defined to be a \c T.
  57702. It is then similar to CImg<T>::value_type.
  57703. - \c CImgList<T>::value_type is actually not used in %CImg methods. It has been mainly defined for
  57704. compatibility with STL naming conventions.
  57705. **/
  57706. typedef T value_type;
  57707. // Define common types related to template type T.
  57708. typedef typename cimg::superset<T,bool>::type Tbool;
  57709. typedef typename cimg::superset<T,unsigned char>::type Tuchar;
  57710. typedef typename cimg::superset<T,char>::type Tchar;
  57711. typedef typename cimg::superset<T,unsigned short>::type Tushort;
  57712. typedef typename cimg::superset<T,short>::type Tshort;
  57713. typedef typename cimg::superset<T,unsigned int>::type Tuint;
  57714. typedef typename cimg::superset<T,int>::type Tint;
  57715. typedef typename cimg::superset<T,cimg_ulong>::type Tulong;
  57716. typedef typename cimg::superset<T,cimg_long>::type Tlong;
  57717. typedef typename cimg::superset<T,float>::type Tfloat;
  57718. typedef typename cimg::superset<T,double>::type Tdouble;
  57719. typedef typename cimg::last<T,bool>::type boolT;
  57720. typedef typename cimg::last<T,unsigned char>::type ucharT;
  57721. typedef typename cimg::last<T,char>::type charT;
  57722. typedef typename cimg::last<T,unsigned short>::type ushortT;
  57723. typedef typename cimg::last<T,short>::type shortT;
  57724. typedef typename cimg::last<T,unsigned int>::type uintT;
  57725. typedef typename cimg::last<T,int>::type intT;
  57726. typedef typename cimg::last<T,cimg_ulong>::type ulongT;
  57727. typedef typename cimg::last<T,cimg_long>::type longT;
  57728. typedef typename cimg::last<T,cimg_uint64>::type uint64T;
  57729. typedef typename cimg::last<T,cimg_int64>::type int64T;
  57730. typedef typename cimg::last<T,float>::type floatT;
  57731. typedef typename cimg::last<T,double>::type doubleT;
  57732. //@}
  57733. //---------------------------
  57734. //
  57735. //! \name Plugins
  57736. //@{
  57737. //---------------------------
  57738. #ifdef cimglist_plugin
  57739. #include cimglist_plugin
  57740. #endif
  57741. #ifdef cimglist_plugin1
  57742. #include cimglist_plugin1
  57743. #endif
  57744. #ifdef cimglist_plugin2
  57745. #include cimglist_plugin2
  57746. #endif
  57747. #ifdef cimglist_plugin3
  57748. #include cimglist_plugin3
  57749. #endif
  57750. #ifdef cimglist_plugin4
  57751. #include cimglist_plugin4
  57752. #endif
  57753. #ifdef cimglist_plugin5
  57754. #include cimglist_plugin5
  57755. #endif
  57756. #ifdef cimglist_plugin6
  57757. #include cimglist_plugin6
  57758. #endif
  57759. #ifdef cimglist_plugin7
  57760. #include cimglist_plugin7
  57761. #endif
  57762. #ifdef cimglist_plugin8
  57763. #include cimglist_plugin8
  57764. #endif
  57765. //@}
  57766. //--------------------------------------------------------
  57767. //
  57768. //! \name Constructors / Destructor / Instance Management
  57769. //@{
  57770. //--------------------------------------------------------
  57771. //! Destructor.
  57772. /**
  57773. Destroy current list instance.
  57774. \note
  57775. - Any allocated buffer is deallocated.
  57776. - Destroying an empty list does nothing actually.
  57777. **/
  57778. ~CImgList() {
  57779. delete[] _data;
  57780. }
  57781. //! Default constructor.
  57782. /**
  57783. Construct a new empty list instance.
  57784. \note
  57785. - An empty list has no pixel data and its dimension width() is set to \c 0, as well as its
  57786. image buffer pointer data().
  57787. - An empty list may be reassigned afterwards, with the family of the assign() methods.
  57788. In all cases, the type of pixels stays \c T.
  57789. **/
  57790. CImgList():
  57791. _width(0),_allocated_width(0),_data(0) {}
  57792. //! Construct list containing empty images.
  57793. /**
  57794. \param n Number of empty images.
  57795. \note Useful when you know by advance the number of images you want to manage, as
  57796. it will allocate the right amount of memory for the list, without needs for reallocation
  57797. (that may occur when starting from an empty list and inserting several images in it).
  57798. **/
  57799. explicit CImgList(const unsigned int n):_width(n) {
  57800. if (n) _data = new CImg<T>[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))];
  57801. else { _allocated_width = 0; _data = 0; }
  57802. }
  57803. //! Construct list containing images of specified size.
  57804. /**
  57805. \param n Number of images.
  57806. \param width Width of images.
  57807. \param height Height of images.
  57808. \param depth Depth of images.
  57809. \param spectrum Number of channels of images.
  57810. \note Pixel values are not initialized and may probably contain garbage.
  57811. **/
  57812. CImgList(const unsigned int n, const unsigned int width, const unsigned int height=1,
  57813. const unsigned int depth=1, const unsigned int spectrum=1):
  57814. _width(0),_allocated_width(0),_data(0) {
  57815. assign(n);
  57816. cimglist_apply(*this,assign)(width,height,depth,spectrum);
  57817. }
  57818. //! Construct list containing images of specified size, and initialize pixel values.
  57819. /**
  57820. \param n Number of images.
  57821. \param width Width of images.
  57822. \param height Height of images.
  57823. \param depth Depth of images.
  57824. \param spectrum Number of channels of images.
  57825. \param val Initialization value for images pixels.
  57826. **/
  57827. CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
  57828. const unsigned int depth, const unsigned int spectrum, const T& val):
  57829. _width(0),_allocated_width(0),_data(0) {
  57830. assign(n);
  57831. cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
  57832. }
  57833. //! Construct list containing images of specified size, and initialize pixel values from a sequence of integers.
  57834. /**
  57835. \param n Number of images.
  57836. \param width Width of images.
  57837. \param height Height of images.
  57838. \param depth Depth of images.
  57839. \param spectrum Number of channels of images.
  57840. \param val0 First value of the initializing integers sequence.
  57841. \param val1 Second value of the initializing integers sequence.
  57842. \warning You must specify at least <tt>width*height*depth*spectrum</tt> values in your argument list,
  57843. or you will probably segfault.
  57844. **/
  57845. CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
  57846. const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...):
  57847. _width(0),_allocated_width(0),_data(0) {
  57848. #define _CImgList_stdarg(t) { \
  57849. assign(n,width,height,depth,spectrum); \
  57850. const ulongT siz = (ulongT)width*height*depth*spectrum, nsiz = siz*n; \
  57851. T *ptrd = _data->_data; \
  57852. va_list ap; \
  57853. va_start(ap,val1); \
  57854. for (ulongT l = 0, s = 0, i = 0; i<nsiz; ++i) { \
  57855. *(ptrd++) = (T)(i==0?val0:(i==1?val1:va_arg(ap,t))); \
  57856. if ((++s)==siz) { ptrd = _data[++l]._data; s = 0; } \
  57857. } \
  57858. va_end(ap); \
  57859. }
  57860. _CImgList_stdarg(int);
  57861. }
  57862. //! Construct list containing images of specified size, and initialize pixel values from a sequence of doubles.
  57863. /**
  57864. \param n Number of images.
  57865. \param width Width of images.
  57866. \param height Height of images.
  57867. \param depth Depth of images.
  57868. \param spectrum Number of channels of images.
  57869. \param val0 First value of the initializing doubles sequence.
  57870. \param val1 Second value of the initializing doubles sequence.
  57871. \warning You must specify at least <tt>width*height*depth*spectrum</tt> values in your argument list,
  57872. or you will probably segfault.
  57873. **/
  57874. CImgList(const unsigned int n, const unsigned int width, const unsigned int height,
  57875. const unsigned int depth, const unsigned int spectrum, const double val0, const double val1, ...):
  57876. _width(0),_allocated_width(0),_data(0) {
  57877. _CImgList_stdarg(double);
  57878. }
  57879. //! Construct list containing copies of an input image.
  57880. /**
  57881. \param n Number of images.
  57882. \param img Input image to copy in the constructed list.
  57883. \param is_shared Tells if the elements of the list are shared or non-shared copies of \c img.
  57884. **/
  57885. template<typename t>
  57886. CImgList(const unsigned int n, const CImg<t>& img, const bool is_shared=false):
  57887. _width(0),_allocated_width(0),_data(0) {
  57888. assign(n);
  57889. cimglist_apply(*this,assign)(img,is_shared);
  57890. }
  57891. //! Construct list from one image.
  57892. /**
  57893. \param img Input image to copy in the constructed list.
  57894. \param is_shared Tells if the element of the list is a shared or non-shared copy of \c img.
  57895. **/
  57896. template<typename t>
  57897. explicit CImgList(const CImg<t>& img, const bool is_shared=false):
  57898. _width(0),_allocated_width(0),_data(0) {
  57899. assign(1);
  57900. _data[0].assign(img,is_shared);
  57901. }
  57902. //! Construct list from two images.
  57903. /**
  57904. \param img1 First input image to copy in the constructed list.
  57905. \param img2 Second input image to copy in the constructed list.
  57906. \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
  57907. **/
  57908. template<typename t1, typename t2>
  57909. CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false):
  57910. _width(0),_allocated_width(0),_data(0) {
  57911. assign(2);
  57912. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared);
  57913. }
  57914. //! Construct list from three images.
  57915. /**
  57916. \param img1 First input image to copy in the constructed list.
  57917. \param img2 Second input image to copy in the constructed list.
  57918. \param img3 Third input image to copy in the constructed list.
  57919. \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
  57920. **/
  57921. template<typename t1, typename t2, typename t3>
  57922. CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool is_shared=false):
  57923. _width(0),_allocated_width(0),_data(0) {
  57924. assign(3);
  57925. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
  57926. }
  57927. //! Construct list from four images.
  57928. /**
  57929. \param img1 First input image to copy in the constructed list.
  57930. \param img2 Second input image to copy in the constructed list.
  57931. \param img3 Third input image to copy in the constructed list.
  57932. \param img4 Fourth input image to copy in the constructed list.
  57933. \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
  57934. **/
  57935. template<typename t1, typename t2, typename t3, typename t4>
  57936. CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
  57937. const bool is_shared=false):
  57938. _width(0),_allocated_width(0),_data(0) {
  57939. assign(4);
  57940. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
  57941. _data[3].assign(img4,is_shared);
  57942. }
  57943. //! Construct list from five images.
  57944. /**
  57945. \param img1 First input image to copy in the constructed list.
  57946. \param img2 Second input image to copy in the constructed list.
  57947. \param img3 Third input image to copy in the constructed list.
  57948. \param img4 Fourth input image to copy in the constructed list.
  57949. \param img5 Fifth input image to copy in the constructed list.
  57950. \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
  57951. **/
  57952. template<typename t1, typename t2, typename t3, typename t4, typename t5>
  57953. CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
  57954. const CImg<t5>& img5, const bool is_shared=false):
  57955. _width(0),_allocated_width(0),_data(0) {
  57956. assign(5);
  57957. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
  57958. _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared);
  57959. }
  57960. //! Construct list from six images.
  57961. /**
  57962. \param img1 First input image to copy in the constructed list.
  57963. \param img2 Second input image to copy in the constructed list.
  57964. \param img3 Third input image to copy in the constructed list.
  57965. \param img4 Fourth input image to copy in the constructed list.
  57966. \param img5 Fifth input image to copy in the constructed list.
  57967. \param img6 Sixth input image to copy in the constructed list.
  57968. \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
  57969. **/
  57970. template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
  57971. CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
  57972. const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false):
  57973. _width(0),_allocated_width(0),_data(0) {
  57974. assign(6);
  57975. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
  57976. _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
  57977. }
  57978. //! Construct list from seven images.
  57979. /**
  57980. \param img1 First input image to copy in the constructed list.
  57981. \param img2 Second input image to copy in the constructed list.
  57982. \param img3 Third input image to copy in the constructed list.
  57983. \param img4 Fourth input image to copy in the constructed list.
  57984. \param img5 Fifth input image to copy in the constructed list.
  57985. \param img6 Sixth input image to copy in the constructed list.
  57986. \param img7 Seventh input image to copy in the constructed list.
  57987. \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
  57988. **/
  57989. template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
  57990. CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
  57991. const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool is_shared=false):
  57992. _width(0),_allocated_width(0),_data(0) {
  57993. assign(7);
  57994. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
  57995. _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
  57996. _data[6].assign(img7,is_shared);
  57997. }
  57998. //! Construct list from eight images.
  57999. /**
  58000. \param img1 First input image to copy in the constructed list.
  58001. \param img2 Second input image to copy in the constructed list.
  58002. \param img3 Third input image to copy in the constructed list.
  58003. \param img4 Fourth input image to copy in the constructed list.
  58004. \param img5 Fifth input image to copy in the constructed list.
  58005. \param img6 Sixth input image to copy in the constructed list.
  58006. \param img7 Seventh input image to copy in the constructed list.
  58007. \param img8 Eighth input image to copy in the constructed list.
  58008. \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
  58009. **/
  58010. template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
  58011. CImgList(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
  58012. const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8,
  58013. const bool is_shared=false):
  58014. _width(0),_allocated_width(0),_data(0) {
  58015. assign(8);
  58016. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
  58017. _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
  58018. _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared);
  58019. }
  58020. //! Construct list copy.
  58021. /**
  58022. \param list Input list to copy.
  58023. \note The shared state of each element of the constructed list is kept the same as in \c list.
  58024. **/
  58025. template<typename t>
  58026. CImgList(const CImgList<t>& list):_width(0),_allocated_width(0),_data(0) {
  58027. assign(list._width);
  58028. cimglist_for(*this,l) _data[l].assign(list[l],false);
  58029. }
  58030. //! Construct list copy \specialization.
  58031. CImgList(const CImgList<T>& list):_width(0),_allocated_width(0),_data(0) {
  58032. assign(list._width);
  58033. cimglist_for(*this,l) _data[l].assign(list[l],list[l]._is_shared);
  58034. }
  58035. //! Construct list copy, and force the shared state of the list elements.
  58036. /**
  58037. \param list Input list to copy.
  58038. \param is_shared Tells if the elements of the list are shared or non-shared copies of input images.
  58039. **/
  58040. template<typename t>
  58041. CImgList(const CImgList<t>& list, const bool is_shared):_width(0),_allocated_width(0),_data(0) {
  58042. assign(list._width);
  58043. cimglist_for(*this,l) _data[l].assign(list[l],is_shared);
  58044. }
  58045. //! Construct list by reading the content of a file.
  58046. /**
  58047. \param filename Filename, as a C-string.
  58048. **/
  58049. explicit CImgList(const char *const filename):_width(0),_allocated_width(0),_data(0) {
  58050. assign(filename);
  58051. }
  58052. //! Construct list from the content of a display window.
  58053. /**
  58054. \param disp Display window to get content from.
  58055. \note Constructed list contains a single image only.
  58056. **/
  58057. explicit CImgList(const CImgDisplay& disp):_width(0),_allocated_width(0),_data(0) {
  58058. assign(disp);
  58059. }
  58060. //! Return a list with elements being shared copies of images in the list instance.
  58061. /**
  58062. \note <tt>list2 = list1.get_shared()</tt> is equivalent to <tt>list2.assign(list1,true)</tt>.
  58063. **/
  58064. CImgList<T> get_shared() {
  58065. CImgList<T> res(_width);
  58066. cimglist_for(*this,l) res[l].assign(_data[l],true);
  58067. return res;
  58068. }
  58069. //! Return a list with elements being shared copies of images in the list instance \const.
  58070. const CImgList<T> get_shared() const {
  58071. CImgList<T> res(_width);
  58072. cimglist_for(*this,l) res[l].assign(_data[l],true);
  58073. return res;
  58074. }
  58075. //! Destructor \inplace.
  58076. /**
  58077. \see CImgList().
  58078. **/
  58079. CImgList<T>& assign() {
  58080. delete[] _data;
  58081. _width = _allocated_width = 0;
  58082. _data = 0;
  58083. return *this;
  58084. }
  58085. //! Destructor \inplace.
  58086. /**
  58087. Equivalent to assign().
  58088. \note Only here for compatibility with STL naming conventions.
  58089. **/
  58090. CImgList<T>& clear() {
  58091. return assign();
  58092. }
  58093. //! Construct list containing empty images \inplace.
  58094. /**
  58095. \see CImgList(unsigned int).
  58096. **/
  58097. CImgList<T>& assign(const unsigned int n) {
  58098. if (!n) return assign();
  58099. if (_allocated_width<n || _allocated_width>(n<<2)) {
  58100. delete[] _data;
  58101. _data = new CImg<T>[_allocated_width = std::max(16U,(unsigned int)cimg::nearest_pow2(n))];
  58102. }
  58103. _width = n;
  58104. return *this;
  58105. }
  58106. //! Construct list containing images of specified size \inplace.
  58107. /**
  58108. \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int).
  58109. **/
  58110. CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height=1,
  58111. const unsigned int depth=1, const unsigned int spectrum=1) {
  58112. assign(n);
  58113. cimglist_apply(*this,assign)(width,height,depth,spectrum);
  58114. return *this;
  58115. }
  58116. //! Construct list containing images of specified size, and initialize pixel values \inplace.
  58117. /**
  58118. \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const T).
  58119. **/
  58120. CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
  58121. const unsigned int depth, const unsigned int spectrum, const T& val) {
  58122. assign(n);
  58123. cimglist_apply(*this,assign)(width,height,depth,spectrum,val);
  58124. return *this;
  58125. }
  58126. //! Construct list with images of specified size, and initialize pixel values from a sequence of integers \inplace.
  58127. /**
  58128. \see CImgList(unsigned int, unsigned int, unsigned int, unsigned int, unsigned int, const int, const int, ...).
  58129. **/
  58130. CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
  58131. const unsigned int depth, const unsigned int spectrum, const int val0, const int val1, ...) {
  58132. _CImgList_stdarg(int);
  58133. return *this;
  58134. }
  58135. //! Construct list with images of specified size, and initialize pixel values from a sequence of doubles \inplace.
  58136. /**
  58137. \see CImgList(unsigned int,unsigned int,unsigned int,unsigned int,unsigned int,const double,const double,...).
  58138. **/
  58139. CImgList<T>& assign(const unsigned int n, const unsigned int width, const unsigned int height,
  58140. const unsigned int depth, const unsigned int spectrum,
  58141. const double val0, const double val1, ...) {
  58142. _CImgList_stdarg(double);
  58143. return *this;
  58144. }
  58145. //! Construct list containing copies of an input image \inplace.
  58146. /**
  58147. \see CImgList(unsigned int, const CImg<t>&, bool).
  58148. **/
  58149. template<typename t>
  58150. CImgList<T>& assign(const unsigned int n, const CImg<t>& img, const bool is_shared=false) {
  58151. assign(n);
  58152. cimglist_apply(*this,assign)(img,is_shared);
  58153. return *this;
  58154. }
  58155. //! Construct list from one image \inplace.
  58156. /**
  58157. \see CImgList(const CImg<t>&, bool).
  58158. **/
  58159. template<typename t>
  58160. CImgList<T>& assign(const CImg<t>& img, const bool is_shared=false) {
  58161. assign(1);
  58162. _data[0].assign(img,is_shared);
  58163. return *this;
  58164. }
  58165. //! Construct list from two images \inplace.
  58166. /**
  58167. \see CImgList(const CImg<t>&, const CImg<t>&, bool).
  58168. **/
  58169. template<typename t1, typename t2>
  58170. CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const bool is_shared=false) {
  58171. assign(2);
  58172. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared);
  58173. return *this;
  58174. }
  58175. //! Construct list from three images \inplace.
  58176. /**
  58177. \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
  58178. **/
  58179. template<typename t1, typename t2, typename t3>
  58180. CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const bool is_shared=false) {
  58181. assign(3);
  58182. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
  58183. return *this;
  58184. }
  58185. //! Construct list from four images \inplace.
  58186. /**
  58187. \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
  58188. **/
  58189. template<typename t1, typename t2, typename t3, typename t4>
  58190. CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
  58191. const bool is_shared=false) {
  58192. assign(4);
  58193. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
  58194. _data[3].assign(img4,is_shared);
  58195. return *this;
  58196. }
  58197. //! Construct list from five images \inplace.
  58198. /**
  58199. \see CImgList(const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, const CImg<t>&, bool).
  58200. **/
  58201. template<typename t1, typename t2, typename t3, typename t4, typename t5>
  58202. CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
  58203. const CImg<t5>& img5, const bool is_shared=false) {
  58204. assign(5);
  58205. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
  58206. _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared);
  58207. return *this;
  58208. }
  58209. //! Construct list from six images \inplace.
  58210. /**
  58211. \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&, bool).
  58212. **/
  58213. template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6>
  58214. CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
  58215. const CImg<t5>& img5, const CImg<t6>& img6, const bool is_shared=false) {
  58216. assign(6);
  58217. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
  58218. _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
  58219. return *this;
  58220. }
  58221. //! Construct list from seven images \inplace.
  58222. /**
  58223. \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,
  58224. const CImg<t>&, bool).
  58225. **/
  58226. template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7>
  58227. CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
  58228. const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const bool is_shared=false) {
  58229. assign(7);
  58230. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
  58231. _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
  58232. _data[6].assign(img7,is_shared);
  58233. return *this;
  58234. }
  58235. //! Construct list from eight images \inplace.
  58236. /**
  58237. \see CImgList(const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,const CImg<t>&,
  58238. const CImg<t>&, const CImg<t>&, bool).
  58239. **/
  58240. template<typename t1, typename t2, typename t3, typename t4, typename t5, typename t6, typename t7, typename t8>
  58241. CImgList<T>& assign(const CImg<t1>& img1, const CImg<t2>& img2, const CImg<t3>& img3, const CImg<t4>& img4,
  58242. const CImg<t5>& img5, const CImg<t6>& img6, const CImg<t7>& img7, const CImg<t8>& img8,
  58243. const bool is_shared=false) {
  58244. assign(8);
  58245. _data[0].assign(img1,is_shared); _data[1].assign(img2,is_shared); _data[2].assign(img3,is_shared);
  58246. _data[3].assign(img4,is_shared); _data[4].assign(img5,is_shared); _data[5].assign(img6,is_shared);
  58247. _data[6].assign(img7,is_shared); _data[7].assign(img8,is_shared);
  58248. return *this;
  58249. }
  58250. //! Construct list as a copy of an existing list and force the shared state of the list elements \inplace.
  58251. /**
  58252. \see CImgList(const CImgList<t>&, bool is_shared).
  58253. **/
  58254. template<typename t>
  58255. CImgList<T>& assign(const CImgList<t>& list, const bool is_shared=false) {
  58256. cimg::unused(is_shared);
  58257. assign(list._width);
  58258. cimglist_for(*this,l) _data[l].assign(list[l],false);
  58259. return *this;
  58260. }
  58261. //! Construct list as a copy of an existing list and force shared state of elements \inplace \specialization.
  58262. CImgList<T>& assign(const CImgList<T>& list, const bool is_shared=false) {
  58263. if (this==&list) return *this;
  58264. CImgList<T> res(list._width);
  58265. cimglist_for(res,l) res[l].assign(list[l],is_shared);
  58266. return res.move_to(*this);
  58267. }
  58268. //! Construct list by reading the content of a file \inplace.
  58269. /**
  58270. \see CImgList(const char *const).
  58271. **/
  58272. CImgList<T>& assign(const char *const filename) {
  58273. return load(filename);
  58274. }
  58275. //! Construct list from the content of a display window \inplace.
  58276. /**
  58277. \see CImgList(const CImgDisplay&).
  58278. **/
  58279. CImgList<T>& assign(const CImgDisplay &disp) {
  58280. return assign(CImg<T>(disp));
  58281. }
  58282. //! Transfer the content of the list instance to another list.
  58283. /**
  58284. \param list Destination list.
  58285. \note When returning, the current list instance is empty and the initial content of \c list is destroyed.
  58286. **/
  58287. template<typename t>
  58288. CImgList<t>& move_to(CImgList<t>& list) {
  58289. list.assign(_width);
  58290. bool is_one_shared_element = false;
  58291. cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
  58292. if (is_one_shared_element) cimglist_for(*this,l) list[l].assign(_data[l]);
  58293. else cimglist_for(*this,l) _data[l].move_to(list[l]);
  58294. assign();
  58295. return list;
  58296. }
  58297. //! Transfer the content of the list instance at a specified position in another list.
  58298. /**
  58299. \param list Destination list.
  58300. \param pos Index of the insertion in the list.
  58301. \note When returning, the list instance is empty and the initial content of \c list is preserved
  58302. (only images indexes may be modified).
  58303. **/
  58304. template<typename t>
  58305. CImgList<t>& move_to(CImgList<t>& list, const unsigned int pos) {
  58306. if (is_empty()) return list;
  58307. const unsigned int npos = pos>list._width?list._width:pos;
  58308. list.insert(_width,npos);
  58309. bool is_one_shared_element = false;
  58310. cimglist_for(*this,l) is_one_shared_element|=_data[l]._is_shared;
  58311. if (is_one_shared_element) cimglist_for(*this,l) list[npos + l].assign(_data[l]);
  58312. else cimglist_for(*this,l) _data[l].move_to(list[npos + l]);
  58313. assign();
  58314. return list;
  58315. }
  58316. //! Swap all fields between two list instances.
  58317. /**
  58318. \param list List to swap fields with.
  58319. \note Can be used to exchange the content of two lists in a fast way.
  58320. **/
  58321. CImgList<T>& swap(CImgList<T>& list) {
  58322. cimg::swap(_width,list._width,_allocated_width,list._allocated_width);
  58323. cimg::swap(_data,list._data);
  58324. return list;
  58325. }
  58326. //! Return a reference to an empty list.
  58327. /**
  58328. \note Can be used to define default values in a function taking a CImgList<T> as an argument.
  58329. \code
  58330. void f(const CImgList<char>& list=CImgList<char>::empty());
  58331. \endcode
  58332. **/
  58333. static CImgList<T>& empty() {
  58334. static CImgList<T> _empty;
  58335. return _empty.assign();
  58336. }
  58337. //! Return a reference to an empty list \const.
  58338. static const CImgList<T>& const_empty() {
  58339. static const CImgList<T> _empty;
  58340. return _empty;
  58341. }
  58342. //@}
  58343. //------------------------------------------
  58344. //
  58345. //! \name Overloaded Operators
  58346. //@{
  58347. //------------------------------------------
  58348. //! Return a reference to one image element of the list.
  58349. /**
  58350. \param pos Index of the image element.
  58351. **/
  58352. CImg<T>& operator()(const unsigned int pos) {
  58353. #if cimg_verbosity>=3
  58354. if (pos>=_width) {
  58355. cimg::warn(_cimglist_instance
  58356. "operator(): Invalid image request, at position [%u].",
  58357. cimglist_instance,
  58358. pos);
  58359. return *_data;
  58360. }
  58361. #endif
  58362. return _data[pos];
  58363. }
  58364. //! Return a reference to one image of the list.
  58365. /**
  58366. \param pos Index of the image element.
  58367. **/
  58368. const CImg<T>& operator()(const unsigned int pos) const {
  58369. return const_cast<CImgList<T>*>(this)->operator()(pos);
  58370. }
  58371. //! Return a reference to one pixel value of one image of the list.
  58372. /**
  58373. \param pos Index of the image element.
  58374. \param x X-coordinate of the pixel value.
  58375. \param y Y-coordinate of the pixel value.
  58376. \param z Z-coordinate of the pixel value.
  58377. \param c C-coordinate of the pixel value.
  58378. \note <tt>list(n,x,y,z,c)</tt> is equivalent to <tt>list[n](x,y,z,c)</tt>.
  58379. **/
  58380. T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
  58381. const unsigned int z=0, const unsigned int c=0) {
  58382. return (*this)[pos](x,y,z,c);
  58383. }
  58384. //! Return a reference to one pixel value of one image of the list \const.
  58385. const T& operator()(const unsigned int pos, const unsigned int x, const unsigned int y=0,
  58386. const unsigned int z=0, const unsigned int c=0) const {
  58387. return (*this)[pos](x,y,z,c);
  58388. }
  58389. //! Return pointer to the first image of the list.
  58390. /**
  58391. \note Images in a list are stored as a buffer of \c CImg<T>.
  58392. **/
  58393. operator CImg<T>*() {
  58394. return _data;
  58395. }
  58396. //! Return pointer to the first image of the list \const.
  58397. operator const CImg<T>*() const {
  58398. return _data;
  58399. }
  58400. //! Construct list from one image \inplace.
  58401. /**
  58402. \param img Input image to copy in the constructed list.
  58403. \note <tt>list = img;</tt> is equivalent to <tt>list.assign(img);</tt>.
  58404. **/
  58405. template<typename t>
  58406. CImgList<T>& operator=(const CImg<t>& img) {
  58407. return assign(img);
  58408. }
  58409. //! Construct list from another list.
  58410. /**
  58411. \param list Input list to copy.
  58412. \note <tt>list1 = list2</tt> is equivalent to <tt>list1.assign(list2);</tt>.
  58413. **/
  58414. template<typename t>
  58415. CImgList<T>& operator=(const CImgList<t>& list) {
  58416. return assign(list);
  58417. }
  58418. //! Construct list from another list \specialization.
  58419. CImgList<T>& operator=(const CImgList<T>& list) {
  58420. return assign(list);
  58421. }
  58422. //! Construct list by reading the content of a file \inplace.
  58423. /**
  58424. \see CImgList(const char *const).
  58425. **/
  58426. CImgList<T>& operator=(const char *const filename) {
  58427. return assign(filename);
  58428. }
  58429. //! Construct list from the content of a display window \inplace.
  58430. /**
  58431. \see CImgList(const CImgDisplay&).
  58432. **/
  58433. CImgList<T>& operator=(const CImgDisplay& disp) {
  58434. return assign(disp);
  58435. }
  58436. //! Return a non-shared copy of a list.
  58437. /**
  58438. \note <tt>+list</tt> is equivalent to <tt>CImgList<T>(list,false)</tt>.
  58439. It forces the copy to have non-shared elements.
  58440. **/
  58441. CImgList<T> operator+() const {
  58442. return CImgList<T>(*this,false);
  58443. }
  58444. //! Return a copy of the list instance, where image \c img has been inserted at the end.
  58445. /**
  58446. \param img Image inserted at the end of the instance copy.
  58447. \note Define a convenient way to create temporary lists of images, as in the following code:
  58448. \code
  58449. (img1,img2,img3,img4).display("My four images");
  58450. \endcode
  58451. **/
  58452. template<typename t>
  58453. CImgList<T>& operator,(const CImg<t>& img) {
  58454. return insert(img);
  58455. }
  58456. //! Return a copy of the list instance, where image \c img has been inserted at the end \const.
  58457. template<typename t>
  58458. CImgList<T> operator,(const CImg<t>& img) const {
  58459. return (+*this).insert(img);
  58460. }
  58461. //! Return a copy of the list instance, where all elements of input list \c list have been inserted at the end.
  58462. /**
  58463. \param list List inserted at the end of the instance copy.
  58464. **/
  58465. template<typename t>
  58466. CImgList<T>& operator,(const CImgList<t>& list) {
  58467. return insert(list);
  58468. }
  58469. //! Return a copy of the list instance, where all elements of input \c list have been inserted at the end \const.
  58470. template<typename t>
  58471. CImgList<T>& operator,(const CImgList<t>& list) const {
  58472. return (+*this).insert(list);
  58473. }
  58474. //! Return image corresponding to the appending of all images of the instance list along specified axis.
  58475. /**
  58476. \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  58477. \note <tt>list>'x'</tt> is equivalent to <tt>list.get_append('x')</tt>.
  58478. **/
  58479. CImg<T> operator>(const char axis) const {
  58480. return get_append(axis,0);
  58481. }
  58482. //! Return list corresponding to the splitting of all images of the instance list along specified axis.
  58483. /**
  58484. \param axis Axis used for image splitting.
  58485. \note <tt>list<'x'</tt> is equivalent to <tt>list.get_split('x')</tt>.
  58486. **/
  58487. CImgList<T> operator<(const char axis) const {
  58488. return get_split(axis);
  58489. }
  58490. //@}
  58491. //-------------------------------------
  58492. //
  58493. //! \name Instance Characteristics
  58494. //@{
  58495. //-------------------------------------
  58496. //! Return the type of image pixel values as a C string.
  58497. /**
  58498. Return a \c char* string containing the usual type name of the image pixel values
  58499. (i.e. a stringified version of the template parameter \c T).
  58500. \note
  58501. - The returned string does not contain any spaces.
  58502. - If the pixel type \c T does not correspond to a registered type, the string <tt>"unknown"</tt> is returned.
  58503. **/
  58504. static const char* pixel_type() {
  58505. return cimg::type<T>::string();
  58506. }
  58507. //! Return the size of the list, i.e. the number of images contained in it.
  58508. /**
  58509. \note Similar to size() but returns result as a (signed) integer.
  58510. **/
  58511. int width() const {
  58512. return (int)_width;
  58513. }
  58514. //! Return the size of the list, i.e. the number of images contained in it.
  58515. /**
  58516. \note Similar to width() but returns result as an unsigned integer.
  58517. **/
  58518. unsigned int size() const {
  58519. return _width;
  58520. }
  58521. //! Return pointer to the first image of the list.
  58522. /**
  58523. \note Images in a list are stored as a buffer of \c CImg<T>.
  58524. **/
  58525. CImg<T> *data() {
  58526. return _data;
  58527. }
  58528. //! Return pointer to the first image of the list \const.
  58529. const CImg<T> *data() const {
  58530. return _data;
  58531. }
  58532. //! Return pointer to the pos-th image of the list.
  58533. /**
  58534. \param pos Index of the image element to access.
  58535. \note <tt>list.data(n);</tt> is equivalent to <tt>list.data + n;</tt>.
  58536. **/
  58537. #if cimg_verbosity>=3
  58538. CImg<T> *data(const unsigned int pos) {
  58539. if (pos>=size())
  58540. cimg::warn(_cimglist_instance
  58541. "data(): Invalid pointer request, at position [%u].",
  58542. cimglist_instance,
  58543. pos);
  58544. return _data + pos;
  58545. }
  58546. const CImg<T> *data(const unsigned int l) const {
  58547. return const_cast<CImgList<T>*>(this)->data(l);
  58548. }
  58549. #else
  58550. CImg<T> *data(const unsigned int l) {
  58551. return _data + l;
  58552. }
  58553. //! Return pointer to the pos-th image of the list \const.
  58554. const CImg<T> *data(const unsigned int l) const {
  58555. return _data + l;
  58556. }
  58557. #endif
  58558. //! Return iterator to the first image of the list.
  58559. /**
  58560. **/
  58561. iterator begin() {
  58562. return _data;
  58563. }
  58564. //! Return iterator to the first image of the list \const.
  58565. const_iterator begin() const {
  58566. return _data;
  58567. }
  58568. //! Return iterator to one position after the last image of the list.
  58569. /**
  58570. **/
  58571. iterator end() {
  58572. return _data + _width;
  58573. }
  58574. //! Return iterator to one position after the last image of the list \const.
  58575. const_iterator end() const {
  58576. return _data + _width;
  58577. }
  58578. //! Return reference to the first image of the list.
  58579. /**
  58580. **/
  58581. CImg<T>& front() {
  58582. return *_data;
  58583. }
  58584. //! Return reference to the first image of the list \const.
  58585. const CImg<T>& front() const {
  58586. return *_data;
  58587. }
  58588. //! Return a reference to the last image of the list.
  58589. /**
  58590. **/
  58591. const CImg<T>& back() const {
  58592. return *(_data + _width - 1);
  58593. }
  58594. //! Return a reference to the last image of the list \const.
  58595. CImg<T>& back() {
  58596. return *(_data + _width - 1);
  58597. }
  58598. //! Return pos-th image of the list.
  58599. /**
  58600. \param pos Index of the image element to access.
  58601. **/
  58602. CImg<T>& at(const int pos) {
  58603. if (is_empty())
  58604. throw CImgInstanceException(_cimglist_instance
  58605. "at(): Empty instance.",
  58606. cimglist_instance);
  58607. return _data[cimg::cut(pos,0,width() - 1)];
  58608. }
  58609. //! Access to pixel value with Dirichlet boundary conditions.
  58610. /**
  58611. \param pos Index of the image element to access.
  58612. \param x X-coordinate of the pixel value.
  58613. \param y Y-coordinate of the pixel value.
  58614. \param z Z-coordinate of the pixel value.
  58615. \param c C-coordinate of the pixel value.
  58616. \param out_value Default value returned if \c offset is outside image bounds.
  58617. \note <tt>list.atNXYZC(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZC(x,y,z,c);</tt>.
  58618. **/
  58619. T& atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
  58620. return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atXYZC(x,y,z,c,out_value);
  58621. }
  58622. //! Access to pixel value with Dirichlet boundary conditions \const.
  58623. T atNXYZC(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
  58624. return (pos<0 || pos>=width())?out_value:_data[pos].atXYZC(x,y,z,c,out_value);
  58625. }
  58626. //! Access to pixel value with Neumann boundary conditions.
  58627. /**
  58628. \param pos Index of the image element to access.
  58629. \param x X-coordinate of the pixel value.
  58630. \param y Y-coordinate of the pixel value.
  58631. \param z Z-coordinate of the pixel value.
  58632. \param c C-coordinate of the pixel value.
  58633. \note <tt>list.atNXYZC(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZC(x,y,z,c);</tt>.
  58634. **/
  58635. T& atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
  58636. if (is_empty())
  58637. throw CImgInstanceException(_cimglist_instance
  58638. "atNXYZC(): Empty instance.",
  58639. cimglist_instance);
  58640. return _atNXYZC(pos,x,y,z,c);
  58641. }
  58642. //! Access to pixel value with Neumann boundary conditions \const.
  58643. T atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
  58644. if (is_empty())
  58645. throw CImgInstanceException(_cimglist_instance
  58646. "atNXYZC(): Empty instance.",
  58647. cimglist_instance);
  58648. return _atNXYZC(pos,x,y,z,c);
  58649. }
  58650. T& _atNXYZC(const int pos, const int x, const int y, const int z, const int c) {
  58651. return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c);
  58652. }
  58653. T _atNXYZC(const int pos, const int x, const int y, const int z, const int c) const {
  58654. return _data[cimg::cut(pos,0,width() - 1)].atXYZC(x,y,z,c);
  58655. }
  58656. //! Access pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y,\c z).
  58657. /**
  58658. \param pos Index of the image element to access.
  58659. \param x X-coordinate of the pixel value.
  58660. \param y Y-coordinate of the pixel value.
  58661. \param z Z-coordinate of the pixel value.
  58662. \param c C-coordinate of the pixel value.
  58663. \param out_value Default value returned if \c offset is outside image bounds.
  58664. \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
  58665. **/
  58666. T& atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
  58667. return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atXYZ(x,y,z,c,out_value);
  58668. }
  58669. //! Access pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y,\c z) \const.
  58670. T atNXYZ(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
  58671. return (pos<0 || pos>=width())?out_value:_data[pos].atXYZ(x,y,z,c,out_value);
  58672. }
  58673. //! Access to pixel value with Neumann boundary conditions for the 4 coordinates (\c pos, \c x,\c y,\c z).
  58674. /**
  58675. \param pos Index of the image element to access.
  58676. \param x X-coordinate of the pixel value.
  58677. \param y Y-coordinate of the pixel value.
  58678. \param z Z-coordinate of the pixel value.
  58679. \param c C-coordinate of the pixel value.
  58680. \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
  58681. **/
  58682. T& atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
  58683. if (is_empty())
  58684. throw CImgInstanceException(_cimglist_instance
  58685. "atNXYZ(): Empty instance.",
  58686. cimglist_instance);
  58687. return _atNXYZ(pos,x,y,z,c);
  58688. }
  58689. //! Access to pixel value with Neumann boundary conditions for the 4 coordinates (\c pos, \c x,\c y,\c z) \const.
  58690. T atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
  58691. if (is_empty())
  58692. throw CImgInstanceException(_cimglist_instance
  58693. "atNXYZ(): Empty instance.",
  58694. cimglist_instance);
  58695. return _atNXYZ(pos,x,y,z,c);
  58696. }
  58697. T& _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) {
  58698. return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c);
  58699. }
  58700. T _atNXYZ(const int pos, const int x, const int y, const int z, const int c=0) const {
  58701. return _data[cimg::cut(pos,0,width() - 1)].atXYZ(x,y,z,c);
  58702. }
  58703. //! Access to pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y).
  58704. /**
  58705. \param pos Index of the image element to access.
  58706. \param x X-coordinate of the pixel value.
  58707. \param y Y-coordinate of the pixel value.
  58708. \param z Z-coordinate of the pixel value.
  58709. \param c C-coordinate of the pixel value.
  58710. \param out_value Default value returned if \c offset is outside image bounds.
  58711. \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
  58712. **/
  58713. T& atNXY(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
  58714. return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atXY(x,y,z,c,out_value);
  58715. }
  58716. //! Access to pixel value with Dirichlet boundary conditions for the 3 coordinates (\c pos, \c x,\c y) \const.
  58717. T atNXY(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
  58718. return (pos<0 || pos>=width())?out_value:_data[pos].atXY(x,y,z,c,out_value);
  58719. }
  58720. //! Access to pixel value with Neumann boundary conditions for the 3 coordinates (\c pos, \c x,\c y).
  58721. /**
  58722. \param pos Index of the image element to access.
  58723. \param x X-coordinate of the pixel value.
  58724. \param y Y-coordinate of the pixel value.
  58725. \param z Z-coordinate of the pixel value.
  58726. \param c C-coordinate of the pixel value.
  58727. \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
  58728. **/
  58729. T& atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
  58730. if (is_empty())
  58731. throw CImgInstanceException(_cimglist_instance
  58732. "atNXY(): Empty instance.",
  58733. cimglist_instance);
  58734. return _atNXY(pos,x,y,z,c);
  58735. }
  58736. //! Access to pixel value with Neumann boundary conditions for the 3 coordinates (\c pos, \c x,\c y) \const.
  58737. T atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
  58738. if (is_empty())
  58739. throw CImgInstanceException(_cimglist_instance
  58740. "atNXY(): Empty instance.",
  58741. cimglist_instance);
  58742. return _atNXY(pos,x,y,z,c);
  58743. }
  58744. T& _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) {
  58745. return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c);
  58746. }
  58747. T _atNXY(const int pos, const int x, const int y, const int z=0, const int c=0) const {
  58748. return _data[cimg::cut(pos,0,width() - 1)].atXY(x,y,z,c);
  58749. }
  58750. //! Access to pixel value with Dirichlet boundary conditions for the 2 coordinates (\c pos,\c x).
  58751. /**
  58752. \param pos Index of the image element to access.
  58753. \param x X-coordinate of the pixel value.
  58754. \param y Y-coordinate of the pixel value.
  58755. \param z Z-coordinate of the pixel value.
  58756. \param c C-coordinate of the pixel value.
  58757. \param out_value Default value returned if \c offset is outside image bounds.
  58758. \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
  58759. **/
  58760. T& atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
  58761. return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):_data[pos].atX(x,y,z,c,out_value);
  58762. }
  58763. //! Access to pixel value with Dirichlet boundary conditions for the 2 coordinates (\c pos,\c x) \const.
  58764. T atNX(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
  58765. return (pos<0 || pos>=width())?out_value:_data[pos].atX(x,y,z,c,out_value);
  58766. }
  58767. //! Access to pixel value with Neumann boundary conditions for the 2 coordinates (\c pos, \c x).
  58768. /**
  58769. \param pos Index of the image element to access.
  58770. \param x X-coordinate of the pixel value.
  58771. \param y Y-coordinate of the pixel value.
  58772. \param z Z-coordinate of the pixel value.
  58773. \param c C-coordinate of the pixel value.
  58774. \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
  58775. **/
  58776. T& atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
  58777. if (is_empty())
  58778. throw CImgInstanceException(_cimglist_instance
  58779. "atNX(): Empty instance.",
  58780. cimglist_instance);
  58781. return _atNX(pos,x,y,z,c);
  58782. }
  58783. //! Access to pixel value with Neumann boundary conditions for the 2 coordinates (\c pos, \c x) \const.
  58784. T atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
  58785. if (is_empty())
  58786. throw CImgInstanceException(_cimglist_instance
  58787. "atNX(): Empty instance.",
  58788. cimglist_instance);
  58789. return _atNX(pos,x,y,z,c);
  58790. }
  58791. T& _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) {
  58792. return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c);
  58793. }
  58794. T _atNX(const int pos, const int x, const int y=0, const int z=0, const int c=0) const {
  58795. return _data[cimg::cut(pos,0,width() - 1)].atX(x,y,z,c);
  58796. }
  58797. //! Access to pixel value with Dirichlet boundary conditions for the coordinate (\c pos).
  58798. /**
  58799. \param pos Index of the image element to access.
  58800. \param x X-coordinate of the pixel value.
  58801. \param y Y-coordinate of the pixel value.
  58802. \param z Z-coordinate of the pixel value.
  58803. \param c C-coordinate of the pixel value.
  58804. \param out_value Default value returned if \c offset is outside image bounds.
  58805. \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
  58806. **/
  58807. T& atN(const int pos, const int x, const int y, const int z, const int c, const T& out_value) {
  58808. return (pos<0 || pos>=width())?(cimg::temporary(out_value)=out_value):(*this)(pos,x,y,z,c);
  58809. }
  58810. //! Access to pixel value with Dirichlet boundary conditions for the coordinate (\c pos) \const.
  58811. T atN(const int pos, const int x, const int y, const int z, const int c, const T& out_value) const {
  58812. return (pos<0 || pos>=width())?out_value:(*this)(pos,x,y,z,c);
  58813. }
  58814. //! Return pixel value with Neumann boundary conditions for the coordinate (\c pos).
  58815. /**
  58816. \param pos Index of the image element to access.
  58817. \param x X-coordinate of the pixel value.
  58818. \param y Y-coordinate of the pixel value.
  58819. \param z Z-coordinate of the pixel value.
  58820. \param c C-coordinate of the pixel value.
  58821. \note <tt>list.atNXYZ(p,x,y,z,c);</tt> is equivalent to <tt>list[p].atXYZ(x,y,z,c);</tt>.
  58822. **/
  58823. T& atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
  58824. if (is_empty())
  58825. throw CImgInstanceException(_cimglist_instance
  58826. "atN(): Empty instance.",
  58827. cimglist_instance);
  58828. return _atN(pos,x,y,z,c);
  58829. }
  58830. //! Return pixel value with Neumann boundary conditions for the coordinate (\c pos) \const.
  58831. T atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
  58832. if (is_empty())
  58833. throw CImgInstanceException(_cimglist_instance
  58834. "atN(): Empty instance.",
  58835. cimglist_instance);
  58836. return _atN(pos,x,y,z,c);
  58837. }
  58838. T& _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) {
  58839. return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c);
  58840. }
  58841. T _atN(const int pos, const int x=0, const int y=0, const int z=0, const int c=0) const {
  58842. return _data[cimg::cut(pos,0,width() - 1)](x,y,z,c);
  58843. }
  58844. //@}
  58845. //-------------------------------------
  58846. //
  58847. //! \name Instance Checking
  58848. //@{
  58849. //-------------------------------------
  58850. //! Return \c true if list is empty.
  58851. /**
  58852. **/
  58853. bool is_empty() const {
  58854. return (!_data || !_width);
  58855. }
  58856. //! Test if number of image elements is equal to specified value.
  58857. /**
  58858. \param size_n Number of image elements to test.
  58859. **/
  58860. bool is_sameN(const unsigned int size_n) const {
  58861. return _width==size_n;
  58862. }
  58863. //! Test if number of image elements is equal between two images lists.
  58864. /**
  58865. \param list Input list to compare with.
  58866. **/
  58867. template<typename t>
  58868. bool is_sameN(const CImgList<t>& list) const {
  58869. return is_sameN(list._width);
  58870. }
  58871. // Define useful functions to check list dimensions.
  58872. // (cannot be documented because macro-generated).
  58873. #define _cimglist_def_is_same1(axis) \
  58874. bool is_same##axis(const unsigned int val) const { \
  58875. bool res = true; \
  58876. for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(val); \
  58877. return res; \
  58878. } \
  58879. bool is_sameN##axis(const unsigned int n, const unsigned int val) const { \
  58880. return is_sameN(n) && is_same##axis(val); \
  58881. } \
  58882. #define _cimglist_def_is_same2(axis1,axis2) \
  58883. bool is_same##axis1##axis2(const unsigned int val1, const unsigned int val2) const { \
  58884. bool res = true; \
  58885. for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2(val1,val2); \
  58886. return res; \
  58887. } \
  58888. bool is_sameN##axis1##axis2(const unsigned int n, const unsigned int val1, const unsigned int val2) const { \
  58889. return is_sameN(n) && is_same##axis1##axis2(val1,val2); \
  58890. } \
  58891. #define _cimglist_def_is_same3(axis1,axis2,axis3) \
  58892. bool is_same##axis1##axis2##axis3(const unsigned int val1, const unsigned int val2, \
  58893. const unsigned int val3) const { \
  58894. bool res = true; \
  58895. for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis1##axis2##axis3(val1,val2,val3); \
  58896. return res; \
  58897. } \
  58898. bool is_sameN##axis1##axis2##axis3(const unsigned int n, const unsigned int val1, \
  58899. const unsigned int val2, const unsigned int val3) const { \
  58900. return is_sameN(n) && is_same##axis1##axis2##axis3(val1,val2,val3); \
  58901. } \
  58902. #define _cimglist_def_is_same(axis) \
  58903. template<typename t> bool is_same##axis(const CImg<t>& img) const { \
  58904. bool res = true; \
  58905. for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_same##axis(img); \
  58906. return res; \
  58907. } \
  58908. template<typename t> bool is_same##axis(const CImgList<t>& list) const { \
  58909. const unsigned int lmin = std::min(_width,list._width); \
  58910. bool res = true; \
  58911. for (unsigned int l = 0; l<lmin && res; ++l) res = _data[l].is_same##axis(list[l]); \
  58912. return res; \
  58913. } \
  58914. template<typename t> bool is_sameN##axis(const unsigned int n, const CImg<t>& img) const { \
  58915. return (is_sameN(n) && is_same##axis(img)); \
  58916. } \
  58917. template<typename t> bool is_sameN##axis(const CImgList<t>& list) const { \
  58918. return (is_sameN(list) && is_same##axis(list)); \
  58919. }
  58920. _cimglist_def_is_same(XY)
  58921. _cimglist_def_is_same(XZ)
  58922. _cimglist_def_is_same(XC)
  58923. _cimglist_def_is_same(YZ)
  58924. _cimglist_def_is_same(YC)
  58925. _cimglist_def_is_same(XYZ)
  58926. _cimglist_def_is_same(XYC)
  58927. _cimglist_def_is_same(YZC)
  58928. _cimglist_def_is_same(XYZC)
  58929. _cimglist_def_is_same1(X)
  58930. _cimglist_def_is_same1(Y)
  58931. _cimglist_def_is_same1(Z)
  58932. _cimglist_def_is_same1(C)
  58933. _cimglist_def_is_same2(X,Y)
  58934. _cimglist_def_is_same2(X,Z)
  58935. _cimglist_def_is_same2(X,C)
  58936. _cimglist_def_is_same2(Y,Z)
  58937. _cimglist_def_is_same2(Y,C)
  58938. _cimglist_def_is_same2(Z,C)
  58939. _cimglist_def_is_same3(X,Y,Z)
  58940. _cimglist_def_is_same3(X,Y,C)
  58941. _cimglist_def_is_same3(X,Z,C)
  58942. _cimglist_def_is_same3(Y,Z,C)
  58943. //! Test if dimensions of each image of the list match specified arguments.
  58944. /**
  58945. \param dx Checked image width.
  58946. \param dy Checked image height.
  58947. \param dz Checked image depth.
  58948. \param dc Checked image spectrum.
  58949. **/
  58950. bool is_sameXYZC(const unsigned int dx, const unsigned int dy,
  58951. const unsigned int dz, const unsigned int dc) const {
  58952. bool res = true;
  58953. for (unsigned int l = 0; l<_width && res; ++l) res = _data[l].is_sameXYZC(dx,dy,dz,dc);
  58954. return res;
  58955. }
  58956. //! Test if list dimensions match specified arguments.
  58957. /**
  58958. \param n Number of images in the list.
  58959. \param dx Checked image width.
  58960. \param dy Checked image height.
  58961. \param dz Checked image depth.
  58962. \param dc Checked image spectrum.
  58963. **/
  58964. bool is_sameNXYZC(const unsigned int n,
  58965. const unsigned int dx, const unsigned int dy,
  58966. const unsigned int dz, const unsigned int dc) const {
  58967. return is_sameN(n) && is_sameXYZC(dx,dy,dz,dc);
  58968. }
  58969. //! Test if list contains one particular pixel location.
  58970. /**
  58971. \param n Index of the image whom checked pixel value belong to.
  58972. \param x X-coordinate of the checked pixel value.
  58973. \param y Y-coordinate of the checked pixel value.
  58974. \param z Z-coordinate of the checked pixel value.
  58975. \param c C-coordinate of the checked pixel value.
  58976. **/
  58977. bool containsNXYZC(const int n, const int x=0, const int y=0, const int z=0, const int c=0) const {
  58978. if (is_empty()) return false;
  58979. return n>=0 && n<width() && x>=0 && x<_data[n].width() && y>=0 && y<_data[n].height() &&
  58980. z>=0 && z<_data[n].depth() && c>=0 && c<_data[n].spectrum();
  58981. }
  58982. //! Test if list contains image with specified index.
  58983. /**
  58984. \param n Index of the checked image.
  58985. **/
  58986. bool containsN(const int n) const {
  58987. if (is_empty()) return false;
  58988. return n>=0 && n<width();
  58989. }
  58990. //! Test if one image of the list contains the specified referenced value.
  58991. /**
  58992. \param pixel Reference to pixel value to test.
  58993. \param[out] n Index of image containing the pixel value, if test succeeds.
  58994. \param[out] x X-coordinate of the pixel value, if test succeeds.
  58995. \param[out] y Y-coordinate of the pixel value, if test succeeds.
  58996. \param[out] z Z-coordinate of the pixel value, if test succeeds.
  58997. \param[out] c C-coordinate of the pixel value, if test succeeds.
  58998. \note If true, set coordinates (n,x,y,z,c).
  58999. **/
  59000. template<typename t>
  59001. bool contains(const T& pixel, t& n, t& x, t&y, t& z, t& c) const {
  59002. if (is_empty()) return false;
  59003. cimglist_for(*this,l) if (_data[l].contains(pixel,x,y,z,c)) { n = (t)l; return true; }
  59004. return false;
  59005. }
  59006. //! Test if one of the image list contains the specified referenced value.
  59007. /**
  59008. \param pixel Reference to pixel value to test.
  59009. \param[out] n Index of image containing the pixel value, if test succeeds.
  59010. \param[out] x X-coordinate of the pixel value, if test succeeds.
  59011. \param[out] y Y-coordinate of the pixel value, if test succeeds.
  59012. \param[out] z Z-coordinate of the pixel value, if test succeeds.
  59013. \note If true, set coordinates (n,x,y,z).
  59014. **/
  59015. template<typename t>
  59016. bool contains(const T& pixel, t& n, t& x, t&y, t& z) const {
  59017. t c;
  59018. return contains(pixel,n,x,y,z,c);
  59019. }
  59020. //! Test if one of the image list contains the specified referenced value.
  59021. /**
  59022. \param pixel Reference to pixel value to test.
  59023. \param[out] n Index of image containing the pixel value, if test succeeds.
  59024. \param[out] x X-coordinate of the pixel value, if test succeeds.
  59025. \param[out] y Y-coordinate of the pixel value, if test succeeds.
  59026. \note If true, set coordinates (n,x,y).
  59027. **/
  59028. template<typename t>
  59029. bool contains(const T& pixel, t& n, t& x, t&y) const {
  59030. t z, c;
  59031. return contains(pixel,n,x,y,z,c);
  59032. }
  59033. //! Test if one of the image list contains the specified referenced value.
  59034. /**
  59035. \param pixel Reference to pixel value to test.
  59036. \param[out] n Index of image containing the pixel value, if test succeeds.
  59037. \param[out] x X-coordinate of the pixel value, if test succeeds.
  59038. \note If true, set coordinates (n,x).
  59039. **/
  59040. template<typename t>
  59041. bool contains(const T& pixel, t& n, t& x) const {
  59042. t y, z, c;
  59043. return contains(pixel,n,x,y,z,c);
  59044. }
  59045. //! Test if one of the image list contains the specified referenced value.
  59046. /**
  59047. \param pixel Reference to pixel value to test.
  59048. \param[out] n Index of image containing the pixel value, if test succeeds.
  59049. \note If true, set coordinates (n).
  59050. **/
  59051. template<typename t>
  59052. bool contains(const T& pixel, t& n) const {
  59053. t x, y, z, c;
  59054. return contains(pixel,n,x,y,z,c);
  59055. }
  59056. //! Test if one of the image list contains the specified referenced value.
  59057. /**
  59058. \param pixel Reference to pixel value to test.
  59059. **/
  59060. bool contains(const T& pixel) const {
  59061. unsigned int n, x, y, z, c;
  59062. return contains(pixel,n,x,y,z,c);
  59063. }
  59064. //! Test if the list contains the image 'img'.
  59065. /**
  59066. \param img Reference to image to test.
  59067. \param[out] n Index of image in the list, if test succeeds.
  59068. \note If true, returns the position (n) of the image in the list.
  59069. **/
  59070. template<typename t>
  59071. bool contains(const CImg<T>& img, t& n) const {
  59072. if (is_empty()) return false;
  59073. const CImg<T> *const ptr = &img;
  59074. cimglist_for(*this,i) if (_data + i==ptr) { n = (t)i; return true; }
  59075. return false;
  59076. }
  59077. //! Test if the list contains the image img.
  59078. /**
  59079. \param img Reference to image to test.
  59080. **/
  59081. bool contains(const CImg<T>& img) const {
  59082. unsigned int n;
  59083. return contains(img,n);
  59084. }
  59085. //@}
  59086. //-------------------------------------
  59087. //
  59088. //! \name Mathematical Functions
  59089. //@{
  59090. //-------------------------------------
  59091. //! Return a reference to the minimum pixel value of the instance list.
  59092. /**
  59093. **/
  59094. T& min() {
  59095. bool is_all_empty = true;
  59096. T *ptr_min = 0;
  59097. cimglist_for(*this,l) if (!_data[l].is_empty()) {
  59098. ptr_min = _data[l]._data;
  59099. is_all_empty = false;
  59100. break;
  59101. }
  59102. if (is_all_empty)
  59103. throw CImgInstanceException(_cimglist_instance
  59104. "min(): %s.",
  59105. _data?"List of empty images":"Empty instance",
  59106. cimglist_instance);
  59107. T min_value = *ptr_min;
  59108. cimglist_for(*this,l) {
  59109. const CImg<T>& img = _data[l];
  59110. cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
  59111. }
  59112. return *ptr_min;
  59113. }
  59114. //! Return a reference to the minimum pixel value of the instance list \const.
  59115. const T& min() const {
  59116. bool is_all_empty = true;
  59117. T *ptr_min = 0;
  59118. cimglist_for(*this,l) if (!_data[l].is_empty()) {
  59119. ptr_min = _data[l]._data;
  59120. is_all_empty = false;
  59121. break;
  59122. }
  59123. if (is_all_empty)
  59124. throw CImgInstanceException(_cimglist_instance
  59125. "min(): %s.",
  59126. _data?"List of empty images":"Empty instance",
  59127. cimglist_instance);
  59128. T min_value = *ptr_min;
  59129. cimglist_for(*this,l) {
  59130. const CImg<T>& img = _data[l];
  59131. cimg_for(img,ptrs,T) if (*ptrs<min_value) min_value = *(ptr_min=ptrs);
  59132. }
  59133. return *ptr_min;
  59134. }
  59135. //! Return a reference to the maximum pixel value of the instance list.
  59136. /**
  59137. **/
  59138. T& max() {
  59139. bool is_all_empty = true;
  59140. T *ptr_max = 0;
  59141. cimglist_for(*this,l) if (!_data[l].is_empty()) {
  59142. ptr_max = _data[l]._data;
  59143. is_all_empty = false;
  59144. break;
  59145. }
  59146. if (is_all_empty)
  59147. throw CImgInstanceException(_cimglist_instance
  59148. "max(): %s.",
  59149. _data?"List of empty images":"Empty instance",
  59150. cimglist_instance);
  59151. T max_value = *ptr_max;
  59152. cimglist_for(*this,l) {
  59153. const CImg<T>& img = _data[l];
  59154. cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
  59155. }
  59156. return *ptr_max;
  59157. }
  59158. //! Return a reference to the maximum pixel value of the instance list \const.
  59159. const T& max() const {
  59160. bool is_all_empty = true;
  59161. T *ptr_max = 0;
  59162. cimglist_for(*this,l) if (!_data[l].is_empty()) {
  59163. ptr_max = _data[l]._data;
  59164. is_all_empty = false;
  59165. break;
  59166. }
  59167. if (is_all_empty)
  59168. throw CImgInstanceException(_cimglist_instance
  59169. "max(): %s.",
  59170. _data?"List of empty images":"Empty instance",
  59171. cimglist_instance);
  59172. T max_value = *ptr_max;
  59173. cimglist_for(*this,l) {
  59174. const CImg<T>& img = _data[l];
  59175. cimg_for(img,ptrs,T) if (*ptrs>max_value) max_value = *(ptr_max=ptrs);
  59176. }
  59177. return *ptr_max;
  59178. }
  59179. //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well.
  59180. /**
  59181. \param[out] max_val Value of the maximum value found.
  59182. **/
  59183. template<typename t>
  59184. T& min_max(t& max_val) {
  59185. bool is_all_empty = true;
  59186. T *ptr_min = 0;
  59187. cimglist_for(*this,l) if (!_data[l].is_empty()) {
  59188. ptr_min = _data[l]._data;
  59189. is_all_empty = false;
  59190. break;
  59191. }
  59192. if (is_all_empty)
  59193. throw CImgInstanceException(_cimglist_instance
  59194. "min_max(): %s.",
  59195. _data?"List of empty images":"Empty instance",
  59196. cimglist_instance);
  59197. T min_value = *ptr_min, max_value = min_value;
  59198. cimglist_for(*this,l) {
  59199. const CImg<T>& img = _data[l];
  59200. cimg_for(img,ptrs,T) {
  59201. const T val = *ptrs;
  59202. if (val<min_value) { min_value = val; ptr_min = ptrs; }
  59203. if (val>max_value) max_value = val;
  59204. }
  59205. }
  59206. max_val = (t)max_value;
  59207. return *ptr_min;
  59208. }
  59209. //! Return a reference to the minimum pixel value of the instance list and return the maximum vvalue as well \const.
  59210. /**
  59211. \param[out] max_val Value of the maximum value found.
  59212. **/
  59213. template<typename t>
  59214. const T& min_max(t& max_val) const {
  59215. bool is_all_empty = true;
  59216. T *ptr_min = 0;
  59217. cimglist_for(*this,l) if (!_data[l].is_empty()) {
  59218. ptr_min = _data[l]._data;
  59219. is_all_empty = false;
  59220. break;
  59221. }
  59222. if (is_all_empty)
  59223. throw CImgInstanceException(_cimglist_instance
  59224. "min_max(): %s.",
  59225. _data?"List of empty images":"Empty instance",
  59226. cimglist_instance);
  59227. T min_value = *ptr_min, max_value = min_value;
  59228. cimglist_for(*this,l) {
  59229. const CImg<T>& img = _data[l];
  59230. cimg_for(img,ptrs,T) {
  59231. const T val = *ptrs;
  59232. if (val<min_value) { min_value = val; ptr_min = ptrs; }
  59233. if (val>max_value) max_value = val;
  59234. }
  59235. }
  59236. max_val = (t)max_value;
  59237. return *ptr_min;
  59238. }
  59239. //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well.
  59240. /**
  59241. \param[out] min_val Value of the minimum value found.
  59242. **/
  59243. template<typename t>
  59244. T& max_min(t& min_val) {
  59245. bool is_all_empty = true;
  59246. T *ptr_max = 0;
  59247. cimglist_for(*this,l) if (!_data[l].is_empty()) {
  59248. ptr_max = _data[l]._data;
  59249. is_all_empty = false;
  59250. break;
  59251. }
  59252. if (is_all_empty)
  59253. throw CImgInstanceException(_cimglist_instance
  59254. "max_min(): %s.",
  59255. _data?"List of empty images":"Empty instance",
  59256. cimglist_instance);
  59257. T min_value = *ptr_max, max_value = min_value;
  59258. cimglist_for(*this,l) {
  59259. const CImg<T>& img = _data[l];
  59260. cimg_for(img,ptrs,T) {
  59261. const T val = *ptrs;
  59262. if (val>max_value) { max_value = val; ptr_max = ptrs; }
  59263. if (val<min_value) min_value = val;
  59264. }
  59265. }
  59266. min_val = (t)min_value;
  59267. return *ptr_max;
  59268. }
  59269. //! Return a reference to the minimum pixel value of the instance list and return the minimum value as well \const.
  59270. template<typename t>
  59271. const T& max_min(t& min_val) const {
  59272. bool is_all_empty = true;
  59273. T *ptr_max = 0;
  59274. cimglist_for(*this,l) if (!_data[l].is_empty()) {
  59275. ptr_max = _data[l]._data;
  59276. is_all_empty = false;
  59277. break;
  59278. }
  59279. if (is_all_empty)
  59280. throw CImgInstanceException(_cimglist_instance
  59281. "max_min(): %s.",
  59282. _data?"List of empty images":"Empty instance",
  59283. cimglist_instance);
  59284. T min_value = *ptr_max, max_value = min_value;
  59285. cimglist_for(*this,l) {
  59286. const CImg<T>& img = _data[l];
  59287. cimg_for(img,ptrs,T) {
  59288. const T val = *ptrs;
  59289. if (val>max_value) { max_value = val; ptr_max = ptrs; }
  59290. if (val<min_value) min_value = val;
  59291. }
  59292. }
  59293. min_val = (t)min_value;
  59294. return *ptr_max;
  59295. }
  59296. //@}
  59297. //---------------------------
  59298. //
  59299. //! \name List Manipulation
  59300. //@{
  59301. //---------------------------
  59302. //! Insert a copy of the image \c img into the current image list, at position \c pos.
  59303. /**
  59304. \param img Image to insert a copy to the list.
  59305. \param pos Index of the insertion.
  59306. \param is_shared Tells if the inserted image is a shared copy of \c img or not.
  59307. **/
  59308. template<typename t>
  59309. CImgList<T>& insert(const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) {
  59310. const unsigned int npos = pos==~0U?_width:pos;
  59311. if (npos>_width)
  59312. throw CImgArgumentException(_cimglist_instance
  59313. "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) "
  59314. "at position %u.",
  59315. cimglist_instance,
  59316. img._width,img._height,img._depth,img._spectrum,img._data,npos);
  59317. if (is_shared)
  59318. throw CImgArgumentException(_cimglist_instance
  59319. "insert(): Invalid insertion request of specified shared image "
  59320. "CImg<%s>(%u,%u,%u,%u,%p) at position %u (pixel types are different).",
  59321. cimglist_instance,
  59322. img.pixel_type(),img._width,img._height,img._depth,img._spectrum,img._data,npos);
  59323. CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):
  59324. (_allocated_width=16)]:0;
  59325. if (!_data) { // Insert new element into empty list
  59326. _data = new_data;
  59327. *_data = img;
  59328. } else {
  59329. if (new_data) { // Insert with re-allocation
  59330. if (npos) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg<T>)*npos);
  59331. if (npos!=_width - 1)
  59332. std::memcpy((void*)(new_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
  59333. std::memset((void*)_data,0,sizeof(CImg<T>)*(_width - 1));
  59334. delete[] _data;
  59335. _data = new_data;
  59336. } else if (npos!=_width - 1) // Insert without re-allocation
  59337. std::memmove((void*)(_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
  59338. _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0;
  59339. _data[npos]._data = 0;
  59340. _data[npos] = img;
  59341. }
  59342. return *this;
  59343. }
  59344. //! Insert a copy of the image \c img into the current image list, at position \c pos \specialization.
  59345. CImgList<T>& insert(const CImg<T>& img, const unsigned int pos=~0U, const bool is_shared=false) {
  59346. const unsigned int npos = pos==~0U?_width:pos;
  59347. if (npos>_width)
  59348. throw CImgArgumentException(_cimglist_instance
  59349. "insert(): Invalid insertion request of specified image (%u,%u,%u,%u,%p) "
  59350. "at position %u.",
  59351. cimglist_instance,
  59352. img._width,img._height,img._depth,img._spectrum,img._data,npos);
  59353. CImg<T> *const new_data = (++_width>_allocated_width)?new CImg<T>[_allocated_width?(_allocated_width<<=1):
  59354. (_allocated_width=16)]:0;
  59355. if (!_data) { // Insert new element into empty list
  59356. _data = new_data;
  59357. if (is_shared && img) {
  59358. _data->_width = img._width;
  59359. _data->_height = img._height;
  59360. _data->_depth = img._depth;
  59361. _data->_spectrum = img._spectrum;
  59362. _data->_is_shared = true;
  59363. _data->_data = img._data;
  59364. } else *_data = img;
  59365. }
  59366. else {
  59367. if (new_data) { // Insert with re-allocation
  59368. if (npos) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg<T>)*npos);
  59369. if (npos!=_width - 1)
  59370. std::memcpy((void*)(new_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
  59371. if (is_shared && img) {
  59372. new_data[npos]._width = img._width;
  59373. new_data[npos]._height = img._height;
  59374. new_data[npos]._depth = img._depth;
  59375. new_data[npos]._spectrum = img._spectrum;
  59376. new_data[npos]._is_shared = true;
  59377. new_data[npos]._data = img._data;
  59378. } else {
  59379. new_data[npos]._width = new_data[npos]._height = new_data[npos]._depth = new_data[npos]._spectrum = 0;
  59380. new_data[npos]._data = 0;
  59381. new_data[npos] = img;
  59382. }
  59383. std::memset((void*)_data,0,sizeof(CImg<T>)*(_width - 1));
  59384. delete[] _data;
  59385. _data = new_data;
  59386. } else { // Insert without re-allocation
  59387. if (npos!=_width - 1)
  59388. std::memmove((void*)(_data + npos + 1),(void*)(_data + npos),sizeof(CImg<T>)*(_width - 1 - npos));
  59389. if (is_shared && img) {
  59390. _data[npos]._width = img._width;
  59391. _data[npos]._height = img._height;
  59392. _data[npos]._depth = img._depth;
  59393. _data[npos]._spectrum = img._spectrum;
  59394. _data[npos]._is_shared = true;
  59395. _data[npos]._data = img._data;
  59396. } else {
  59397. _data[npos]._width = _data[npos]._height = _data[npos]._depth = _data[npos]._spectrum = 0;
  59398. _data[npos]._data = 0;
  59399. _data[npos] = img;
  59400. }
  59401. }
  59402. }
  59403. return *this;
  59404. }
  59405. //! Insert a copy of the image \c img into the current image list, at position \c pos \newinstance.
  59406. template<typename t>
  59407. CImgList<T> get_insert(const CImg<t>& img, const unsigned int pos=~0U, const bool is_shared=false) const {
  59408. return (+*this).insert(img,pos,is_shared);
  59409. }
  59410. //! Insert n empty images img into the current image list, at position \p pos.
  59411. /**
  59412. \param n Number of empty images to insert.
  59413. \param pos Index of the insertion.
  59414. **/
  59415. CImgList<T>& insert(const unsigned int n, const unsigned int pos=~0U) {
  59416. CImg<T> empty;
  59417. if (!n) return *this;
  59418. const unsigned int npos = pos==~0U?_width:pos;
  59419. for (unsigned int i = 0; i<n; ++i) insert(empty,npos+i);
  59420. return *this;
  59421. }
  59422. //! Insert n empty images img into the current image list, at position \p pos \newinstance.
  59423. CImgList<T> get_insert(const unsigned int n, const unsigned int pos=~0U) const {
  59424. return (+*this).insert(n,pos);
  59425. }
  59426. //! Insert \c n copies of the image \c img into the current image list, at position \c pos.
  59427. /**
  59428. \param n Number of image copies to insert.
  59429. \param img Image to insert by copy.
  59430. \param pos Index of the insertion.
  59431. \param is_shared Tells if inserted images are shared copies of \c img or not.
  59432. **/
  59433. template<typename t>
  59434. CImgList<T>& insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U,
  59435. const bool is_shared=false) {
  59436. if (!n) return *this;
  59437. const unsigned int npos = pos==~0U?_width:pos;
  59438. insert(img,npos,is_shared);
  59439. for (unsigned int i = 1; i<n; ++i) insert(_data[npos],npos + i,is_shared);
  59440. return *this;
  59441. }
  59442. //! Insert \c n copies of the image \c img into the current image list, at position \c pos \newinstance.
  59443. template<typename t>
  59444. CImgList<T> get_insert(const unsigned int n, const CImg<t>& img, const unsigned int pos=~0U,
  59445. const bool is_shared=false) const {
  59446. return (+*this).insert(n,img,pos,is_shared);
  59447. }
  59448. //! Insert a copy of the image list \c list into the current image list, starting from position \c pos.
  59449. /**
  59450. \param list Image list to insert.
  59451. \param pos Index of the insertion.
  59452. \param is_shared Tells if inserted images are shared copies of images of \c list or not.
  59453. **/
  59454. template<typename t>
  59455. CImgList<T>& insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) {
  59456. const unsigned int npos = pos==~0U?_width:pos;
  59457. if ((void*)this!=(void*)&list) cimglist_for(list,l) insert(list[l],npos + l,is_shared);
  59458. else insert(CImgList<T>(list),npos,is_shared);
  59459. return *this;
  59460. }
  59461. //! Insert a copy of the image list \c list into the current image list, starting from position \c pos \newinstance.
  59462. template<typename t>
  59463. CImgList<T> get_insert(const CImgList<t>& list, const unsigned int pos=~0U, const bool is_shared=false) const {
  59464. return (+*this).insert(list,pos,is_shared);
  59465. }
  59466. //! Insert n copies of the list \c list at position \c pos of the current list.
  59467. /**
  59468. \param n Number of list copies to insert.
  59469. \param list Image list to insert.
  59470. \param pos Index of the insertion.
  59471. \param is_shared Tells if inserted images are shared copies of images of \c list or not.
  59472. **/
  59473. template<typename t>
  59474. CImgList<T>& insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U,
  59475. const bool is_shared=false) {
  59476. if (!n) return *this;
  59477. const unsigned int npos = pos==~0U?_width:pos;
  59478. for (unsigned int i = 0; i<n; ++i) insert(list,npos,is_shared);
  59479. return *this;
  59480. }
  59481. //! Insert n copies of the list \c list at position \c pos of the current list \newinstance.
  59482. template<typename t>
  59483. CImgList<T> get_insert(const unsigned int n, const CImgList<t>& list, const unsigned int pos=~0U,
  59484. const bool is_shared=false) const {
  59485. return (+*this).insert(n,list,pos,is_shared);
  59486. }
  59487. //! Remove all images between from indexes.
  59488. /**
  59489. \param pos1 Starting index of the removal.
  59490. \param pos2 Ending index of the removal.
  59491. **/
  59492. CImgList<T>& remove(const unsigned int pos1, const unsigned int pos2) {
  59493. const unsigned int
  59494. npos1 = pos1<pos2?pos1:pos2,
  59495. tpos2 = pos1<pos2?pos2:pos1,
  59496. npos2 = tpos2<_width?tpos2:_width - 1;
  59497. if (npos1>=_width)
  59498. throw CImgArgumentException(_cimglist_instance
  59499. "remove(): Invalid remove request at positions %u->%u.",
  59500. cimglist_instance,
  59501. npos1,tpos2);
  59502. else {
  59503. if (tpos2>=_width)
  59504. throw CImgArgumentException(_cimglist_instance
  59505. "remove(): Invalid remove request at positions %u->%u.",
  59506. cimglist_instance,
  59507. npos1,tpos2);
  59508. for (unsigned int k = npos1; k<=npos2; ++k) _data[k].assign();
  59509. const unsigned int nb = 1 + npos2 - npos1;
  59510. if (!(_width-=nb)) return assign();
  59511. if (_width>(_allocated_width>>4) || _allocated_width<=16) { // Removing items without reallocation
  59512. if (npos1!=_width)
  59513. std::memmove((void*)(_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg<T>)*(_width - npos1));
  59514. std::memset((void*)(_data + _width),0,sizeof(CImg<T>)*nb);
  59515. } else { // Removing items with reallocation
  59516. _allocated_width>>=4;
  59517. while (_allocated_width>16 && _width<(_allocated_width>>1)) _allocated_width>>=1;
  59518. CImg<T> *const new_data = new CImg<T>[_allocated_width];
  59519. if (npos1) std::memcpy((void*)new_data,(void*)_data,sizeof(CImg<T>)*npos1);
  59520. if (npos1!=_width)
  59521. std::memcpy((void*)(new_data + npos1),(void*)(_data + npos2 + 1),sizeof(CImg<T>)*(_width - npos1));
  59522. if (_width!=_allocated_width)
  59523. std::memset((void*)(new_data + _width),0,sizeof(CImg<T>)*(_allocated_width - _width));
  59524. std::memset((void*)_data,0,sizeof(CImg<T>)*(_width + nb));
  59525. delete[] _data;
  59526. _data = new_data;
  59527. }
  59528. }
  59529. return *this;
  59530. }
  59531. //! Remove all images between from indexes \newinstance.
  59532. CImgList<T> get_remove(const unsigned int pos1, const unsigned int pos2) const {
  59533. return (+*this).remove(pos1,pos2);
  59534. }
  59535. //! Remove image at index \c pos from the image list.
  59536. /**
  59537. \param pos Index of the image to remove.
  59538. **/
  59539. CImgList<T>& remove(const unsigned int pos) {
  59540. return remove(pos,pos);
  59541. }
  59542. //! Remove image at index \c pos from the image list \newinstance.
  59543. CImgList<T> get_remove(const unsigned int pos) const {
  59544. return (+*this).remove(pos);
  59545. }
  59546. //! Remove last image.
  59547. /**
  59548. **/
  59549. CImgList<T>& remove() {
  59550. return remove(_width - 1);
  59551. }
  59552. //! Remove last image \newinstance.
  59553. CImgList<T> get_remove() const {
  59554. return (+*this).remove();
  59555. }
  59556. //! Reverse list order.
  59557. CImgList<T>& reverse() {
  59558. for (unsigned int l = 0; l<_width/2; ++l) (*this)[l].swap((*this)[_width - 1 - l]);
  59559. return *this;
  59560. }
  59561. //! Reverse list order \newinstance.
  59562. CImgList<T> get_reverse() const {
  59563. return (+*this).reverse();
  59564. }
  59565. //! Return a sublist.
  59566. /**
  59567. \param pos0 Starting index of the sublist.
  59568. \param pos1 Ending index of the sublist.
  59569. **/
  59570. CImgList<T>& images(const unsigned int pos0, const unsigned int pos1) {
  59571. return get_images(pos0,pos1).move_to(*this);
  59572. }
  59573. //! Return a sublist \newinstance.
  59574. CImgList<T> get_images(const unsigned int pos0, const unsigned int pos1) const {
  59575. if (pos0>pos1 || pos1>=_width)
  59576. throw CImgArgumentException(_cimglist_instance
  59577. "images(): Specified sub-list indices (%u->%u) are out of bounds.",
  59578. cimglist_instance,
  59579. pos0,pos1);
  59580. CImgList<T> res(pos1 - pos0 + 1);
  59581. cimglist_for(res,l) res[l].assign(_data[pos0 + l]);
  59582. return res;
  59583. }
  59584. //! Return a shared sublist.
  59585. /**
  59586. \param pos0 Starting index of the sublist.
  59587. \param pos1 Ending index of the sublist.
  59588. **/
  59589. CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) {
  59590. if (pos0>pos1 || pos1>=_width)
  59591. throw CImgArgumentException(_cimglist_instance
  59592. "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.",
  59593. cimglist_instance,
  59594. pos0,pos1);
  59595. CImgList<T> res(pos1 - pos0 + 1);
  59596. cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false);
  59597. return res;
  59598. }
  59599. //! Return a shared sublist \newinstance.
  59600. const CImgList<T> get_shared_images(const unsigned int pos0, const unsigned int pos1) const {
  59601. if (pos0>pos1 || pos1>=_width)
  59602. throw CImgArgumentException(_cimglist_instance
  59603. "get_shared_images(): Specified sub-list indices (%u->%u) are out of bounds.",
  59604. cimglist_instance,
  59605. pos0,pos1);
  59606. CImgList<T> res(pos1 - pos0 + 1);
  59607. cimglist_for(res,l) res[l].assign(_data[pos0 + l],_data[pos0 + l]?true:false);
  59608. return res;
  59609. }
  59610. //! Return a single image which is the appending of all images of the current CImgList instance.
  59611. /**
  59612. \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  59613. \param align Appending alignment.
  59614. **/
  59615. CImg<T> get_append(const char axis, const float align=0) const {
  59616. if (is_empty()) return CImg<T>();
  59617. if (_width==1) return +((*this)[0]);
  59618. unsigned int dx = 0, dy = 0, dz = 0, dc = 0, pos = 0;
  59619. CImg<T> res;
  59620. switch (cimg::lowercase(axis)) {
  59621. case 'x' : { // Along the X-axis
  59622. cimglist_for(*this,l) {
  59623. const CImg<T>& img = (*this)[l];
  59624. if (img) {
  59625. dx+=img._width;
  59626. dy = std::max(dy,img._height);
  59627. dz = std::max(dz,img._depth);
  59628. dc = std::max(dc,img._spectrum);
  59629. }
  59630. }
  59631. res.assign(dx,dy,dz,dc,(T)0);
  59632. if (res) cimglist_for(*this,l) {
  59633. const CImg<T>& img = (*this)[l];
  59634. if (img) {
  59635. if (img._height==1 && img._depth==1 && img._spectrum==1 &&
  59636. res._height==1 && res._depth==1 && res._spectrum==1)
  59637. std::memcpy(&res[pos],img._data,sizeof(T)*img._width);
  59638. else
  59639. res.draw_image(pos,
  59640. (int)(align*(dy - img._height)),
  59641. (int)(align*(dz - img._depth)),
  59642. (int)(align*(dc - img._spectrum)),
  59643. img);
  59644. }
  59645. pos+=img._width;
  59646. }
  59647. } break;
  59648. case 'y' : { // Along the Y-axis
  59649. cimglist_for(*this,l) {
  59650. const CImg<T>& img = (*this)[l];
  59651. if (img) {
  59652. dx = std::max(dx,img._width);
  59653. dy+=img._height;
  59654. dz = std::max(dz,img._depth);
  59655. dc = std::max(dc,img._spectrum);
  59656. }
  59657. }
  59658. res.assign(dx,dy,dz,dc,(T)0);
  59659. if (res) cimglist_for(*this,l) {
  59660. const CImg<T>& img = (*this)[l];
  59661. if (img) {
  59662. if (img._width==1 && img._depth==1 && img._spectrum==1 &&
  59663. res._width==1 && res._depth==1 && res._spectrum==1)
  59664. std::memcpy(&res[pos],img._data,sizeof(T)*img._height);
  59665. else
  59666. res.draw_image((int)(align*(dx - img._width)),
  59667. pos,
  59668. (int)(align*(dz - img._depth)),
  59669. (int)(align*(dc - img._spectrum)),
  59670. img);
  59671. }
  59672. pos+=img._height;
  59673. }
  59674. } break;
  59675. case 'z' : { // Along the Z-axis
  59676. cimglist_for(*this,l) {
  59677. const CImg<T>& img = (*this)[l];
  59678. if (img) {
  59679. dx = std::max(dx,img._width);
  59680. dy = std::max(dy,img._height);
  59681. dz+=img._depth;
  59682. dc = std::max(dc,img._spectrum);
  59683. }
  59684. }
  59685. res.assign(dx,dy,dz,dc,(T)0);
  59686. if (res) cimglist_for(*this,l) {
  59687. const CImg<T>& img = (*this)[l];
  59688. if (img) {
  59689. if (img._width==1 && img._height==1 && img._spectrum==1 &&
  59690. res._width==1 && res._height==1 && res._spectrum==1)
  59691. std::memcpy(&res[pos],img._data,sizeof(T)*img._depth);
  59692. else
  59693. res.draw_image((int)(align*(dx - img._width)),
  59694. (int)(align*(dy - img._height)),
  59695. pos,
  59696. (int)(align*(dc - img._spectrum)),
  59697. img);
  59698. }
  59699. pos+=img._depth;
  59700. }
  59701. } break;
  59702. default : { // Along the C-axis
  59703. cimglist_for(*this,l) {
  59704. const CImg<T>& img = (*this)[l];
  59705. if (img) {
  59706. dx = std::max(dx,img._width);
  59707. dy = std::max(dy,img._height);
  59708. dz = std::max(dz,img._depth);
  59709. dc+=img._spectrum;
  59710. }
  59711. }
  59712. res.assign(dx,dy,dz,dc,(T)0);
  59713. if (res) cimglist_for(*this,l) {
  59714. const CImg<T>& img = (*this)[l];
  59715. if (img) {
  59716. if (img._width==1 && img._height==1 && img._depth==1 &&
  59717. res._width==1 && res._height==1 && res._depth==1)
  59718. std::memcpy(&res[pos],img._data,sizeof(T)*img._spectrum);
  59719. else
  59720. res.draw_image((int)(align*(dx - img._width)),
  59721. (int)(align*(dy - img._height)),
  59722. (int)(align*(dz - img._depth)),
  59723. pos,
  59724. img);
  59725. }
  59726. pos+=img._spectrum;
  59727. }
  59728. }
  59729. }
  59730. return res;
  59731. }
  59732. //! Return a list where each image has been split along the specified axis.
  59733. /**
  59734. \param axis Axis to split images along.
  59735. \param nb Number of split parts for each image.
  59736. **/
  59737. CImgList<T>& split(const char axis, const int nb=-1) {
  59738. return get_split(axis,nb).move_to(*this);
  59739. }
  59740. //! Return a list where each image has been split along the specified axis \newinstance.
  59741. CImgList<T> get_split(const char axis, const int nb=-1) const {
  59742. CImgList<T> res;
  59743. cimglist_for(*this,l) _data[l].get_split(axis,nb).move_to(res,~0U);
  59744. return res;
  59745. }
  59746. //! Insert image at the end of the list.
  59747. /**
  59748. \param img Image to insert.
  59749. **/
  59750. template<typename t>
  59751. CImgList<T>& push_back(const CImg<t>& img) {
  59752. return insert(img);
  59753. }
  59754. //! Insert image at the front of the list.
  59755. /**
  59756. \param img Image to insert.
  59757. **/
  59758. template<typename t>
  59759. CImgList<T>& push_front(const CImg<t>& img) {
  59760. return insert(img,0);
  59761. }
  59762. //! Insert list at the end of the current list.
  59763. /**
  59764. \param list List to insert.
  59765. **/
  59766. template<typename t>
  59767. CImgList<T>& push_back(const CImgList<t>& list) {
  59768. return insert(list);
  59769. }
  59770. //! Insert list at the front of the current list.
  59771. /**
  59772. \param list List to insert.
  59773. **/
  59774. template<typename t>
  59775. CImgList<T>& push_front(const CImgList<t>& list) {
  59776. return insert(list,0);
  59777. }
  59778. //! Remove last image.
  59779. /**
  59780. **/
  59781. CImgList<T>& pop_back() {
  59782. return remove(_width - 1);
  59783. }
  59784. //! Remove first image.
  59785. /**
  59786. **/
  59787. CImgList<T>& pop_front() {
  59788. return remove(0);
  59789. }
  59790. //! Remove image pointed by iterator.
  59791. /**
  59792. \param iter Iterator pointing to the image to remove.
  59793. **/
  59794. CImgList<T>& erase(const iterator iter) {
  59795. return remove(iter - _data);
  59796. }
  59797. //@}
  59798. //----------------------------------
  59799. //
  59800. //! \name Data Input
  59801. //@{
  59802. //----------------------------------
  59803. //! Display a simple interactive interface to select images or sublists.
  59804. /**
  59805. \param disp Window instance to display selection and user interface.
  59806. \param feature_type Can be \c false to select a single image, or \c true to select a sublist.
  59807. \param axis Axis along whom images are appended for visualization.
  59808. \param align Alignment setting when images have not all the same size.
  59809. \param exit_on_anykey Exit function when any key is pressed.
  59810. \return A one-column vector containing the selected image indexes.
  59811. **/
  59812. CImg<intT> get_select(CImgDisplay &disp, const bool feature_type=true,
  59813. const char axis='x', const float align=0,
  59814. const bool exit_on_anykey=false) const {
  59815. return _select(disp,0,feature_type,axis,align,exit_on_anykey,0,false,false,false);
  59816. }
  59817. //! Display a simple interactive interface to select images or sublists.
  59818. /**
  59819. \param title Title of a new window used to display selection and user interface.
  59820. \param feature_type Can be \c false to select a single image, or \c true to select a sublist.
  59821. \param axis Axis along whom images are appended for visualization.
  59822. \param align Alignment setting when images have not all the same size.
  59823. \param exit_on_anykey Exit function when any key is pressed.
  59824. \return A one-column vector containing the selected image indexes.
  59825. **/
  59826. CImg<intT> get_select(const char *const title, const bool feature_type=true,
  59827. const char axis='x', const float align=0,
  59828. const bool exit_on_anykey=false) const {
  59829. CImgDisplay disp;
  59830. return _select(disp,title,feature_type,axis,align,exit_on_anykey,0,false,false,false);
  59831. }
  59832. CImg<intT> _select(CImgDisplay &disp, const char *const title, const bool feature_type,
  59833. const char axis, const float align, const bool exit_on_anykey,
  59834. const unsigned int orig, const bool resize_disp,
  59835. const bool exit_on_rightbutton, const bool exit_on_wheel) const {
  59836. if (is_empty())
  59837. throw CImgInstanceException(_cimglist_instance
  59838. "select(): Empty instance.",
  59839. cimglist_instance);
  59840. // Create image correspondence table and get list dimensions for visualization.
  59841. CImgList<uintT> _indices;
  59842. unsigned int max_width = 0, max_height = 0, sum_width = 0, sum_height = 0;
  59843. cimglist_for(*this,l) {
  59844. const CImg<T>& img = _data[l];
  59845. const unsigned int
  59846. w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
  59847. h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
  59848. if (w>max_width) max_width = w;
  59849. if (h>max_height) max_height = h;
  59850. sum_width+=w; sum_height+=h;
  59851. if (axis=='x') CImg<uintT>(w,1,1,1,(unsigned int)l).move_to(_indices);
  59852. else CImg<uintT>(h,1,1,1,(unsigned int)l).move_to(_indices);
  59853. }
  59854. const CImg<uintT> indices0 = _indices>'x';
  59855. // Create display window.
  59856. if (!disp) {
  59857. if (axis=='x') disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:0,1);
  59858. else disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:0,1);
  59859. if (!title) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
  59860. } else {
  59861. if (title) disp.set_title("%s",title);
  59862. disp.move_inside_screen();
  59863. }
  59864. if (resize_disp) {
  59865. if (axis=='x') disp.resize(cimg_fitscreen(sum_width,max_height,1),false);
  59866. else disp.resize(cimg_fitscreen(max_width,sum_height,1),false);
  59867. }
  59868. const unsigned int old_normalization = disp.normalization();
  59869. bool old_is_resized = disp.is_resized();
  59870. disp._normalization = 0;
  59871. disp.show().set_key(0).show_mouse();
  59872. static const unsigned char foreground_color[] = { 255,255,255 }, background_color[] = { 0,0,0 };
  59873. // Enter event loop.
  59874. CImg<ucharT> visu0, visu;
  59875. CImg<uintT> indices;
  59876. CImg<intT> positions(_width,4,1,1,-1);
  59877. int oindex0 = -1, oindex1 = -1, index0 = -1, index1 = -1;
  59878. bool is_clicked = false, is_selected = false, text_down = false, update_display = true;
  59879. unsigned int key = 0, font_size = 32;
  59880. while (!is_selected && !disp.is_closed() && !key) {
  59881. // Create background image.
  59882. if (!visu0) {
  59883. visu0.assign(disp._width,disp._height,1,3,0); visu.assign();
  59884. (indices0.get_resize(axis=='x'?visu0._width:visu0._height,1)).move_to(indices);
  59885. unsigned int _ind = 0;
  59886. const CImg<T> onexone(1,1,1,1,(T)0);
  59887. if (axis=='x')
  59888. cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width,4))
  59889. cimglist_for(*this,ind) {
  59890. unsigned int x0 = 0;
  59891. while (x0<visu0._width && indices[x0++]!=(unsigned int)ind) {}
  59892. unsigned int x1 = x0;
  59893. while (x1<visu0._width && indices[x1++]==(unsigned int)ind) {}
  59894. const CImg<T> &src = _data[ind]?_data[ind]:onexone;
  59895. CImg<ucharT> res;
  59896. src._get_select(disp,old_normalization,src._width/2,src._height/2,src._depth/2).
  59897. move_to(res);
  59898. const unsigned int h = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,true);
  59899. res.resize(x1 - x0,std::max(32U,h*disp._height/max_height),1,res._spectrum==1?3:-100);
  59900. positions(ind,0) = positions(ind,2) = (int)x0;
  59901. positions(ind,1) = positions(ind,3) = (int)(align*(visu0.height() - res.height()));
  59902. positions(ind,2)+=res._width;
  59903. positions(ind,3)+=res._height - 1;
  59904. visu0.draw_image(positions(ind,0),positions(ind,1),res);
  59905. }
  59906. else
  59907. cimg_pragma_openmp(parallel for cimg_openmp_if_size(_width,4))
  59908. cimglist_for(*this,ind) {
  59909. unsigned int y0 = 0;
  59910. while (y0<visu0._height && indices[y0++]!=(unsigned int)ind) {}
  59911. unsigned int y1 = y0;
  59912. while (y1<visu0._height && indices[y1++]==(unsigned int)ind) {}
  59913. const CImg<T> &src = _data[ind]?_data[ind]:onexone;
  59914. CImg<ucharT> res;
  59915. src._get_select(disp,old_normalization,(src._width - 1)/2,(src._height - 1)/2,(src._depth - 1)/2).
  59916. move_to(res);
  59917. const unsigned int w = CImgDisplay::_fitscreen(res._width,res._height,1,128,-85,false);
  59918. res.resize(std::max(32U,w*disp._width/max_width),y1 - y0,1,res._spectrum==1?3:-100);
  59919. positions(ind,0) = positions(ind,2) = (int)(align*(visu0.width() - res.width()));
  59920. positions(ind,1) = positions(ind,3) = (int)y0;
  59921. positions(ind,2)+=res._width - 1;
  59922. positions(ind,3)+=res._height;
  59923. visu0.draw_image(positions(ind,0),positions(ind,1),res);
  59924. }
  59925. if (axis=='x') --positions(_ind,2); else --positions(_ind,3);
  59926. update_display = true;
  59927. }
  59928. if (!visu || oindex0!=index0 || oindex1!=index1) {
  59929. if (index0>=0 && index1>=0) {
  59930. visu.assign(visu0,false);
  59931. const int indm = std::min(index0,index1), indM = std::max(index0,index1);
  59932. for (int ind = indm; ind<=indM; ++ind) if (positions(ind,0)>=0) {
  59933. visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),
  59934. background_color,0.2f);
  59935. if ((axis=='x' && positions(ind,2) - positions(ind,0)>=8) ||
  59936. (axis!='x' && positions(ind,3) - positions(ind,1)>=8))
  59937. visu.draw_rectangle(positions(ind,0),positions(ind,1),positions(ind,2),positions(ind,3),
  59938. foreground_color,0.9f,0xAAAAAAAA);
  59939. }
  59940. if (is_clicked) visu.__draw_text(" Images #%u - #%u, Size = %u ",font_size,(int)text_down,
  59941. orig + indm,orig + indM,indM - indm + 1);
  59942. else visu.__draw_text(" Image #%u (%u,%u,%u,%u) ",font_size,(int)text_down,
  59943. orig + index0,
  59944. _data[index0]._width,
  59945. _data[index0]._height,
  59946. _data[index0]._depth,
  59947. _data[index0]._spectrum);
  59948. update_display = true;
  59949. } else visu.assign();
  59950. }
  59951. if (!visu) { visu.assign(visu0,true); update_display = true; }
  59952. if (update_display) { visu.display(disp); update_display = false; }
  59953. disp.wait();
  59954. // Manage user events.
  59955. const int xm = disp.mouse_x(), ym = disp.mouse_y();
  59956. int index = -1;
  59957. if (xm>=0) {
  59958. index = (int)indices(axis=='x'?xm:ym);
  59959. if (disp.button()&1) {
  59960. if (!is_clicked) { is_clicked = true; oindex0 = index0; index0 = index; }
  59961. oindex1 = index1; index1 = index;
  59962. if (!feature_type) is_selected = true;
  59963. } else {
  59964. if (!is_clicked) { oindex0 = oindex1 = index0; index0 = index1 = index; }
  59965. else is_selected = true;
  59966. }
  59967. } else {
  59968. if (is_clicked) {
  59969. if (!(disp.button()&1)) { is_clicked = is_selected = false; index0 = index1 = -1; }
  59970. else index1 = -1;
  59971. } else index0 = index1 = -1;
  59972. }
  59973. if (disp.button()&4) { is_clicked = is_selected = false; index0 = index1 = -1; }
  59974. if (disp.button()&2 && exit_on_rightbutton) { is_selected = true; index1 = index0 = -1; }
  59975. if (disp.wheel() && exit_on_wheel) is_selected = true;
  59976. CImg<charT> filename(32);
  59977. switch (key = disp.key()) {
  59978. #if cimg_OS!=2
  59979. case cimg::keyCTRLRIGHT :
  59980. #endif
  59981. case 0 : case cimg::keyCTRLLEFT : key = 0; break;
  59982. case cimg::keyD : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  59983. disp.set_fullscreen(false).
  59984. resize(CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,false),
  59985. CImgDisplay::_fitscreen(3*disp.width()/2,3*disp.height()/2,1,128,-100,true),false).
  59986. _is_resized = true;
  59987. disp.set_key(key,false); key = 0; visu0.assign();
  59988. } break;
  59989. case cimg::keyC : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  59990. disp.set_fullscreen(false).
  59991. resize(cimg_fitscreen(2*disp.width()/3,2*disp.height()/3,1),false)._is_resized = true;
  59992. disp.set_key(key,false); key = 0; visu0.assign();
  59993. } break;
  59994. case cimg::keyR : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  59995. disp.set_fullscreen(false).
  59996. resize(cimg_fitscreen(axis=='x'?sum_width:max_width,axis=='x'?max_height:sum_height,1),false).
  59997. _is_resized = true;
  59998. disp.set_key(key,false); key = 0; visu0.assign();
  59999. } break;
  60000. case cimg::keyF : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  60001. disp.resize(disp.screen_width(),disp.screen_height(),false).toggle_fullscreen()._is_resized = true;
  60002. disp.set_key(key,false); key = 0; visu0.assign();
  60003. } break;
  60004. case cimg::keyS : if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  60005. static unsigned int snap_number = 0;
  60006. std::FILE *file;
  60007. do {
  60008. cimg_snprintf(filename,filename._width,cimg_appname "_%.6u."
  60009. #ifdef cimg_use_png
  60010. "png",
  60011. #else
  60012. "bmp",
  60013. #endif
  60014. snap_number++);
  60015. if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
  60016. } while (file);
  60017. if (visu0) {
  60018. (+visu0).__draw_text(" Saving snapshot... ",font_size,(int)text_down).display(disp);
  60019. visu0.save(filename);
  60020. (+visu0).__draw_text(" Snapshot '%s' saved. ",font_size,(int)text_down,filename._data).display(disp);
  60021. }
  60022. disp.set_key(key,false).wait(); key = 0;
  60023. } break;
  60024. case cimg::keyO :
  60025. if (disp.is_keyCTRLLEFT() || disp.is_keyCTRLRIGHT()) {
  60026. static unsigned int snap_number = 0;
  60027. std::FILE *file;
  60028. do {
  60029. #ifdef cimg_use_zlib
  60030. cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimgz",snap_number++);
  60031. #else
  60032. cimg_snprintf(filename,filename._width,cimg_appname "_%.6u.cimg",snap_number++);
  60033. #endif
  60034. if ((file=cimg::std_fopen(filename,"r"))!=0) cimg::fclose(file);
  60035. } while (file);
  60036. (+visu0).__draw_text(" Saving instance... ",font_size,(int)text_down).display(disp);
  60037. save(filename);
  60038. (+visu0).__draw_text(" Instance '%s' saved. ",font_size,(int)text_down,filename._data).display(disp);
  60039. disp.set_key(key,false).wait(); key = 0;
  60040. } break;
  60041. }
  60042. if (disp.is_resized()) { disp.resize(false); visu0.assign(); }
  60043. if (ym>=0 && ym<13) { if (!text_down) { visu.assign(); text_down = true; }}
  60044. else if (ym>=visu.height() - 13) { if (text_down) { visu.assign(); text_down = false; }}
  60045. if (!exit_on_anykey && key && key!=cimg::keyESC &&
  60046. (key!=cimg::keyW || (!disp.is_keyCTRLLEFT() && !disp.is_keyCTRLRIGHT()))) {
  60047. key = 0;
  60048. }
  60049. }
  60050. CImg<intT> res(1,2,1,1,-1);
  60051. if (is_selected) {
  60052. if (feature_type) res.fill(std::min(index0,index1),std::max(index0,index1));
  60053. else res.fill(index0);
  60054. }
  60055. if (!(disp.button()&2)) disp.set_button();
  60056. disp._normalization = old_normalization;
  60057. disp._is_resized = old_is_resized;
  60058. disp.set_key(key);
  60059. return res;
  60060. }
  60061. //! Load a list from a file.
  60062. /**
  60063. \param filename Filename to read data from.
  60064. **/
  60065. CImgList<T>& load(const char *const filename) {
  60066. if (!filename)
  60067. throw CImgArgumentException(_cimglist_instance
  60068. "load(): Specified filename is (null).",
  60069. cimglist_instance);
  60070. if (!cimg::strncasecmp(filename,"http://",7) || !cimg::strncasecmp(filename,"https://",8)) {
  60071. CImg<charT> filename_local(256);
  60072. load(cimg::load_network(filename,filename_local));
  60073. std::remove(filename_local);
  60074. return *this;
  60075. }
  60076. const bool is_stdin = *filename=='-' && (!filename[1] || filename[1]=='.');
  60077. const char *const ext = cimg::split_filename(filename);
  60078. const unsigned int omode = cimg::exception_mode();
  60079. cimg::exception_mode(0);
  60080. bool is_loaded = true;
  60081. try {
  60082. #ifdef cimglist_load_plugin
  60083. cimglist_load_plugin(filename);
  60084. #endif
  60085. #ifdef cimglist_load_plugin1
  60086. cimglist_load_plugin1(filename);
  60087. #endif
  60088. #ifdef cimglist_load_plugin2
  60089. cimglist_load_plugin2(filename);
  60090. #endif
  60091. #ifdef cimglist_load_plugin3
  60092. cimglist_load_plugin3(filename);
  60093. #endif
  60094. #ifdef cimglist_load_plugin4
  60095. cimglist_load_plugin4(filename);
  60096. #endif
  60097. #ifdef cimglist_load_plugin5
  60098. cimglist_load_plugin5(filename);
  60099. #endif
  60100. #ifdef cimglist_load_plugin6
  60101. cimglist_load_plugin6(filename);
  60102. #endif
  60103. #ifdef cimglist_load_plugin7
  60104. cimglist_load_plugin7(filename);
  60105. #endif
  60106. #ifdef cimglist_load_plugin8
  60107. cimglist_load_plugin8(filename);
  60108. #endif
  60109. if (!cimg::strcasecmp(ext,"tif") ||
  60110. !cimg::strcasecmp(ext,"tiff")) load_tiff(filename);
  60111. else if (!cimg::strcasecmp(ext,"gif")) load_gif_external(filename);
  60112. else if (!cimg::strcasecmp(ext,"cimg") ||
  60113. !cimg::strcasecmp(ext,"cimgz") ||
  60114. !*ext) load_cimg(filename);
  60115. else if (!cimg::strcasecmp(ext,"rec") ||
  60116. !cimg::strcasecmp(ext,"par")) load_parrec(filename);
  60117. else if (!cimg::strcasecmp(ext,"avi") ||
  60118. !cimg::strcasecmp(ext,"mov") ||
  60119. !cimg::strcasecmp(ext,"asf") ||
  60120. !cimg::strcasecmp(ext,"divx") ||
  60121. !cimg::strcasecmp(ext,"flv") ||
  60122. !cimg::strcasecmp(ext,"mpg") ||
  60123. !cimg::strcasecmp(ext,"m1v") ||
  60124. !cimg::strcasecmp(ext,"m2v") ||
  60125. !cimg::strcasecmp(ext,"m4v") ||
  60126. !cimg::strcasecmp(ext,"mjp") ||
  60127. !cimg::strcasecmp(ext,"mp4") ||
  60128. !cimg::strcasecmp(ext,"mkv") ||
  60129. !cimg::strcasecmp(ext,"mpe") ||
  60130. !cimg::strcasecmp(ext,"movie") ||
  60131. !cimg::strcasecmp(ext,"ogm") ||
  60132. !cimg::strcasecmp(ext,"ogg") ||
  60133. !cimg::strcasecmp(ext,"ogv") ||
  60134. !cimg::strcasecmp(ext,"qt") ||
  60135. !cimg::strcasecmp(ext,"rm") ||
  60136. !cimg::strcasecmp(ext,"vob") ||
  60137. !cimg::strcasecmp(ext,"webm") ||
  60138. !cimg::strcasecmp(ext,"wmv") ||
  60139. !cimg::strcasecmp(ext,"xvid") ||
  60140. !cimg::strcasecmp(ext,"mpeg")) load_video(filename);
  60141. else if (!cimg::strcasecmp(ext,"gz")) load_gzip_external(filename);
  60142. else is_loaded = false;
  60143. } catch (CImgIOException&) { is_loaded = false; }
  60144. // If nothing loaded, try to guess file format from magic number in file.
  60145. if (!is_loaded && !is_stdin) {
  60146. std::FILE *const file = cimg::std_fopen(filename,"rb");
  60147. if (!file) {
  60148. cimg::exception_mode(omode);
  60149. throw CImgIOException(_cimglist_instance
  60150. "load(): Failed to open file '%s'.",
  60151. cimglist_instance,
  60152. filename);
  60153. }
  60154. const char *const f_type = cimg::ftype(file,filename);
  60155. cimg::fclose(file);
  60156. is_loaded = true;
  60157. try {
  60158. if (!cimg::strcasecmp(f_type,"gif")) load_gif_external(filename);
  60159. else if (!cimg::strcasecmp(f_type,"tif") &&
  60160. cimg::strcasecmp(ext,"nef") &&
  60161. cimg::strcasecmp(ext,"dng")) load_tiff(filename);
  60162. else is_loaded = false;
  60163. } catch (CImgIOException&) { is_loaded = false; }
  60164. }
  60165. // If nothing loaded, try to load file as a single image.
  60166. if (!is_loaded) {
  60167. assign(1);
  60168. try {
  60169. _data->load(filename);
  60170. } catch (CImgIOException&) {
  60171. cimg::exception_mode(omode);
  60172. throw CImgIOException(_cimglist_instance
  60173. "load(): Failed to recognize format of file '%s'.",
  60174. cimglist_instance,
  60175. filename);
  60176. }
  60177. }
  60178. cimg::exception_mode(omode);
  60179. return *this;
  60180. }
  60181. //! Load a list from a file \newinstance.
  60182. static CImgList<T> get_load(const char *const filename) {
  60183. return CImgList<T>().load(filename);
  60184. }
  60185. //! Load a list from a .cimg file.
  60186. /**
  60187. \param filename Filename to read data from.
  60188. **/
  60189. CImgList<T>& load_cimg(const char *const filename) {
  60190. return _load_cimg(0,filename);
  60191. }
  60192. //! Load a list from a .cimg file \newinstance.
  60193. static CImgList<T> get_load_cimg(const char *const filename) {
  60194. return CImgList<T>().load_cimg(filename);
  60195. }
  60196. //! Load a list from a .cimg file.
  60197. /**
  60198. \param file File to read data from.
  60199. **/
  60200. CImgList<T>& load_cimg(std::FILE *const file) {
  60201. return _load_cimg(file,0);
  60202. }
  60203. //! Load a list from a .cimg file \newinstance.
  60204. static CImgList<T> get_load_cimg(std::FILE *const file) {
  60205. return CImgList<T>().load_cimg(file);
  60206. }
  60207. CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename) {
  60208. #ifdef cimg_use_zlib
  60209. #define _cimgz_load_cimg_case(Tss) { \
  60210. Bytef *const cbuf = new Bytef[csiz]; \
  60211. cimg::fread(cbuf,(size_t)csiz,nfile); \
  60212. if (is_bool) { \
  60213. CImg<ucharT> raw(W*H*D*C/8); \
  60214. uLongf destlen = (uLongf)raw.size(); \
  60215. uncompress((Bytef*)raw._data,&destlen,cbuf,(uLong)csiz); \
  60216. img.assign(W,H,D,C); \
  60217. img._uchar2bool(raw,raw.size(),false); \
  60218. } else { \
  60219. CImg<Tss> raw(W,H,D,C); \
  60220. uLongf destlen = (uLongf)(raw.size()*sizeof(Tss)); \
  60221. uncompress((Bytef*)raw._data,&destlen,cbuf,(uLong)csiz); \
  60222. if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
  60223. raw.move_to(img); \
  60224. } \
  60225. delete[] cbuf; \
  60226. }
  60227. #else
  60228. #define _cimgz_load_cimg_case(Tss) \
  60229. throw CImgIOException(_cimglist_instance \
  60230. "load_cimg(): Unable to load compressed data from file '%s' unless zlib is enabled.", \
  60231. cimglist_instance, \
  60232. filename?filename:"(FILE*)");
  60233. #endif
  60234. #define _cimg_load_cimg_case(Ts1,Ts2,Ts3,Tss) \
  60235. if (!loaded && ((Ts1 && !cimg::strcasecmp(Ts1,str_pixeltype)) || \
  60236. (Ts2 && !cimg::strcasecmp(Ts2,str_pixeltype)) || \
  60237. (Ts3 && !cimg::strcasecmp(Ts3,str_pixeltype)))) { \
  60238. const bool is_bool = cimg::type<Tss>::string()==cimg::type<bool>::string(); \
  60239. for (unsigned int l = 0; l<N; ++l) { \
  60240. j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0; \
  60241. W = H = D = C = 0; csiz = 0; \
  60242. if ((err = cimg_sscanf(tmp,"%u %u %u %u #" cimg_fuint64,&W,&H,&D,&C,&csiz))<4) \
  60243. throw CImgIOException(_cimglist_instance \
  60244. "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'.", \
  60245. cimglist_instance, \
  60246. W,H,D,C,l,filename?filename:("(FILE*)")); \
  60247. if (W*H*D*C>0) { \
  60248. CImg<T> &img = _data[l]; \
  60249. if (err==5) _cimgz_load_cimg_case(Tss) \
  60250. else { \
  60251. img.assign(W,H,D,C); \
  60252. T *ptrd = img._data; \
  60253. if (is_bool) { \
  60254. CImg<ucharT> raw; \
  60255. for (ulongT to_read = img.size(); to_read; ) { \
  60256. raw.assign((unsigned int)std::min(to_read,cimg_iobuffer)); \
  60257. cimg::fread(raw._data,raw._width,nfile); \
  60258. CImg<T>(ptrd,std::min(8*raw._width,(unsigned int)(img.end() - ptrd)),1,1,1,true).\
  60259. _uchar2bool(raw,raw._width,false); \
  60260. to_read-=raw._width; \
  60261. } \
  60262. } else { \
  60263. CImg<Tss> raw; \
  60264. for (ulongT to_read = img.size(); to_read; ) { \
  60265. raw.assign((unsigned int)std::min(to_read,cimg_iobuffer)); \
  60266. cimg::fread(raw._data,raw._width,nfile); \
  60267. if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
  60268. const Tss *ptrs = raw._data; \
  60269. for (ulongT off = (ulongT)raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
  60270. to_read-=raw._width; \
  60271. } \
  60272. } \
  60273. } \
  60274. } \
  60275. } \
  60276. loaded = true; \
  60277. }
  60278. if (!filename && !file)
  60279. throw CImgArgumentException(_cimglist_instance
  60280. "load_cimg(): Specified filename is (null).",
  60281. cimglist_instance);
  60282. const ulongT cimg_iobuffer = (ulongT)24*1024*1024;
  60283. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  60284. bool loaded = false, endian = cimg::endianness();
  60285. CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
  60286. *tmp = *str_pixeltype = *str_endian = 0;
  60287. unsigned int j, N = 0, W, H, D, C;
  60288. cimg_uint64 csiz;
  60289. int i, err;
  60290. do {
  60291. j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0 && j<255) tmp[j++] = (char)i; tmp[j] = 0;
  60292. } while (*tmp=='#' && i>=0);
  60293. err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z123468_]%*c%255[sA-Za-z_ ]",
  60294. &N,str_pixeltype._data,str_endian._data);
  60295. if (err<2) {
  60296. if (!file) cimg::fclose(nfile);
  60297. throw CImgIOException(_cimglist_instance
  60298. "load_cimg(): File or CImg header not found in file '%s'.",
  60299. cimglist_instance,
  60300. filename?filename:"(FILE*)");
  60301. }
  60302. if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
  60303. else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
  60304. assign(N);
  60305. _cimg_load_cimg_case("bool",0,0,cimg_uint8);
  60306. _cimg_load_cimg_case("uint8","unsigned_char","uchar",cimg_uint8);
  60307. _cimg_load_cimg_case("int8",0,0,cimg_int8);
  60308. _cimg_load_cimg_case("char",0,0,char);
  60309. _cimg_load_cimg_case("uint16","unsigned_short","ushort",cimg_uint16);
  60310. _cimg_load_cimg_case("int16","short",0,cimg_int16);
  60311. _cimg_load_cimg_case("uint32","unsigned_int","uint",cimg_uint32);
  60312. _cimg_load_cimg_case("int32","int",0,cimg_int32);
  60313. _cimg_load_cimg_case("unsigned_long","ulong",0,cimg_ulong);
  60314. _cimg_load_cimg_case("long",0,0,cimg_long);
  60315. _cimg_load_cimg_case("uint64","unsigned_int64",0,cimg_uint64);
  60316. _cimg_load_cimg_case("int64",0,0,cimg_int64);
  60317. _cimg_load_cimg_case("float32","float",0,cimg_float32);
  60318. _cimg_load_cimg_case("float64","double",0,cimg_float64);
  60319. if (!loaded) {
  60320. if (!file) cimg::fclose(nfile);
  60321. throw CImgIOException(_cimglist_instance
  60322. "load_cimg(): Unsupported pixel type '%s' for file '%s'.",
  60323. cimglist_instance,
  60324. str_pixeltype._data,filename?filename:"(FILE*)");
  60325. }
  60326. if (!file) cimg::fclose(nfile);
  60327. return *this;
  60328. }
  60329. //! Load a sublist list from a (non compressed) .cimg file.
  60330. /**
  60331. \param filename Filename to read data from.
  60332. \param n0 Starting index of images to read (~0U for max).
  60333. \param n1 Ending index of images to read (~0U for max).
  60334. \param x0 Starting X-coordinates of image regions to read.
  60335. \param y0 Starting Y-coordinates of image regions to read.
  60336. \param z0 Starting Z-coordinates of image regions to read.
  60337. \param c0 Starting C-coordinates of image regions to read.
  60338. \param x1 Ending X-coordinates of image regions to read (~0U for max).
  60339. \param y1 Ending Y-coordinates of image regions to read (~0U for max).
  60340. \param z1 Ending Z-coordinates of image regions to read (~0U for max).
  60341. \param c1 Ending C-coordinates of image regions to read (~0U for max).
  60342. **/
  60343. CImgList<T>& load_cimg(const char *const filename,
  60344. const unsigned int n0, const unsigned int n1,
  60345. const unsigned int x0, const unsigned int y0,
  60346. const unsigned int z0, const unsigned int c0,
  60347. const unsigned int x1, const unsigned int y1,
  60348. const unsigned int z1, const unsigned int c1) {
  60349. return _load_cimg(0,filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
  60350. }
  60351. //! Load a sublist list from a (non compressed) .cimg file \newinstance.
  60352. static CImgList<T> get_load_cimg(const char *const filename,
  60353. const unsigned int n0, const unsigned int n1,
  60354. const unsigned int x0, const unsigned int y0,
  60355. const unsigned int z0, const unsigned int c0,
  60356. const unsigned int x1, const unsigned int y1,
  60357. const unsigned int z1, const unsigned int c1) {
  60358. return CImgList<T>().load_cimg(filename,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
  60359. }
  60360. //! Load a sub-image list from a (non compressed) .cimg file \overloading.
  60361. CImgList<T>& load_cimg(std::FILE *const file,
  60362. const unsigned int n0, const unsigned int n1,
  60363. const unsigned int x0, const unsigned int y0,
  60364. const unsigned int z0, const unsigned int c0,
  60365. const unsigned int x1, const unsigned int y1,
  60366. const unsigned int z1, const unsigned int c1) {
  60367. return _load_cimg(file,0,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
  60368. }
  60369. //! Load a sub-image list from a (non compressed) .cimg file \newinstance.
  60370. static CImgList<T> get_load_cimg(std::FILE *const file,
  60371. const unsigned int n0, const unsigned int n1,
  60372. const unsigned int x0, const unsigned int y0,
  60373. const unsigned int z0, const unsigned int c0,
  60374. const unsigned int x1, const unsigned int y1,
  60375. const unsigned int z1, const unsigned int c1) {
  60376. return CImgList<T>().load_cimg(file,n0,n1,x0,y0,z0,c0,x1,y1,z1,c1);
  60377. }
  60378. CImgList<T>& _load_cimg(std::FILE *const file, const char *const filename,
  60379. const unsigned int n0, const unsigned int n1,
  60380. const unsigned int x0, const unsigned int y0,
  60381. const unsigned int z0, const unsigned int c0,
  60382. const unsigned int x1, const unsigned int y1,
  60383. const unsigned int z1, const unsigned int c1) {
  60384. #define _cimg_load_cimg_case2(Ts1,Ts2,Ts3,Tss) \
  60385. if (!loaded && ((Ts1 && !cimg::strcasecmp(Ts1,str_pixeltype)) || \
  60386. (Ts2 && !cimg::strcasecmp(Ts2,str_pixeltype)) || \
  60387. (Ts3 && !cimg::strcasecmp(Ts3,str_pixeltype)))) { \
  60388. for (unsigned int l = 0; l<=nn1; ++l) { \
  60389. j = 0; while ((i=std::fgetc(nfile))!='\n' && i>=0) tmp[j++] = (char)i; tmp[j] = 0; \
  60390. W = H = D = C = 0; \
  60391. if (cimg_sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
  60392. throw CImgIOException(_cimglist_instance \
  60393. "load_cimg(): Invalid specified size (%u,%u,%u,%u) of image %u in file '%s'", \
  60394. cimglist_instance, \
  60395. W,H,D,C,l,filename?filename:"(FILE*)"); \
  60396. if (W*H*D*C>0) { \
  60397. if (l<nn0 || nx0>=W || ny0>=H || nz0>=D || nc0>=C) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
  60398. else { \
  60399. const unsigned int \
  60400. _nx1 = nx1==~0U?W - 1:nx1, \
  60401. _ny1 = ny1==~0U?H - 1:ny1, \
  60402. _nz1 = nz1==~0U?D - 1:nz1, \
  60403. _nc1 = nc1==~0U?C - 1:nc1; \
  60404. if (_nx1>=W || _ny1>=H || _nz1>=D || _nc1>=C) \
  60405. throw CImgArgumentException(_cimglist_instance \
  60406. "load_cimg(): Invalid specified coordinates " \
  60407. "[%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) " \
  60408. "because image [%u] in file '%s' has size (%u,%u,%u,%u).", \
  60409. cimglist_instance, \
  60410. n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,l,filename?filename:"(FILE*)",W,H,D,C); \
  60411. CImg<Tss> raw(1 + _nx1 - nx0); \
  60412. CImg<T> &img = _data[l - nn0]; \
  60413. img.assign(1 + _nx1 - nx0,1 + _ny1 - ny0,1 + _nz1 - nz0,1 + _nc1 - nc0); \
  60414. T *ptrd = img._data; \
  60415. ulongT skipvb = nc0*W*H*D*sizeof(Tss); \
  60416. if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \
  60417. for (unsigned int c = 1 + _nc1 - nc0; c; --c) { \
  60418. const ulongT skipzb = nz0*W*H*sizeof(Tss); \
  60419. if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \
  60420. for (unsigned int z = 1 + _nz1 - nz0; z; --z) { \
  60421. const ulongT skipyb = ny0*W*sizeof(Tss); \
  60422. if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \
  60423. for (unsigned int y = 1 + _ny1 - ny0; y; --y) { \
  60424. const ulongT skipxb = nx0*sizeof(Tss); \
  60425. if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \
  60426. cimg::fread(raw._data,raw._width,nfile); \
  60427. if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw._width); \
  60428. const Tss *ptrs = raw._data; \
  60429. for (unsigned int off = raw._width; off; --off) *(ptrd++) = (T)*(ptrs++); \
  60430. const ulongT skipxe = (W - 1 - _nx1)*sizeof(Tss); \
  60431. if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \
  60432. } \
  60433. const ulongT skipye = (H - 1 - _ny1)*W*sizeof(Tss); \
  60434. if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \
  60435. } \
  60436. const ulongT skipze = (D - 1 - _nz1)*W*H*sizeof(Tss); \
  60437. if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \
  60438. } \
  60439. const ulongT skipve = (C - 1 - _nc1)*W*H*D*sizeof(Tss); \
  60440. if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \
  60441. } \
  60442. } \
  60443. } \
  60444. loaded = true; \
  60445. }
  60446. if (!filename && !file)
  60447. throw CImgArgumentException(_cimglist_instance
  60448. "load_cimg(): Specified filename is (null).",
  60449. cimglist_instance);
  60450. unsigned int
  60451. nn0 = std::min(n0,n1), nn1 = std::max(n0,n1),
  60452. nx0 = std::min(x0,x1), nx1 = std::max(x0,x1),
  60453. ny0 = std::min(y0,y1), ny1 = std::max(y0,y1),
  60454. nz0 = std::min(z0,z1), nz1 = std::max(z0,z1),
  60455. nc0 = std::min(c0,c1), nc1 = std::max(c0,c1);
  60456. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  60457. bool loaded = false, endian = cimg::endianness();
  60458. CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
  60459. *tmp = *str_pixeltype = *str_endian = 0;
  60460. unsigned int j, N, W, H, D, C;
  60461. int i, err;
  60462. j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
  60463. err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z123468_]%*c%255[sA-Za-z_ ]",
  60464. &N,str_pixeltype._data,str_endian._data);
  60465. if (err<2) {
  60466. if (!file) cimg::fclose(nfile);
  60467. throw CImgIOException(_cimglist_instance
  60468. "load_cimg(): CImg header not found in file '%s'.",
  60469. cimglist_instance,
  60470. filename?filename:"(FILE*)");
  60471. }
  60472. if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
  60473. else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
  60474. nn1 = n1==~0U?N - 1:n1;
  60475. if (nn1>=N)
  60476. throw CImgArgumentException(_cimglist_instance
  60477. "load_cimg(): Invalid specified coordinates [%u](%u,%u,%u,%u) -> [%u](%u,%u,%u,%u) "
  60478. "because file '%s' contains only %u images.",
  60479. cimglist_instance,
  60480. n0,x0,y0,z0,c0,n1,x1,y1,z1,c1,filename?filename:"(FILE*)",N);
  60481. assign(1 + nn1 - n0);
  60482. _cimg_load_cimg_case2("bool",0,0,cimg_uint8);
  60483. _cimg_load_cimg_case2("uint8","unsigned char","uchar",cimg_uint8);
  60484. _cimg_load_cimg_case2("int8",0,0,cimg_int8);
  60485. _cimg_load_cimg_case2("char",0,0,char);
  60486. _cimg_load_cimg_case2("uint16","unsigned_short","ushort",cimg_uint16);
  60487. _cimg_load_cimg_case2("int16","short",0,cimg_int16);
  60488. _cimg_load_cimg_case2("uint32","unsigned_int","uint",cimg_uint32);
  60489. _cimg_load_cimg_case2("int32","int",0,cimg_int32);
  60490. _cimg_load_cimg_case2("unsigned_long","ulong",0,cimg_ulong);
  60491. _cimg_load_cimg_case2("long",0,0,cimg_long);
  60492. _cimg_load_cimg_case2("uint64","unsigned_int64",0,cimg_uint64);
  60493. _cimg_load_cimg_case2("int64",0,0,cimg_int64);
  60494. _cimg_load_cimg_case2("float32","float",0,cimg_float32);
  60495. _cimg_load_cimg_case2("float64","double",0,cimg_float64);
  60496. if (!loaded) {
  60497. if (!file) cimg::fclose(nfile);
  60498. throw CImgIOException(_cimglist_instance
  60499. "load_cimg(): Unsupported pixel type '%s' for file '%s'.",
  60500. cimglist_instance,
  60501. str_pixeltype._data,filename?filename:"(FILE*)");
  60502. }
  60503. if (!file) cimg::fclose(nfile);
  60504. return *this;
  60505. }
  60506. //! Load a list from a PAR/REC (Philips) file.
  60507. /**
  60508. \param filename Filename to read data from.
  60509. **/
  60510. CImgList<T>& load_parrec(const char *const filename) {
  60511. if (!filename)
  60512. throw CImgArgumentException(_cimglist_instance
  60513. "load_parrec(): Specified filename is (null).",
  60514. cimglist_instance);
  60515. CImg<charT> body(1024), filenamepar(1024), filenamerec(1024);
  60516. *body = *filenamepar = *filenamerec = 0;
  60517. const char *const ext = cimg::split_filename(filename,body);
  60518. if (!std::strcmp(ext,"par")) {
  60519. std::strncpy(filenamepar,filename,filenamepar._width - 1);
  60520. cimg_snprintf(filenamerec,filenamerec._width,"%s.rec",body._data);
  60521. }
  60522. if (!std::strcmp(ext,"PAR")) {
  60523. std::strncpy(filenamepar,filename,filenamepar._width - 1);
  60524. cimg_snprintf(filenamerec,filenamerec._width,"%s.REC",body._data);
  60525. }
  60526. if (!std::strcmp(ext,"rec")) {
  60527. std::strncpy(filenamerec,filename,filenamerec._width - 1);
  60528. cimg_snprintf(filenamepar,filenamepar._width,"%s.par",body._data);
  60529. }
  60530. if (!std::strcmp(ext,"REC")) {
  60531. std::strncpy(filenamerec,filename,filenamerec._width - 1);
  60532. cimg_snprintf(filenamepar,filenamepar._width,"%s.PAR",body._data);
  60533. }
  60534. std::FILE *file = cimg::fopen(filenamepar,"r");
  60535. // Parse header file
  60536. CImgList<floatT> st_slices;
  60537. CImgList<uintT> st_global;
  60538. CImg<charT> line(256); *line = 0;
  60539. int err;
  60540. do { err = std::fscanf(file,"%255[^\n]%*c",line._data); } while (err!=EOF && (*line=='#' || *line=='.'));
  60541. do {
  60542. unsigned int sn,size_x,size_y,pixsize;
  60543. float rs,ri,ss;
  60544. 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);
  60545. if (err==7) {
  60546. CImg<floatT>::vector((float)sn,(float)pixsize,(float)size_x,(float)size_y,ri,rs,ss,0).move_to(st_slices);
  60547. unsigned int i; for (i = 0; i<st_global._width && sn<=st_global[i][2]; ++i) {}
  60548. if (i==st_global._width) CImg<uintT>::vector(size_x,size_y,sn).move_to(st_global);
  60549. else {
  60550. CImg<uintT> &vec = st_global[i];
  60551. if (size_x>vec[0]) vec[0] = size_x;
  60552. if (size_y>vec[1]) vec[1] = size_y;
  60553. vec[2] = sn;
  60554. }
  60555. st_slices[st_slices._width - 1][7] = (float)i;
  60556. }
  60557. } while (err==7);
  60558. // Read data
  60559. std::FILE *file2 = cimg::fopen(filenamerec,"rb");
  60560. cimglist_for(st_global,l) {
  60561. const CImg<uintT>& vec = st_global[l];
  60562. CImg<T>(vec[0],vec[1],vec[2]).move_to(*this);
  60563. }
  60564. cimglist_for(st_slices,l) {
  60565. const CImg<floatT>& vec = st_slices[l];
  60566. const unsigned int
  60567. sn = (unsigned int)vec[0] - 1,
  60568. pixsize = (unsigned int)vec[1],
  60569. size_x = (unsigned int)vec[2],
  60570. size_y = (unsigned int)vec[3],
  60571. imn = (unsigned int)vec[7];
  60572. const float ri = vec[4], rs = vec[5], ss = vec[6];
  60573. switch (pixsize) {
  60574. case 8 : {
  60575. CImg<ucharT> buf(size_x,size_y);
  60576. cimg::fread(buf._data,size_x*size_y,file2);
  60577. if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
  60578. CImg<T>& img = (*this)[imn];
  60579. cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
  60580. } break;
  60581. case 16 : {
  60582. CImg<ushortT> buf(size_x,size_y);
  60583. cimg::fread(buf._data,size_x*size_y,file2);
  60584. if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
  60585. CImg<T>& img = (*this)[imn];
  60586. cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
  60587. } break;
  60588. case 32 : {
  60589. CImg<uintT> buf(size_x,size_y);
  60590. cimg::fread(buf._data,size_x*size_y,file2);
  60591. if (cimg::endianness()) cimg::invert_endianness(buf._data,size_x*size_y);
  60592. CImg<T>& img = (*this)[imn];
  60593. cimg_forXY(img,x,y) img(x,y,sn) = (T)(( buf(x,y)*rs + ri )/(rs*ss));
  60594. } break;
  60595. default :
  60596. cimg::fclose(file);
  60597. cimg::fclose(file2);
  60598. throw CImgIOException(_cimglist_instance
  60599. "load_parrec(): Unsupported %d-bits pixel type for file '%s'.",
  60600. cimglist_instance,
  60601. pixsize,filename);
  60602. }
  60603. }
  60604. cimg::fclose(file);
  60605. cimg::fclose(file2);
  60606. if (!_width)
  60607. throw CImgIOException(_cimglist_instance
  60608. "load_parrec(): Failed to recognize valid PAR-REC data in file '%s'.",
  60609. cimglist_instance,
  60610. filename);
  60611. return *this;
  60612. }
  60613. //! Load a list from a PAR/REC (Philips) file \newinstance.
  60614. static CImgList<T> get_load_parrec(const char *const filename) {
  60615. return CImgList<T>().load_parrec(filename);
  60616. }
  60617. //! Load a list from a YUV image sequence file.
  60618. /**
  60619. \param filename Filename to read data from.
  60620. \param size_x Width of the images.
  60621. \param size_y Height of the images.
  60622. \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
  60623. \param first_frame Index of first image frame to read.
  60624. \param last_frame Index of last image frame to read.
  60625. \param step_frame Step applied between each frame.
  60626. \param yuv2rgb Apply YUV to RGB transformation during reading.
  60627. **/
  60628. CImgList<T>& load_yuv(const char *const filename,
  60629. const unsigned int size_x, const unsigned int size_y,
  60630. const unsigned int chroma_subsampling=444,
  60631. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  60632. const unsigned int step_frame=1, const bool yuv2rgb=true) {
  60633. return _load_yuv(0,filename,size_x,size_y,chroma_subsampling,
  60634. first_frame,last_frame,step_frame,yuv2rgb);
  60635. }
  60636. //! Load a list from a YUV image sequence file \newinstance.
  60637. static CImgList<T> get_load_yuv(const char *const filename,
  60638. const unsigned int size_x, const unsigned int size_y=1,
  60639. const unsigned int chroma_subsampling=444,
  60640. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  60641. const unsigned int step_frame=1, const bool yuv2rgb=true) {
  60642. return CImgList<T>().load_yuv(filename,size_x,size_y,chroma_subsampling,
  60643. first_frame,last_frame,step_frame,yuv2rgb);
  60644. }
  60645. //! Load a list from an image sequence YUV file \overloading.
  60646. CImgList<T>& load_yuv(std::FILE *const file,
  60647. const unsigned int size_x, const unsigned int size_y,
  60648. const unsigned int chroma_subsampling=444,
  60649. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  60650. const unsigned int step_frame=1, const bool yuv2rgb=true) {
  60651. return _load_yuv(file,0,size_x,size_y,chroma_subsampling,
  60652. first_frame,last_frame,step_frame,yuv2rgb);
  60653. }
  60654. //! Load a list from an image sequence YUV file \newinstance.
  60655. static CImgList<T> get_load_yuv(std::FILE *const file,
  60656. const unsigned int size_x, const unsigned int size_y=1,
  60657. const unsigned int chroma_subsampling=444,
  60658. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  60659. const unsigned int step_frame=1, const bool yuv2rgb=true) {
  60660. return CImgList<T>().load_yuv(file,size_x,size_y,chroma_subsampling,
  60661. first_frame,last_frame,step_frame,yuv2rgb);
  60662. }
  60663. CImgList<T>& _load_yuv(std::FILE *const file, const char *const filename,
  60664. const unsigned int size_x, const unsigned int size_y,
  60665. const unsigned int chroma_subsampling,
  60666. const unsigned int first_frame, const unsigned int last_frame,
  60667. const unsigned int step_frame, const bool yuv2rgb) {
  60668. if (!filename && !file)
  60669. throw CImgArgumentException(_cimglist_instance
  60670. "load_yuv(): Specified filename is (null).",
  60671. cimglist_instance);
  60672. if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444)
  60673. throw CImgArgumentException(_cimglist_instance
  60674. "load_yuv(): Specified chroma subsampling %u is invalid, for file '%s'.",
  60675. cimglist_instance,
  60676. chroma_subsampling,filename?filename:"(FILE*)");
  60677. const unsigned int
  60678. cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1,
  60679. cfy = chroma_subsampling==420?2:1,
  60680. nfirst_frame = first_frame<last_frame?first_frame:last_frame,
  60681. nlast_frame = first_frame<last_frame?last_frame:first_frame,
  60682. nstep_frame = step_frame?step_frame:1;
  60683. if (!size_x || !size_y || size_x%cfx || size_y%cfy)
  60684. throw CImgArgumentException(_cimglist_instance
  60685. "load_yuv(): Specified dimensions (%u,%u) are invalid, for file '%s'.",
  60686. cimglist_instance,
  60687. size_x,size_y,filename?filename:"(FILE*)");
  60688. CImg<ucharT> YUV(size_x,size_y,1,3), UV(size_x/cfx,size_y/cfy,1,2);
  60689. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb");
  60690. bool stop_flag = false;
  60691. int err;
  60692. if (nfirst_frame) {
  60693. err = cimg::fseek(nfile,(uint64T)nfirst_frame*(YUV._width*YUV._height + 2*UV._width*UV._height),SEEK_CUR);
  60694. if (err) {
  60695. if (!file) cimg::fclose(nfile);
  60696. throw CImgIOException(_cimglist_instance
  60697. "load_yuv(): File '%s' doesn't contain frame number %u.",
  60698. cimglist_instance,
  60699. filename?filename:"(FILE*)",nfirst_frame);
  60700. }
  60701. }
  60702. unsigned int frame;
  60703. for (frame = nfirst_frame; !stop_flag && frame<=nlast_frame; frame+=nstep_frame) {
  60704. YUV.get_shared_channel(0).fill(0);
  60705. // *TRY* to read the luminance part, do not replace by cimg::fread!
  60706. err = (int)std::fread((void*)(YUV._data),1,(size_t)YUV._width*YUV._height,nfile);
  60707. if (err!=(int)(YUV._width*YUV._height)) {
  60708. stop_flag = true;
  60709. if (err>0)
  60710. cimg::warn(_cimglist_instance
  60711. "load_yuv(): File '%s' contains incomplete data or given image dimensions "
  60712. "(%u,%u) are incorrect.",
  60713. cimglist_instance,
  60714. filename?filename:"(FILE*)",size_x,size_y);
  60715. } else {
  60716. UV.fill(0);
  60717. // *TRY* to read the luminance part, do not replace by cimg::fread!
  60718. err = (int)std::fread((void*)(UV._data),1,(size_t)UV.size(),nfile);
  60719. if (err!=(int)(UV.size())) {
  60720. stop_flag = true;
  60721. if (err>0)
  60722. cimg::warn(_cimglist_instance
  60723. "load_yuv(): File '%s' contains incomplete data or given image dimensions "
  60724. "(%u,%u) are incorrect.",
  60725. cimglist_instance,
  60726. filename?filename:"(FILE*)",size_x,size_y);
  60727. } else {
  60728. const ucharT *ptrs1 = UV._data, *ptrs2 = UV.data(0,0,0,1);
  60729. ucharT *ptrd1 = YUV.data(0,0,0,1), *ptrd2 = YUV.data(0,0,0,2);
  60730. const unsigned int wd = YUV._width;
  60731. switch (chroma_subsampling) {
  60732. case 420 :
  60733. cimg_forY(UV,y) {
  60734. cimg_forX(UV,x) {
  60735. const ucharT U = *(ptrs1++), V = *(ptrs2++);
  60736. ptrd1[wd] = U; *(ptrd1)++ = U;
  60737. ptrd1[wd] = U; *(ptrd1)++ = U;
  60738. ptrd2[wd] = V; *(ptrd2)++ = V;
  60739. ptrd2[wd] = V; *(ptrd2)++ = V;
  60740. }
  60741. ptrd1+=wd; ptrd2+=wd;
  60742. }
  60743. break;
  60744. case 422 :
  60745. cimg_forXY(UV,x,y) {
  60746. const ucharT U = *(ptrs1++), V = *(ptrs2++);
  60747. *(ptrd1++) = U; *(ptrd1++) = U;
  60748. *(ptrd2++) = V; *(ptrd2++) = V;
  60749. }
  60750. break;
  60751. default :
  60752. YUV.draw_image(0,0,0,1,UV);
  60753. }
  60754. if (yuv2rgb) YUV.YCbCrtoRGB();
  60755. insert(YUV);
  60756. if (nstep_frame>1) cimg::fseek(nfile,(uint64T)(nstep_frame - 1)*(size_x*size_y + size_x*size_y/2),SEEK_CUR);
  60757. }
  60758. }
  60759. }
  60760. if (is_empty())
  60761. throw CImgIOException(_cimglist_instance
  60762. "load_yuv() : Missing data in file '%s'.",
  60763. cimglist_instance,
  60764. filename?filename:"(FILE*)");
  60765. if (stop_flag && nlast_frame!=~0U && frame!=nlast_frame)
  60766. cimg::warn(_cimglist_instance
  60767. "load_yuv(): Frame %d not reached since only %u frames were found in file '%s'.",
  60768. cimglist_instance,
  60769. nlast_frame,frame - 1,filename?filename:"(FILE*)");
  60770. if (!file) cimg::fclose(nfile);
  60771. return *this;
  60772. }
  60773. //! Load an image from a video file, using OpenCV library.
  60774. /**
  60775. \param filename Filename, as a C-string.
  60776. \param first_frame Index of the first frame to read.
  60777. \param last_frame Index of the last frame to read (can be higher than the actual number of frames, e.g. '~0U').
  60778. \param step_frame Step value for frame reading.
  60779. \note If step_frame==0, the current video stream is forced to be released (without any frames read).
  60780. **/
  60781. CImgList<T>& load_video(const char *const filename,
  60782. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  60783. const unsigned int step_frame=1) {
  60784. #ifndef cimg_use_opencv
  60785. if (first_frame || last_frame!=~0U || step_frame>1)
  60786. throw CImgArgumentException(_cimglist_instance
  60787. "load_video() : File '%s', arguments 'first_frame', 'last_frame' "
  60788. "and 'step_frame' requires features from the OpenCV library "
  60789. "('-Dcimg_use_opencv' must be defined).",
  60790. cimglist_instance,filename);
  60791. return load_ffmpeg_external(filename);
  60792. #else
  60793. static cv::VideoCapture *captures[32] = {};
  60794. static CImgList<charT> filenames(32);
  60795. static CImg<uintT> positions(32,1,1,1,0);
  60796. static int last_used_index = -1;
  60797. // Detect if a video capture already exists for the specified filename.
  60798. cimg::mutex(9);
  60799. int index = -1;
  60800. if (filename) {
  60801. if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) {
  60802. index = last_used_index;
  60803. } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) {
  60804. index = l; break;
  60805. }
  60806. } else index = last_used_index;
  60807. cimg::mutex(9,0);
  60808. // Release stream if needed.
  60809. if (!step_frame || (index>=0 && positions[index]>first_frame)) {
  60810. if (index>=0) {
  60811. cimg::mutex(9);
  60812. captures[index]->release();
  60813. delete captures[index];
  60814. captures[index] = 0;
  60815. positions[index] = 0;
  60816. filenames[index].assign();
  60817. if (last_used_index==index) last_used_index = -1;
  60818. index = -1;
  60819. cimg::mutex(9,0);
  60820. } else
  60821. if (filename)
  60822. cimg::warn(_cimglist_instance
  60823. "load_video() : File '%s', no opened video stream associated with filename found.",
  60824. cimglist_instance,filename);
  60825. else
  60826. cimg::warn(_cimglist_instance
  60827. "load_video() : No opened video stream found.",
  60828. cimglist_instance,filename);
  60829. if (!step_frame) return *this;
  60830. }
  60831. // Find empty slot for capturing video stream.
  60832. if (index<0) {
  60833. if (!filename)
  60834. throw CImgArgumentException(_cimglist_instance
  60835. "load_video(): No already open video reader found. You must specify a "
  60836. "non-(null) filename argument for the first call.",
  60837. cimglist_instance);
  60838. else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); }
  60839. if (index<0)
  60840. throw CImgIOException(_cimglist_instance
  60841. "load_video(): File '%s', no video reader slots available. "
  60842. "You have to release some of your previously opened videos.",
  60843. cimglist_instance,filename);
  60844. cimg::mutex(9);
  60845. captures[index] = new cv::VideoCapture(filename);
  60846. positions[index] = 0;
  60847. if (!captures[index]->isOpened()) {
  60848. delete captures[index];
  60849. captures[index] = 0;
  60850. cimg::mutex(9,0);
  60851. cimg::fclose(cimg::fopen(filename,"rb")); // Check file availability
  60852. throw CImgIOException(_cimglist_instance
  60853. "load_video(): File '%s', unable to detect format of video file.",
  60854. cimglist_instance,filename);
  60855. }
  60856. CImg<charT>::string(filename).move_to(filenames[index]);
  60857. cimg::mutex(9,0);
  60858. }
  60859. cimg::mutex(9);
  60860. const unsigned int nb_frames = (unsigned int)std::max(0.,captures[index]->get(_cimg_cap_prop_frame_count));
  60861. cimg::mutex(9,0);
  60862. assign();
  60863. // Skip frames if requested.
  60864. bool go_on = true;
  60865. unsigned int &pos = positions[index];
  60866. while (pos<first_frame) {
  60867. cimg::mutex(9);
  60868. if (!captures[index]->grab()) { cimg::mutex(9,0); go_on = false; break; }
  60869. cimg::mutex(9,0);
  60870. ++pos;
  60871. }
  60872. // Read and convert frames.
  60873. const unsigned int _last_frame = std::min(nb_frames?nb_frames - 1:~0U,last_frame);
  60874. while (go_on && pos<=_last_frame) {
  60875. cv::Mat cvimg;
  60876. cimg::mutex(9);
  60877. if (captures[index]->read(cvimg)) { CImg<T>::_cvmat2cimg(cvimg).move_to(*this); ++pos; }
  60878. else go_on = false;
  60879. cimg::mutex(9,0);
  60880. if (go_on)
  60881. for (unsigned int i = 1; go_on && i<step_frame && pos<=_last_frame; ++i, ++pos) {
  60882. cimg::mutex(9);
  60883. if (!captures[index]->grab()) go_on = false;
  60884. cimg::mutex(9,0);
  60885. }
  60886. }
  60887. if (!go_on || (nb_frames && pos>=nb_frames)) { // Close video stream when necessary
  60888. cimg::mutex(9);
  60889. captures[index]->release();
  60890. delete captures[index];
  60891. captures[index] = 0;
  60892. filenames[index].assign();
  60893. positions[index] = 0;
  60894. index = -1;
  60895. cimg::mutex(9,0);
  60896. }
  60897. cimg::mutex(9);
  60898. last_used_index = index;
  60899. cimg::mutex(9,0);
  60900. if (is_empty())
  60901. throw CImgIOException(_cimglist_instance
  60902. "load_video(): File '%s', unable to locate frame %u.",
  60903. cimglist_instance,filename,first_frame);
  60904. return *this;
  60905. #endif
  60906. }
  60907. //! Load an image from a video file, using OpenCV library \newinstance.
  60908. static CImgList<T> get_load_video(const char *const filename,
  60909. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  60910. const unsigned int step_frame=1) {
  60911. return CImgList<T>().load_video(filename,first_frame,last_frame,step_frame);
  60912. }
  60913. //! Load an image from a video file using the external tool 'ffmpeg'.
  60914. /**
  60915. \param filename Filename to read data from.
  60916. **/
  60917. CImgList<T>& load_ffmpeg_external(const char *const filename) {
  60918. if (!filename)
  60919. throw CImgArgumentException(_cimglist_instance
  60920. "load_ffmpeg_external(): Specified filename is (null).",
  60921. cimglist_instance);
  60922. cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
  60923. CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
  60924. std::FILE *file = 0;
  60925. do {
  60926. cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
  60927. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  60928. cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data);
  60929. if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
  60930. } while (file);
  60931. cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%%6d.ppm",filename_tmp._data);
  60932. cimg_snprintf(command,command._width,"\"%s\" -v -8 -i \"%s\" \"%s\"",
  60933. cimg::ffmpeg_path(),
  60934. CImg<charT>::string(filename)._system_strescape().data(),
  60935. CImg<charT>::string(filename_tmp2)._system_strescape().data());
  60936. cimg::system(command,cimg::ffmpeg_path());
  60937. const unsigned int omode = cimg::exception_mode();
  60938. cimg::exception_mode(0);
  60939. assign();
  60940. unsigned int i = 1;
  60941. for (bool stop_flag = false; !stop_flag; ++i) {
  60942. cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,i);
  60943. CImg<T> img;
  60944. try { img.load_pnm(filename_tmp2); }
  60945. catch (CImgException&) { stop_flag = true; }
  60946. if (img) { img.move_to(*this); std::remove(filename_tmp2); }
  60947. }
  60948. cimg::exception_mode(omode);
  60949. if (is_empty())
  60950. throw CImgIOException(_cimglist_instance
  60951. "load_ffmpeg_external(): Failed to open file '%s' with external command 'ffmpeg'.",
  60952. cimglist_instance,
  60953. filename);
  60954. return *this;
  60955. }
  60956. //! Load an image from a video file using the external tool 'ffmpeg' \newinstance.
  60957. static CImgList<T> get_load_ffmpeg_external(const char *const filename) {
  60958. return CImgList<T>().load_ffmpeg_external(filename);
  60959. }
  60960. //! Load gif file, using ImageMagick or GraphicsMagick's external tools.
  60961. /**
  60962. \param filename Filename to read data from.
  60963. **/
  60964. CImgList<T>& load_gif_external(const char *const filename) {
  60965. if (!filename)
  60966. throw CImgArgumentException(_cimglist_instance
  60967. "load_gif_external(): Specified filename is (null).",
  60968. cimglist_instance);
  60969. cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
  60970. if (!_load_gif_external(filename,false))
  60971. if (!_load_gif_external(filename,true))
  60972. try { assign(CImg<T>().load_other(filename)); } catch (CImgException&) { assign(); }
  60973. if (is_empty())
  60974. throw CImgIOException(_cimglist_instance
  60975. "load_gif_external(): Failed to open file '%s'.",
  60976. cimglist_instance,filename);
  60977. return *this;
  60978. }
  60979. CImgList<T>& _load_gif_external(const char *const filename, const bool use_graphicsmagick=false) {
  60980. CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
  60981. std::FILE *file = 0;
  60982. do {
  60983. cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
  60984. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  60985. if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.0",filename_tmp._data);
  60986. else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-0.png",filename_tmp._data);
  60987. if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
  60988. } while (file);
  60989. if (use_graphicsmagick) cimg_snprintf(command,command._width,"%s convert \"%s\" \"%s.png\"",
  60990. cimg::graphicsmagick_path(),
  60991. CImg<charT>::string(filename)._system_strescape().data(),
  60992. CImg<charT>::string(filename_tmp)._system_strescape().data());
  60993. else cimg_snprintf(command,command._width,"\"%s\" -coalesce \"%s\" \"%s.png\"",
  60994. cimg::imagemagick_path(),
  60995. CImg<charT>::string(filename)._system_strescape().data(),
  60996. CImg<charT>::string(filename_tmp)._system_strescape().data());
  60997. cimg::system(command,cimg::imagemagick_path());
  60998. const unsigned int omode = cimg::exception_mode();
  60999. cimg::exception_mode(0);
  61000. assign();
  61001. // Try to read a single frame gif.
  61002. cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png",filename_tmp._data);
  61003. CImg<T> img;
  61004. try { img.load_png(filename_tmp2); }
  61005. catch (CImgException&) { }
  61006. if (img) { img.move_to(*this); std::remove(filename_tmp2); }
  61007. else { // Try to read animated gif
  61008. unsigned int i = 0;
  61009. for (bool stop_flag = false; !stop_flag; ++i) {
  61010. if (use_graphicsmagick) cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s.png.%u",filename_tmp._data,i);
  61011. else cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s-%u.png",filename_tmp._data,i);
  61012. try { img.load_png(filename_tmp2); }
  61013. catch (CImgException&) { stop_flag = true; }
  61014. if (img) { img.move_to(*this); std::remove(filename_tmp2); }
  61015. }
  61016. }
  61017. cimg::exception_mode(omode);
  61018. return *this;
  61019. }
  61020. //! Load gif file, using ImageMagick or GraphicsMagick's external tools \newinstance.
  61021. static CImgList<T> get_load_gif_external(const char *const filename) {
  61022. return CImgList<T>().load_gif_external(filename);
  61023. }
  61024. //! Load a gzipped list, using external tool 'gunzip'.
  61025. /**
  61026. \param filename Filename to read data from.
  61027. **/
  61028. CImgList<T>& load_gzip_external(const char *const filename) {
  61029. if (!filename)
  61030. throw CImgIOException(_cimglist_instance
  61031. "load_gzip_external(): Specified filename is (null).",
  61032. cimglist_instance);
  61033. cimg::fclose(cimg::fopen(filename,"rb")); // Check if file exists
  61034. CImg<charT> command(1024), filename_tmp(256), body(256);
  61035. const char
  61036. *ext = cimg::split_filename(filename,body),
  61037. *ext2 = cimg::split_filename(body,0);
  61038. std::FILE *file = 0;
  61039. do {
  61040. if (!cimg::strcasecmp(ext,"gz")) {
  61041. if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
  61042. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
  61043. else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
  61044. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  61045. } else {
  61046. if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
  61047. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
  61048. else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
  61049. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  61050. }
  61051. if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
  61052. } while (file);
  61053. cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"",
  61054. cimg::gunzip_path(),
  61055. CImg<charT>::string(filename)._system_strescape().data(),
  61056. CImg<charT>::string(filename_tmp)._system_strescape().data());
  61057. cimg::system(command,cimg::gunzip_path());
  61058. if (!(file=cimg::std_fopen(filename_tmp,"rb"))) {
  61059. cimg::fclose(cimg::fopen(filename,"r"));
  61060. throw CImgIOException(_cimglist_instance
  61061. "load_gzip_external(): Failed to open file '%s'.",
  61062. cimglist_instance,
  61063. filename);
  61064. } else cimg::fclose(file);
  61065. load(filename_tmp);
  61066. std::remove(filename_tmp);
  61067. return *this;
  61068. }
  61069. //! Load a gzipped list, using external tool 'gunzip' \newinstance.
  61070. static CImgList<T> get_load_gzip_external(const char *const filename) {
  61071. return CImgList<T>().load_gzip_external(filename);
  61072. }
  61073. //! Load images from a TIFF file.
  61074. /**
  61075. \param filename Filename to read data from.
  61076. \param first_frame Index of first image frame to read.
  61077. \param last_frame Index of last image frame to read.
  61078. \param step_frame Step applied between each frame.
  61079. \param[out] bits_per_value Number of bits used to store a scalar value in the image file.
  61080. \param[out] voxel_size Voxel size, as stored in the filename.
  61081. \param[out] description Description, as stored in the filename.
  61082. **/
  61083. CImgList<T>& load_tiff(const char *const filename,
  61084. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  61085. const unsigned int step_frame=1, unsigned int *const bits_per_value=0,
  61086. float *const voxel_size=0, CImg<charT> *const description=0) {
  61087. const unsigned int
  61088. nfirst_frame = first_frame<last_frame?first_frame:last_frame,
  61089. nstep_frame = step_frame?step_frame:1;
  61090. unsigned int nlast_frame = first_frame<last_frame?last_frame:first_frame;
  61091. #ifndef cimg_use_tiff
  61092. cimg::unused(bits_per_value,voxel_size,description);
  61093. if (nfirst_frame || nlast_frame!=~0U || nstep_frame!=1)
  61094. throw CImgArgumentException(_cimglist_instance
  61095. "load_tiff(): Unable to load sub-images from file '%s' unless libtiff is enabled.",
  61096. cimglist_instance,
  61097. filename);
  61098. return assign(CImg<T>::get_load_tiff(filename));
  61099. #else
  61100. #if cimg_verbosity<3
  61101. TIFFSetWarningHandler(0);
  61102. TIFFSetErrorHandler(0);
  61103. #endif
  61104. TIFF *tif = TIFFOpen(filename,"r");
  61105. if (tif) {
  61106. unsigned int nb_images = 0;
  61107. do ++nb_images; while (TIFFReadDirectory(tif));
  61108. if (nfirst_frame>=nb_images || (nlast_frame!=~0U && nlast_frame>=nb_images))
  61109. cimg::warn(_cimglist_instance
  61110. "load_tiff(): Invalid specified frame range is [%u,%u] (step %u) since "
  61111. "file '%s' contains %u image(s).",
  61112. cimglist_instance,
  61113. nfirst_frame,nlast_frame,nstep_frame,filename,nb_images);
  61114. if (nfirst_frame>=nb_images) return assign();
  61115. if (nlast_frame>=nb_images) nlast_frame = nb_images - 1;
  61116. assign(1 + (nlast_frame - nfirst_frame)/nstep_frame);
  61117. TIFFSetDirectory(tif,0);
  61118. cimglist_for(*this,l)
  61119. _data[l]._load_tiff(tif,nfirst_frame + l*nstep_frame,bits_per_value,voxel_size,description);
  61120. TIFFClose(tif);
  61121. } else throw CImgIOException(_cimglist_instance
  61122. "load_tiff(): Failed to open file '%s'.",
  61123. cimglist_instance,
  61124. filename);
  61125. return *this;
  61126. #endif
  61127. }
  61128. //! Load a multi-page TIFF file \newinstance.
  61129. static CImgList<T> get_load_tiff(const char *const filename,
  61130. const unsigned int first_frame=0, const unsigned int last_frame=~0U,
  61131. const unsigned int step_frame=1, unsigned int *const bits_per_value=0,
  61132. float *const voxel_size=0, CImg<charT> *const description=0) {
  61133. return CImgList<T>().load_tiff(filename,first_frame,last_frame,step_frame,bits_per_value,voxel_size,description);
  61134. }
  61135. //@}
  61136. //----------------------------------
  61137. //
  61138. //! \name Data Output
  61139. //@{
  61140. //----------------------------------
  61141. //! Print information about the list on the standard output.
  61142. /**
  61143. \param title Label set to the information displayed.
  61144. \param display_stats Tells if image statistics must be computed and displayed.
  61145. **/
  61146. const CImgList<T>& print(const char *const title=0, const bool display_stats=true) const {
  61147. unsigned int msiz = 0;
  61148. cimglist_for(*this,l) msiz+=_data[l].size();
  61149. msiz*=sizeof(T);
  61150. const unsigned int mdisp = msiz<8*1024?0U:msiz<8*1024*1024?1U:2U;
  61151. CImg<charT> _title(64);
  61152. if (!title) cimg_snprintf(_title,_title._width,"CImgList<%s>",pixel_type());
  61153. std::fprintf(cimg::output(),"%s%s%s%s: %sthis%s = %p, %ssize%s = %u/%u [%u %s], %sdata%s = (CImg<%s>*)%p",
  61154. cimg::t_magenta,cimg::t_bold,title?title:_title._data,cimg::t_normal,
  61155. cimg::t_bold,cimg::t_normal,(void*)this,
  61156. cimg::t_bold,cimg::t_normal,_width,_allocated_width,
  61157. mdisp==0?msiz:(mdisp==1?(msiz>>10):(msiz>>20)),
  61158. mdisp==0?"b":(mdisp==1?"Kio":"Mio"),
  61159. cimg::t_bold,cimg::t_normal,pixel_type(),(void*)begin());
  61160. if (_data) std::fprintf(cimg::output(),"..%p.\n",(void*)((char*)end() - 1));
  61161. else std::fprintf(cimg::output(),".\n");
  61162. char tmp[16] = {};
  61163. cimglist_for(*this,ll) {
  61164. cimg_snprintf(tmp,sizeof(tmp),"[%d]",ll);
  61165. std::fprintf(cimg::output()," ");
  61166. _data[ll].print(tmp,display_stats);
  61167. if (ll==3 && width()>8) { ll = width() - 5; std::fprintf(cimg::output()," ...\n"); }
  61168. }
  61169. std::fflush(cimg::output());
  61170. return *this;
  61171. }
  61172. //! Display the current CImgList instance in an existing CImgDisplay window (by reference).
  61173. /**
  61174. \param disp Reference to an existing CImgDisplay instance, where the current image list will be displayed.
  61175. \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  61176. \param align Appending alignment.
  61177. \note This function displays the list images of the current CImgList instance into an existing
  61178. CImgDisplay window.
  61179. Images of the list are appended in a single temporary image for visualization purposes.
  61180. The function returns immediately.
  61181. **/
  61182. const CImgList<T>& display(CImgDisplay &disp, const char axis='x', const float align=0) const {
  61183. disp.display(*this,axis,align);
  61184. return *this;
  61185. }
  61186. //! Display the current CImgList instance in a new display window.
  61187. /**
  61188. \param disp Display window.
  61189. \param display_info Tells if image information are displayed on the standard output.
  61190. \param axis Alignment axis for images viewing.
  61191. \param align Appending alignment.
  61192. \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
  61193. \param exit_on_anykey Exit function when any key is pressed.
  61194. \note This function opens a new window with a specific title and displays the list images of the
  61195. current CImgList instance into it.
  61196. Images of the list are appended in a single temporary image for visualization purposes.
  61197. The function returns when a key is pressed or the display window is closed by the user.
  61198. **/
  61199. const CImgList<T>& display(CImgDisplay &disp, const bool display_info,
  61200. const char axis='x', const float align=0,
  61201. unsigned int *const XYZ=0, const bool exit_on_anykey=false) const {
  61202. bool is_exit = false;
  61203. return _display(disp,0,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit);
  61204. }
  61205. //! Display the current CImgList instance in a new display window.
  61206. /**
  61207. \param title Title of the opening display window.
  61208. \param display_info Tells if list information must be written on standard output.
  61209. \param axis Appending axis. Can be <tt>{ 'x' | 'y' | 'z' | 'c' }</tt>.
  61210. \param align Appending alignment.
  61211. \param[in,out] XYZ Contains the XYZ coordinates at start / exit of the function.
  61212. \param exit_on_anykey Exit function when any key is pressed.
  61213. **/
  61214. const CImgList<T>& display(const char *const title=0, const bool display_info=true,
  61215. const char axis='x', const float align=0,
  61216. unsigned int *const XYZ=0, const bool exit_on_anykey=false) const {
  61217. CImgDisplay disp;
  61218. bool is_exit = false;
  61219. return _display(disp,title,0,display_info,axis,align,XYZ,exit_on_anykey,0,true,is_exit);
  61220. }
  61221. const CImgList<T>& _display(CImgDisplay &disp, const char *const title, const CImgList<charT> *const titles,
  61222. const bool display_info, const char axis, const float align, unsigned int *const XYZ,
  61223. const bool exit_on_anykey, const unsigned int orig, const bool is_first_call,
  61224. bool &is_exit) const {
  61225. if (is_empty())
  61226. throw CImgInstanceException(_cimglist_instance
  61227. "display(): Empty instance.",
  61228. cimglist_instance);
  61229. if (!disp) {
  61230. if (axis=='x') {
  61231. unsigned int sum_width = 0, max_height = 0;
  61232. cimglist_for(*this,l) {
  61233. const CImg<T> &img = _data[l];
  61234. const unsigned int
  61235. w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
  61236. h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
  61237. sum_width+=w;
  61238. if (h>max_height) max_height = h;
  61239. }
  61240. disp.assign(cimg_fitscreen(sum_width,max_height,1),title?title:titles?titles->__display()._data:0,1);
  61241. } else {
  61242. unsigned int max_width = 0, sum_height = 0;
  61243. cimglist_for(*this,l) {
  61244. const CImg<T> &img = _data[l];
  61245. const unsigned int
  61246. w = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,false),
  61247. h = CImgDisplay::_fitscreen(img._width,img._height,img._depth,128,-85,true);
  61248. if (w>max_width) max_width = w;
  61249. sum_height+=h;
  61250. }
  61251. disp.assign(cimg_fitscreen(max_width,sum_height,1),title?title:titles?titles->__display()._data:0,1);
  61252. }
  61253. if (!title && !titles) disp.set_title("CImgList<%s> (%u)",pixel_type(),_width);
  61254. } else if (title) disp.set_title("%s",title);
  61255. else if (titles) disp.set_title("%s",titles->__display()._data);
  61256. const CImg<char> dtitle = CImg<char>::string(disp.title());
  61257. if (display_info) print(disp.title());
  61258. disp.show().flush();
  61259. if (_width==1) {
  61260. const unsigned int dw = disp._width, dh = disp._height;
  61261. if (!is_first_call)
  61262. disp.resize(cimg_fitscreen(_data[0]._width,_data[0]._height,_data[0]._depth),false);
  61263. disp.set_title("%s (%ux%ux%ux%u)",
  61264. dtitle.data(),_data[0]._width,_data[0]._height,_data[0]._depth,_data[0]._spectrum);
  61265. _data[0]._display(disp,0,false,XYZ,exit_on_anykey,!is_first_call);
  61266. if (disp.key()) is_exit = true;
  61267. disp.resize(cimg_fitscreen(dw,dh,1),false).set_title("%s",dtitle.data());
  61268. } else {
  61269. bool disp_resize = !is_first_call;
  61270. while (!disp.is_closed() && !is_exit) {
  61271. const CImg<intT> s = _select(disp,0,true,axis,align,exit_on_anykey,orig,disp_resize,!is_first_call,true);
  61272. disp_resize = true;
  61273. if (s[0]<0 && !disp.wheel()) { // No selections done
  61274. if (disp.button()&2) { disp.flush(); break; }
  61275. is_exit = true;
  61276. } else if (disp.wheel()) { // Zoom in/out
  61277. const int wheel = disp.wheel();
  61278. disp.set_wheel();
  61279. if (!is_first_call && wheel<0) break;
  61280. if (wheel>0 && _width>=4) {
  61281. const unsigned int
  61282. delta = std::max(1U,(unsigned int)cimg::round(0.3*_width)),
  61283. ind0 = (unsigned int)std::max(0,s[0] - (int)delta),
  61284. ind1 = (unsigned int)std::min(width() - 1,s[0] + (int)delta);
  61285. if ((ind0!=0 || ind1!=_width - 1) && ind1 - ind0>=3) {
  61286. const CImgList<T> sublist = get_shared_images(ind0,ind1);
  61287. CImgList<charT> t_sublist;
  61288. if (titles) t_sublist = titles->get_shared_images(ind0,ind1);
  61289. sublist._display(disp,0,titles?&t_sublist:0,false,axis,align,XYZ,exit_on_anykey,
  61290. orig + ind0,false,is_exit);
  61291. }
  61292. }
  61293. } else if (s[0]!=0 || s[1]!=width() - 1) {
  61294. const CImgList<T> sublist = get_shared_images(s[0],s[1]);
  61295. CImgList<charT> t_sublist;
  61296. if (titles) t_sublist = titles->get_shared_images(s[0],s[1]);
  61297. sublist._display(disp,0,titles?&t_sublist:0,false,axis,align,XYZ,exit_on_anykey,
  61298. orig + s[0],false,is_exit);
  61299. }
  61300. disp.set_title("%s",dtitle.data());
  61301. }
  61302. }
  61303. return *this;
  61304. }
  61305. // [internal] Return string to describe display title.
  61306. CImg<charT> __display() const {
  61307. CImg<charT> res, str;
  61308. cimglist_for(*this,l) {
  61309. CImg<charT>::string((char*)_data[l]._data).move_to(str);
  61310. if (l!=width() - 1) {
  61311. str.resize(str._width + 1,1,1,1,0);
  61312. str[str._width - 2] = ',';
  61313. str[str._width - 1] = ' ';
  61314. }
  61315. res.append(str,'x');
  61316. }
  61317. if (!res) return CImg<charT>(1,1,1,1,0).move_to(res);
  61318. cimg::strellipsize(res,128,false);
  61319. if (_width>1) {
  61320. const unsigned int l = (unsigned int)std::strlen(res);
  61321. if (res._width<=l + 16) res.resize(l + 16,1,1,1,0);
  61322. cimg_snprintf(res._data + l,16," (#%u)",_width);
  61323. }
  61324. return res;
  61325. }
  61326. //! Save list into a file.
  61327. /**
  61328. \param filename Filename to write data to.
  61329. \param number When positive, represents an index added to the filename. Otherwise, no number is added.
  61330. \param digits Number of digits used for adding the number to the filename.
  61331. **/
  61332. const CImgList<T>& save(const char *const filename, const int number=-1, const unsigned int digits=6) const {
  61333. if (!filename)
  61334. throw CImgArgumentException(_cimglist_instance
  61335. "save(): Specified filename is (null).",
  61336. cimglist_instance);
  61337. // Do not test for empty instances, since .cimg format is able to manage empty instances.
  61338. const bool is_stdout = *filename=='-' && (!filename[1] || filename[1]=='.');
  61339. const char *const ext = cimg::split_filename(filename);
  61340. CImg<charT> nfilename(1024);
  61341. const char *const fn = is_stdout?filename:number>=0?cimg::number_filename(filename,number,digits,nfilename):
  61342. filename;
  61343. #ifdef cimglist_save_plugin
  61344. cimglist_save_plugin(fn);
  61345. #endif
  61346. #ifdef cimglist_save_plugin1
  61347. cimglist_save_plugin1(fn);
  61348. #endif
  61349. #ifdef cimglist_save_plugin2
  61350. cimglist_save_plugin2(fn);
  61351. #endif
  61352. #ifdef cimglist_save_plugin3
  61353. cimglist_save_plugin3(fn);
  61354. #endif
  61355. #ifdef cimglist_save_plugin4
  61356. cimglist_save_plugin4(fn);
  61357. #endif
  61358. #ifdef cimglist_save_plugin5
  61359. cimglist_save_plugin5(fn);
  61360. #endif
  61361. #ifdef cimglist_save_plugin6
  61362. cimglist_save_plugin6(fn);
  61363. #endif
  61364. #ifdef cimglist_save_plugin7
  61365. cimglist_save_plugin7(fn);
  61366. #endif
  61367. #ifdef cimglist_save_plugin8
  61368. cimglist_save_plugin8(fn);
  61369. #endif
  61370. if (!cimg::strcasecmp(ext,"cimgz")) return save_cimg(fn,true);
  61371. else if (!cimg::strcasecmp(ext,"cimg") || !*ext) return save_cimg(fn,false);
  61372. else if (!cimg::strcasecmp(ext,"yuv")) return save_yuv(fn,444,true);
  61373. else if (!cimg::strcasecmp(ext,"avi") ||
  61374. !cimg::strcasecmp(ext,"mov") ||
  61375. !cimg::strcasecmp(ext,"asf") ||
  61376. !cimg::strcasecmp(ext,"divx") ||
  61377. !cimg::strcasecmp(ext,"flv") ||
  61378. !cimg::strcasecmp(ext,"mpg") ||
  61379. !cimg::strcasecmp(ext,"m1v") ||
  61380. !cimg::strcasecmp(ext,"m2v") ||
  61381. !cimg::strcasecmp(ext,"m4v") ||
  61382. !cimg::strcasecmp(ext,"mjp") ||
  61383. !cimg::strcasecmp(ext,"mp4") ||
  61384. !cimg::strcasecmp(ext,"mkv") ||
  61385. !cimg::strcasecmp(ext,"mpe") ||
  61386. !cimg::strcasecmp(ext,"movie") ||
  61387. !cimg::strcasecmp(ext,"ogm") ||
  61388. !cimg::strcasecmp(ext,"ogg") ||
  61389. !cimg::strcasecmp(ext,"ogv") ||
  61390. !cimg::strcasecmp(ext,"qt") ||
  61391. !cimg::strcasecmp(ext,"rm") ||
  61392. !cimg::strcasecmp(ext,"vob") ||
  61393. !cimg::strcasecmp(ext,"webm") ||
  61394. !cimg::strcasecmp(ext,"wmv") ||
  61395. !cimg::strcasecmp(ext,"xvid") ||
  61396. !cimg::strcasecmp(ext,"mpeg")) return save_video(fn);
  61397. #ifdef cimg_use_tiff
  61398. else if (!cimg::strcasecmp(ext,"tif") ||
  61399. !cimg::strcasecmp(ext,"tiff")) return save_tiff(fn);
  61400. #endif
  61401. else if (!cimg::strcasecmp(ext,"gz")) return save_gzip_external(fn);
  61402. else {
  61403. if (_width==1) _data[0].save(fn,-1);
  61404. else cimglist_for(*this,l) { _data[l].save(fn,is_stdout?-1:l); if (is_stdout) std::fputc(EOF,cimg::_stdout()); }
  61405. }
  61406. return *this;
  61407. }
  61408. //! Tell if an image list can be saved as one single file.
  61409. /**
  61410. \param filename Filename, as a C-string.
  61411. \return \c true if the file format supports multiple images, \c false otherwise.
  61412. **/
  61413. static bool is_saveable(const char *const filename) {
  61414. const char *const ext = cimg::split_filename(filename);
  61415. if (!cimg::strcasecmp(ext,"cimgz") ||
  61416. #ifdef cimg_use_tiff
  61417. !cimg::strcasecmp(ext,"tif") ||
  61418. !cimg::strcasecmp(ext,"tiff") ||
  61419. #endif
  61420. !cimg::strcasecmp(ext,"yuv") ||
  61421. !cimg::strcasecmp(ext,"avi") ||
  61422. !cimg::strcasecmp(ext,"mov") ||
  61423. !cimg::strcasecmp(ext,"asf") ||
  61424. !cimg::strcasecmp(ext,"divx") ||
  61425. !cimg::strcasecmp(ext,"flv") ||
  61426. !cimg::strcasecmp(ext,"mpg") ||
  61427. !cimg::strcasecmp(ext,"m1v") ||
  61428. !cimg::strcasecmp(ext,"m2v") ||
  61429. !cimg::strcasecmp(ext,"m4v") ||
  61430. !cimg::strcasecmp(ext,"mjp") ||
  61431. !cimg::strcasecmp(ext,"mp4") ||
  61432. !cimg::strcasecmp(ext,"mkv") ||
  61433. !cimg::strcasecmp(ext,"mpe") ||
  61434. !cimg::strcasecmp(ext,"movie") ||
  61435. !cimg::strcasecmp(ext,"ogm") ||
  61436. !cimg::strcasecmp(ext,"ogg") ||
  61437. !cimg::strcasecmp(ext,"ogv") ||
  61438. !cimg::strcasecmp(ext,"qt") ||
  61439. !cimg::strcasecmp(ext,"rm") ||
  61440. !cimg::strcasecmp(ext,"vob") ||
  61441. !cimg::strcasecmp(ext,"webm") ||
  61442. !cimg::strcasecmp(ext,"wmv") ||
  61443. !cimg::strcasecmp(ext,"xvid") ||
  61444. !cimg::strcasecmp(ext,"mpeg")) return true;
  61445. return false;
  61446. }
  61447. //! Save image sequence as a GIF animated file.
  61448. /**
  61449. \param filename Filename to write data to.
  61450. \param fps Number of desired frames per second.
  61451. \param nb_loops Number of loops (\c 0 for infinite looping).
  61452. **/
  61453. const CImgList<T>& save_gif_external(const char *const filename, const float fps=25,
  61454. const unsigned int nb_loops=0) {
  61455. CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
  61456. CImgList<charT> filenames;
  61457. std::FILE *file = 0;
  61458. #ifdef cimg_use_png
  61459. #define _cimg_save_gif_extension "png"
  61460. #else
  61461. #define _cimg_save_gif_extension "ppm"
  61462. #endif
  61463. do {
  61464. cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
  61465. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  61466. cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001." _cimg_save_gif_extension,filename_tmp._data);
  61467. if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
  61468. } while (file);
  61469. cimglist_for(*this,l) {
  61470. cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u." _cimg_save_gif_extension,filename_tmp._data,l + 1);
  61471. CImg<charT>::string(filename_tmp2).move_to(filenames);
  61472. CImg<T> frame;
  61473. if (_data[l]._depth>1) _data[l].get_slice(0).move_to(frame); else frame.assign(_data[l],true);
  61474. if (frame._spectrum>4) frame.assign(frame.get_channels(0,3),false);
  61475. else if (frame._spectrum==1) frame.assign(frame.get_resize(-100,-100,1,3),false);
  61476. else if (frame._spectrum==2)
  61477. frame.assign(frame.get_resize(-100,-100,1,4).draw_image(0,0,0,2,frame.get_shared_channel(0)),false);
  61478. frame.save(filename_tmp2);
  61479. }
  61480. cimg_snprintf(command,command._width,"\"%s\" -delay %u -loop %u -dispose previous",
  61481. cimg::imagemagick_path(),
  61482. (unsigned int)std::max(0.f,cimg::round(100/fps)),
  61483. nb_loops);
  61484. CImg<ucharT>::string(command).move_to(filenames,0);
  61485. cimg_snprintf(command,command._width,"\"%s\"",
  61486. CImg<charT>::string(filename)._system_strescape().data());
  61487. CImg<ucharT>::string(command).move_to(filenames);
  61488. CImg<charT> _command = filenames>'x';
  61489. cimg_for(_command,p,char) if (!*p) *p = ' ';
  61490. _command.back() = 0;
  61491. cimg::system(_command,cimg::imagemagick_path());
  61492. file = cimg::std_fopen(filename,"rb");
  61493. if (!file)
  61494. throw CImgIOException(_cimglist_instance
  61495. "save_gif_external(): Failed to save file '%s' with external command 'magick/convert'.",
  61496. cimglist_instance,
  61497. filename);
  61498. else cimg::fclose(file);
  61499. cimglist_for_in(*this,1,filenames._width - 1,l) std::remove(filenames[l]);
  61500. return *this;
  61501. }
  61502. //! Save list as a YUV image sequence file.
  61503. /**
  61504. \param filename Filename to write data to.
  61505. \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
  61506. \param is_rgb Tells if the RGB to YUV conversion must be done for saving.
  61507. **/
  61508. const CImgList<T>& save_yuv(const char *const filename=0,
  61509. const unsigned int chroma_subsampling=444,
  61510. const bool is_rgb=true) const {
  61511. return _save_yuv(0,filename,chroma_subsampling,is_rgb);
  61512. }
  61513. //! Save image sequence into a YUV file.
  61514. /**
  61515. \param file File to write data to.
  61516. \param chroma_subsampling Type of chroma subsampling. Can be <tt>{ 420 | 422 | 444 }</tt>.
  61517. \param is_rgb Tells if the RGB to YUV conversion must be done for saving.
  61518. **/
  61519. const CImgList<T>& save_yuv(std::FILE *const file,
  61520. const unsigned int chroma_subsampling=444,
  61521. const bool is_rgb=true) const {
  61522. return _save_yuv(file,0,chroma_subsampling,is_rgb);
  61523. }
  61524. const CImgList<T>& _save_yuv(std::FILE *const file, const char *const filename,
  61525. const unsigned int chroma_subsampling,
  61526. const bool is_rgb) const {
  61527. if (!file && !filename)
  61528. throw CImgArgumentException(_cimglist_instance
  61529. "save_yuv(): Specified filename is (null).",
  61530. cimglist_instance);
  61531. if (chroma_subsampling!=420 && chroma_subsampling!=422 && chroma_subsampling!=444)
  61532. throw CImgArgumentException(_cimglist_instance
  61533. "save_yuv(): Specified chroma subsampling %u is invalid, for file '%s'.",
  61534. cimglist_instance,
  61535. chroma_subsampling,filename?filename:"(FILE*)");
  61536. if (is_empty()) { cimg::fempty(file,filename); return *this; }
  61537. const unsigned int
  61538. cfx = chroma_subsampling==420 || chroma_subsampling==422?2:1,
  61539. cfy = chroma_subsampling==420?2:1,
  61540. w0 = (*this)[0]._width, h0 = (*this)[0]._height,
  61541. width0 = w0 + (w0%cfx), height0 = h0 + (h0%cfy);
  61542. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  61543. cimglist_for(*this,l) {
  61544. const CImg<T> &frame = (*this)[l];
  61545. cimg_forZ(frame,z) {
  61546. CImg<ucharT> YUV;
  61547. if (sizeof(T)==1 && !is_rgb &&
  61548. frame._width==width0 && frame._height==height0 && frame._depth==1 && frame._spectrum==3)
  61549. YUV.assign((unsigned char*)frame._data,width0,height0,1,3,true);
  61550. else {
  61551. YUV = frame.get_slice(z);
  61552. if (YUV._width!=width0 || YUV._height!=height0) YUV.resize(width0,height0,1,-100,0);
  61553. if (YUV._spectrum!=3) YUV.resize(-100,-100,1,3,YUV._spectrum==1?1:0);
  61554. if (is_rgb) YUV.RGBtoYCbCr();
  61555. }
  61556. if (chroma_subsampling==444)
  61557. cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height*3,nfile);
  61558. else {
  61559. cimg::fwrite(YUV._data,(size_t)YUV._width*YUV._height,nfile);
  61560. CImg<ucharT> UV = YUV.get_channels(1,2);
  61561. UV.resize(UV._width/cfx,UV._height/cfy,1,2,2);
  61562. cimg::fwrite(UV._data,(size_t)UV._width*UV._height*2,nfile);
  61563. }
  61564. }
  61565. }
  61566. if (!file) cimg::fclose(nfile);
  61567. return *this;
  61568. }
  61569. //! Save list into a .cimg file.
  61570. /**
  61571. \param filename Filename to write data to.
  61572. \param is_compressed Tells if data compression must be enabled.
  61573. **/
  61574. const CImgList<T>& save_cimg(const char *const filename, const bool is_compressed=false) const {
  61575. return _save_cimg(0,filename,is_compressed);
  61576. }
  61577. const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename, const bool is_compressed) const {
  61578. if (!file && !filename)
  61579. throw CImgArgumentException(_cimglist_instance
  61580. "save_cimg(): Specified filename is (null).",
  61581. cimglist_instance);
  61582. #ifndef cimg_use_zlib
  61583. if (is_compressed)
  61584. cimg::warn(_cimglist_instance
  61585. "save_cimg(): Unable to save compressed data in file '%s' unless zlib is enabled, "
  61586. "saving them uncompressed.",
  61587. cimglist_instance,
  61588. filename?filename:"(FILE*)");
  61589. #endif
  61590. const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little";
  61591. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  61592. const bool is_bool = ptype==cimg::type<bool>::string();
  61593. std::fprintf(nfile,"%u %s %s_endian\n",_width,ptype,etype);
  61594. cimglist_for(*this,l) {
  61595. const CImg<T>& img = _data[l];
  61596. std::fprintf(nfile,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum);
  61597. if (img._data) {
  61598. CImg<T> tmp;
  61599. if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); }
  61600. const CImg<T>& ref = cimg::endianness()?tmp:img;
  61601. bool failed_to_compress = true;
  61602. if (is_compressed) {
  61603. #ifdef cimg_use_zlib
  61604. Bytef *cbuf = 0;
  61605. uLongf csiz = 0;
  61606. if (is_bool) { // Boolean data (bitwise)
  61607. ulongT siz;
  61608. const unsigned char *const buf = ref._bool2uchar(siz,false);
  61609. csiz = siz + siz/100 + 16;
  61610. cbuf = new Bytef[csiz];
  61611. failed_to_compress = (bool)compress(cbuf,&csiz,(Bytef*)buf,siz);
  61612. if (!failed_to_compress) {
  61613. std::fprintf(nfile," #%lu\n",csiz);
  61614. cimg::fwrite(cbuf,csiz,nfile);
  61615. }
  61616. delete[] buf;
  61617. } else { // Non-boolean data
  61618. const ulongT siz = sizeof(T)*ref.size();
  61619. csiz = siz + siz/100 + 16;
  61620. cbuf = new Bytef[csiz];
  61621. failed_to_compress = (bool)compress(cbuf,&csiz,(Bytef*)ref._data,siz);
  61622. if (!failed_to_compress) {
  61623. std::fprintf(nfile," #%lu\n",csiz);
  61624. cimg::fwrite(cbuf,csiz,nfile);
  61625. }
  61626. }
  61627. if (failed_to_compress)
  61628. cimg::warn(_cimglist_instance
  61629. "save_cimg(): Failed to save compressed data for file '%s', saving them uncompressed.",
  61630. cimglist_instance,
  61631. filename?filename:"(FILE*)");
  61632. delete[] cbuf;
  61633. #endif
  61634. }
  61635. if (failed_to_compress) { // Write non-compressed
  61636. std::fputc('\n',nfile);
  61637. if (is_bool) { // Boolean data (bitwise)
  61638. ulongT siz;
  61639. const unsigned char *const buf = ref._bool2uchar(siz,false);
  61640. cimg::fwrite(buf,siz,nfile);
  61641. delete[] buf;
  61642. } else cimg::fwrite(ref._data,ref.size(),nfile); // Non-boolean data
  61643. }
  61644. } else std::fputc('\n',nfile);
  61645. }
  61646. if (!file) cimg::fclose(nfile);
  61647. return *this;
  61648. }
  61649. //! Save list into a .cimg file.
  61650. /**
  61651. \param file File to write data to.
  61652. \param is_compressed Tells if data compression must be enabled.
  61653. **/
  61654. const CImgList<T>& save_cimg(std::FILE *file, const bool is_compressed=false) const {
  61655. return _save_cimg(file,0,is_compressed);
  61656. }
  61657. const CImgList<T>& _save_cimg(std::FILE *const file, const char *const filename,
  61658. const unsigned int n0,
  61659. const unsigned int x0, const unsigned int y0,
  61660. const unsigned int z0, const unsigned int c0) const {
  61661. #define _cimg_save_cimg_case(Ts1,Ts2,Ts3,Tss) \
  61662. if (!saved && ((Ts1 && !cimg::strcasecmp(Ts1,str_pixeltype)) || \
  61663. (Ts2 && !cimg::strcasecmp(Ts2,str_pixeltype)) || \
  61664. (Ts3 && !cimg::strcasecmp(Ts3,str_pixeltype)))) { \
  61665. for (unsigned int l = 0; l<lmax; ++l) { \
  61666. j = 0; while ((i=std::fgetc(nfile))!='\n') tmp[j++]=(char)i; tmp[j] = 0; \
  61667. W = H = D = C = 0; \
  61668. if (cimg_sscanf(tmp,"%u %u %u %u",&W,&H,&D,&C)!=4) \
  61669. throw CImgIOException(_cimglist_instance \
  61670. "save_cimg(): Invalid size (%u,%u,%u,%u) of image[%u], for file '%s'.", \
  61671. cimglist_instance, \
  61672. W,H,D,C,l,filename?filename:"(FILE*)"); \
  61673. if (W*H*D*C>0) { \
  61674. if (l<n0 || x0>=W || y0>=H || z0>=D || c0>=D) cimg::fseek(nfile,W*H*D*C*sizeof(Tss),SEEK_CUR); \
  61675. else { \
  61676. const CImg<T>& img = (*this)[l - n0]; \
  61677. const T *ptrs = img._data; \
  61678. const unsigned int \
  61679. x1 = x0 + img._width - 1, \
  61680. y1 = y0 + img._height - 1, \
  61681. z1 = z0 + img._depth - 1, \
  61682. c1 = c0 + img._spectrum - 1, \
  61683. nx1 = x1>=W?W - 1:x1, \
  61684. ny1 = y1>=H?H - 1:y1, \
  61685. nz1 = z1>=D?D - 1:z1, \
  61686. nc1 = c1>=C?C - 1:c1; \
  61687. CImg<Tss> raw(1 + nx1 - x0); \
  61688. const unsigned int skipvb = c0*W*H*D*sizeof(Tss); \
  61689. if (skipvb) cimg::fseek(nfile,skipvb,SEEK_CUR); \
  61690. for (unsigned int v = 1 + nc1 - c0; v; --v) { \
  61691. const unsigned int skipzb = z0*W*H*sizeof(Tss); \
  61692. if (skipzb) cimg::fseek(nfile,skipzb,SEEK_CUR); \
  61693. for (unsigned int z = 1 + nz1 - z0; z; --z) { \
  61694. const unsigned int skipyb = y0*W*sizeof(Tss); \
  61695. if (skipyb) cimg::fseek(nfile,skipyb,SEEK_CUR); \
  61696. for (unsigned int y = 1 + ny1 - y0; y; --y) { \
  61697. const unsigned int skipxb = x0*sizeof(Tss); \
  61698. if (skipxb) cimg::fseek(nfile,skipxb,SEEK_CUR); \
  61699. raw.assign(ptrs, raw._width); \
  61700. ptrs+=img._width; \
  61701. if (endian) cimg::invert_endianness(raw._data,raw._width); \
  61702. cimg::fwrite(raw._data,raw._width,nfile); \
  61703. const unsigned int skipxe = (W - 1 - nx1)*sizeof(Tss); \
  61704. if (skipxe) cimg::fseek(nfile,skipxe,SEEK_CUR); \
  61705. } \
  61706. const unsigned int skipye = (H - 1 - ny1)*W*sizeof(Tss); \
  61707. if (skipye) cimg::fseek(nfile,skipye,SEEK_CUR); \
  61708. } \
  61709. const unsigned int skipze = (D - 1 - nz1)*W*H*sizeof(Tss); \
  61710. if (skipze) cimg::fseek(nfile,skipze,SEEK_CUR); \
  61711. } \
  61712. const unsigned int skipve = (C - 1 - nc1)*W*H*D*sizeof(Tss); \
  61713. if (skipve) cimg::fseek(nfile,skipve,SEEK_CUR); \
  61714. } \
  61715. } \
  61716. } \
  61717. saved = true; \
  61718. }
  61719. if (!file && !filename)
  61720. throw CImgArgumentException(_cimglist_instance
  61721. "save_cimg(): Specified filename is (null).",
  61722. cimglist_instance);
  61723. if (is_empty())
  61724. throw CImgInstanceException(_cimglist_instance
  61725. "save_cimg(): Empty instance, for file '%s'.",
  61726. cimglist_instance,
  61727. filename?filename:"(FILE*)");
  61728. std::FILE *const nfile = file?file:cimg::fopen(filename,"rb+");
  61729. bool saved = false, endian = cimg::endianness();
  61730. CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
  61731. *tmp = *str_pixeltype = *str_endian = 0;
  61732. unsigned int j, N, W, H, D, C;
  61733. int i, err;
  61734. j = 0; while ((i=std::fgetc(nfile))!='\n' && i!=EOF && j<256) tmp[j++] = (char)i; tmp[j] = 0;
  61735. err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z123468_]%*c%255[sA-Za-z_ ]",&N,str_pixeltype._data,str_endian._data);
  61736. if (err<2) {
  61737. if (!file) cimg::fclose(nfile);
  61738. throw CImgIOException(_cimglist_instance
  61739. "save_cimg(): CImg header not found in file '%s'.",
  61740. cimglist_instance,
  61741. filename?filename:"(FILE*)");
  61742. }
  61743. if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
  61744. else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
  61745. const unsigned int lmax = std::min(N,n0 + _width);
  61746. _cimg_save_cimg_case("bool",0,0,cimg_uint8);
  61747. _cimg_save_cimg_case("uint8","unsigned_char","uchar",cimg_uint8);
  61748. _cimg_save_cimg_case("int8",0,0,cimg_int8);
  61749. _cimg_save_cimg_case("char",0,0,char);
  61750. _cimg_save_cimg_case("uint16","unsigned_short","ushort",cimg_uint16);
  61751. _cimg_save_cimg_case("int16","short",0,cimg_int16);
  61752. _cimg_save_cimg_case("uint32","unsigned_int","uint",cimg_uint32);
  61753. _cimg_save_cimg_case("int32","int",0,cimg_int32);
  61754. _cimg_save_cimg_case("uint64","unsigned_int64",0,cimg_uint64);
  61755. _cimg_save_cimg_case("int64",0,0,cimg_int64);
  61756. _cimg_save_cimg_case("float","float32",0,cimg_float32);
  61757. _cimg_save_cimg_case("float64","double",0,cimg_float64);
  61758. if (!saved) {
  61759. if (!file) cimg::fclose(nfile);
  61760. throw CImgIOException(_cimglist_instance
  61761. "save_cimg(): Unsupported data type '%s' for file '%s'.",
  61762. cimglist_instance,
  61763. filename?filename:"(FILE*)",str_pixeltype._data);
  61764. }
  61765. if (!file) cimg::fclose(nfile);
  61766. return *this;
  61767. }
  61768. //! Insert the image instance into into an existing .cimg file, at specified coordinates.
  61769. /**
  61770. \param filename Filename to write data to.
  61771. \param n0 Starting index of images to write.
  61772. \param x0 Starting X-coordinates of image regions to write.
  61773. \param y0 Starting Y-coordinates of image regions to write.
  61774. \param z0 Starting Z-coordinates of image regions to write.
  61775. \param c0 Starting C-coordinates of image regions to write.
  61776. **/
  61777. const CImgList<T>& save_cimg(const char *const filename,
  61778. const unsigned int n0,
  61779. const unsigned int x0, const unsigned int y0,
  61780. const unsigned int z0, const unsigned int c0) const {
  61781. return _save_cimg(0,filename,n0,x0,y0,z0,c0);
  61782. }
  61783. //! Insert the image instance into into an existing .cimg file, at specified coordinates.
  61784. /**
  61785. \param file File to write data to.
  61786. \param n0 Starting index of images to write.
  61787. \param x0 Starting X-coordinates of image regions to write.
  61788. \param y0 Starting Y-coordinates of image regions to write.
  61789. \param z0 Starting Z-coordinates of image regions to write.
  61790. \param c0 Starting C-coordinates of image regions to write.
  61791. **/
  61792. const CImgList<T>& save_cimg(std::FILE *const file,
  61793. const unsigned int n0,
  61794. const unsigned int x0, const unsigned int y0,
  61795. const unsigned int z0, const unsigned int c0) const {
  61796. return _save_cimg(file,0,n0,x0,y0,z0,c0);
  61797. }
  61798. static void _save_empty_cimg(std::FILE *const file, const char *const filename,
  61799. const unsigned int nb,
  61800. const unsigned int dx, const unsigned int dy,
  61801. const unsigned int dz, const unsigned int dc) {
  61802. std::FILE *const nfile = file?file:cimg::fopen(filename,"wb");
  61803. const ulongT siz = (ulongT)dx*dy*dz*dc*sizeof(T);
  61804. std::fprintf(nfile,"%u %s\n",nb,pixel_type());
  61805. for (unsigned int i=nb; i; --i) {
  61806. std::fprintf(nfile,"%u %u %u %u\n",dx,dy,dz,dc);
  61807. for (ulongT off = siz; off; --off) std::fputc(0,nfile);
  61808. }
  61809. if (!file) cimg::fclose(nfile);
  61810. }
  61811. //! Save empty (non-compressed) .cimg file with specified dimensions.
  61812. /**
  61813. \param filename Filename to write data to.
  61814. \param nb Number of images to write.
  61815. \param dx Width of images in the written file.
  61816. \param dy Height of images in the written file.
  61817. \param dz Depth of images in the written file.
  61818. \param dc Spectrum of images in the written file.
  61819. **/
  61820. static void save_empty_cimg(const char *const filename,
  61821. const unsigned int nb,
  61822. const unsigned int dx, const unsigned int dy=1,
  61823. const unsigned int dz=1, const unsigned int dc=1) {
  61824. return _save_empty_cimg(0,filename,nb,dx,dy,dz,dc);
  61825. }
  61826. //! Save empty .cimg file with specified dimensions.
  61827. /**
  61828. \param file File to write data to.
  61829. \param nb Number of images to write.
  61830. \param dx Width of images in the written file.
  61831. \param dy Height of images in the written file.
  61832. \param dz Depth of images in the written file.
  61833. \param dc Spectrum of images in the written file.
  61834. **/
  61835. static void save_empty_cimg(std::FILE *const file,
  61836. const unsigned int nb,
  61837. const unsigned int dx, const unsigned int dy=1,
  61838. const unsigned int dz=1, const unsigned int dc=1) {
  61839. return _save_empty_cimg(file,0,nb,dx,dy,dz,dc);
  61840. }
  61841. //! Save list as a TIFF file.
  61842. /**
  61843. \param filename Filename to write data to.
  61844. \param compression_type Compression mode used to write data.
  61845. \param voxel_size Voxel size, to be stored in the filename.
  61846. \param description Description, to be stored in the filename.
  61847. \param use_bigtiff Allow to save big tiff files (>4Gb).
  61848. **/
  61849. const CImgList<T>& save_tiff(const char *const filename, const unsigned int compression_type=0,
  61850. const float *const voxel_size=0, const char *const description=0,
  61851. const bool use_bigtiff=true) const {
  61852. if (!filename)
  61853. throw CImgArgumentException(_cimglist_instance
  61854. "save_tiff(): Specified filename is (null).",
  61855. cimglist_instance);
  61856. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  61857. #ifndef cimg_use_tiff
  61858. if (_width==1) _data[0].save_tiff(filename,compression_type,voxel_size,description,use_bigtiff);
  61859. else cimglist_for(*this,l) {
  61860. CImg<charT> nfilename(1024);
  61861. cimg::number_filename(filename,l,6,nfilename);
  61862. _data[l].save_tiff(nfilename,compression_type,voxel_size,description,use_bigtiff);
  61863. }
  61864. #else
  61865. ulongT siz = 0;
  61866. cimglist_for(*this,l) siz+=_data[l].size();
  61867. const bool _use_bigtiff = use_bigtiff && sizeof(siz)>=8 && siz*sizeof(T)>=1UL<<31; // No bigtiff for small images
  61868. TIFF *tif = TIFFOpen(filename,_use_bigtiff?"w8":"w4");
  61869. if (tif) {
  61870. for (unsigned int dir = 0, l = 0; l<_width; ++l) {
  61871. const CImg<T>& img = (*this)[l];
  61872. cimg_forZ(img,z) img._save_tiff(tif,dir++,z,compression_type,voxel_size,description);
  61873. }
  61874. TIFFClose(tif);
  61875. } else
  61876. throw CImgIOException(_cimglist_instance
  61877. "save_tiff(): Failed to open stream for file '%s'.",
  61878. cimglist_instance,
  61879. filename);
  61880. #endif
  61881. return *this;
  61882. }
  61883. //! Save list as a gzipped file, using external tool 'gzip'.
  61884. /**
  61885. \param filename Filename to write data to.
  61886. **/
  61887. const CImgList<T>& save_gzip_external(const char *const filename) const {
  61888. if (!filename)
  61889. throw CImgIOException(_cimglist_instance
  61890. "save_gzip_external(): Specified filename is (null).",
  61891. cimglist_instance);
  61892. CImg<charT> command(1024), filename_tmp(256), body(256);
  61893. const char
  61894. *ext = cimg::split_filename(filename,body),
  61895. *ext2 = cimg::split_filename(body,0);
  61896. std::FILE *file;
  61897. do {
  61898. if (!cimg::strcasecmp(ext,"gz")) {
  61899. if (*ext2) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
  61900. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext2);
  61901. else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
  61902. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  61903. } else {
  61904. if (*ext) cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.%s",
  61905. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext);
  61906. else cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s.cimg",
  61907. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  61908. }
  61909. if ((file=cimg::std_fopen(filename_tmp,"rb"))!=0) cimg::fclose(file);
  61910. } while (file);
  61911. if (is_saveable(body)) {
  61912. save(filename_tmp);
  61913. cimg_snprintf(command,command._width,"\"%s\" -c \"%s\" > \"%s\"",
  61914. cimg::gzip_path(),
  61915. CImg<charT>::string(filename_tmp)._system_strescape().data(),
  61916. CImg<charT>::string(filename)._system_strescape().data());
  61917. cimg::system(command,cimg::gzip_path());
  61918. file = cimg::std_fopen(filename,"rb");
  61919. if (!file)
  61920. throw CImgIOException(_cimglist_instance
  61921. "save_gzip_external(): Failed to save file '%s' with external command 'gzip'.",
  61922. cimglist_instance,
  61923. filename);
  61924. else cimg::fclose(file);
  61925. std::remove(filename_tmp);
  61926. } else {
  61927. CImg<charT> nfilename(1024);
  61928. cimglist_for(*this,l) {
  61929. cimg::number_filename(body,l,6,nfilename);
  61930. if (*ext) cimg_snprintf(nfilename._data + std::strlen(nfilename),64,".%s",ext);
  61931. _data[l].save_gzip_external(nfilename);
  61932. }
  61933. }
  61934. return *this;
  61935. }
  61936. //! Save image sequence (using the OpenCV library when available).
  61937. /**
  61938. \param filename Filename to write data to.
  61939. \param fps Number of frames per second.
  61940. \param codec Type of compression (See http://www.fourcc.org/codecs.php to see available codecs).
  61941. \param keep_open Tells if the video writer associated to the specified filename
  61942. must be kept open or not (to allow frames to be added in the same file afterwards).
  61943. **/
  61944. const CImgList<T>& save_video(const char *const filename, const unsigned int fps=25,
  61945. const char *codec=0, const bool keep_open=false) const {
  61946. #ifndef cimg_use_opencv
  61947. cimg::unused(codec,keep_open);
  61948. if (keep_open) cimg::warn(_cimglist_instance
  61949. "save_video(): Cannot output streamed video, as this requires features from the "
  61950. "OpenCV library ('-Dcimg_use_opencv') must be defined).",
  61951. cimglist_instance);
  61952. if (!is_empty()) return save_ffmpeg_external(filename,fps);
  61953. return *this;
  61954. #else
  61955. try {
  61956. static cv::VideoWriter *writers[32] = {};
  61957. static CImgList<charT> filenames(32);
  61958. static CImg<intT> sizes(32,2,1,1,0);
  61959. static int last_used_index = -1;
  61960. // Detect if a video writer already exists for the specified filename.
  61961. cimg::mutex(9);
  61962. int index = -1;
  61963. if (filename) {
  61964. if (last_used_index>=0 && !std::strcmp(filename,filenames[last_used_index])) {
  61965. index = last_used_index;
  61966. } else cimglist_for(filenames,l) if (filenames[l] && !std::strcmp(filename,filenames[l])) {
  61967. index = l; break;
  61968. }
  61969. } else index = last_used_index;
  61970. cimg::mutex(9,0);
  61971. // Find empty slot for capturing video stream.
  61972. if (index<0) {
  61973. if (!filename)
  61974. throw CImgArgumentException(_cimglist_instance
  61975. "save_video(): No already open video writer found. You must specify a "
  61976. "non-(null) filename argument for the first call.",
  61977. cimglist_instance);
  61978. else { cimg::mutex(9); cimglist_for(filenames,l) if (!filenames[l]) { index = l; break; } cimg::mutex(9,0); }
  61979. if (index<0)
  61980. throw CImgIOException(_cimglist_instance
  61981. "save_video(): File '%s', no video writer slots available. "
  61982. "You have to release some of your previously opened videos.",
  61983. cimglist_instance,filename);
  61984. if (is_empty())
  61985. throw CImgInstanceException(_cimglist_instance
  61986. "save_video(): Instance list is empty.",
  61987. cimglist_instance);
  61988. const unsigned int W = _data?_data[0]._width:0, H = _data?_data[0]._height:0;
  61989. if (!W || !H)
  61990. throw CImgInstanceException(_cimglist_instance
  61991. "save_video(): Frame [0] is an empty image.",
  61992. cimglist_instance);
  61993. const char
  61994. *const _codec = codec && *codec?codec:"h264",
  61995. codec0 = cimg::uppercase(_codec[0]),
  61996. codec1 = _codec[0]?cimg::uppercase(_codec[1]):0,
  61997. codec2 = _codec[1]?cimg::uppercase(_codec[2]):0,
  61998. codec3 = _codec[2]?cimg::uppercase(_codec[3]):0;
  61999. cimg::mutex(9);
  62000. writers[index] = new cv::VideoWriter(filename,_cimg_fourcc(codec0,codec1,codec2,codec3),fps,cv::Size(W,H));
  62001. if (!writers[index]->isOpened()) {
  62002. delete writers[index];
  62003. writers[index] = 0;
  62004. cimg::mutex(9,0);
  62005. throw CImgIOException(_cimglist_instance
  62006. "save_video(): File '%s', unable to initialize video writer with codec '%c%c%c%c'.",
  62007. cimglist_instance,filename,
  62008. codec0,codec1,codec2,codec3);
  62009. }
  62010. CImg<charT>::string(filename).move_to(filenames[index]);
  62011. sizes(index,0) = W;
  62012. sizes(index,1) = H;
  62013. cimg::mutex(9,0);
  62014. }
  62015. if (!is_empty()) {
  62016. const unsigned int W = sizes(index,0), H = sizes(index,1);
  62017. cimg::mutex(9);
  62018. cimglist_for(*this,l) {
  62019. CImg<T> &src = _data[l];
  62020. if (src.is_empty())
  62021. cimg::warn(_cimglist_instance
  62022. "save_video(): Skip empty frame %d for file '%s'.",
  62023. cimglist_instance,l,filename);
  62024. if (src._spectrum>3)
  62025. cimg::warn(_cimglist_instance
  62026. "save_video(): Frame %u has incompatible dimension (%u,%u,%u,%u). "
  62027. "Some image data may be ignored when writing frame into video file '%s'.",
  62028. cimglist_instance,l,src._width,src._height,src._depth,src._spectrum,filename);
  62029. cimg_forZ(src,z) {
  62030. CImg<T> _src = src._depth>1?src.get_slice(z):src.get_shared();
  62031. if (_src._width==W && _src._height==H && _src._spectrum==3)
  62032. writers[index]->write(CImg<ucharT>(_src)._cimg2cvmat());
  62033. else {
  62034. CImg<ucharT> __src(_src,false);
  62035. __src.channels(0,std::min(__src._spectrum - 1,2U)).resize(W,H);
  62036. __src.resize(W,H,1,3,__src._spectrum==1);
  62037. writers[index]->write(__src._cimg2cvmat());
  62038. }
  62039. }
  62040. }
  62041. cimg::mutex(9,0);
  62042. }
  62043. cimg::mutex(9);
  62044. if (!keep_open) {
  62045. delete writers[index];
  62046. writers[index] = 0;
  62047. filenames[index].assign();
  62048. sizes(index,0) = sizes(index,1) = 0;
  62049. last_used_index = -1;
  62050. } else last_used_index = index;
  62051. cimg::mutex(9,0);
  62052. } catch (CImgIOException &e) {
  62053. if (!keep_open) return save_ffmpeg_external(filename,fps);
  62054. throw e;
  62055. }
  62056. return *this;
  62057. #endif
  62058. }
  62059. //! Save image sequence, using the external tool 'ffmpeg'.
  62060. /**
  62061. \param filename Filename to write data to.
  62062. \param fps Number of frames per second.
  62063. \param codec Type of compression.
  62064. \param bitrate Output bitrate
  62065. **/
  62066. const CImgList<T>& save_ffmpeg_external(const char *const filename, const unsigned int fps=25,
  62067. const char *const codec=0, const unsigned int bitrate=2048) const {
  62068. if (!filename)
  62069. throw CImgArgumentException(_cimglist_instance
  62070. "save_ffmpeg_external(): Specified filename is (null).",
  62071. cimglist_instance);
  62072. if (is_empty()) { cimg::fempty(0,filename); return *this; }
  62073. const char
  62074. *const ext = cimg::split_filename(filename),
  62075. *const _codec = codec?codec:
  62076. !cimg::strcasecmp(ext,"flv")?"flv":
  62077. !cimg::strcasecmp(ext,"mp4")?"h264":"mpeg2video";
  62078. CImg<charT> command(1024), filename_tmp(256), filename_tmp2(256);
  62079. CImgList<charT> filenames;
  62080. std::FILE *file = 0;
  62081. cimglist_for(*this,l) if (!_data[l].is_sameXYZ(_data[0]))
  62082. throw CImgInstanceException(_cimglist_instance
  62083. "save_ffmpeg_external(): Invalid instance dimensions for file '%s'.",
  62084. cimglist_instance,
  62085. filename);
  62086. do {
  62087. cimg_snprintf(filename_tmp,filename_tmp._width,"%s%c%s",
  62088. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand());
  62089. cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_000001.ppm",filename_tmp._data);
  62090. if ((file=cimg::std_fopen(filename_tmp2,"rb"))!=0) cimg::fclose(file);
  62091. } while (file);
  62092. unsigned int frame = 1;
  62093. cimglist_for(*this,l) {
  62094. CImg<T>& src = _data[l];
  62095. cimg_forZ(src,z) {
  62096. cimg_snprintf(filename_tmp2,filename_tmp2._width,"%s_%.6u.ppm",filename_tmp._data,frame);
  62097. CImg<charT>::string(filename_tmp2).move_to(filenames);
  62098. CImg<T> _src = src._depth>1?src.get_slice(z):src.get_shared();
  62099. if (_src._width%2 || _src._height%2) // Force output to have an even number of columns and rows
  62100. _src.assign(_src.get_resize(_src._width + (_src._width%2),_src._height + (_src._height%2),1,-100,0),false);
  62101. if (_src._spectrum!=3) // Force output to be one slice, in color
  62102. _src.assign(_src.get_resize(-100,-100,1,3),false);
  62103. _src.save_pnm(filename_tmp2);
  62104. ++frame;
  62105. }
  62106. }
  62107. cimg_snprintf(command,command._width,
  62108. "\"%s\" -framerate %u -v -8 -y -i \"%s_%%6d.ppm\" -pix_fmt yuv420p -vcodec %s -b %uk -r %u \"%s\"",
  62109. cimg::ffmpeg_path(),
  62110. fps,CImg<charT>::string(filename_tmp)._system_strescape().data(),
  62111. _codec,bitrate,fps,
  62112. CImg<charT>::string(filename)._system_strescape().data());
  62113. cimg::system(command,cimg::ffmpeg_path());
  62114. file = cimg::std_fopen(filename,"rb");
  62115. if (!file)
  62116. throw CImgIOException(_cimglist_instance
  62117. "save_ffmpeg_external(): Failed to save file '%s' with external command 'ffmpeg'.",
  62118. cimglist_instance,
  62119. filename);
  62120. else cimg::fclose(file);
  62121. cimglist_for(*this,l) std::remove(filenames[l]);
  62122. return *this;
  62123. }
  62124. //! Serialize a CImgList<T> instance into a raw CImg<unsigned char> buffer.
  62125. /**
  62126. \param is_compressed tells if zlib compression must be used for serialization
  62127. (this requires 'cimg_use_zlib' been enabled).
  62128. \param header_size Reserve empty bytes as a starting header.
  62129. **/
  62130. CImg<ucharT> get_serialize(const bool is_compressed=false, const unsigned int header_size=0) const {
  62131. #ifndef cimg_use_zlib
  62132. if (is_compressed)
  62133. cimg::warn(_cimglist_instance
  62134. "get_serialize(): Unable to compress data unless zlib is enabled, "
  62135. "storing them uncompressed.",
  62136. cimglist_instance);
  62137. #endif
  62138. CImgList<ucharT> stream;
  62139. if (header_size) CImg<ucharT>(1,header_size,1,1,0).move_to(stream);
  62140. CImg<charT> tmpstr(128);
  62141. const char *const ptype = pixel_type(), *const etype = cimg::endianness()?"big":"little";
  62142. cimg_snprintf(tmpstr,tmpstr._width,"%u %s %s_endian\n",_width,ptype,etype);
  62143. CImg<ucharT>::string(tmpstr,false).move_to(stream);
  62144. cimglist_for(*this,l) {
  62145. const CImg<T>& img = _data[l];
  62146. cimg_snprintf(tmpstr,tmpstr._width,"%u %u %u %u",img._width,img._height,img._depth,img._spectrum);
  62147. CImg<ucharT>::string(tmpstr,false).move_to(stream);
  62148. if (img._data) {
  62149. CImg<T> tmp;
  62150. if (cimg::endianness()) { tmp = img; cimg::invert_endianness(tmp._data,tmp.size()); }
  62151. const CImg<T>& ref = cimg::endianness()?tmp:img;
  62152. bool failed_to_compress = true;
  62153. if (is_compressed) {
  62154. #ifdef cimg_use_zlib
  62155. const ulongT siz = sizeof(T)*ref.size();
  62156. uLongf csiz = (ulongT)compressBound(siz);
  62157. Bytef *const cbuf = new Bytef[csiz];
  62158. if (compress(cbuf,&csiz,(Bytef*)ref._data,siz))
  62159. cimg::warn(_cimglist_instance
  62160. "get_serialize(): Failed to save compressed data, saving them uncompressed.",
  62161. cimglist_instance);
  62162. else {
  62163. cimg_snprintf(tmpstr,tmpstr._width," #%lu\n",csiz);
  62164. CImg<ucharT>::string(tmpstr,false).move_to(stream);
  62165. CImg<ucharT>(cbuf,csiz).move_to(stream);
  62166. delete[] cbuf;
  62167. failed_to_compress = false;
  62168. }
  62169. #endif
  62170. }
  62171. if (failed_to_compress) { // Write in a non-compressed way
  62172. CImg<charT>::string("\n",false).move_to(stream);
  62173. stream.insert(1);
  62174. stream.back().
  62175. assign((unsigned char*)ref._data,ref._width,ref._height,ref._depth,ref._spectrum*sizeof(T),true);
  62176. }
  62177. } else CImg<charT>::string("\n",false).move_to(stream);
  62178. }
  62179. // Determine best serialized image dimensions to store the whole buffer.
  62180. ulongT siz = 0;
  62181. cimglist_for(stream,l) siz+=stream[l].size();
  62182. const ulongT max_siz = (ulongT)cimg::type<int>::max();
  62183. const unsigned int
  62184. nw = (unsigned int)(siz/max_siz + ((siz%max_siz)?1:0)),
  62185. nh = (unsigned int)(siz/nw + (siz%nw?1:0));
  62186. CImg<ucharT> res(nw,nh,1,1,0);
  62187. unsigned char *ptr = res.data();
  62188. cimglist_for(stream,l) { siz = stream[l].size(); std::memcpy(ptr,stream[l]._data,siz); ptr+=siz; }
  62189. return res;
  62190. }
  62191. //! Unserialize a CImg<unsigned char> serialized buffer into a CImgList<T> list.
  62192. template<typename t>
  62193. static CImgList<T> get_unserialize(const CImg<t>& buffer, const unsigned int header_size=0) {
  62194. #ifdef cimg_use_zlib
  62195. #define _cimgz_unserialize_case(Tss) { \
  62196. Bytef *cbuf = 0; \
  62197. if (sizeof(t)!=1 || buffer.pixel_type()==cimg::type<bool>::string()) { \
  62198. cbuf = new Bytef[csiz]; Bytef *_cbuf = cbuf; \
  62199. for (ulongT k = 0; k<csiz && stream<estream; ++k) *(_cbuf++) = (Bytef)*(stream++); \
  62200. is_bytef = false; \
  62201. } else { cbuf = (Bytef*)stream; stream+=csiz; is_bytef = true; } \
  62202. raw.assign(W,H,D,C); \
  62203. uLongf destlen = raw.size()*sizeof(Tss); \
  62204. uncompress((Bytef*)raw._data,&destlen,cbuf,csiz); \
  62205. if (!is_bytef) delete[] cbuf; \
  62206. }
  62207. #else
  62208. #define _cimgz_unserialize_case(Tss) \
  62209. throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unable to unserialize compressed data " \
  62210. "unless zlib is enabled.", \
  62211. pixel_type());
  62212. #endif
  62213. #define _cimg_unserialize_case(Ts1,Ts2,Ts3,Tss) \
  62214. if (!loaded && ((Ts1 && !cimg::strcasecmp(Ts1,str_pixeltype)) || \
  62215. (Ts2 && !cimg::strcasecmp(Ts2,str_pixeltype)) || \
  62216. (Ts3 && !cimg::strcasecmp(Ts3,str_pixeltype)))) { \
  62217. for (unsigned int l = 0; l<N; ++l) { \
  62218. j = 0; while ((i=(int)*stream)!='\n' && stream<estream && j<255) { ++stream; tmp[j++] = (char)i; } \
  62219. ++stream; tmp[j] = 0; \
  62220. W = H = D = C = 0; csiz = 0; \
  62221. if ((err = cimg_sscanf(tmp,"%u %u %u %u #" cimg_fuint64,&W,&H,&D,&C,&csiz))<4) \
  62222. throw CImgArgumentException("CImgList<%s>::unserialize(): Invalid specified size (%u,%u,%u,%u) for " \
  62223. "image #%u in serialized buffer.", \
  62224. pixel_type(),W,H,D,C,l); \
  62225. if (W*H*D*C>0) { \
  62226. CImg<Tss> raw; \
  62227. CImg<T> &img = res._data[l]; \
  62228. if (err==5) _cimgz_unserialize_case(Tss) \
  62229. else { \
  62230. raw.assign(W,H,D,C); \
  62231. CImg<ucharT> _raw((unsigned char*)raw._data,W*sizeof(Tss),H,D,C,true); \
  62232. if (sizeof(t)==1) { std::memcpy(_raw,stream,_raw.size()); stream+=_raw.size(); } \
  62233. else cimg_for(_raw,p,unsigned char) *p = (unsigned char)*(stream++); \
  62234. } \
  62235. if (endian!=cimg::endianness()) cimg::invert_endianness(raw._data,raw.size()); \
  62236. raw.move_to(img); \
  62237. } \
  62238. } \
  62239. loaded = true; \
  62240. }
  62241. if (buffer.is_empty())
  62242. throw CImgArgumentException("CImgList<%s>::get_unserialize(): Specified serialized buffer is (null).",
  62243. pixel_type());
  62244. CImgList<T> res;
  62245. const t *stream = buffer._data + header_size, *const estream = buffer._data + buffer.size();
  62246. bool loaded = false, endian = cimg::endianness(), is_bytef = false;
  62247. CImg<charT> tmp(256), str_pixeltype(256), str_endian(256);
  62248. *tmp = *str_pixeltype = *str_endian = 0;
  62249. unsigned int j, N = 0, W, H, D, C;
  62250. uint64T csiz;
  62251. int i, err;
  62252. cimg::unused(is_bytef);
  62253. do {
  62254. j = 0; while ((i=(int)*stream)!='\n' && stream<estream && j<255) { ++stream; tmp[j++] = (char)i; }
  62255. ++stream; tmp[j] = 0;
  62256. } while (*tmp=='#' && stream<estream);
  62257. err = cimg_sscanf(tmp,"%u%*c%255[A-Za-z123468_]%*c%255[sA-Za-z_ ]",
  62258. &N,str_pixeltype._data,str_endian._data);
  62259. if (err<2)
  62260. throw CImgArgumentException("CImgList<%s>::get_unserialize(): CImg header not found in serialized buffer.",
  62261. pixel_type());
  62262. if (!cimg::strncasecmp("little",str_endian,6)) endian = false;
  62263. else if (!cimg::strncasecmp("big",str_endian,3)) endian = true;
  62264. res.assign(N);
  62265. _cimg_unserialize_case("bool",0,0,cimg_uint8);
  62266. _cimg_unserialize_case("uint8","unsigned_char","uchar",cimg_uint8);
  62267. _cimg_unserialize_case("int8",0,0,cimg_int8);
  62268. _cimg_unserialize_case("char",0,0,char);
  62269. _cimg_unserialize_case("uint16","unsigned_short","ushort",cimg_uint16);
  62270. _cimg_unserialize_case("int16","short",0,cimg_int16);
  62271. _cimg_unserialize_case("uint32","unsigned_int","uint",cimg_uint32);
  62272. _cimg_unserialize_case("int32","int",0,cimg_int32);
  62273. _cimg_unserialize_case("uint64","unsigned_int64",0,cimg_uint64);
  62274. _cimg_unserialize_case("int64",0,0,cimg_int64);
  62275. _cimg_unserialize_case("float32","float",0,cimg_float32);
  62276. _cimg_unserialize_case("float64","double",0,cimg_float64);
  62277. if (!loaded)
  62278. throw CImgArgumentException("CImgList<%s>::get_unserialize(): Unsupported pixel type '%s' defined "
  62279. "in serialized buffer.",
  62280. pixel_type(),str_pixeltype._data);
  62281. return res;
  62282. }
  62283. //@}
  62284. //----------------------------------
  62285. //
  62286. //! \name Others
  62287. //@{
  62288. //----------------------------------
  62289. //! Return a CImg pre-defined font with requested height.
  62290. /**
  62291. \param font_height Height of the desired font (exact match for 13,23,53,103).
  62292. \param is_variable_width Decide if the font has a variable (\c true) or fixed (\c false) width.
  62293. **/
  62294. static const CImgList<ucharT>& font(const unsigned int requested_height, const bool is_variable_width=true) {
  62295. if (!requested_height) return CImgList<ucharT>::const_empty();
  62296. cimg::mutex(11);
  62297. static const unsigned char font_resizemap[] = {
  62298. 0, 4, 7, 9, 11, 13, 15, 17, 19, 21, 22, 24, 26, 27, 29, 30,
  62299. 32, 33, 35, 36, 38, 39, 41, 42, 43, 45, 46, 47, 49, 50, 51, 52,
  62300. 54, 55, 56, 58, 59, 60, 61, 62, 64, 65, 66, 67, 68, 69, 71, 72,
  62301. 73, 74, 75, 76, 77, 78, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
  62302. 90, 91, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106,
  62303. 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122,
  62304. 123, 124, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137,
  62305. 138, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 148, 149, 150, 151,
  62306. 152, 153, 154, 155, 156, 157, 157, 158, 159, 160, 161, 162, 163, 164, 164, 165,
  62307. 166, 167, 168, 169, 170, 170, 171, 172, 173, 174, 175, 176, 176, 177, 178, 179,
  62308. 180, 181, 181, 182, 183, 184, 185, 186, 186, 187, 188, 189, 190, 191, 191, 192,
  62309. 193, 194, 195, 196, 196, 197, 198, 199, 200, 200, 201, 202, 203, 204, 205, 205,
  62310. 206, 207, 208, 209, 209, 210, 211, 212, 213, 213, 214, 215, 216, 216, 217, 218,
  62311. 219, 220, 220, 221, 222, 223, 224, 224, 225, 226, 227, 227, 228, 229, 230, 231,
  62312. 231, 232, 233, 234, 234, 235, 236, 237, 238, 238, 239, 240, 241, 241, 242, 243,
  62313. 244, 244, 245, 246, 247, 247, 248, 249, 250, 250, 251, 252, 253, 253, 254, 255 };
  62314. static const char *const *font_data[] = {
  62315. cimg::data_font_small,
  62316. cimg::data_font_normal,
  62317. cimg::data_font_large,
  62318. cimg::data_font_huge };
  62319. static const unsigned int
  62320. font_width[] = { 10,26,52,104 },
  62321. font_height[] = { 13,32,64,128 },
  62322. font_M[] = { 86,91,91,47 },
  62323. font_chunk[] = { sizeof(cimg::data_font_small)/sizeof(char*),
  62324. sizeof(cimg::data_font_normal)/sizeof(char*),
  62325. sizeof(cimg::data_font_large)/sizeof(char*),
  62326. sizeof(cimg::data_font_huge)/sizeof(char*) };
  62327. static const unsigned char font_is_binary[] = { 1,0,0,1 };
  62328. static CImg<ucharT> font_base[4];
  62329. unsigned int ind =
  62330. requested_height<=font_height[0]?0U:
  62331. requested_height<=font_height[1]?1U:
  62332. requested_height<=font_height[2]?2U:3U;
  62333. // Decompress nearest base font data if needed.
  62334. CImg<ucharT> &basef = font_base[ind];
  62335. if (!basef) {
  62336. basef.assign(256*font_width[ind],font_height[ind]);
  62337. unsigned char *ptrd = basef;
  62338. const unsigned char *const ptrde = basef.end();
  62339. // Recompose font data from several chunks, to deal with MS compiler limit with big strings (64 Kb).
  62340. CImg<char> dataf;
  62341. for (unsigned int k = 0; k<font_chunk[ind]; ++k)
  62342. dataf.append(CImg<char>::string(font_data[ind][k],k==font_chunk[ind] - 1,true),'x');
  62343. // Uncompress font data (decode RLE).
  62344. const unsigned int M = font_M[ind];
  62345. if (font_is_binary[ind])
  62346. for (const char *ptrs = dataf; *ptrs; ++ptrs) {
  62347. const int _n = (int)(*ptrs - M - 32), v = _n>=0?255:0, n = _n>=0?_n:-_n;
  62348. if (ptrd + n<=ptrde) { std::memset(ptrd,v,n); ptrd+=n; }
  62349. else { std::memset(ptrd,v,ptrde - ptrd); break; }
  62350. }
  62351. else
  62352. for (const char *ptrs = dataf; *ptrs; ++ptrs) {
  62353. int n = (int)*ptrs - M - 32, v = 0;
  62354. if (n>=0) { v = 85*n; n = 1; }
  62355. else {
  62356. n = -n;
  62357. v = (int)*(++ptrs) - M - 32;
  62358. if (v<0) { v = 0; --ptrs; } else v*=85;
  62359. }
  62360. if (ptrd + n<=ptrde) { std::memset(ptrd,v,n); ptrd+=n; }
  62361. else { std::memset(ptrd,v,ptrde - ptrd); break; }
  62362. }
  62363. }
  62364. // Find optimal font cache location to return.
  62365. static CImgList<ucharT> fonts[16];
  62366. static bool is_variable_widths[16] = {};
  62367. ind = ~0U;
  62368. for (int i = 0; i<16; ++i)
  62369. if (!fonts[i] || (is_variable_widths[i]==is_variable_width && requested_height==fonts[i][0]._height)) {
  62370. ind = (unsigned int)i; break; // Found empty slot or cached font
  62371. }
  62372. if (ind==~0U) { // No empty slots nor existing font in cache
  62373. fonts->assign();
  62374. std::memmove((void*)fonts,(void*)(fonts + 1),15*sizeof(CImgList<ucharT>));
  62375. std::memmove(is_variable_widths,is_variable_widths + 1,15*sizeof(bool));
  62376. std::memset((void*)(fonts + (ind=15)),0,sizeof(CImgList<ucharT>)); // Free a slot in cache for new font
  62377. }
  62378. CImgList<ucharT> &font = fonts[ind];
  62379. // Render requested font.
  62380. if (!font) {
  62381. is_variable_widths[ind] = is_variable_width;
  62382. basef.get_split('x',256).move_to(font);
  62383. if (requested_height!=font[0]._height)
  62384. cimglist_for(font,l) {
  62385. font[l].resize(std::max(1U,font[l]._width*requested_height/font[l]._height),requested_height,-100,-100,5);
  62386. cimg_for(font[l],ptr,ucharT) *ptr = font_resizemap[*ptr];
  62387. }
  62388. if (is_variable_width) { // Crop font
  62389. cimglist_for(font,l) {
  62390. CImg<ucharT>& letter = font[l];
  62391. int xmin = letter.width(), xmax = 0;
  62392. cimg_forX(letter,x) { // Find xmin
  62393. cimg_forY(letter,y) if (letter(x,y)) { xmin = x; break; }
  62394. if (xmin!=letter.width()) break;
  62395. }
  62396. cimg_rofX(letter,x) { // Find xmax
  62397. cimg_forY(letter,y) if (letter(x,y)) { xmax = x; break; }
  62398. if (xmax) break;
  62399. }
  62400. if (xmin<=xmax) letter.crop(xmin,0,xmax,letter._height - 1);
  62401. }
  62402. font[(int)' '].resize(font[(int)'f']._width,-100,-100,-100,0);
  62403. if (' ' + 256<font.size()) font[' ' + 256].resize(font[(int)'f']._width,-100,-100,-100,0);
  62404. }
  62405. font.insert(256,0);
  62406. cimglist_for_in(font,0,255,l) font[l].assign(font[l + 256]._width,font[l + 256]._height,1,1,255);
  62407. }
  62408. cimg::mutex(11,0);
  62409. return font;
  62410. }
  62411. //! Compute a 1D Fast Fourier Transform, along specified axis.
  62412. /**
  62413. \param axis Axis along which the Fourier transform is computed.
  62414. \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed.
  62415. **/
  62416. CImgList<T>& FFT(const char axis, const bool invert=false) {
  62417. if (is_empty()) return *this;
  62418. if (_width==1) insert(1);
  62419. if (_width>2)
  62420. cimg::warn(_cimglist_instance
  62421. "FFT(): Instance has more than 2 images",
  62422. cimglist_instance);
  62423. CImg<T>::FFT(_data[0],_data[1],axis,invert);
  62424. return *this;
  62425. }
  62426. //! Compute a 1-D Fast Fourier Transform, along specified axis \newinstance.
  62427. CImgList<Tfloat> get_FFT(const char axis, const bool invert=false) const {
  62428. return CImgList<Tfloat>(*this,false).FFT(axis,invert);
  62429. }
  62430. //! Compute n-D Fast Fourier Transform.
  62431. /**
  62432. \param invert Tells if the direct (\c false) or inverse transform (\c true) is computed.
  62433. **/
  62434. CImgList<T>& FFT(const bool invert=false) {
  62435. if (is_empty()) return *this;
  62436. if (_width==1) insert(1);
  62437. if (_width>2)
  62438. cimg::warn(_cimglist_instance
  62439. "FFT(): Instance has more than 2 images",
  62440. cimglist_instance);
  62441. CImg<T>::FFT(_data[0],_data[1],invert);
  62442. return *this;
  62443. }
  62444. //! Compute n-D Fast Fourier Transform \newinstance.
  62445. CImgList<Tfloat> get_FFT(const bool invert=false) const {
  62446. return CImgList<Tfloat>(*this,false).FFT(invert);
  62447. }
  62448. //! Reverse primitives orientations of a 3D object.
  62449. /**
  62450. **/
  62451. CImgList<T>& reverse_object3d() {
  62452. cimglist_for(*this,l) {
  62453. CImg<T>& p = _data[l];
  62454. switch (p.size()) {
  62455. case 2 : case 3: cimg::swap(p[0],p[1]); break;
  62456. case 6 : cimg::swap(p[0],p[1],p[2],p[4],p[3],p[5]); break;
  62457. case 9 : cimg::swap(p[0],p[1],p[3],p[5],p[4],p[6]); break;
  62458. case 4 : cimg::swap(p[0],p[1],p[2],p[3]); break;
  62459. 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;
  62460. }
  62461. }
  62462. return *this;
  62463. }
  62464. //! Reverse primitives orientations of a 3D object \newinstance.
  62465. CImgList<T> get_reverse_object3d() const {
  62466. return (+*this).reverse_object3d();
  62467. }
  62468. //@}
  62469. }; // struct CImgList { ...
  62470. // Completion of previously declared functions
  62471. //--------------------------------------------
  62472. namespace cimg {
  62473. // Functions to return standard streams 'stdin', 'stdout' and 'stderr'.
  62474. // (throw a CImgIOException when macro 'cimg_use_r' is defined).
  62475. inline FILE* _stdin(const bool throw_exception) {
  62476. #ifndef cimg_use_r
  62477. cimg::unused(throw_exception);
  62478. return stdin;
  62479. #else
  62480. if (throw_exception) {
  62481. cimg::exception_mode(0);
  62482. throw CImgIOException("cimg::stdin(): Reference to 'stdin' stream not allowed in R mode "
  62483. "('cimg_use_r' is defined).");
  62484. }
  62485. return 0;
  62486. #endif
  62487. }
  62488. inline FILE* _stdout(const bool throw_exception) {
  62489. #ifndef cimg_use_r
  62490. cimg::unused(throw_exception);
  62491. return stdout;
  62492. #else
  62493. if (throw_exception) {
  62494. cimg::exception_mode(0);
  62495. throw CImgIOException("cimg::stdout(): Reference to 'stdout' stream not allowed in R mode "
  62496. "('cimg_use_r' is defined).");
  62497. }
  62498. return 0;
  62499. #endif
  62500. }
  62501. inline FILE* _stderr(const bool throw_exception) {
  62502. #ifndef cimg_use_r
  62503. cimg::unused(throw_exception);
  62504. return stderr;
  62505. #else
  62506. if (throw_exception) {
  62507. cimg::exception_mode(0);
  62508. throw CImgIOException("cimg::stderr(): Reference to 'stderr' stream not allowed in R mode "
  62509. "('cimg_use_r' is defined).");
  62510. }
  62511. return 0;
  62512. #endif
  62513. }
  62514. // Open a file (similar to std:: fopen(), but with wide character support on Windows).
  62515. inline std::FILE *std_fopen(const char *const path, const char *const mode) {
  62516. std::FILE *const res = std::fopen(path,mode);
  62517. if (res) return res;
  62518. #if cimg_OS==2
  62519. // Try alternative method, with wide-character string.
  62520. int err = MultiByteToWideChar(CP_UTF8,0,path,-1,0,0);
  62521. if (err) {
  62522. CImg<wchar_t> wpath((unsigned int)err);
  62523. err = MultiByteToWideChar(CP_UTF8,0,path,-1,wpath,err);
  62524. if (err) { // Convert 'mode' to a wide-character string
  62525. err = MultiByteToWideChar(CP_UTF8,0,mode,-1,0,0);
  62526. if (err) {
  62527. CImg<wchar_t> wmode((unsigned int)err);
  62528. if (MultiByteToWideChar(CP_UTF8,0,mode,-1,wmode,err))
  62529. return _wfopen(wpath,wmode);
  62530. }
  62531. }
  62532. }
  62533. #endif
  62534. return 0;
  62535. }
  62536. //! Search path of an executable (Windows only).
  62537. #if cimg_OS==2
  62538. inline bool win_searchpath(const char *const exec_name, char *const res, const unsigned int size_res) {
  62539. char *ptr = 0;
  62540. DWORD err = SearchPathA(0,exec_name,0,size_res,res,&ptr);
  62541. return err!=0;
  62542. }
  62543. #endif
  62544. //! Get the file or directory attributes with support for UTF-8 paths (Windows only).
  62545. #if cimg_OS==2
  62546. inline DWORD win_getfileattributes(const char *const path) {
  62547. DWORD res = GetFileAttributesA(path);
  62548. if (res==INVALID_FILE_ATTRIBUTES) {
  62549. // Try alternative method, with wide-character string.
  62550. int err = MultiByteToWideChar(CP_UTF8,0,path,-1,0,0);
  62551. if (err) {
  62552. CImg<wchar_t> wpath((unsigned int)err);
  62553. if (MultiByteToWideChar(CP_UTF8,0,path,-1,wpath,err)) res = GetFileAttributesW(wpath);
  62554. }
  62555. }
  62556. return res;
  62557. }
  62558. #endif
  62559. //! Get/set path to the <i>Program Files/</i> directory (Windows only).
  62560. /**
  62561. \param user_path Specified path, or \c 0 to get the path currently used.
  62562. \param reinit_path Force path to be recalculated (may take some time).
  62563. \return Path containing the program files.
  62564. **/
  62565. #if cimg_OS==2
  62566. inline const char* win_programfiles_path(const char *const user_path=0, const bool reinit_path=false) {
  62567. static CImg<char> s_path;
  62568. cimg::mutex(7);
  62569. if (reinit_path) s_path.assign();
  62570. if (user_path) {
  62571. if (!s_path) s_path.assign(1024);
  62572. std::strncpy(s_path,user_path,1023);
  62573. } else if (!s_path) {
  62574. s_path.assign(MAX_PATH);
  62575. *s_path = 0;
  62576. // Note: in the following line, 0x26 = CSIDL_PROGRAM_FILES (not defined on every compiler).
  62577. #if !defined(__INTEL_COMPILER)
  62578. if (!SHGetSpecialFolderPathA(0,s_path,0x0026,false)) {
  62579. const char *const pfPath = std::getenv("PROGRAMFILES");
  62580. if (pfPath) std::strncpy(s_path,pfPath,MAX_PATH - 1);
  62581. else std::strcpy(s_path,"C:\\PROGRA~1");
  62582. }
  62583. #else
  62584. std::strcpy(s_path,"C:\\PROGRA~1");
  62585. #endif
  62586. }
  62587. cimg::mutex(7,0);
  62588. return s_path;
  62589. }
  62590. #endif
  62591. //! Get/set path to the \c curl binary.
  62592. /**
  62593. \param user_path Specified path, or \c 0 to get the path currently used.
  62594. \param reinit_path Force path to be recalculated (may take some time).
  62595. \return Path containing the \c curl binary.
  62596. **/
  62597. inline const char *curl_path(const char *const user_path, const bool reinit_path) {
  62598. static CImg<char> s_path;
  62599. cimg::mutex(7);
  62600. if (reinit_path) s_path.assign();
  62601. if (user_path) {
  62602. if (!s_path) s_path.assign(1024);
  62603. std::strncpy(s_path,user_path,1023);
  62604. } else if (!s_path) {
  62605. s_path.assign(1024);
  62606. bool path_found = false;
  62607. std::FILE *file = 0;
  62608. #if cimg_OS==2
  62609. if (win_searchpath("curl.exe",s_path,s_path._width)) path_found = true;
  62610. if (!path_found) {
  62611. std::strcpy(s_path,".\\curl.exe");
  62612. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62613. }
  62614. if (!path_found) std::strcpy(s_path,"curl.exe");
  62615. #else
  62616. if (!path_found) {
  62617. std::strcpy(s_path,"./curl");
  62618. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62619. }
  62620. if (!path_found) std::strcpy(s_path,"curl");
  62621. #endif
  62622. winformat_string(s_path);
  62623. }
  62624. cimg::mutex(7,0);
  62625. return s_path;
  62626. }
  62627. //! Get/set path to the \c dcraw binary.
  62628. /**
  62629. \param user_path Specified path, or \c 0 to get the path currently used.
  62630. \param reinit_path Force path to be recalculated (may take some time).
  62631. \return Path containing the \c dcraw binary.
  62632. **/
  62633. inline const char *dcraw_path(const char *const user_path, const bool reinit_path) {
  62634. static CImg<char> s_path;
  62635. cimg::mutex(7);
  62636. if (reinit_path) s_path.assign();
  62637. if (user_path) {
  62638. if (!s_path) s_path.assign(1024);
  62639. std::strncpy(s_path,user_path,1023);
  62640. } else if (!s_path) {
  62641. s_path.assign(1024);
  62642. bool path_found = false;
  62643. std::FILE *file = 0;
  62644. #if cimg_OS==2
  62645. if (win_searchpath("dcraw.exe",s_path,s_path._width)) path_found = true;
  62646. if (!path_found) {
  62647. std::strcpy(s_path,".\\dcraw.exe");
  62648. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62649. }
  62650. if (!path_found) std::strcpy(s_path,"dcraw.exe");
  62651. #else
  62652. if (!path_found) {
  62653. std::strcpy(s_path,"./dcraw");
  62654. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62655. }
  62656. if (!path_found) std::strcpy(s_path,"dcraw");
  62657. #endif
  62658. winformat_string(s_path);
  62659. }
  62660. cimg::mutex(7,0);
  62661. return s_path;
  62662. }
  62663. //! Get/set path to the FFMPEG's \c ffmpeg binary.
  62664. /**
  62665. \param user_path Specified path, or \c 0 to get the path currently used.
  62666. \param reinit_path Force path to be recalculated (may take some time).
  62667. \return Path containing the \c ffmpeg binary.
  62668. **/
  62669. inline const char *ffmpeg_path(const char *const user_path, const bool reinit_path) {
  62670. static CImg<char> s_path;
  62671. cimg::mutex(7);
  62672. if (reinit_path) s_path.assign();
  62673. if (user_path) {
  62674. if (!s_path) s_path.assign(1024);
  62675. std::strncpy(s_path,user_path,1023);
  62676. } else if (!s_path) {
  62677. s_path.assign(1024);
  62678. bool path_found = false;
  62679. std::FILE *file = 0;
  62680. #if cimg_OS==2
  62681. if (win_searchpath("ffmpeg.exe",s_path,s_path._width)) path_found = true;
  62682. if (!path_found) {
  62683. std::strcpy(s_path,".\\ffmpeg.exe");
  62684. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62685. }
  62686. if (!path_found) std::strcpy(s_path,"ffmpeg.exe");
  62687. #else
  62688. if (!path_found) {
  62689. std::strcpy(s_path,"./ffmpeg");
  62690. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62691. }
  62692. if (!path_found) std::strcpy(s_path,"ffmpeg");
  62693. #endif
  62694. winformat_string(s_path);
  62695. }
  62696. cimg::mutex(7,0);
  62697. return s_path;
  62698. }
  62699. //! Get/set path to the GraphicsMagick's \c gm binary.
  62700. /**
  62701. \param user_path Specified path, or \c 0 to get the path currently used.
  62702. \param reinit_path Force path to be recalculated (may take some time).
  62703. \return Path containing the \c gm binary.
  62704. **/
  62705. inline const char* graphicsmagick_path(const char *const user_path, const bool reinit_path) {
  62706. static CImg<char> s_path;
  62707. cimg::mutex(7);
  62708. if (reinit_path) s_path.assign();
  62709. if (user_path) {
  62710. if (!s_path) s_path.assign(1024);
  62711. std::strncpy(s_path,user_path,1023);
  62712. } else if (!s_path) {
  62713. s_path.assign(1024);
  62714. bool path_found = false;
  62715. std::FILE *file = 0;
  62716. #if cimg_OS==2
  62717. if (win_searchpath("gm.exe",s_path,s_path._width)) path_found = true;
  62718. const char *const pf_path = win_programfiles_path();
  62719. if (!path_found) {
  62720. std::strcpy(s_path,".\\gm.exe");
  62721. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62722. }
  62723. for (int k = 32; k>=10 && !path_found; --k) {
  62724. cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%.2d-\\gm.exe",pf_path,k);
  62725. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62726. }
  62727. for (int k = 9; k>=0 && !path_found; --k) {
  62728. cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d-Q\\gm.exe",pf_path,k);
  62729. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62730. }
  62731. for (int k = 32; k>=0 && !path_found; --k) {
  62732. cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d\\gm.exe",pf_path,k);
  62733. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62734. }
  62735. for (int k = 32; k>=10 && !path_found; --k) {
  62736. cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",pf_path,k);
  62737. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62738. }
  62739. for (int k = 9; k>=0 && !path_found; --k) {
  62740. cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",pf_path,k);
  62741. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62742. }
  62743. for (int k = 32; k>=0 && !path_found; --k) {
  62744. cimg_snprintf(s_path,s_path._width,"%s\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",pf_path,k);
  62745. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62746. }
  62747. for (int k = 32; k>=10 && !path_found; --k) {
  62748. cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%.2d-\\gm.exe",k);
  62749. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62750. }
  62751. for (int k = 9; k>=0 && !path_found; --k) {
  62752. cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d-Q\\gm.exe",k);
  62753. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62754. }
  62755. for (int k = 32; k>=0 && !path_found; --k) {
  62756. cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d\\gm.exe",k);
  62757. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62758. }
  62759. for (int k = 32; k>=10 && !path_found; --k) {
  62760. cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
  62761. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62762. }
  62763. for (int k = 9; k>=0 && !path_found; --k) {
  62764. cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
  62765. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62766. }
  62767. for (int k = 32; k>=0 && !path_found; --k) {
  62768. cimg_snprintf(s_path,s_path._width,"C:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
  62769. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62770. }
  62771. for (int k = 32; k>=10 && !path_found; --k) {
  62772. cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%.2d-\\gm.exe",k);
  62773. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62774. }
  62775. for (int k = 9; k>=0 && !path_found; --k) {
  62776. cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d-Q\\gm.exe",k);
  62777. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62778. }
  62779. for (int k = 32; k>=0 && !path_found; --k) {
  62780. cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d\\gm.exe",k);
  62781. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62782. }
  62783. for (int k = 32; k>=10 && !path_found; --k) {
  62784. cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%.2d-\\VISUA~1\\BIN\\gm.exe",k);
  62785. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62786. }
  62787. for (int k = 9; k>=0 && !path_found; --k) {
  62788. cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d-Q\\VISUA~1\\BIN\\gm.exe",k);
  62789. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62790. }
  62791. for (int k = 32; k>=0 && !path_found; --k) {
  62792. cimg_snprintf(s_path,s_path._width,"D:\\GRAPHI~1.%d\\VISUA~1\\BIN\\gm.exe",k);
  62793. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62794. }
  62795. if (!path_found) std::strcpy(s_path,"gm.exe");
  62796. #else
  62797. if (!path_found) {
  62798. std::strcpy(s_path,"./gm");
  62799. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62800. }
  62801. if (!path_found) std::strcpy(s_path,"gm");
  62802. #endif
  62803. winformat_string(s_path);
  62804. }
  62805. cimg::mutex(7,0);
  62806. return s_path;
  62807. }
  62808. //! Get/set path to the \c gunzip binary.
  62809. /**
  62810. \param user_path Specified path, or \c 0 to get the path currently used.
  62811. \param reinit_path Force path to be recalculated (may take some time).
  62812. \return Path containing the \c gunzip binary.
  62813. **/
  62814. inline const char *gunzip_path(const char *const user_path, const bool reinit_path) {
  62815. static CImg<char> s_path;
  62816. cimg::mutex(7);
  62817. if (reinit_path) s_path.assign();
  62818. if (user_path) {
  62819. if (!s_path) s_path.assign(1024);
  62820. std::strncpy(s_path,user_path,1023);
  62821. } else if (!s_path) {
  62822. s_path.assign(1024);
  62823. bool path_found = false;
  62824. std::FILE *file = 0;
  62825. #if cimg_OS==2
  62826. if (win_searchpath("gunzip.exe",s_path,s_path._width)) path_found = true;
  62827. if (!path_found) {
  62828. std::strcpy(s_path,".\\gunzip.exe");
  62829. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62830. }
  62831. if (!path_found) std::strcpy(s_path,"gunzip.exe");
  62832. #else
  62833. if (!path_found) {
  62834. std::strcpy(s_path,"./gunzip");
  62835. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62836. }
  62837. if (!path_found) std::strcpy(s_path,"gunzip");
  62838. #endif
  62839. winformat_string(s_path);
  62840. }
  62841. cimg::mutex(7,0);
  62842. return s_path;
  62843. }
  62844. //! Get/set path to the \c gzip binary.
  62845. /**
  62846. \param user_path Specified path, or \c 0 to get the path currently used.
  62847. \param reinit_path Force path to be recalculated (may take some time).
  62848. \return Path containing the \c gzip binary.
  62849. **/
  62850. inline const char *gzip_path(const char *const user_path, const bool reinit_path) {
  62851. static CImg<char> s_path;
  62852. cimg::mutex(7);
  62853. if (reinit_path) s_path.assign();
  62854. if (user_path) {
  62855. if (!s_path) s_path.assign(1024);
  62856. std::strncpy(s_path,user_path,1023);
  62857. } else if (!s_path) {
  62858. s_path.assign(1024);
  62859. bool path_found = false;
  62860. std::FILE *file = 0;
  62861. #if cimg_OS==2
  62862. if (win_searchpath("gzip.exe",s_path,s_path._width)) path_found = true;
  62863. if (!path_found) {
  62864. std::strcpy(s_path,".\\gzip.exe");
  62865. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62866. }
  62867. if (!path_found) std::strcpy(s_path,"gzip.exe");
  62868. #else
  62869. if (!path_found) {
  62870. std::strcpy(s_path,"./gzip");
  62871. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62872. }
  62873. if (!path_found) std::strcpy(s_path,"gzip");
  62874. #endif
  62875. winformat_string(s_path);
  62876. }
  62877. cimg::mutex(7,0);
  62878. return s_path;
  62879. }
  62880. //! Get/set path to the ImageMagick's \c convert binary.
  62881. /**
  62882. \param user_path Specified path, or \c 0 to get the path currently used.
  62883. \param reinit_path Force path to be recalculated (may take some time).
  62884. \return Path containing the \c convert binary.
  62885. **/
  62886. inline const char* imagemagick_path(const char *const user_path, const bool reinit_path) {
  62887. static CImg<char> s_path;
  62888. cimg::mutex(7);
  62889. if (reinit_path) s_path.assign();
  62890. if (user_path) {
  62891. if (!s_path) s_path.assign(1024);
  62892. std::strncpy(s_path,user_path,1023);
  62893. } else if (!s_path) {
  62894. s_path.assign(1024);
  62895. bool path_found = false;
  62896. std::FILE *file = 0;
  62897. #if cimg_OS==2
  62898. if (win_searchpath("magick.exe",s_path,s_path._width)) path_found = true;
  62899. const char *const pf_path = win_programfiles_path();
  62900. for (int l = 0; l<2 && !path_found; ++l) {
  62901. const char *const s_exe = l?"convert":"magick";
  62902. cimg_snprintf(s_path,s_path._width,".\\%s.exe",s_exe);
  62903. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62904. for (int k = 32; k>=10 && !path_found; --k) {
  62905. cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\%s.exe",pf_path,k,s_exe);
  62906. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62907. }
  62908. for (int k = 9; k>=0 && !path_found; --k) {
  62909. cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\%s.exe",pf_path,k,s_exe);
  62910. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62911. }
  62912. for (int k = 32; k>=0 && !path_found; --k) {
  62913. cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\%s.exe",pf_path,k,s_exe);
  62914. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62915. }
  62916. for (int k = 32; k>=10 && !path_found; --k) {
  62917. cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
  62918. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62919. }
  62920. for (int k = 9; k>=0 && !path_found; --k) {
  62921. cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
  62922. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62923. }
  62924. for (int k = 32; k>=0 && !path_found; --k) {
  62925. cimg_snprintf(s_path,s_path._width,"%s\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",pf_path,k,s_exe);
  62926. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62927. }
  62928. for (int k = 32; k>=10 && !path_found; --k) {
  62929. cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe);
  62930. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62931. }
  62932. for (int k = 9; k>=0 && !path_found; --k) {
  62933. cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe);
  62934. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62935. }
  62936. for (int k = 32; k>=0 && !path_found; --k) {
  62937. cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\%s.exe",k,s_exe);
  62938. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62939. }
  62940. for (int k = 32; k>=10 && !path_found; --k) {
  62941. cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe);
  62942. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62943. }
  62944. for (int k = 9; k>=0 && !path_found; --k) {
  62945. cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe);
  62946. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62947. }
  62948. for (int k = 32; k>=0 && !path_found; --k) {
  62949. cimg_snprintf(s_path,s_path._width,"C:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe);
  62950. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62951. }
  62952. for (int k = 32; k>=10 && !path_found; --k) {
  62953. cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\%s.exe",k,s_exe);
  62954. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62955. }
  62956. for (int k = 9; k>=0 && !path_found; --k) {
  62957. cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\%s.exe",k,s_exe);
  62958. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62959. }
  62960. for (int k = 32; k>=0 && !path_found; --k) {
  62961. cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\%s.exe",k,s_exe);
  62962. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62963. }
  62964. for (int k = 32; k>=10 && !path_found; --k) {
  62965. cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%.2d-\\VISUA~1\\BIN\\%s.exe",k,s_exe);
  62966. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62967. }
  62968. for (int k = 9; k>=0 && !path_found; --k) {
  62969. cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d-Q\\VISUA~1\\BIN\\%s.exe",k,s_exe);
  62970. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62971. }
  62972. for (int k = 32; k>=0 && !path_found; --k) {
  62973. cimg_snprintf(s_path,s_path._width,"D:\\IMAGEM~1.%d\\VISUA~1\\BIN\\%s.exe",k,s_exe);
  62974. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62975. }
  62976. if (!path_found) cimg_snprintf(s_path,s_path._width,"%s.exe",s_exe);
  62977. }
  62978. #else
  62979. std::strcpy(s_path,"./magick");
  62980. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62981. if (!path_found) {
  62982. std::strcpy(s_path,"./convert");
  62983. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  62984. }
  62985. if (!path_found) std::strcpy(s_path,"convert");
  62986. #endif
  62987. winformat_string(s_path);
  62988. }
  62989. cimg::mutex(7,0);
  62990. return s_path;
  62991. }
  62992. //! Get/set path to the Medcon's \c medcon binary.
  62993. /**
  62994. \param user_path Specified path, or \c 0 to get the path currently used.
  62995. \param reinit_path Force path to be recalculated (may take some time).
  62996. \return Path containing the \c medcon binary.
  62997. **/
  62998. inline const char* medcon_path(const char *const user_path, const bool reinit_path) {
  62999. static CImg<char> s_path;
  63000. cimg::mutex(7);
  63001. if (reinit_path) s_path.assign();
  63002. if (user_path) {
  63003. if (!s_path) s_path.assign(1024);
  63004. std::strncpy(s_path,user_path,1023);
  63005. } else if (!s_path) {
  63006. s_path.assign(1024);
  63007. bool path_found = false;
  63008. std::FILE *file = 0;
  63009. #if cimg_OS==2
  63010. if (win_searchpath("medcon.exe",s_path,s_path._width)) path_found = true;
  63011. const char *const pf_path = win_programfiles_path();
  63012. if (!path_found) {
  63013. std::strcpy(s_path,".\\medcon.exe");
  63014. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  63015. }
  63016. if (!path_found) {
  63017. cimg_snprintf(s_path,s_path._width,"%s\\XMedCon\\bin\\medcon.bat",pf_path);
  63018. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  63019. }
  63020. if (!path_found) {
  63021. cimg_snprintf(s_path,s_path._width,"%s\\XMedCon\\bin\\medcon.exe",pf_path);
  63022. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  63023. }
  63024. if (!path_found) {
  63025. std::strcpy(s_path,"C:\\XMedCon\\bin\\medcon.exe");
  63026. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  63027. }
  63028. if (!path_found) std::strcpy(s_path,"medcon.exe");
  63029. #else
  63030. if (!path_found) {
  63031. std::strcpy(s_path,"./medcon");
  63032. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  63033. }
  63034. if (!path_found) std::strcpy(s_path,"medcon");
  63035. #endif
  63036. winformat_string(s_path);
  63037. }
  63038. cimg::mutex(7,0);
  63039. return s_path;
  63040. }
  63041. //! Get/set path to store temporary files.
  63042. /**
  63043. \param user_path Specified path, or \c 0 to get the path currently used.
  63044. \param reinit_path Force path to be recalculated (may take some time).
  63045. \return Path where temporary files can be saved.
  63046. **/
  63047. inline const char* temporary_path(const char *const user_path, const bool reinit_path) {
  63048. #define _cimg_test_temporary_path(p) \
  63049. if (!path_found) { \
  63050. cimg_snprintf(s_path,s_path._width,"%s",p); \
  63051. cimg_snprintf(tmp,tmp._width,"%s%c%s",s_path.data(),cimg_file_separator,filename_tmp._data); \
  63052. if ((file=cimg::std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; } \
  63053. }
  63054. static CImg<char> s_path;
  63055. cimg::mutex(7);
  63056. if (reinit_path) s_path.assign();
  63057. if (user_path) {
  63058. if (!s_path) s_path.assign(1024);
  63059. std::strncpy(s_path,user_path,1023);
  63060. } else if (!s_path) {
  63061. s_path.assign(1024);
  63062. bool path_found = false;
  63063. CImg<char> tmp(1024), filename_tmp(256);
  63064. std::FILE *file = 0;
  63065. cimg_snprintf(filename_tmp,filename_tmp._width,"%s.tmp",cimg::filenamerand());
  63066. char *tmpPath = std::getenv("TMP");
  63067. if (!tmpPath) { tmpPath = std::getenv("TEMP"); winformat_string(tmpPath); }
  63068. if (tmpPath) _cimg_test_temporary_path(tmpPath);
  63069. #if cimg_OS==2
  63070. _cimg_test_temporary_path("C:\\WINNT\\Temp");
  63071. _cimg_test_temporary_path("C:\\WINDOWS\\Temp");
  63072. _cimg_test_temporary_path("C:\\Temp");
  63073. _cimg_test_temporary_path("C:");
  63074. _cimg_test_temporary_path("D:\\WINNT\\Temp");
  63075. _cimg_test_temporary_path("D:\\WINDOWS\\Temp");
  63076. _cimg_test_temporary_path("D:\\Temp");
  63077. _cimg_test_temporary_path("D:");
  63078. #else
  63079. _cimg_test_temporary_path("/tmp");
  63080. _cimg_test_temporary_path("/var/tmp");
  63081. #endif
  63082. if (!path_found) {
  63083. *s_path = 0;
  63084. std::strncpy(tmp,filename_tmp,tmp._width - 1);
  63085. if ((file=cimg::std_fopen(tmp,"wb"))!=0) { cimg::fclose(file); std::remove(tmp); path_found = true; }
  63086. }
  63087. if (!path_found) {
  63088. cimg::mutex(7,0);
  63089. throw CImgIOException("cimg::temporary_path(): Failed to locate path for writing temporary files.\n");
  63090. }
  63091. }
  63092. cimg::mutex(7,0);
  63093. return s_path;
  63094. }
  63095. //! Get/set path to the \c wget binary.
  63096. /**
  63097. \param user_path Specified path, or \c 0 to get the path currently used.
  63098. \param reinit_path Force path to be recalculated (may take some time).
  63099. \return Path containing the \c wget binary.
  63100. **/
  63101. inline const char *wget_path(const char *const user_path, const bool reinit_path) {
  63102. static CImg<char> s_path;
  63103. cimg::mutex(7);
  63104. if (reinit_path) s_path.assign();
  63105. if (user_path) {
  63106. if (!s_path) s_path.assign(1024);
  63107. std::strncpy(s_path,user_path,1023);
  63108. } else if (!s_path) {
  63109. s_path.assign(1024);
  63110. bool path_found = false;
  63111. std::FILE *file = 0;
  63112. #if cimg_OS==2
  63113. if (win_searchpath("wget.exe",s_path,s_path._width)) path_found = true;
  63114. if (!path_found) {
  63115. std::strcpy(s_path,".\\wget.exe");
  63116. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  63117. }
  63118. if (!path_found) std::strcpy(s_path,"wget.exe");
  63119. #else
  63120. if (!path_found) {
  63121. std::strcpy(s_path,"./wget");
  63122. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  63123. }
  63124. if (!path_found) std::strcpy(s_path,"wget");
  63125. #endif
  63126. winformat_string(s_path);
  63127. }
  63128. cimg::mutex(7,0);
  63129. return s_path;
  63130. }
  63131. #if cimg_OS==2
  63132. //! Get/set path to the \c powershell binary.
  63133. /**
  63134. \param user_path Specified path, or \c 0 to get the path currently used.
  63135. \param reinit_path Force path to be recalculated (may take some time).
  63136. \return Path containing the \c wget binary.
  63137. **/
  63138. inline const char *powershell_path(const char *const user_path, const bool reinit_path) {
  63139. static CImg<char> s_path;
  63140. cimg::mutex(7);
  63141. if (reinit_path) s_path.assign();
  63142. if (user_path) {
  63143. if (!s_path) s_path.assign(1024);
  63144. std::strncpy(s_path,user_path,1023);
  63145. } else if (!s_path) {
  63146. s_path.assign(1024);
  63147. bool path_found = false;
  63148. std::FILE *file = 0;
  63149. if (win_searchpath("powershell.exe",s_path,s_path._width)) path_found = true;
  63150. if (!path_found) {
  63151. std::strcpy(s_path,".\\powershell.exe");
  63152. if ((file=cimg::std_fopen(s_path,"r"))!=0) { cimg::fclose(file); path_found = true; }
  63153. }
  63154. if (!path_found) std::strcpy(s_path,"powershell.exe");
  63155. winformat_string(s_path);
  63156. }
  63157. cimg::mutex(7,0);
  63158. return s_path;
  63159. }
  63160. #endif
  63161. // [internal] Sorting function, used by cimg::files().
  63162. inline int _sort_files(const void* a, const void* b) {
  63163. const CImg<char> &sa = *(CImg<char>*)a, &sb = *(CImg<char>*)b;
  63164. return std::strcmp(sa._data,sb._data);
  63165. }
  63166. //! Generate a numbered version of a filename.
  63167. inline char* number_filename(const char *const filename, const int number,
  63168. const unsigned int digits, char *const str) {
  63169. if (!filename) { if (str) *str = 0; return 0; }
  63170. const unsigned int siz = (unsigned int)std::strlen(filename);
  63171. CImg<char> format(16), body(siz + 32);
  63172. const char *const ext = cimg::split_filename(filename,body);
  63173. if (*ext) cimg_snprintf(format,format._width,"%%s_%%.%ud.%%s",digits);
  63174. else cimg_snprintf(format,format._width,"%%s_%%.%ud",digits);
  63175. cimg_snprintf(str,1024,format._data,body._data,number,ext);
  63176. return str;
  63177. }
  63178. //! Return list of files/directories in specified directory.
  63179. /**
  63180. \param path Path to the directory. Set to 0 for current directory.
  63181. \param is_pattern Tell if specified path has a matching pattern in it.
  63182. \param mode Output type, can be primary { 0=files only | 1=folders only | 2=files + folders }.
  63183. \param include_path Tell if \c path must be included in resulting filenames.
  63184. \return A list of filenames.
  63185. **/
  63186. inline CImgList<char> files(const char *const path, const bool is_pattern=false,
  63187. const unsigned int mode=2, const bool include_path=false) {
  63188. if (!path || !*path) return files("*",true,mode,include_path);
  63189. CImgList<char> res;
  63190. // If path is a valid folder name, ignore argument 'is_pattern'.
  63191. const bool _is_pattern = is_pattern && !cimg::is_directory(path);
  63192. bool is_root = false, is_current = false;
  63193. cimg::unused(is_root,is_current);
  63194. // Clean format of input path.
  63195. CImg<char> pattern, _path = CImg<char>::string(path);
  63196. #if cimg_OS==2
  63197. for (char *ps = _path; *ps; ++ps) if (*ps=='\\') *ps='/';
  63198. #endif
  63199. char *pd = _path;
  63200. for (char *ps = pd; *ps; ++ps) { if (*ps!='/' || *ps!=*(ps+1)) *(pd++) = *ps; }
  63201. *pd = 0;
  63202. unsigned int lp = (unsigned int)std::strlen(_path);
  63203. if (!_is_pattern && lp && _path[lp - 1]=='/') {
  63204. _path[lp - 1] = 0; --lp;
  63205. #if cimg_OS!=2
  63206. is_root = !*_path;
  63207. #endif
  63208. }
  63209. // Separate folder path and matching pattern.
  63210. if (_is_pattern) {
  63211. const unsigned int bpos = (unsigned int)(cimg::basename(_path,'/') - _path.data());
  63212. CImg<char>::string(_path).move_to(pattern);
  63213. if (bpos) {
  63214. _path[bpos - 1] = 0; // End 'path' at last slash
  63215. #if cimg_OS!=2
  63216. is_root = !*_path;
  63217. #endif
  63218. } else { // No path to folder specified, assuming current folder
  63219. is_current = true; *_path = 0;
  63220. }
  63221. lp = (unsigned int)std::strlen(_path);
  63222. }
  63223. // Windows version.
  63224. #if cimg_OS==2
  63225. if (!_is_pattern) {
  63226. pattern.assign(lp + 3);
  63227. std::memcpy(pattern,_path,lp);
  63228. pattern[lp] = '/'; pattern[lp + 1] = '*'; pattern[lp + 2] = 0;
  63229. }
  63230. WIN32_FIND_DATAA file_data;
  63231. const HANDLE dir = FindFirstFileA(pattern.data(),&file_data);
  63232. if (dir==INVALID_HANDLE_VALUE) return CImgList<char>::const_empty();
  63233. do {
  63234. const char *const filename = file_data.cFileName;
  63235. if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) {
  63236. const unsigned int lf = (unsigned int)std::strlen(filename);
  63237. const bool is_directory = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)!=0;
  63238. if ((!mode && !is_directory) || (mode==1 && is_directory) || mode>=2) {
  63239. if (include_path) {
  63240. CImg<char> full_filename((lp?lp+1:0) + lf + 1);
  63241. if (lp) { std::memcpy(full_filename,_path,lp); full_filename[lp] = '/'; }
  63242. std::memcpy(full_filename._data + (lp?lp + 1:0),filename,lf + 1);
  63243. full_filename.move_to(res);
  63244. } else CImg<char>(filename,lf + 1).move_to(res);
  63245. }
  63246. }
  63247. } while (FindNextFileA(dir,&file_data));
  63248. FindClose(dir);
  63249. // Unix version (posix).
  63250. #elif cimg_OS == 1
  63251. DIR *const dir = opendir(is_root?"/":is_current?".":_path.data());
  63252. if (!dir) return CImgList<char>::const_empty();
  63253. struct dirent *ent;
  63254. while ((ent=readdir(dir))!=0) {
  63255. const char *const filename = ent->d_name;
  63256. if (*filename!='.' || (filename[1] && (filename[1]!='.' || filename[2]))) {
  63257. const unsigned int lf = (unsigned int)std::strlen(filename);
  63258. CImg<char> full_filename(lp + lf + 2);
  63259. if (!is_current) {
  63260. full_filename.assign(lp + lf + 2);
  63261. if (lp) std::memcpy(full_filename,_path,lp);
  63262. full_filename[lp] = '/';
  63263. std::memcpy(full_filename._data + lp + 1,filename,lf + 1);
  63264. } else full_filename.assign(filename,lf + 1);
  63265. struct stat st;
  63266. if (stat(full_filename,&st)==-1) continue;
  63267. const bool is_directory = (st.st_mode & S_IFDIR)!=0;
  63268. if ((!mode && !is_directory) || (mode==1 && is_directory) || mode==2) {
  63269. if (include_path) {
  63270. if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0)))
  63271. full_filename.move_to(res);
  63272. } else {
  63273. if (!_is_pattern || (_is_pattern && !fnmatch(pattern,full_filename,0)))
  63274. CImg<char>(filename,lf + 1).move_to(res);
  63275. }
  63276. }
  63277. }
  63278. }
  63279. closedir(dir);
  63280. #endif
  63281. // Sort resulting list by lexicographic order.
  63282. if (res._width>=2) std::qsort(res._data,res._width,sizeof(CImg<char>),_sort_files);
  63283. return res;
  63284. }
  63285. //! Try to guess format from an image file.
  63286. /**
  63287. \param file Input file (can be \c 0 if \c filename is set).
  63288. \param filename Filename, as a C-string (can be \c 0 if \c file is set).
  63289. \return C-string containing the guessed file format, or \c 0 if nothing has been guessed.
  63290. **/
  63291. inline const char *ftype(std::FILE *const file, const char *const filename) {
  63292. if (!file && !filename)
  63293. throw CImgArgumentException("cimg::ftype(): Specified filename is (null).");
  63294. static const char
  63295. *const _bmp = "bmp",
  63296. *const _cr2 = "cr2",
  63297. *const _dcm = "dcm",
  63298. *const _gif = "gif",
  63299. *const _inr = "inr",
  63300. *const _jpg = "jpg",
  63301. *const _off = "off",
  63302. *const _pan = "pan",
  63303. *const _pfm = "pfm",
  63304. *const _png = "png",
  63305. *const _pnm = "pnm",
  63306. *const _tif = "tif";
  63307. const char *f_type = 0;
  63308. CImg<char> header;
  63309. const unsigned int omode = cimg::exception_mode();
  63310. cimg::exception_mode(0);
  63311. try {
  63312. header._load_raw(file,filename,512,1,1,1,false,false,0);
  63313. const unsigned char *const uheader = (unsigned char*)header._data;
  63314. if (!std::strncmp(header,"OFF\n",4)) f_type = _off; // OFF
  63315. else if (!std::strncmp(header,"#INRIMAGE",9)) // INRIMAGE
  63316. f_type = _inr;
  63317. else if (!std::strncmp(header,"PANDORE",7)) // PANDORE
  63318. f_type = _pan;
  63319. else if (!std::strncmp(header.data() + 128,"DICM",4)) // DICOM
  63320. f_type = _dcm;
  63321. else if (uheader[0]==0xFF && uheader[1]==0xD8 && uheader[2]==0xFF) // JPEG
  63322. f_type = _jpg;
  63323. else if (header[0]=='B' && header[1]=='M') // BMP
  63324. f_type = _bmp;
  63325. else if (header[0]=='G' && header[1]=='I' && header[2]=='F' && header[3]=='8' && header[5]=='a' &&
  63326. (header[4]=='7' || header[4]=='9')) // GIF
  63327. f_type = _gif;
  63328. else if (uheader[0]==0x89 && uheader[1]==0x50 && uheader[2]==0x4E && uheader[3]==0x47 &&
  63329. uheader[4]==0x0D && uheader[5]==0x0A && uheader[6]==0x1A && uheader[7]==0x0A) // PNG
  63330. f_type = _png;
  63331. else if (uheader[0]==0x49 && uheader[1]==0x49 && uheader[2]==0x2A && uheader[3]==0x00 && // CR2
  63332. uheader[4]==0x10 && uheader[5]==0x00 && uheader[6]==0x00 && uheader[7]==0x00 &&
  63333. uheader[8]==0x43 && uheader[9]==0x52)
  63334. f_type = _cr2;
  63335. else if ((uheader[0]==0x49 && uheader[1]==0x49 && uheader[2]==0x2A && uheader[3]==0x00) ||
  63336. (uheader[0]==0x4D && uheader[1]==0x4D && uheader[2]==0x00 && uheader[3]==0x2A)) // TIFF
  63337. f_type = _tif;
  63338. else { // PNM or PFM
  63339. CImgList<char> _header = header.get_split(CImg<char>::vector('\n'),0,false);
  63340. cimglist_for(_header,l) {
  63341. if (_header(l,0)=='#') continue;
  63342. if (_header[l]._width==2 && _header(l,0)=='P') {
  63343. const char c = _header(l,1);
  63344. if (c=='f' || c=='F') { f_type = _pfm; break; }
  63345. if (c>='1' && c<='9') { f_type = _pnm; break; }
  63346. }
  63347. f_type = 0; break;
  63348. }
  63349. }
  63350. } catch (CImgIOException&) { }
  63351. cimg::exception_mode(omode);
  63352. return f_type;
  63353. }
  63354. //! Load file from network as a local temporary file.
  63355. /**
  63356. \param url URL of the filename, as a C-string.
  63357. \param[out] filename_local C-string containing the path to a local copy of \c filename.
  63358. \param timeout Maximum time (in seconds) authorized for downloading the file from the URL.
  63359. \param try_fallback When using libcurl, tells using system calls as fallbacks in case of libcurl failure.
  63360. \param referer Referer used, as a C-string.
  63361. \param user_agent User agent used, as a C-string.
  63362. \return Value of \c filename_local.
  63363. \note Use the \c libcurl library, or the external binaries \c wget or \c curl to perform the download.
  63364. **/
  63365. inline char *load_network(const char *const url, char *const filename_local,
  63366. const unsigned int timeout, const bool try_fallback,
  63367. const char *const referer, const char *const user_agent) {
  63368. if (!url)
  63369. throw CImgArgumentException("cimg::load_network(): Specified URL is (null).");
  63370. if (!filename_local)
  63371. throw CImgArgumentException("cimg::load_network(): Specified destination string is (null).");
  63372. if (!network_mode())
  63373. throw CImgIOException("cimg::load_network(): Loading files from network is disabled.");
  63374. const char *const __ext = cimg::split_filename(url), *const _ext = (*__ext && __ext>url)?__ext - 1:__ext;
  63375. CImg<char> ext = CImg<char>::string(_ext);
  63376. std::FILE *file = 0;
  63377. *filename_local = 0;
  63378. if (ext._width>16 || !cimg::strncasecmp(ext,"cgi",3)) *ext = 0;
  63379. else cimg::strwindows_reserved(ext);
  63380. do {
  63381. cimg_snprintf(filename_local,256,"%s%c%s%s",
  63382. cimg::temporary_path(),cimg_file_separator,cimg::filenamerand(),ext._data);
  63383. if ((file = cimg::std_fopen(filename_local,"rb"))!=0) cimg::fclose(file);
  63384. } while (file);
  63385. #ifdef cimg_use_curl
  63386. const unsigned int omode = cimg::exception_mode();
  63387. cimg::exception_mode(0);
  63388. try {
  63389. CURL *curl = 0;
  63390. CURLcode res;
  63391. curl = curl_easy_init();
  63392. if (curl) {
  63393. file = cimg::fopen(filename_local,"wb");
  63394. curl_easy_setopt(curl,CURLOPT_URL,url);
  63395. curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,0);
  63396. curl_easy_setopt(curl,CURLOPT_WRITEDATA,file);
  63397. curl_easy_setopt(curl,CURLOPT_SSL_VERIFYPEER,0L);
  63398. curl_easy_setopt(curl,CURLOPT_SSL_VERIFYHOST,0L);
  63399. curl_easy_setopt(curl,CURLOPT_FOLLOWLOCATION,1L);
  63400. curl_easy_setopt(curl,CURLOPT_MAXREDIRS,20L);
  63401. if (timeout) curl_easy_setopt(curl,CURLOPT_TIMEOUT,(long)timeout);
  63402. if (std::strchr(url,'?')) curl_easy_setopt(curl,CURLOPT_HTTPGET,1L);
  63403. if (referer) curl_easy_setopt(curl,CURLOPT_REFERER,referer);
  63404. if (user_agent) curl_easy_setopt(curl,CURLOPT_USERAGENT,user_agent);
  63405. res = curl_easy_perform(curl);
  63406. curl_easy_cleanup(curl);
  63407. cimg::fseek(file,0,SEEK_END); // Check if file size is 0
  63408. const cimg_ulong siz = cimg::ftell(file);
  63409. cimg::fclose(file);
  63410. if (siz>0 && res==CURLE_OK) {
  63411. cimg::exception_mode(omode);
  63412. return filename_local;
  63413. } else std::remove(filename_local);
  63414. }
  63415. } catch (...) { }
  63416. cimg::exception_mode(omode);
  63417. if (!try_fallback) throw CImgIOException("cimg::load_network(): Failed to load file '%s' with libcurl.",url);
  63418. #endif
  63419. CImg<char> command((unsigned int)std::strlen(url) + 1024), s_referer, s_user_agent, s_timeout;
  63420. cimg::unused(try_fallback);
  63421. // Try with 'curl' first.
  63422. if (timeout) cimg_snprintf(s_timeout.assign(64),64,"-m %u ",timeout);
  63423. else s_timeout.assign(1,1,1,1,0);
  63424. if (referer) cimg_snprintf(s_referer.assign(1024),1024,"-e %s ",referer);
  63425. else s_referer.assign(1,1,1,1,0);
  63426. if (user_agent) cimg_snprintf(s_user_agent.assign(1024),1024,"-A \"%s\" ",user_agent);
  63427. else s_user_agent.assign(1,1,1,1,0);
  63428. cimg_snprintf(command,command._width,
  63429. "\"%s\" -L --max-redirs 20 %s%s%s-f --silent --compressed -o \"%s\" \"%s\"",
  63430. cimg::curl_path(),s_timeout._data,s_referer._data,s_user_agent._data,filename_local,
  63431. CImg<char>::string(url)._system_strescape().data());
  63432. cimg::system(command,cimg::curl_path());
  63433. #if cimg_OS==2
  63434. if (cimg::fsize(filename_local)<=0) { // Try with 'powershell' otherwise.
  63435. if (timeout) cimg_snprintf(s_timeout.assign(64),64,"-TimeoutSec %u ",timeout);
  63436. else s_timeout.assign(1,1,1,1,0);
  63437. if (referer) cimg_snprintf(s_referer.assign(1024),1024,"-Headers @{'Referer'='%s'} ",referer);
  63438. else s_referer.assign(1,1,1,1,0);
  63439. if (user_agent) cimg_snprintf(s_user_agent.assign(1024),1024,"-UserAgent \"%s\" ",user_agent);
  63440. else s_user_agent.assign(1,1,1,1,0);
  63441. cimg_snprintf(command,command._width,
  63442. "\"%s\" -NonInteractive -Command Invoke-WebRequest %s%s%s-OutFile \"%s\" -Uri \"%s\"",
  63443. cimg::powershell_path(),s_timeout._data,s_referer._data,s_user_agent._data,filename_local,
  63444. CImg<char>::string(url)._system_strescape().data());
  63445. cimg::system(command,cimg::powershell_path());
  63446. }
  63447. #endif
  63448. if (cimg::fsize(filename_local)<=0) { // Try with 'wget' otherwise.
  63449. if (timeout) cimg_snprintf(s_timeout.assign(64),64,"-T %u ",timeout);
  63450. else s_timeout.assign(1,1,1,1,0);
  63451. if (referer) cimg_snprintf(s_referer.assign(1024),1024,"--referer=%s ",referer);
  63452. else s_referer.assign(1,1,1,1,0);
  63453. if (user_agent) cimg_snprintf(s_user_agent.assign(1024),1024,"--user-agent=\"%s\" ",user_agent);
  63454. else s_user_agent.assign(1,1,1,1,0);
  63455. cimg_snprintf(command,command._width,
  63456. "\"%s\" --max-redirect=20 %s%s%s-q -r -l 0 --no-cache -O \"%s\" \"%s\"",
  63457. cimg::wget_path(),s_timeout._data,s_referer._data,s_user_agent._data,filename_local,
  63458. CImg<char>::string(url)._system_strescape().data());
  63459. cimg::system(command,cimg::wget_path());
  63460. if (cimg::fsize(filename_local)<=0)
  63461. throw CImgIOException("cimg::load_network(): Failed to load file '%s' with external commands "
  63462. #if cimg_OS==2
  63463. "'wget', 'curl', or 'powershell'.",url);
  63464. #else
  63465. "'wget' or 'curl'.",url);
  63466. #endif
  63467. cimg::fclose(file);
  63468. // Try gunzip it.
  63469. cimg_snprintf(command,command._width,"%s.gz",filename_local);
  63470. std::rename(filename_local,command);
  63471. cimg_snprintf(command,command._width,"\"%s\" --quiet \"%s.gz\"",
  63472. gunzip_path(),filename_local);
  63473. cimg::system(command,gunzip_path());
  63474. file = cimg::std_fopen(filename_local,"rb");
  63475. if (!file) {
  63476. cimg_snprintf(command,command._width,"%s.gz",filename_local);
  63477. std::rename(command,filename_local);
  63478. file = cimg::std_fopen(filename_local,"rb");
  63479. }
  63480. }
  63481. if (file) cimg::fclose(file);
  63482. return filename_local;
  63483. }
  63484. // Implement a tic/toc mechanism to display elapsed time of algorithms.
  63485. inline cimg_uint64 tictoc(const bool is_tic) {
  63486. cimg::mutex(2);
  63487. static CImg<cimg_uint64> times(64);
  63488. static unsigned int pos = 0;
  63489. const cimg_uint64 t1 = cimg::time();
  63490. if (is_tic) {
  63491. // Tic
  63492. times[pos++] = t1;
  63493. if (pos>=times._width)
  63494. throw CImgArgumentException("cimg::tic(): Too much calls to 'cimg::tic()' without calls to 'cimg::toc()'.");
  63495. cimg::mutex(2,0);
  63496. return t1;
  63497. }
  63498. // Toc
  63499. if (!pos)
  63500. throw CImgArgumentException("cimg::toc(): No previous call to 'cimg::tic()' has been made.");
  63501. const cimg_uint64
  63502. t0 = times[--pos],
  63503. dt = t1>=t0?(t1 - t0):cimg::type<cimg_uint64>::max();
  63504. const unsigned int
  63505. edays = (unsigned int)(dt/86400000.),
  63506. ehours = (unsigned int)((dt - edays*86400000.)/3600000.),
  63507. emin = (unsigned int)((dt - edays*86400000. - ehours*3600000.)/60000.),
  63508. esec = (unsigned int)((dt - edays*86400000. - ehours*3600000. - emin*60000.)/1000.),
  63509. ems = (unsigned int)(dt - edays*86400000. - ehours*3600000. - emin*60000. - esec*1000.);
  63510. if (!edays && !ehours && !emin && !esec)
  63511. std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u ms%s\n",
  63512. cimg::t_red,1 + 2*pos,"",ems,cimg::t_normal);
  63513. else {
  63514. if (!edays && !ehours && !emin)
  63515. std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u sec %u ms%s\n",
  63516. cimg::t_red,1 + 2*pos,"",esec,ems,cimg::t_normal);
  63517. else {
  63518. if (!edays && !ehours)
  63519. std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u min %u sec %u ms%s\n",
  63520. cimg::t_red,1 + 2*pos,"",emin,esec,ems,cimg::t_normal);
  63521. else{
  63522. if (!edays)
  63523. std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u hours %u min %u sec %u ms%s\n",
  63524. cimg::t_red,1 + 2*pos,"",ehours,emin,esec,ems,cimg::t_normal);
  63525. else{
  63526. std::fprintf(cimg::output(),"%s[CImg]%*sElapsed time: %u days %u hours %u min %u sec %u ms%s\n",
  63527. cimg::t_red,1 + 2*pos,"",edays,ehours,emin,esec,ems,cimg::t_normal);
  63528. }
  63529. }
  63530. }
  63531. }
  63532. cimg::mutex(2,0);
  63533. return dt;
  63534. }
  63535. // Return a temporary string describing the size of a memory buffer.
  63536. inline const char *strbuffersize(const cimg_ulong size) {
  63537. static CImg<char> res(256);
  63538. cimg::mutex(5);
  63539. if (size<1024LU) cimg_snprintf(res,res._width,"%lu byte%s",(unsigned long)size,size>1?"s":"");
  63540. else if (size<1024*1024LU) { const float nsize = size/1024.f; cimg_snprintf(res,res._width,"%.1f Kio",nsize); }
  63541. else if (size<1024*1024*1024LU) {
  63542. const float nsize = size/(1024*1024.f); cimg_snprintf(res,res._width,"%.1f Mio",nsize);
  63543. } else { const float nsize = size/(1024*1024*1024.f); cimg_snprintf(res,res._width,"%.1f Gio",nsize); }
  63544. cimg::mutex(5,0);
  63545. return res;
  63546. }
  63547. //! Display a simple dialog box, and wait for the user's response.
  63548. /**
  63549. \param title Title of the dialog window.
  63550. \param msg Main message displayed inside the dialog window.
  63551. \param button1_label Label of the 1st button.
  63552. \param button2_label Label of the 2nd button (\c 0 to hide button).
  63553. \param button3_label Label of the 3rd button (\c 0 to hide button).
  63554. \param button4_label Label of the 4th button (\c 0 to hide button).
  63555. \param button5_label Label of the 5th button (\c 0 to hide button).
  63556. \param button6_label Label of the 6th button (\c 0 to hide button).
  63557. \param logo Image logo displayed at the left of the main message.
  63558. \param is_centered Tells if the dialog window must be centered on the screen.
  63559. \return Index of clicked button (from \c 0 to \c 5), or \c -1 if the dialog window has been closed by the user.
  63560. \note
  63561. - Up to 6 buttons can be defined in the dialog window.
  63562. - The function returns when a user clicked one of the button or closed the dialog window.
  63563. - If a button text is set to 0, the corresponding button (and the following) will not appear in the dialog box.
  63564. At least one button must be specified.
  63565. **/
  63566. template<typename t>
  63567. inline int dialog(const char *const title, const char *const msg,
  63568. const char *const button1_label, const char *const button2_label,
  63569. const char *const button3_label, const char *const button4_label,
  63570. const char *const button5_label, const char *const button6_label,
  63571. const CImg<t>& logo, const bool is_centered=false) {
  63572. #if cimg_display==0
  63573. cimg::unused(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,
  63574. logo._data,is_centered);
  63575. throw CImgIOException("cimg::dialog(): No display available.");
  63576. #else
  63577. static const unsigned char
  63578. black[] = { 0,0,0 }, white[] = { 255,255,255 }, gray[] = { 200,200,200 }, gray2[] = { 150,150,150 };
  63579. // Create buttons and canvas graphics
  63580. CImgList<unsigned char> buttons, cbuttons, sbuttons;
  63581. if (button1_label) {
  63582. CImg<unsigned char>().draw_text(0,0,button1_label,black,gray,1,13).move_to(buttons);
  63583. if (button2_label) {
  63584. CImg<unsigned char>().draw_text(0,0,button2_label,black,gray,1,13).move_to(buttons);
  63585. if (button3_label) {
  63586. CImg<unsigned char>().draw_text(0,0,button3_label,black,gray,1,13).move_to(buttons);
  63587. if (button4_label) {
  63588. CImg<unsigned char>().draw_text(0,0,button4_label,black,gray,1,13).move_to(buttons);
  63589. if (button5_label) {
  63590. CImg<unsigned char>().draw_text(0,0,button5_label,black,gray,1,13).move_to(buttons);
  63591. if (button6_label) {
  63592. CImg<unsigned char>().draw_text(0,0,button6_label,black,gray,1,13).move_to(buttons);
  63593. }}}}}}
  63594. if (!buttons._width)
  63595. throw CImgArgumentException("cimg::dialog(): No buttons have been defined.");
  63596. cimglist_for(buttons,l) buttons[l].resize(-100,-100,1,3);
  63597. unsigned int bw = 0, bh = 0;
  63598. cimglist_for(buttons,l) { bw = std::max(bw,buttons[l]._width); bh = std::max(bh,buttons[l]._height); }
  63599. bw+=8; bh+=8;
  63600. if (bw<64) bw = 64;
  63601. if (bw>128) bw = 128;
  63602. if (bh<24) bh = 24;
  63603. if (bh>48) bh = 48;
  63604. CImg<unsigned char> button(bw,bh,1,3);
  63605. button.draw_rectangle(0,0,bw - 1,bh - 1,gray);
  63606. button.draw_line(0,0,bw - 1,0,white).draw_line(0,bh - 1,0,0,white);
  63607. button.draw_line(bw - 1,0,bw - 1,bh - 1,black).draw_line(bw - 1,bh - 1,0,bh - 1,black);
  63608. button.draw_line(1,bh - 2,bw - 2,bh - 2,gray2).draw_line(bw - 2,bh - 2,bw - 2,1,gray2);
  63609. CImg<unsigned char> sbutton(bw,bh,1,3);
  63610. sbutton.draw_rectangle(0,0,bw - 1,bh - 1,gray);
  63611. sbutton.draw_line(0,0,bw - 1,0,black).draw_line(bw - 1,0,bw - 1,bh - 1,black);
  63612. sbutton.draw_line(bw - 1,bh - 1,0,bh - 1,black).draw_line(0,bh - 1,0,0,black);
  63613. sbutton.draw_line(1,1,bw - 2,1,white).draw_line(1,bh - 2,1,1,white);
  63614. sbutton.draw_line(bw - 2,1,bw - 2,bh - 2,black).draw_line(bw - 2,bh - 2,1,bh - 2,black);
  63615. sbutton.draw_line(2,bh - 3,bw - 3,bh - 3,gray2).draw_line(bw - 3,bh - 3,bw - 3,2,gray2);
  63616. sbutton.draw_line(4,4,bw - 5,4,black,1,0xAAAAAAAA,true).
  63617. draw_line(bw - 5,4,bw - 5,bh - 5,black,1,0xAAAAAAAA,false);
  63618. sbutton.draw_line(bw - 5,bh - 5,4,bh - 5,black,1,0xAAAAAAAA,false).
  63619. draw_line(4,bh - 5,4,4,black,1,0xAAAAAAAA,false);
  63620. CImg<unsigned char> cbutton(bw,bh,1,3);
  63621. cbutton.draw_rectangle(0,0,bw - 1,bh - 1,black).draw_rectangle(1,1,bw - 2,bh - 2,gray2).
  63622. draw_rectangle(2,2,bw - 3,bh - 3,gray);
  63623. cbutton.draw_line(4,4,bw - 5,4,black,1,0xAAAAAAAA,true).
  63624. draw_line(bw - 5,4,bw - 5,bh - 5,black,1,0xAAAAAAAA,false);
  63625. cbutton.draw_line(bw - 5,bh - 5,4,bh - 5,black,1,0xAAAAAAAA,false).
  63626. draw_line(4,bh - 5,4,4,black,1,0xAAAAAAAA,false);
  63627. cimglist_for(buttons,ll) {
  63628. CImg<unsigned char>(cbutton).
  63629. draw_image(1 + (bw -buttons[ll].width())/2,1 + (bh - buttons[ll].height())/2,buttons[ll]).
  63630. move_to(cbuttons);
  63631. CImg<unsigned char>(sbutton).
  63632. draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]).
  63633. move_to(sbuttons);
  63634. CImg<unsigned char>(button).
  63635. draw_image((bw - buttons[ll].width())/2,(bh - buttons[ll].height())/2,buttons[ll]).
  63636. move_to(buttons[ll]);
  63637. }
  63638. CImg<unsigned char> canvas;
  63639. if (msg)
  63640. ((CImg<unsigned char>().draw_text(0,0,"%s",gray,0,1,13,msg)*=-1)+=200).resize(-100,-100,1,3).move_to(canvas);
  63641. const unsigned int
  63642. bwall = (buttons._width - 1)*(12 + bw) + bw,
  63643. w = cimg::max(196U,36 + logo._width + canvas._width,24 + bwall),
  63644. h = cimg::max(96U,36 + canvas._height + bh,36 + logo._height + bh),
  63645. lx = 12 + (canvas._data?0:((w - 24 - logo._width)/2)),
  63646. ly = (h - 12 - bh - logo._height)/2,
  63647. tx = lx + logo._width + 12,
  63648. ty = (h - 12 - bh - canvas._height)/2,
  63649. bx = (w - bwall)/2,
  63650. by = h - 12 - bh;
  63651. if (canvas._data)
  63652. canvas = CImg<unsigned char>(w,h,1,3).
  63653. draw_rectangle(0,0,w - 1,h - 1,gray).
  63654. draw_line(0,0,w - 1,0,white).draw_line(0,h - 1,0,0,white).
  63655. draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black).
  63656. draw_image(tx,ty,canvas);
  63657. else
  63658. canvas = CImg<unsigned char>(w,h,1,3).
  63659. draw_rectangle(0,0,w - 1,h - 1,gray).
  63660. draw_line(0,0,w - 1,0,white).draw_line(0,h - 1,0,0,white).
  63661. draw_line(w - 1,0,w - 1,h - 1,black).draw_line(w - 1,h - 1,0,h - 1,black);
  63662. if (logo._data) canvas.draw_image(lx,ly,logo);
  63663. unsigned int xbuttons[6] = {};
  63664. cimglist_for(buttons,lll) {
  63665. xbuttons[lll] = bx + (bw + 12)*lll;
  63666. canvas.draw_image(xbuttons[lll],by,buttons[lll]);
  63667. }
  63668. // Open window and enter events loop
  63669. CImgDisplay disp(canvas,title?title:" ",0,false,is_centered?true:false);
  63670. if (is_centered) disp.move((CImgDisplay::screen_width() - disp.width())/2,
  63671. (CImgDisplay::screen_height() - disp.height())/2);
  63672. bool stop_flag = false, refresh = false;
  63673. int oselected = -1, oclicked = -1, selected = -1, clicked = -1;
  63674. while (!disp.is_closed() && !stop_flag) {
  63675. if (refresh) {
  63676. if (clicked>=0)
  63677. CImg<unsigned char>(canvas).draw_image(xbuttons[clicked],by,cbuttons[clicked]).display(disp);
  63678. else {
  63679. if (selected>=0)
  63680. CImg<unsigned char>(canvas).draw_image(xbuttons[selected],by,sbuttons[selected]).display(disp);
  63681. else canvas.display(disp);
  63682. }
  63683. refresh = false;
  63684. }
  63685. disp.wait(15);
  63686. if (disp.is_resized()) disp.resize(disp,false);
  63687. if (disp.button()&1) {
  63688. oclicked = clicked;
  63689. clicked = -1;
  63690. cimglist_for(buttons,l)
  63691. if (disp.mouse_y()>=(int)by && disp.mouse_y()<(int)(by + bh) &&
  63692. disp.mouse_x()>=(int)xbuttons[l] && disp.mouse_x()<(int)(xbuttons[l] + bw)) {
  63693. clicked = selected = l;
  63694. refresh = true;
  63695. }
  63696. if (clicked!=oclicked) refresh = true;
  63697. } else if (clicked>=0) stop_flag = true;
  63698. if (disp.key()) {
  63699. oselected = selected;
  63700. switch (disp.key()) {
  63701. case cimg::keyESC : selected = -1; stop_flag = true; break;
  63702. case cimg::keyENTER : if (selected<0) selected = 0; stop_flag = true; break;
  63703. case cimg::keyTAB :
  63704. case cimg::keyARROWRIGHT :
  63705. case cimg::keyARROWDOWN : selected = (selected + 1)%buttons.width(); break;
  63706. case cimg::keyARROWLEFT :
  63707. case cimg::keyARROWUP : selected = (selected + buttons.width() - 1)%buttons.width(); break;
  63708. }
  63709. disp.set_key();
  63710. if (selected!=oselected) refresh = true;
  63711. }
  63712. }
  63713. if (!disp) selected = -1;
  63714. return selected;
  63715. #endif
  63716. }
  63717. //! Display a simple dialog box, and wait for the user's response \specialization.
  63718. inline int dialog(const char *const title, const char *const msg,
  63719. const char *const button1_label, const char *const button2_label,
  63720. const char *const button3_label, const char *const button4_label,
  63721. const char *const button5_label, const char *const button6_label,
  63722. const bool is_centered) {
  63723. return dialog(title,msg,button1_label,button2_label,button3_label,button4_label,button5_label,button6_label,
  63724. CImg<unsigned char>::_logo40x38(),is_centered);
  63725. }
  63726. //! Evaluate math expression.
  63727. /**
  63728. \param expression C-string describing the formula to evaluate.
  63729. \param x Value of the pre-defined variable \c x.
  63730. \param y Value of the pre-defined variable \c y.
  63731. \param z Value of the pre-defined variable \c z.
  63732. \param c Value of the pre-defined variable \c c.
  63733. \return Result of the formula evaluation.
  63734. \note Set \c expression to \c 0 to keep evaluating the last specified \c expression.
  63735. \par Example
  63736. \code
  63737. const double
  63738. res1 = cimg::eval("cos(x)^2 + sin(y)^2",2,2), // will return '1'
  63739. res2 = cimg::eval(0,1,1); // will return '1' too
  63740. \endcode
  63741. **/
  63742. inline double eval(const char *const expression, const double x, const double y, const double z, const double c) {
  63743. static const CImg<float> empty;
  63744. return empty.eval(expression,x,y,z,c);
  63745. }
  63746. template<typename t>
  63747. inline CImg<typename cimg::superset<double,t>::type> eval(const char *const expression, const CImg<t>& xyzc) {
  63748. static const CImg<float> empty;
  63749. return empty.eval(expression,xyzc);
  63750. }
  63751. } // namespace cimg { ...
  63752. } // namespace cimg_library { ...
  63753. //! Short alias name.
  63754. namespace cil = cimg_library;
  63755. #ifdef _cimg_redefine_False
  63756. #define False 0
  63757. #endif
  63758. #ifdef _cimg_redefine_True
  63759. #define True 1
  63760. #endif
  63761. #ifdef _cimg_redefine_Status
  63762. #define Status int
  63763. #endif
  63764. #ifdef _cimg_redefine_Success
  63765. #define Success 0
  63766. #endif
  63767. #ifdef _cimg_redefine_min
  63768. #define min(a,b) (((a)<(b))?(a):(b))
  63769. #endif
  63770. #ifdef _cimg_redefine_max
  63771. #define max(a,b) (((a)>(b))?(a):(b))
  63772. #endif
  63773. #ifdef _cimg_redefine_PI
  63774. #define PI 3.141592653589793238462643383
  63775. #endif
  63776. #ifdef _MSC_VER
  63777. #pragma warning(pop)
  63778. #endif
  63779. #endif
  63780. // Local Variables:
  63781. // mode: c++
  63782. // End: